PluginProbe
Elementor Website Builder – more than just a page builder / 4.0.2
Elementor Website Builder – more than just a page builder v4.0.2
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / assets / js / 6caa76d2f6eec6c4e665.bundle.js

6caa76d2f6eec6c4e665.bundle.js in Elementor Website Builder – more than just a page builder 4.0.2, at assets/js/6caa76d2f6eec6c4e665.bundle.js

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