/*! @license DOMPurify 3.4.17 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.17/LICENSE */ function _OverloadYield(e, d) { this.v = e, this.k = d; } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function AsyncGenerator(e) { var t, n; function resume(t, n) { try { var r = e[t](n), o = r.value, u = o instanceof _OverloadYield; Promise.resolve(u ? o.v : o).then(function(n) { if (u) { var i = "return" === t && o.k ? t : "next"; if (!o.k || n.done) return resume(i, n); n = e[i](n).value; } settle(!!r.done, n); }, function(e) { resume("throw", e); }); } catch (e) { settle(2, e); } } function settle(e, r) { 2 === e ? t.reject(r) : t.resolve({ value: r, done: e }), (t = t.next) ? resume(t.key, t.arg) : n = null; } this._invoke = function(e, r) { return new Promise(function(o, u) { var i = { key: e, arg: r, resolve: o, reject: u, next: null }; n ? n = n.next = i : (t = n = i, resume(e, r)); }); }, "function" != typeof e.return && (this.return = void 0); } AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function() { return this; }, AsyncGenerator.prototype.next = function(e) { return this._invoke("next", e); }, AsyncGenerator.prototype.throw = function(e) { return this._invoke("throw", e); }, AsyncGenerator.prototype.return = function(e) { return this._invoke("return", e); }; const entries = Object.entries; const setPrototypeOf = Object.setPrototypeOf; const isFrozen = Object.isFrozen; const getPrototypeOf = Object.getPrototypeOf; const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; let freeze = Object.freeze; let seal = Object.seal; let create = Object.create; let _ref = typeof Reflect !== "undefined" && Reflect; let apply = _ref.apply; let construct = _ref.construct; if (!freeze) freeze = function freeze(x) { return x; }; if (!seal) seal = function seal(x) { return x; }; if (!apply) apply = function apply(func, thisArg) { for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) args[_key - 2] = arguments[_key]; return func.apply(thisArg, args); }; if (!construct) construct = function construct(Func) { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) args[_key2 - 1] = arguments[_key2]; return new Func(...args); }; const arrayForEach = unapply(Array.prototype.forEach); Array.prototype.indexOf; const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf); const arrayPop = unapply(Array.prototype.pop); const arrayPush = unapply(Array.prototype.push); const arraySlice = unapply(Array.prototype.slice); const arraySplice = unapply(Array.prototype.splice); const arrayIsArray = Array.isArray; const setCreate = unconstruct(Set); const setAdd = unapply(Set.prototype.add); const setHas = unapply(Set.prototype.has); const stringToLowerCase = unapply(String.prototype.toLowerCase); const stringToString = unapply(String.prototype.toString); const stringMatch = unapply(String.prototype.match); const stringReplace = unapply(String.prototype.replace); const stringIndexOf = unapply(String.prototype.indexOf); const stringTrim = unapply(String.prototype.trim); const numberToString = unapply(Number.prototype.toString); const booleanToString = unapply(Boolean.prototype.toString); const bigintToString = typeof BigInt === "undefined" ? null : unapply(BigInt.prototype.toString); const symbolToString = typeof Symbol === "undefined" ? null : unapply(Symbol.prototype.toString); const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty); const objectToString = unapply(Object.prototype.toString); const regExpTest = unapply(RegExp.prototype.test); const typeErrorCreate = unconstruct(TypeError); /** * Creates a new function that calls the given function with a specified thisArg and arguments. * * @param func - The function to be wrapped and called. * @returns A new function that calls the given function with a specified thisArg and arguments. */ function unapply(func) { return function(thisArg) { if (thisArg instanceof RegExp) thisArg.lastIndex = 0; for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) args[_key3 - 1] = arguments[_key3]; return apply(func, thisArg, args); }; } /** * Creates a new function that constructs an instance of the given constructor function with the provided arguments. * * @param func - The constructor function to be wrapped and called. * @returns A new function that constructs an instance of the given constructor function with the provided arguments. */ function unconstruct(Func) { return function() { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) args[_key4] = arguments[_key4]; return construct(Func, args); }; } /** * Add properties to a lookup table * * @param set - The set to which elements will be added. * @param array - The array containing elements to be added to the set. * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set. * @returns The modified set with added elements. */ function addToSet(set, array) { let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase; if (setPrototypeOf) setPrototypeOf(set, null); if (!arrayIsArray(array)) return set; let l = array.length; while (l--) { let element = array[l]; if (typeof element === "string") { const lcElement = transformCaseFunc(element); if (lcElement !== element) { if (!isFrozen(array)) array[l] = lcElement; element = lcElement; } } set[element] = true; } return set; } /** * Clean up an array to harden against CSPP * * @param array - The array to be cleaned. * @returns The cleaned version of the array */ function cleanArray(array) { for (let index = 0; index < array.length; index++) if (!objectHasOwnProperty(array, index)) array[index] = null; return array; } /** * Shallow clone an object * * @param object - The object to be cloned. * @returns A new object that copies the original. */ function clone(object) { const newObject = create(null); for (const _ref2 of entries(object)) { var _ref3 = _slicedToArray(_ref2, 2); const property = _ref3[0]; const value = _ref3[1]; if (objectHasOwnProperty(object, property)) { if (arrayIsArray(value)) newObject[property] = cleanArray(value); else if (value && typeof value === "object" && value.constructor === Object) newObject[property] = clone(value); else newObject[property] = value; } } return newObject; } /** * Convert non-node values into strings without depending on direct property access. * * @param value - The value to stringify. * @returns A string representation of the provided value. */ function stringifyValue(value) { switch (typeof value) { case "string": return value; case "number": return numberToString(value); case "boolean": return booleanToString(value); case "bigint": return bigintToString ? bigintToString(value) : "0"; case "symbol": return symbolToString ? symbolToString(value) : "Symbol()"; case "undefined": return objectToString(value); case "function": case "object": { if (value === null) return objectToString(value); const valueAsRecord = value; const valueToString = lookupGetter(valueAsRecord, "toString"); if (typeof valueToString === "function") { const stringified = valueToString(valueAsRecord); return typeof stringified === "string" ? stringified : objectToString(stringified); } return objectToString(value); } default: return objectToString(value); } } /** * This method automatically checks if the prop is function or getter and behaves accordingly. * * @param object - The object to look up the getter function in its prototype chain. * @param prop - The property name for which to find the getter function. * @returns The getter function found in the prototype chain or a fallback function. */ function lookupGetter(object, prop) { while (object !== null) { const desc = getOwnPropertyDescriptor(object, prop); if (desc) { if (desc.get) return unapply(desc.get); if (typeof desc.value === "function") return unapply(desc.value); } object = getPrototypeOf(object); } function fallbackValue() { return null; } return fallbackValue; } /** * Like lookupGetter, but for accessor properties only, and returns null * instead of a null-returning fallback when no getter exists on the chain. * Lets a caller fall back to a direct property read in environments that * lack the getter, where a fallback returning null would be indistinguishable * from a legitimate null value (e.g. namespaceURI of a no-namespace element). * * @param object - The object (usually a prototype) to start the lookup at. * @param prop - The accessor property name. * @returns The unapplied getter, or null if none was found. */ function lookupAccessor(object, prop) { while (object !== null) { const desc = getOwnPropertyDescriptor(object, prop); if (desc) return desc.get ? unapply(desc.get) : null; object = getPrototypeOf(object); } return null; } function isRegex(value) { try { regExpTest(value, ""); return true; } catch (_unused) { return false; } } const html$1 = freeze([ "a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr" ]); const svg$1 = freeze([ "svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern" ]); 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" ]); 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" ]); 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" ]); const mathMlDisallowed = freeze([ "maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none" ]); const text = freeze(["#text"]); const html = freeze([ "accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns" ]); const svg = freeze([ "accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dominant-baseline", "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", "pointer-events", "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-orientation", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "vector-effect", "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" ]); const mathMl = freeze([ "accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns" ]); const xml = freeze([ "xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink" ]); const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g); const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g); const TMPLIT_EXPR = seal(/\${[\w\W]*/g); const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); const ARIA_ATTR = seal(/^aria-[\-\w]+$/); const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i); const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g); const DOCTYPE_NAME = seal(/^html$/i); const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g); const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g); const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i); const SELF_CLOSING_TAG = seal(/\/>/i); const NODE_TYPE = { element: 1, attribute: 2, text: 3, cdataSection: 4, entityReference: 5, entityNode: 6, processingInstruction: 7, comment: 8, document: 9, documentType: 10, documentFragment: 11, notation: 12 }; const LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, [ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript" ])); const getGlobal = function getGlobal() { return typeof window === "undefined" ? null : window; }; /** * Creates a no-op policy for internal use only. * Don't export this function outside this module! * @param trustedTypes The policy factory. * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix). * @return The policy created (or null, if Trusted Types * are not supported or creating the policy failed). */ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) { if (typeof trustedTypes !== "object" || typeof trustedTypes.createPolicy !== "function") return null; let suffix = null; const ATTR_NAME = "data-tt-policy-suffix"; if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) suffix = purifyHostElement.getAttribute(ATTR_NAME); const policyName = "dompurify" + (suffix ? "#" + suffix : ""); try { return trustedTypes.createPolicy(policyName, { createHTML(html) { return html; }, createScriptURL(scriptUrl) { return scriptUrl; } }); } catch (_) { console.warn("TrustedTypes policy " + policyName + " could not be created."); return null; } }; const _createHooksMap = function _createHooksMap() { return { afterSanitizeAttributes: [], afterSanitizeElements: [], afterSanitizeShadowDOM: [], beforeSanitizeAttributes: [], beforeSanitizeElements: [], beforeSanitizeShadowDOM: [], uponSanitizeAttribute: [], uponSanitizeElement: [], uponSanitizeShadowNode: [] }; }; /** * Resolve a set-valued configuration option: a fresh set built from * cfg[key] when it is an own array property (seeded with a clone of * options.base when given, case-normalized via options.transform), * the fallback set otherwise. * * @param cfg the cloned, prototype-free configuration object * @param key the configuration property to read * @param fallback the set to use when the option is absent or not an array * @param options transform and optional base set to merge into * @returns the resolved set */ const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) { return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback; }; /** * Resolve an object-valued configuration option: a prototype-free clone * of cfg[key] when it is an own, truthy object property, else a fresh * fallback built by makeFallback (fresh on every parse, so a previous * parse can never leak state into the next one). * * @param cfg the cloned, prototype-free configuration object * @param key the configuration property to read * @param makeFallback builds the fallback value when the option is absent * @returns the resolved object */ const _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback) { const value = objectHasOwnProperty(cfg, key) ? cfg[key] : void 0; return value && typeof value === "object" ? clone(value) : makeFallback(); }; function createDOMPurify() { let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal(); const DOMPurify = (root) => createDOMPurify(root); DOMPurify.version = "3.4.17"; DOMPurify.removed = []; if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) { DOMPurify.isSupported = false; return DOMPurify; } let document = window.document; const originalDocument = document; const currentScript = originalDocument.currentScript; window.DocumentFragment; const HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter; window.NamedNodeMap === void 0 && (window.NamedNodeMap || window.MozNamedAttrMap); window.HTMLFormElement; const DOMParser = window.DOMParser, trustedTypes = window.trustedTypes; const ElementPrototype = Element.prototype; const cloneNode = lookupGetter(ElementPrototype, "cloneNode"); const remove = lookupGetter(ElementPrototype, "remove"); const removeAttributeNode = lookupGetter(ElementPrototype, "removeAttributeNode"); const getNextSibling = lookupGetter(ElementPrototype, "nextSibling"); const getChildNodes = lookupGetter(ElementPrototype, "childNodes"); const getParentNode = lookupGetter(ElementPrototype, "parentNode"); const getShadowRoot = lookupGetter(ElementPrototype, "shadowRoot"); const getAttributes = lookupGetter(ElementPrototype, "attributes"); const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null; const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null; const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, "ownerDocument") : null; const _readNodeType = function _readNodeType(node) { return getNodeType ? getNodeType(node) : node.nodeType; }; const _readNodeName = function _readNodeName(node) { return getNodeName ? getNodeName(node) : node.nodeName; }; const getNamespaceURI = lookupAccessor(ElementPrototype, "namespaceURI"); const _readNamespaceURI = function _readNamespaceURI(element) { return getNamespaceURI ? getNamespaceURI(element) : element.namespaceURI; }; if (typeof HTMLTemplateElement === "function") { const template = document.createElement("template"); if (template.content && template.content.ownerDocument) document = template.content.ownerDocument; } let trustedTypesPolicy; let emptyHTML = ""; let defaultTrustedTypesPolicy; let defaultTrustedTypesPolicyResolved = false; let IN_TRUSTED_TYPES_POLICY = 0; const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() { if (IN_TRUSTED_TYPES_POLICY > 0) throw typeErrorCreate("A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the \"DOMPurify and Trusted Types\" section of the README."); }; const _createTrustedHTML = function _createTrustedHTML(html) { _assertNotInTrustedTypesPolicy(); IN_TRUSTED_TYPES_POLICY++; try { return trustedTypesPolicy.createHTML(html); } finally { IN_TRUSTED_TYPES_POLICY--; } }; const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) { _assertNotInTrustedTypesPolicy(); IN_TRUSTED_TYPES_POLICY++; try { return trustedTypesPolicy.createScriptURL(scriptUrl); } finally { IN_TRUSTED_TYPES_POLICY--; } }; const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() { if (!defaultTrustedTypesPolicyResolved) { defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript); defaultTrustedTypesPolicyResolved = true; } return defaultTrustedTypesPolicy; }; const _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName; const importNode = originalDocument.importNode; let hooks = _createHooksMap(); /** * Expose whether this browser supports running the full DOMPurify. */ DOMPurify.isSupported = typeof entries === "function" && typeof getParentNode === "function" && implementation && implementation.createHTMLDocument !== void 0; const MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, TMPLIT_EXPR$1 = TMPLIT_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE, CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT; let IS_ALLOWED_URI$1 = IS_ALLOWED_URI; /** * We consider the elements and attributes below to be safe. Ideally * don't add any new ones but feel free to remove unwanted ones. */ let ALLOWED_TAGS = null; const DEFAULT_ALLOWED_TAGS = addToSet({}, [ ...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text ]); let ALLOWED_ATTR = null; const DEFAULT_ALLOWED_ATTR = addToSet({}, [ ...html, ...svg, ...mathMl, ...xml ]); let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, { tagNameCheck: { writable: true, configurable: false, enumerable: true, value: null }, attributeNameCheck: { writable: true, configurable: false, enumerable: true, value: null }, allowCustomizedBuiltInElements: { writable: true, configurable: false, enumerable: true, value: false } })); let FORBID_TAGS = null; let FORBID_ATTR = null; const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, { tagCheck: { writable: true, configurable: false, enumerable: true, value: null }, attributeCheck: { writable: true, configurable: false, enumerable: true, value: null } })); let ALLOW_ARIA_ATTR = true; let ALLOW_DATA_ATTR = true; let ALLOW_UNKNOWN_PROTOCOLS = false; let ALLOW_SELF_CLOSE_IN_ATTR = true; let SAFE_FOR_TEMPLATES = false; let SAFE_FOR_XML = true; let WHOLE_DOCUMENT = false; let SET_CONFIG = false; let SET_CONFIG_ALLOWED_TAGS = null; let SET_CONFIG_ALLOWED_ATTR = null; let FORCE_BODY = false; let RETURN_DOM = false; let RETURN_DOM_FRAGMENT = false; let RETURN_TRUSTED_TYPE = false; let SANITIZE_DOM = true; let SANITIZE_NAMED_PROPS = false; const SANITIZE_NAMED_PROPS_PREFIX = "user-content-"; let KEEP_CONTENT = true; let IN_PLACE = false; let IN_PLACE_VISITED = null; let USE_PROFILES = {}; let FORBID_CONTENTS = null; const DEFAULT_FORBID_CONTENTS = addToSet({}, [ "annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "selectedcontent", "style", "svg", "template", "thead", "title", "video", "xmp" ]); let DATA_URI_TAGS = null; const DEFAULT_DATA_URI_TAGS = addToSet({}, [ "audio", "video", "img", "source", "image", "track" ]); let URI_SAFE_ATTRIBUTES = null; const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [ "alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns" ]); const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML"; const SVG_NAMESPACE = "http://www.w3.org/2000/svg"; const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; let NAMESPACE = HTML_NAMESPACE; let IS_EMPTY_INPUT = false; let ALLOWED_NAMESPACES = null; const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [ MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE ], stringToString); const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze([ "mi", "mo", "mn", "ms", "mtext" ]); let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); const DEFAULT_HTML_INTEGRATION_POINTS = freeze(["annotation-xml"]); let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [ "title", "style", "font", "a", "script" ]); let PARSER_MEDIA_TYPE = null; const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"]; const DEFAULT_PARSER_MEDIA_TYPE = "text/html"; let transformCaseFunc = null; let CONFIG = null; const formElement = document.createElement("form"); const isRegexOrFunction = function isRegexOrFunction(testValue) { return testValue instanceof RegExp || testValue instanceof Function; }; /** * _parseConfig * * @param cfg optional config literal */ const _parseConfig = function _parseConfig() { let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; if (CONFIG && CONFIG === cfg) return; if (!cfg || typeof cfg !== "object") cfg = {}; cfg = clone(cfg); PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE; transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase; ALLOWED_TAGS = _resolveSetOption(cfg, "ALLOWED_TAGS", DEFAULT_ALLOWED_TAGS, { transform: transformCaseFunc }); ALLOWED_ATTR = _resolveSetOption(cfg, "ALLOWED_ATTR", DEFAULT_ALLOWED_ATTR, { transform: transformCaseFunc }); ALLOWED_NAMESPACES = _resolveSetOption(cfg, "ALLOWED_NAMESPACES", DEFAULT_ALLOWED_NAMESPACES, { transform: stringToString }); URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, "ADD_URI_SAFE_ATTR", DEFAULT_URI_SAFE_ATTRIBUTES, { transform: transformCaseFunc, base: DEFAULT_URI_SAFE_ATTRIBUTES }); DATA_URI_TAGS = _resolveSetOption(cfg, "ADD_DATA_URI_TAGS", DEFAULT_DATA_URI_TAGS, { transform: transformCaseFunc, base: DEFAULT_DATA_URI_TAGS }); FORBID_CONTENTS = _resolveSetOption(cfg, "FORBID_CONTENTS", DEFAULT_FORBID_CONTENTS, { transform: transformCaseFunc }); FORBID_TAGS = _resolveSetOption(cfg, "FORBID_TAGS", clone({}), { transform: transformCaseFunc }); FORBID_ATTR = _resolveSetOption(cfg, "FORBID_ATTR", clone({}), { transform: transformCaseFunc }); USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false; ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; RETURN_DOM = cfg.RETURN_DOM || false; RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; FORCE_BODY = cfg.FORCE_BODY || false; SANITIZE_DOM = cfg.SANITIZE_DOM !== false; SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; KEEP_CONTENT = cfg.KEEP_CONTENT !== false; IN_PLACE = cfg.IN_PLACE || false; IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE; MATHML_TEXT_INTEGRATION_POINTS = _resolveObjectOption(cfg, "MATHML_TEXT_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS)); HTML_INTEGRATION_POINTS = _resolveObjectOption(cfg, "HTML_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS)); const customElementHandling = _resolveObjectOption(cfg, "CUSTOM_ELEMENT_HANDLING", () => create(null)); CUSTOM_ELEMENT_HANDLING = create(null); if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; if (objectHasOwnProperty(customElementHandling, "attributeNameCheck") && isRegexOrFunction(customElementHandling.attributeNameCheck)) CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; seal(CUSTOM_ELEMENT_HANDLING); if (SAFE_FOR_TEMPLATES) ALLOW_DATA_ATTR = false; if (RETURN_DOM_FRAGMENT) RETURN_DOM = true; if (USE_PROFILES) { ALLOWED_TAGS = addToSet({}, text); ALLOWED_ATTR = create(null); if (USE_PROFILES.html === true) { addToSet(ALLOWED_TAGS, html$1); addToSet(ALLOWED_ATTR, html); } if (USE_PROFILES.svg === true) { addToSet(ALLOWED_TAGS, svg$1); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } if (USE_PROFILES.svgFilters === true) { addToSet(ALLOWED_TAGS, svgFilters); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } if (USE_PROFILES.mathMl === true) { addToSet(ALLOWED_TAGS, mathMl$1); addToSet(ALLOWED_ATTR, mathMl); addToSet(ALLOWED_ATTR, xml); } } EXTRA_ELEMENT_HANDLING.tagCheck = null; EXTRA_ELEMENT_HANDLING.attributeCheck = null; if (objectHasOwnProperty(cfg, "ADD_TAGS")) { if (typeof cfg.ADD_TAGS === "function") EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS; else if (arrayIsArray(cfg.ADD_TAGS)) { if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) ALLOWED_TAGS = clone(ALLOWED_TAGS); addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc); } } if (objectHasOwnProperty(cfg, "ADD_ATTR")) { if (typeof cfg.ADD_ATTR === "function") EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR; else if (arrayIsArray(cfg.ADD_ATTR)) { if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) ALLOWED_ATTR = clone(ALLOWED_ATTR); addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc); } } if (objectHasOwnProperty(cfg, "ADD_FORBID_CONTENTS") && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) { if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS); addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc); } if (KEEP_CONTENT) ALLOWED_TAGS["#text"] = true; if (WHOLE_DOCUMENT) addToSet(ALLOWED_TAGS, [ "html", "head", "body" ]); if (ALLOWED_TAGS.table) { addToSet(ALLOWED_TAGS, ["tbody"]); delete FORBID_TAGS.tbody; } if (cfg.TRUSTED_TYPES_POLICY) { if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== "function") throw typeErrorCreate("TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook."); if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") throw typeErrorCreate("TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook."); const previousTrustedTypesPolicy = trustedTypesPolicy; trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; try { emptyHTML = _createTrustedHTML(""); } catch (error) { trustedTypesPolicy = previousTrustedTypesPolicy; throw error; } } else if (cfg.TRUSTED_TYPES_POLICY === null) { trustedTypesPolicy = void 0; emptyHTML = ""; } else { if (trustedTypesPolicy === void 0) trustedTypesPolicy = _getDefaultTrustedTypesPolicy(); if (trustedTypesPolicy && typeof emptyHTML === "string") emptyHTML = _createTrustedHTML(""); } if (freeze) freeze(cfg); CONFIG = cfg; }; const ALL_SVG_TAGS = addToSet({}, [ ...svg$1, ...svgFilters, ...svgDisallowed ]); const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]); /** * Namespace rules for an element in the SVG namespace. * * @param tagName the element's lowercase tag name * @param parent the (possibly simulated) parent node * @param parentTagName the parent's lowercase tag name * @returns true if a spec-compliant parser could produce this element */ const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) { if (parent.namespaceURI === HTML_NAMESPACE) return tagName === "svg"; if (parent.namespaceURI === MATHML_NAMESPACE) return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); return Boolean(ALL_SVG_TAGS[tagName]); }; /** * Namespace rules for an element in the MathML namespace. * * @param tagName the element's lowercase tag name * @param parent the (possibly simulated) parent node * @param parentTagName the parent's lowercase tag name * @returns true if a spec-compliant parser could produce this element */ const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) { if (parent.namespaceURI === HTML_NAMESPACE) return tagName === "math"; if (parent.namespaceURI === SVG_NAMESPACE) return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName]; return Boolean(ALL_MATHML_TAGS[tagName]); }; /** * Namespace rules for an element in the HTML namespace. * * @param tagName the element's lowercase tag name * @param parent the (possibly simulated) parent node * @param parentTagName the parent's lowercase tag name * @returns true if a spec-compliant parser could produce this element */ const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) { if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) return false; if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) return false; return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]); }; /** * @param element a DOM element whose namespace is being checked * @returns Return false if the element has a * namespace that a spec-compliant parser would never * return. Return true otherwise. */ const _checkValidNamespace = function _checkValidNamespace(element) { const elementNamespace = _readNamespaceURI(element); const tagName = stringToLowerCase(_readNodeName(element)); const parentNode = getParentNode(element); let parent; let parentTagName; if (parentNode && _readNodeType(parentNode) === NODE_TYPE.element) { parent = { namespaceURI: _readNamespaceURI(parentNode) }; parentTagName = stringToLowerCase(_readNodeName(parentNode)); } else { parent = { namespaceURI: NAMESPACE }; parentTagName = "template"; } if (!ALLOWED_NAMESPACES[elementNamespace]) return false; if (elementNamespace === SVG_NAMESPACE) return _checkSvgNamespace(tagName, parent, parentTagName); if (elementNamespace === MATHML_NAMESPACE) return _checkMathMlNamespace(tagName, parent, parentTagName); if (elementNamespace === HTML_NAMESPACE) return _checkHtmlNamespace(tagName, parent, parentTagName); if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[elementNamespace]) return true; return false; }; /** * _forceRemove * * @param node a DOM node */ const _forceRemove = function _forceRemove(node) { arrayPush(DOMPurify.removed, { element: node }); try { getParentNode(node).removeChild(node); } catch (_) { remove(node); if (!getParentNode(node)) throw typeErrorCreate("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place"); } }; /** * _stripAttributeNode * * Remove a single Attr node case/namespace-exactly on an attribute-teardown * path. Name-based removeAttribute() ASCII-lowercases its lookup key for an * HTML element in an HTML document and so silently misses a case-preserved * handler (e.g. `ONERROR` off an XML/XHTML import) - the same defect * _removeAttribute() was fixed for, which a name-based call would reintroduce * on these IN_PLACE teardown paths. Unlike _removeAttribute this does not * record into DOMPurify.removed: the neutralize passes intentionally do not * book-keep. A clobbered/detached node falls back to best-effort name-based * removal. * * @param element the element to strip the attribute from * @param attribute the Attr node to remove * @param name the attribute's name, for the fallback path */ const _stripAttributeNode = function _stripAttributeNode(element, attribute, name) { try { removeAttributeNode(element, attribute); } catch (_) { try { element.removeAttribute(name); } catch (_) {} } }; /** * Fail-closed teardown of an in-place root: strip every disallowed attribute * in the subtree first, so nothing armed is left behind, then remove all * children and the root's own attributes. * * @param root the in-place root to empty */ const _neutralizeRoot = function _neutralizeRoot(root) { _neutralizeSubtree(root); const childNodes = getChildNodes(root); if (childNodes) arrayForEach(arraySlice(childNodes), (child) => { try { remove(child); } catch (_) {} }); const attributes = _readNodeType(root) === NODE_TYPE.element ? getAttributes(root) : null; if (attributes) for (let i = attributes.length - 1; i >= 0; --i) { const attribute = attributes[i]; const name = attribute && attribute.name; if (typeof name === "string") _stripAttributeNode(root, attribute, name); } }; /** * Removes an attribute, preferably as its exact Attr node: name-based * removal lowercases the name in HTML documents and misses case-preserved * names, e.g. from imported XHTML. * * @param name an Attribute name * @param element a DOM node * @param attr the exact Attr node to remove, when the caller has it */ const _removeAttribute = function _removeAttribute(name, element, attr) { if (!attr) try { attr = element.getAttributeNode(name); } catch (_) { attr = null; } arrayPush(DOMPurify.removed, { attribute: attr || null, from: element }); try { if (attr) removeAttributeNode(element, attr); else element.removeAttribute(name); } catch (_) { try { element.removeAttribute(name); } catch (_) {} } if (name === "is") { if (RETURN_DOM || RETURN_DOM_FRAGMENT) try { _forceRemove(element); } catch (_) {} else try { element.setAttribute(name, ""); } catch (_) {} } }; /** * Removes every attribute the configuration does not allow. Used to * neutralize nodes discarded from an in-place tree. * * @param element the element to strip */ const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) { const attributes = getAttributes(element); if (!attributes) return; for (let i = attributes.length - 1; i >= 0; --i) { const attribute = attributes[i]; const name = attribute && attribute.name; if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) continue; _stripAttributeNode(element, attribute, name); } }; /** * Iterative depth-first walk, so deep trees cannot overflow the call stack, * reading through the cached getters, so clobbering cannot hide children. * When `visit` returns false, the node's children are skipped. With * `intoShadow`, open shadow roots are walked after their host's children. * * @param root where to start * @param visit called for every node * @param intoShadow whether to enter open shadow roots */ const _walkTree = function _walkTree(root, visit) { let intoShadow = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false; const stack = [root]; while (stack.length > 0) { const node = stack.pop(); if (visit(node) === false) continue; if (intoShadow && _readNodeType(node) === NODE_TYPE.element) { const shadowRoot = getShadowRoot(node); if (_isDocumentFragment(shadowRoot)) stack.push(shadowRoot); } const childNodes = getChildNodes(node); if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]); } }; /** * Strips disallowed attributes from every element of a discarded subtree, * so a handler that is already queued (a loading image) cannot fire. * * @param root the root of a removed subtree */ const _neutralizeSubtree = function _neutralizeSubtree(root) { _walkTree(root, (node) => { if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node); }); }; /** * All nodes under `root`, root first, including open shadow roots. Template * content is not entered, it is inert. * * @param root the in-place root * @return the nodes */ const _collectLiveNodes = function _collectLiveNodes(root) { const nodes = []; _walkTree(root, (node) => { nodes.push(node); }, true); return nodes; }; /** * Strips disallowed attributes from every element that was under the * in-place root when the call started and no longer is, whatever removed * or moved it. * * @param root the in-place root * @param before the root's nodes, collected before the walk */ const _neutralizeEscapedElements = function _neutralizeEscapedElements(root, before) { const reachable = setCreate(); arrayForEach(_collectLiveNodes(root), (node) => { if (_readNodeType(node) === NODE_TYPE.element) setAdd(reachable, node); }); arrayForEach(before, (node) => { if (_readNodeType(node) === NODE_TYPE.element && !setHas(reachable, node)) _stripDisallowedAttributes(node); }); }; /** * Neutralizes everything that left the in-place tree during the call: what * DOMPurify removed, and what hooks or custom elements removed or moved out. * * @param root the in-place root * @param before the root's nodes, collected before the walk * @return true if the root itself was removed */ const _neutralizeDiscarded = function _neutralizeDiscarded(root, before) { let rootRemoved = false; arrayForEach(DOMPurify.removed, (entry) => { if (entry.element) { rootRemoved = rootRemoved || entry.element === root; _neutralizeSubtree(entry.element); } }); _neutralizeEscapedElements(root, before); return rootRemoved; }; const MAX_UNVISITED_ROUNDS = 8; /** * The walk only moves forward, so a node moved to a position it already * passed is never visited. Give every original node that is still in the * tree but unvisited the normal treatment, hooks included. Nodes created * during the call are left alone. Fails closed if the tree keeps changing. * * @param root the in-place root * @param before the root's nodes, collected before the walk */ const _sanitizeUnvisitedNodes = function _sanitizeUnvisitedNodes(root, before) { const visited = IN_PLACE_VISITED; let original = null; const getOriginal = () => { if (!original) { original = setCreate(); arrayForEach(before, (beforeNode) => { setAdd(original, beforeNode); }); } return original; }; for (let round = 0;; round++) { const live = _collectLiveNodes(root); const containers = [root]; let pending = false; arrayForEach(live, (node) => { if (_readNodeType(node) === NODE_TYPE.documentFragment) arrayPush(containers, node); else if (!pending && !setHas(visited, node)) pending = setHas(getOriginal(), node); }); if (!pending) return; if (round >= MAX_UNVISITED_ROUNDS) throw typeErrorCreate("the tree kept changing during in-place sanitization; refusing to return it"); arrayForEach(containers, (container, index) => { _sanitizeUnvisitedIn(container, index > 0, root, getOriginal()); }); } }; /** * One _sanitizeUnvisitedNodes pass over the root or an open shadow root. * * @param container the root or an open shadow root * @param inShadow whether container is a shadow root * @param root the in-place root * @param original the root's nodes collected before the walk, as a set */ const _sanitizeUnvisitedIn = function _sanitizeUnvisitedIn(container, inShadow, root, original) { const iterator = _createNodeIterator(container); let node; while (node = iterator.nextNode()) { if (!setHas(original, node) || setHas(IN_PLACE_VISITED, node)) continue; if (inShadow) _executeHooks(hooks.uponSanitizeShadowNode, node, null); _sanitizeElements(node, root); _sanitizeAttributes(node, root); if (_isDocumentFragment(node.content)) _sanitizeShadowDOM2(node.content); } }; /** * Records that _sanitizeElements processed `node`. No-op outside IN_PLACE. * * @param node the node being processed */ const _markVisited = function _markVisited(node) { if (IN_PLACE_VISITED) setAdd(IN_PLACE_VISITED, node); }; /** * Patch-linkage attributes (declarative partial updates): `patchsrc` * always, `for` everywhere except on