dompurify 3.0.1 → 3.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,412 +1,285 @@
1
- /*! @license DOMPurify 3.0.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.1/LICENSE */
1
+ /*! @license DOMPurify 3.2.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.7/LICENSE */
2
2
 
3
3
  'use strict';
4
4
 
5
- function _typeof(obj) {
6
- "@babel/helpers - typeof";
7
-
8
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
9
- return typeof obj;
10
- } : function (obj) {
11
- return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
12
- }, _typeof(obj);
13
- }
14
-
15
- function _setPrototypeOf(o, p) {
16
- _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
17
- o.__proto__ = p;
18
- return o;
19
- };
20
-
21
- return _setPrototypeOf(o, p);
22
- }
23
-
24
- function _isNativeReflectConstruct() {
25
- if (typeof Reflect === "undefined" || !Reflect.construct) return false;
26
- if (Reflect.construct.sham) return false;
27
- if (typeof Proxy === "function") return true;
28
-
29
- try {
30
- Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
31
- return true;
32
- } catch (e) {
33
- return false;
34
- }
35
- }
36
-
37
- function _construct(Parent, args, Class) {
38
- if (_isNativeReflectConstruct()) {
39
- _construct = Reflect.construct;
40
- } else {
41
- _construct = function _construct(Parent, args, Class) {
42
- var a = [null];
43
- a.push.apply(a, args);
44
- var Constructor = Function.bind.apply(Parent, a);
45
- var instance = new Constructor();
46
- if (Class) _setPrototypeOf(instance, Class.prototype);
47
- return instance;
48
- };
49
- }
50
-
51
- return _construct.apply(null, arguments);
52
- }
53
-
54
- function _slicedToArray(arr, i) {
55
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
56
- }
57
-
58
- function _toConsumableArray(arr) {
59
- return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
60
- }
61
-
62
- function _arrayWithoutHoles(arr) {
63
- if (Array.isArray(arr)) return _arrayLikeToArray(arr);
64
- }
65
-
66
- function _arrayWithHoles(arr) {
67
- if (Array.isArray(arr)) return arr;
68
- }
69
-
70
- function _iterableToArray(iter) {
71
- if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
72
- }
73
-
74
- function _iterableToArrayLimit(arr, i) {
75
- var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
76
-
77
- if (_i == null) return;
78
- var _arr = [];
79
- var _n = true;
80
- var _d = false;
81
-
82
- var _s, _e;
83
-
84
- try {
85
- for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
86
- _arr.push(_s.value);
87
-
88
- if (i && _arr.length === i) break;
89
- }
90
- } catch (err) {
91
- _d = true;
92
- _e = err;
93
- } finally {
94
- try {
95
- if (!_n && _i["return"] != null) _i["return"]();
96
- } finally {
97
- if (_d) throw _e;
98
- }
99
- }
100
-
101
- return _arr;
102
- }
103
-
104
- function _unsupportedIterableToArray(o, minLen) {
105
- if (!o) return;
106
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
107
- var n = Object.prototype.toString.call(o).slice(8, -1);
108
- if (n === "Object" && o.constructor) n = o.constructor.name;
109
- if (n === "Map" || n === "Set") return Array.from(o);
110
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
111
- }
112
-
113
- function _arrayLikeToArray(arr, len) {
114
- if (len == null || len > arr.length) len = arr.length;
115
-
116
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
117
-
118
- return arr2;
119
- }
120
-
121
- function _nonIterableSpread() {
122
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
123
- }
124
-
125
- function _nonIterableRest() {
126
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
127
- }
128
-
129
- function _createForOfIteratorHelper(o, allowArrayLike) {
130
- var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
131
-
132
- if (!it) {
133
- if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
134
- if (it) o = it;
135
- var i = 0;
136
-
137
- var F = function () {};
138
-
139
- return {
140
- s: F,
141
- n: function () {
142
- if (i >= o.length) return {
143
- done: true
144
- };
145
- return {
146
- done: false,
147
- value: o[i++]
148
- };
149
- },
150
- e: function (e) {
151
- throw e;
152
- },
153
- f: F
154
- };
155
- }
156
-
157
- throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
158
- }
159
-
160
- var normalCompletion = true,
161
- didErr = false,
162
- err;
163
- return {
164
- s: function () {
165
- it = it.call(o);
166
- },
167
- n: function () {
168
- var step = it.next();
169
- normalCompletion = step.done;
170
- return step;
171
- },
172
- e: function (e) {
173
- didErr = true;
174
- err = e;
175
- },
176
- f: function () {
177
- try {
178
- if (!normalCompletion && it.return != null) it.return();
179
- } finally {
180
- if (didErr) throw err;
181
- }
182
- }
183
- };
184
- }
185
-
186
- var entries = Object.entries,
187
- setPrototypeOf = Object.setPrototypeOf,
188
- isFrozen = Object.isFrozen,
189
- getPrototypeOf = Object.getPrototypeOf,
190
- getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
191
- var freeze = Object.freeze,
192
- seal = Object.seal,
193
- create = Object.create; // eslint-disable-line import/no-mutable-exports
194
-
195
- var _ref = typeof Reflect !== 'undefined' && Reflect,
196
- apply = _ref.apply,
197
- construct = _ref.construct;
198
-
199
- if (!apply) {
200
- apply = function apply(fun, thisValue, args) {
201
- return fun.apply(thisValue, args);
202
- };
203
- }
204
-
5
+ const {
6
+ entries,
7
+ setPrototypeOf,
8
+ isFrozen,
9
+ getPrototypeOf,
10
+ getOwnPropertyDescriptor
11
+ } = Object;
12
+ let {
13
+ freeze,
14
+ seal,
15
+ create
16
+ } = Object; // eslint-disable-line import/no-mutable-exports
17
+ let {
18
+ apply,
19
+ construct
20
+ } = typeof Reflect !== 'undefined' && Reflect;
205
21
  if (!freeze) {
206
22
  freeze = function freeze(x) {
207
23
  return x;
208
24
  };
209
25
  }
210
-
211
26
  if (!seal) {
212
27
  seal = function seal(x) {
213
28
  return x;
214
29
  };
215
30
  }
216
-
31
+ if (!apply) {
32
+ apply = function apply(func, thisArg) {
33
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
34
+ args[_key - 2] = arguments[_key];
35
+ }
36
+ return func.apply(thisArg, args);
37
+ };
38
+ }
217
39
  if (!construct) {
218
- construct = function construct(Func, args) {
219
- return _construct(Func, _toConsumableArray(args));
40
+ construct = function construct(Func) {
41
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
42
+ args[_key2 - 1] = arguments[_key2];
43
+ }
44
+ return new Func(...args);
220
45
  };
221
46
  }
222
-
223
- var arrayForEach = unapply(Array.prototype.forEach);
224
- var arrayPop = unapply(Array.prototype.pop);
225
- var arrayPush = unapply(Array.prototype.push);
226
- var stringToLowerCase = unapply(String.prototype.toLowerCase);
227
- var stringToString = unapply(String.prototype.toString);
228
- var stringMatch = unapply(String.prototype.match);
229
- var stringReplace = unapply(String.prototype.replace);
230
- var stringIndexOf = unapply(String.prototype.indexOf);
231
- var stringTrim = unapply(String.prototype.trim);
232
- var regExpTest = unapply(RegExp.prototype.test);
233
- var typeErrorCreate = unconstruct(TypeError);
47
+ const arrayForEach = unapply(Array.prototype.forEach);
48
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
49
+ const arrayPop = unapply(Array.prototype.pop);
50
+ const arrayPush = unapply(Array.prototype.push);
51
+ const arraySplice = unapply(Array.prototype.splice);
52
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
53
+ const stringToString = unapply(String.prototype.toString);
54
+ const stringMatch = unapply(String.prototype.match);
55
+ const stringReplace = unapply(String.prototype.replace);
56
+ const stringIndexOf = unapply(String.prototype.indexOf);
57
+ const stringTrim = unapply(String.prototype.trim);
58
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
59
+ const regExpTest = unapply(RegExp.prototype.test);
60
+ const typeErrorCreate = unconstruct(TypeError);
61
+ /**
62
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
63
+ *
64
+ * @param func - The function to be wrapped and called.
65
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
66
+ */
234
67
  function unapply(func) {
235
68
  return function (thisArg) {
236
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
237
- args[_key - 1] = arguments[_key];
69
+ if (thisArg instanceof RegExp) {
70
+ thisArg.lastIndex = 0;
71
+ }
72
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
73
+ args[_key3 - 1] = arguments[_key3];
238
74
  }
239
-
240
75
  return apply(func, thisArg, args);
241
76
  };
242
77
  }
243
- function unconstruct(func) {
78
+ /**
79
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
80
+ *
81
+ * @param func - The constructor function to be wrapped and called.
82
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
83
+ */
84
+ function unconstruct(Func) {
244
85
  return function () {
245
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
246
- args[_key2] = arguments[_key2];
86
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
87
+ args[_key4] = arguments[_key4];
247
88
  }
248
-
249
- return construct(func, args);
89
+ return construct(Func, args);
250
90
  };
251
91
  }
252
- /* Add properties to a lookup table */
253
-
254
- function addToSet(set, array, transformCaseFunc) {
255
- transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
256
-
92
+ /**
93
+ * Add properties to a lookup table
94
+ *
95
+ * @param set - The set to which elements will be added.
96
+ * @param array - The array containing elements to be added to the set.
97
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
98
+ * @returns The modified set with added elements.
99
+ */
100
+ function addToSet(set, array) {
101
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
257
102
  if (setPrototypeOf) {
258
103
  // Make 'in' and truthy checks like Boolean(set.constructor)
259
104
  // independent of any properties defined on Object.prototype.
260
105
  // Prevent prototype setters from intercepting set as a this value.
261
106
  setPrototypeOf(set, null);
262
107
  }
263
-
264
- var l = array.length;
265
-
108
+ let l = array.length;
266
109
  while (l--) {
267
- var element = array[l];
268
-
110
+ let element = array[l];
269
111
  if (typeof element === 'string') {
270
- var lcElement = transformCaseFunc(element);
271
-
112
+ const lcElement = transformCaseFunc(element);
272
113
  if (lcElement !== element) {
273
114
  // Config presets (e.g. tags.js, attrs.js) are immutable.
274
115
  if (!isFrozen(array)) {
275
116
  array[l] = lcElement;
276
117
  }
277
-
278
118
  element = lcElement;
279
119
  }
280
120
  }
281
-
282
121
  set[element] = true;
283
122
  }
284
-
285
123
  return set;
286
124
  }
287
- /* Shallow clone an object */
288
-
125
+ /**
126
+ * Clean up an array to harden against CSPP
127
+ *
128
+ * @param array - The array to be cleaned.
129
+ * @returns The cleaned version of the array
130
+ */
131
+ function cleanArray(array) {
132
+ for (let index = 0; index < array.length; index++) {
133
+ const isPropertyExist = objectHasOwnProperty(array, index);
134
+ if (!isPropertyExist) {
135
+ array[index] = null;
136
+ }
137
+ }
138
+ return array;
139
+ }
140
+ /**
141
+ * Shallow clone an object
142
+ *
143
+ * @param object - The object to be cloned.
144
+ * @returns A new object that copies the original.
145
+ */
289
146
  function clone(object) {
290
- var newObject = create(null);
291
-
292
- var _iterator = _createForOfIteratorHelper(entries(object)),
293
- _step;
294
-
295
- try {
296
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
297
- var _step$value = _slicedToArray(_step.value, 2),
298
- property = _step$value[0],
299
- value = _step$value[1];
300
-
301
- newObject[property] = value;
147
+ const newObject = create(null);
148
+ for (const [property, value] of entries(object)) {
149
+ const isPropertyExist = objectHasOwnProperty(object, property);
150
+ if (isPropertyExist) {
151
+ if (Array.isArray(value)) {
152
+ newObject[property] = cleanArray(value);
153
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
154
+ newObject[property] = clone(value);
155
+ } else {
156
+ newObject[property] = value;
157
+ }
302
158
  }
303
- } catch (err) {
304
- _iterator.e(err);
305
- } finally {
306
- _iterator.f();
307
159
  }
308
-
309
160
  return newObject;
310
161
  }
311
- /* This method automatically checks if the prop is function
312
- * or getter and behaves accordingly. */
313
-
162
+ /**
163
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
164
+ *
165
+ * @param object - The object to look up the getter function in its prototype chain.
166
+ * @param prop - The property name for which to find the getter function.
167
+ * @returns The getter function found in the prototype chain or a fallback function.
168
+ */
314
169
  function lookupGetter(object, prop) {
315
170
  while (object !== null) {
316
- var desc = getOwnPropertyDescriptor(object, prop);
317
-
171
+ const desc = getOwnPropertyDescriptor(object, prop);
318
172
  if (desc) {
319
173
  if (desc.get) {
320
174
  return unapply(desc.get);
321
175
  }
322
-
323
176
  if (typeof desc.value === 'function') {
324
177
  return unapply(desc.value);
325
178
  }
326
179
  }
327
-
328
180
  object = getPrototypeOf(object);
329
181
  }
330
-
331
- function fallbackValue(element) {
332
- console.warn('fallback value for', element);
182
+ function fallbackValue() {
333
183
  return null;
334
184
  }
335
-
336
185
  return fallbackValue;
337
186
  }
338
187
 
339
- var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
340
-
341
- var svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
342
- var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default.
188
+ 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']);
189
+ 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', 'slot', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
190
+ 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']);
191
+ // List of SVG elements that are disallowed by default.
343
192
  // We still need to know them so that we can do namespace
344
193
  // checks properly in case one wants to add them to
345
194
  // allow-list.
346
-
347
- var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', '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']);
348
- var 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']); // Similarly to SVG, we want to know all MathML elements,
195
+ 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']);
196
+ 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']);
197
+ // Similarly to SVG, we want to know all MathML elements,
349
198
  // even those that we disallow by default.
350
-
351
- var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
352
- var text = freeze(['#text']);
353
-
354
- var html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
355
- var svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', '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', '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', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', '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', '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']);
356
- var 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']);
357
- var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
358
-
359
- var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
360
-
361
- var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
362
- var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
363
- var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
364
-
365
- var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
366
-
367
- var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
199
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
200
+ const text = freeze(['#text']);
201
+
202
+ 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']);
203
+ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
204
+ 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']);
205
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
206
+
207
+ // eslint-disable-next-line unicorn/better-regex
208
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
209
+ const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
210
+ const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
211
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
212
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
213
+ 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
368
214
  );
369
- var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
370
- var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
215
+ const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
216
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
371
217
  );
372
- var DOCTYPE_NAME = seal(/^html$/i);
373
-
374
- var getGlobal = function getGlobal() {
218
+ const DOCTYPE_NAME = seal(/^html$/i);
219
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
220
+
221
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
222
+ __proto__: null,
223
+ ARIA_ATTR: ARIA_ATTR,
224
+ ATTR_WHITESPACE: ATTR_WHITESPACE,
225
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
226
+ DATA_ATTR: DATA_ATTR,
227
+ DOCTYPE_NAME: DOCTYPE_NAME,
228
+ ERB_EXPR: ERB_EXPR,
229
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
230
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
231
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
232
+ TMPLIT_EXPR: TMPLIT_EXPR
233
+ });
234
+
235
+ /* eslint-disable @typescript-eslint/indent */
236
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
237
+ const NODE_TYPE = {
238
+ element: 1,
239
+ attribute: 2,
240
+ text: 3,
241
+ cdataSection: 4,
242
+ entityReference: 5,
243
+ // Deprecated
244
+ entityNode: 6,
245
+ // Deprecated
246
+ progressingInstruction: 7,
247
+ comment: 8,
248
+ document: 9,
249
+ documentType: 10,
250
+ documentFragment: 11,
251
+ notation: 12 // Deprecated
252
+ };
253
+ const getGlobal = function getGlobal() {
375
254
  return typeof window === 'undefined' ? null : window;
376
255
  };
377
256
  /**
378
257
  * Creates a no-op policy for internal use only.
379
258
  * Don't export this function outside this module!
380
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
381
- * @param {Document} document The document object (to determine policy name suffix)
382
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
383
- * are not supported).
259
+ * @param trustedTypes The policy factory.
260
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
261
+ * @return The policy created (or null, if Trusted Types
262
+ * are not supported or creating the policy failed).
384
263
  */
385
-
386
-
387
- var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
388
- if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
264
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
265
+ if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
389
266
  return null;
390
- } // Allow the callers to control the unique policy name
267
+ }
268
+ // Allow the callers to control the unique policy name
391
269
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
392
270
  // Policy creation with duplicate names throws in Trusted Types.
393
-
394
-
395
- var suffix = null;
396
- var ATTR_NAME = 'data-tt-policy-suffix';
397
-
398
- if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
399
- suffix = document.currentScript.getAttribute(ATTR_NAME);
271
+ let suffix = null;
272
+ const ATTR_NAME = 'data-tt-policy-suffix';
273
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
274
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
400
275
  }
401
-
402
- var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
403
-
276
+ const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
404
277
  try {
405
278
  return trustedTypes.createPolicy(policyName, {
406
- createHTML: function createHTML(html) {
279
+ createHTML(html) {
407
280
  return html;
408
281
  },
409
- createScriptURL: function createScriptURL(scriptUrl) {
282
+ createScriptURL(scriptUrl) {
410
283
  return scriptUrl;
411
284
  }
412
285
  });
@@ -418,109 +291,110 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes,
418
291
  return null;
419
292
  }
420
293
  };
421
-
422
- function createDOMPurify() {
423
- var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
424
-
425
- var DOMPurify = function DOMPurify(root) {
426
- return createDOMPurify(root);
294
+ const _createHooksMap = function _createHooksMap() {
295
+ return {
296
+ afterSanitizeAttributes: [],
297
+ afterSanitizeElements: [],
298
+ afterSanitizeShadowDOM: [],
299
+ beforeSanitizeAttributes: [],
300
+ beforeSanitizeElements: [],
301
+ beforeSanitizeShadowDOM: [],
302
+ uponSanitizeAttribute: [],
303
+ uponSanitizeElement: [],
304
+ uponSanitizeShadowNode: []
427
305
  };
428
- /**
429
- * Version label, exposed for easier checks
430
- * if DOMPurify is up to date or not
431
- */
432
-
433
-
434
- DOMPurify.version = '3.0.1';
435
- /**
436
- * Array of elements that DOMPurify removed during sanitation.
437
- * Empty if nothing was removed.
438
- */
439
-
306
+ };
307
+ function createDOMPurify() {
308
+ let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
309
+ const DOMPurify = root => createDOMPurify(root);
310
+ DOMPurify.version = '3.2.7';
440
311
  DOMPurify.removed = [];
441
-
442
- if (!window || !window.document || window.document.nodeType !== 9) {
312
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
443
313
  // Not running in a browser, provide a factory function
444
314
  // so that you can pass your own Window
445
315
  DOMPurify.isSupported = false;
446
316
  return DOMPurify;
447
317
  }
448
-
449
- var originalDocument = window.document;
450
- var document = window.document;
451
- var DocumentFragment = window.DocumentFragment,
452
- HTMLTemplateElement = window.HTMLTemplateElement,
453
- Node = window.Node,
454
- Element = window.Element,
455
- NodeFilter = window.NodeFilter,
456
- _window$NamedNodeMap = window.NamedNodeMap,
457
- NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
458
- HTMLFormElement = window.HTMLFormElement,
459
- DOMParser = window.DOMParser,
460
- trustedTypes = window.trustedTypes;
461
- var ElementPrototype = Element.prototype;
462
- var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
463
- var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
464
- var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
465
- var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
318
+ let {
319
+ document
320
+ } = window;
321
+ const originalDocument = document;
322
+ const currentScript = originalDocument.currentScript;
323
+ const {
324
+ DocumentFragment,
325
+ HTMLTemplateElement,
326
+ Node,
327
+ Element,
328
+ NodeFilter,
329
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
330
+ HTMLFormElement,
331
+ DOMParser,
332
+ trustedTypes
333
+ } = window;
334
+ const ElementPrototype = Element.prototype;
335
+ const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
336
+ const remove = lookupGetter(ElementPrototype, 'remove');
337
+ const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
338
+ const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
339
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
340
+ // As per issue #47, the web-components registry is inherited by a
466
341
  // new document created via createHTMLDocument. As per the spec
467
342
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
468
343
  // a new empty registry is used when creating a template contents owner
469
344
  // document, so we use that as our parent document to ensure nothing
470
345
  // is inherited.
471
-
472
346
  if (typeof HTMLTemplateElement === 'function') {
473
- var template = document.createElement('template');
474
-
347
+ const template = document.createElement('template');
475
348
  if (template.content && template.content.ownerDocument) {
476
349
  document = template.content.ownerDocument;
477
350
  }
478
351
  }
479
-
480
- var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
481
-
482
- var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
483
- var _document = document,
484
- implementation = _document.implementation,
485
- createNodeIterator = _document.createNodeIterator,
486
- createDocumentFragment = _document.createDocumentFragment,
487
- getElementsByTagName = _document.getElementsByTagName;
488
- var importNode = originalDocument.importNode;
489
- var hooks = {};
352
+ let trustedTypesPolicy;
353
+ let emptyHTML = '';
354
+ const {
355
+ implementation,
356
+ createNodeIterator,
357
+ createDocumentFragment,
358
+ getElementsByTagName
359
+ } = document;
360
+ const {
361
+ importNode
362
+ } = originalDocument;
363
+ let hooks = _createHooksMap();
490
364
  /**
491
365
  * Expose whether this browser supports running the full DOMPurify.
492
366
  */
493
-
494
- DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined';
495
- var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
496
- ERB_EXPR$1 = ERB_EXPR,
497
- TMPLIT_EXPR$1 = TMPLIT_EXPR,
498
- DATA_ATTR$1 = DATA_ATTR,
499
- ARIA_ATTR$1 = ARIA_ATTR,
500
- IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
501
- ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
502
- var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
367
+ DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
368
+ const {
369
+ MUSTACHE_EXPR,
370
+ ERB_EXPR,
371
+ TMPLIT_EXPR,
372
+ DATA_ATTR,
373
+ ARIA_ATTR,
374
+ IS_SCRIPT_OR_DATA,
375
+ ATTR_WHITESPACE,
376
+ CUSTOM_ELEMENT
377
+ } = EXPRESSIONS;
378
+ let {
379
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
380
+ } = EXPRESSIONS;
503
381
  /**
504
382
  * We consider the elements and attributes below to be safe. Ideally
505
383
  * don't add any new ones but feel free to remove unwanted ones.
506
384
  */
507
-
508
385
  /* allowed element names */
509
-
510
- var ALLOWED_TAGS = null;
511
- var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
386
+ let ALLOWED_TAGS = null;
387
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
512
388
  /* Allowed attribute names */
513
-
514
- var ALLOWED_ATTR = null;
515
- var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
389
+ let ALLOWED_ATTR = null;
390
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
516
391
  /*
517
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
392
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
518
393
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
519
394
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
520
395
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
521
396
  */
522
-
523
- var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
397
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
524
398
  tagNameCheck: {
525
399
  writable: true,
526
400
  configurable: false,
@@ -541,58 +415,48 @@ function createDOMPurify() {
541
415
  }
542
416
  }));
543
417
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
544
-
545
- var FORBID_TAGS = null;
418
+ let FORBID_TAGS = null;
546
419
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
547
-
548
- var FORBID_ATTR = null;
420
+ let FORBID_ATTR = null;
549
421
  /* Decide if ARIA attributes are okay */
550
-
551
- var ALLOW_ARIA_ATTR = true;
422
+ let ALLOW_ARIA_ATTR = true;
552
423
  /* Decide if custom data attributes are okay */
553
-
554
- var ALLOW_DATA_ATTR = true;
424
+ let ALLOW_DATA_ATTR = true;
555
425
  /* Decide if unknown protocols are okay */
556
-
557
- var ALLOW_UNKNOWN_PROTOCOLS = false;
426
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
558
427
  /* Decide if self-closing tags in attributes are allowed.
559
428
  * Usually removed due to a mXSS issue in jQuery 3.0 */
560
-
561
- var ALLOW_SELF_CLOSE_IN_ATTR = true;
429
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
562
430
  /* Output should be safe for common template engines.
563
431
  * This means, DOMPurify removes data attributes, mustaches and ERB
564
432
  */
565
-
566
- var SAFE_FOR_TEMPLATES = false;
433
+ let SAFE_FOR_TEMPLATES = false;
434
+ /* Output should be safe even for XML used within HTML and alike.
435
+ * This means, DOMPurify removes comments when containing risky content.
436
+ */
437
+ let SAFE_FOR_XML = true;
567
438
  /* Decide if document with <html>... should be returned */
568
-
569
- var WHOLE_DOCUMENT = false;
439
+ let WHOLE_DOCUMENT = false;
570
440
  /* Track whether config is already set on this instance of DOMPurify. */
571
-
572
- var SET_CONFIG = false;
441
+ let SET_CONFIG = false;
573
442
  /* Decide if all elements (e.g. style, script) must be children of
574
443
  * document.body. By default, browsers might move them to document.head */
575
-
576
- var FORCE_BODY = false;
444
+ let FORCE_BODY = false;
577
445
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
578
446
  * string (or a TrustedHTML object if Trusted Types are supported).
579
447
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
580
448
  */
581
-
582
- var RETURN_DOM = false;
449
+ let RETURN_DOM = false;
583
450
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
584
451
  * string (or a TrustedHTML object if Trusted Types are supported) */
585
-
586
- var RETURN_DOM_FRAGMENT = false;
452
+ let RETURN_DOM_FRAGMENT = false;
587
453
  /* Try to return a Trusted Type object instead of a string, return a string in
588
454
  * case Trusted Types are not supported */
589
-
590
- var RETURN_TRUSTED_TYPE = false;
455
+ let RETURN_TRUSTED_TYPE = false;
591
456
  /* Output should be free from DOM clobbering attacks?
592
457
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
593
458
  */
594
-
595
- var SANITIZE_DOM = true;
459
+ let SANITIZE_DOM = true;
596
460
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
597
461
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
598
462
  *
@@ -606,180 +470,138 @@ function createDOMPurify() {
606
470
  * Namespace isolation is implemented by prefixing `id` and `name` attributes
607
471
  * with a constant string, i.e., `user-content-`
608
472
  */
609
-
610
- var SANITIZE_NAMED_PROPS = false;
611
- var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
473
+ let SANITIZE_NAMED_PROPS = false;
474
+ const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
612
475
  /* Keep element content when removing element? */
613
-
614
- var KEEP_CONTENT = true;
476
+ let KEEP_CONTENT = true;
615
477
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
616
478
  * of importing it into a new Document and returning a sanitized copy */
617
-
618
- var IN_PLACE = false;
479
+ let IN_PLACE = false;
619
480
  /* Allow usage of profiles like html, svg and mathMl */
620
-
621
- var USE_PROFILES = {};
481
+ let USE_PROFILES = {};
622
482
  /* Tags to ignore content of when KEEP_CONTENT is true */
623
-
624
- var FORBID_CONTENTS = null;
625
- var 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']);
483
+ let FORBID_CONTENTS = null;
484
+ 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']);
626
485
  /* Tags that are safe for data: URIs */
627
-
628
- var DATA_URI_TAGS = null;
629
- var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
486
+ let DATA_URI_TAGS = null;
487
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
630
488
  /* Attributes safe for values like "javascript:" */
631
-
632
- var URI_SAFE_ATTRIBUTES = null;
633
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
634
- var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
635
- var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
636
- var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
489
+ let URI_SAFE_ATTRIBUTES = null;
490
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
491
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
492
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
493
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
637
494
  /* Document namespace */
638
-
639
- var NAMESPACE = HTML_NAMESPACE;
640
- var IS_EMPTY_INPUT = false;
495
+ let NAMESPACE = HTML_NAMESPACE;
496
+ let IS_EMPTY_INPUT = false;
641
497
  /* Allowed XHTML+XML namespaces */
642
-
643
- var ALLOWED_NAMESPACES = null;
644
- var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
498
+ let ALLOWED_NAMESPACES = null;
499
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
500
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
501
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
502
+ // Certain elements are allowed in both SVG and HTML
503
+ // namespace. We need to specify them explicitly
504
+ // so that they don't get erroneously deleted from
505
+ // HTML namespace.
506
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
645
507
  /* Parsing of strict XHTML documents */
646
-
647
- var PARSER_MEDIA_TYPE;
648
- var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
649
- var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
650
- var transformCaseFunc;
508
+ let PARSER_MEDIA_TYPE = null;
509
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
510
+ const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
511
+ let transformCaseFunc = null;
651
512
  /* Keep a reference to config to pass to hooks */
652
-
653
- var CONFIG = null;
513
+ let CONFIG = null;
654
514
  /* Ideally, do not touch anything below this line */
655
-
656
515
  /* ______________________________________________ */
657
-
658
- var formElement = document.createElement('form');
659
-
660
- var isRegexOrFunction = function isRegexOrFunction(testValue) {
516
+ const formElement = document.createElement('form');
517
+ const isRegexOrFunction = function isRegexOrFunction(testValue) {
661
518
  return testValue instanceof RegExp || testValue instanceof Function;
662
519
  };
663
520
  /**
664
521
  * _parseConfig
665
522
  *
666
- * @param {Object} cfg optional config literal
523
+ * @param cfg optional config literal
667
524
  */
668
525
  // eslint-disable-next-line complexity
669
-
670
-
671
- var _parseConfig = function _parseConfig(cfg) {
526
+ const _parseConfig = function _parseConfig() {
527
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
672
528
  if (CONFIG && CONFIG === cfg) {
673
529
  return;
674
530
  }
675
531
  /* Shield configuration object from tampering */
676
-
677
-
678
- if (!cfg || _typeof(cfg) !== 'object') {
532
+ if (!cfg || typeof cfg !== 'object') {
679
533
  cfg = {};
680
534
  }
681
535
  /* Shield configuration object from prototype pollution */
682
-
683
-
684
536
  cfg = clone(cfg);
685
- PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
686
- SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
687
-
537
+ PARSER_MEDIA_TYPE =
538
+ // eslint-disable-next-line unicorn/prefer-includes
539
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
540
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
688
541
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
689
542
  /* Set configuration parameters */
690
-
691
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
692
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
693
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
694
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
695
- cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
696
- transformCaseFunc // eslint-disable-line indent
697
- ) // eslint-disable-line indent
698
- : DEFAULT_URI_SAFE_ATTRIBUTES;
699
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
700
- cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
701
- transformCaseFunc // eslint-disable-line indent
702
- ) // eslint-disable-line indent
703
- : DEFAULT_DATA_URI_TAGS;
704
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
705
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
706
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
707
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
543
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
544
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
545
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
546
+ 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;
547
+ 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;
548
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
549
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
550
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
551
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
708
552
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
709
-
710
553
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
711
-
712
554
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
713
-
714
555
  ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
715
-
716
556
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
717
-
557
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
718
558
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
719
-
720
559
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
721
-
722
560
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
723
-
724
561
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
725
-
726
562
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
727
-
728
563
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
729
-
730
564
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
731
-
732
565
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
733
-
734
566
  IN_PLACE = cfg.IN_PLACE || false; // Default false
735
-
736
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
567
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
737
568
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
569
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
570
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
738
571
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
739
-
740
572
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
741
573
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
742
574
  }
743
-
744
575
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
745
576
  CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
746
577
  }
747
-
748
578
  if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
749
579
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
750
580
  }
751
-
752
581
  if (SAFE_FOR_TEMPLATES) {
753
582
  ALLOW_DATA_ATTR = false;
754
583
  }
755
-
756
584
  if (RETURN_DOM_FRAGMENT) {
757
585
  RETURN_DOM = true;
758
586
  }
759
587
  /* Parse profile info */
760
-
761
-
762
588
  if (USE_PROFILES) {
763
- ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
589
+ ALLOWED_TAGS = addToSet({}, text);
764
590
  ALLOWED_ATTR = [];
765
-
766
591
  if (USE_PROFILES.html === true) {
767
592
  addToSet(ALLOWED_TAGS, html$1);
768
593
  addToSet(ALLOWED_ATTR, html);
769
594
  }
770
-
771
595
  if (USE_PROFILES.svg === true) {
772
596
  addToSet(ALLOWED_TAGS, svg$1);
773
597
  addToSet(ALLOWED_ATTR, svg);
774
598
  addToSet(ALLOWED_ATTR, xml);
775
599
  }
776
-
777
600
  if (USE_PROFILES.svgFilters === true) {
778
601
  addToSet(ALLOWED_TAGS, svgFilters);
779
602
  addToSet(ALLOWED_ATTR, svg);
780
603
  addToSet(ALLOWED_ATTR, xml);
781
604
  }
782
-
783
605
  if (USE_PROFILES.mathMl === true) {
784
606
  addToSet(ALLOWED_TAGS, mathMl$1);
785
607
  addToSet(ALLOWED_ATTR, mathMl);
@@ -787,146 +609,127 @@ function createDOMPurify() {
787
609
  }
788
610
  }
789
611
  /* Merge configuration parameters */
790
-
791
-
792
612
  if (cfg.ADD_TAGS) {
793
613
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
794
614
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
795
615
  }
796
-
797
616
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
798
617
  }
799
-
800
618
  if (cfg.ADD_ATTR) {
801
619
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
802
620
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
803
621
  }
804
-
805
622
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
806
623
  }
807
-
808
624
  if (cfg.ADD_URI_SAFE_ATTR) {
809
625
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
810
626
  }
811
-
812
627
  if (cfg.FORBID_CONTENTS) {
813
628
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
814
629
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
815
630
  }
816
-
817
631
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
818
632
  }
819
633
  /* Add #text in case KEEP_CONTENT is set to true */
820
-
821
-
822
634
  if (KEEP_CONTENT) {
823
635
  ALLOWED_TAGS['#text'] = true;
824
636
  }
825
637
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
826
-
827
-
828
638
  if (WHOLE_DOCUMENT) {
829
639
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
830
640
  }
831
641
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
832
-
833
-
834
642
  if (ALLOWED_TAGS.table) {
835
643
  addToSet(ALLOWED_TAGS, ['tbody']);
836
644
  delete FORBID_TAGS.tbody;
837
- } // Prevent further manipulation of configuration.
645
+ }
646
+ if (cfg.TRUSTED_TYPES_POLICY) {
647
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
648
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
649
+ }
650
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
651
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
652
+ }
653
+ // Overwrite existing TrustedTypes policy.
654
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
655
+ // Sign local variables required by `sanitize`.
656
+ emptyHTML = trustedTypesPolicy.createHTML('');
657
+ } else {
658
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
659
+ if (trustedTypesPolicy === undefined) {
660
+ trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
661
+ }
662
+ // If creating the internal policy succeeded sign internal variables.
663
+ if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
664
+ emptyHTML = trustedTypesPolicy.createHTML('');
665
+ }
666
+ }
667
+ // Prevent further manipulation of configuration.
838
668
  // Not available in IE8, Safari 5, etc.
839
-
840
-
841
669
  if (freeze) {
842
670
  freeze(cfg);
843
671
  }
844
-
845
672
  CONFIG = cfg;
846
673
  };
847
-
848
- var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
849
- var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
850
- // namespace. We need to specify them explicitly
851
- // so that they don't get erroneously deleted from
852
- // HTML namespace.
853
-
854
- var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
855
674
  /* Keep track of all possible SVG and MathML tags
856
675
  * so that we can perform the namespace checks
857
676
  * correctly. */
858
-
859
- var ALL_SVG_TAGS = addToSet({}, svg$1);
860
- addToSet(ALL_SVG_TAGS, svgFilters);
861
- addToSet(ALL_SVG_TAGS, svgDisallowed);
862
- var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
863
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
677
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
678
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
864
679
  /**
865
- *
866
- *
867
- * @param {Element} element a DOM element whose namespace is being checked
868
- * @returns {boolean} Return false if the element has a
680
+ * @param element a DOM element whose namespace is being checked
681
+ * @returns Return false if the element has a
869
682
  * namespace that a spec-compliant parser would never
870
683
  * return. Return true otherwise.
871
684
  */
872
-
873
- var _checkValidNamespace = function _checkValidNamespace(element) {
874
- var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
685
+ const _checkValidNamespace = function _checkValidNamespace(element) {
686
+ let parent = getParentNode(element);
687
+ // In JSDOM, if we're inside shadow DOM, then parentNode
875
688
  // can be null. We just simulate parent in this case.
876
-
877
689
  if (!parent || !parent.tagName) {
878
690
  parent = {
879
691
  namespaceURI: NAMESPACE,
880
692
  tagName: 'template'
881
693
  };
882
694
  }
883
-
884
- var tagName = stringToLowerCase(element.tagName);
885
- var parentTagName = stringToLowerCase(parent.tagName);
886
-
695
+ const tagName = stringToLowerCase(element.tagName);
696
+ const parentTagName = stringToLowerCase(parent.tagName);
887
697
  if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
888
698
  return false;
889
699
  }
890
-
891
700
  if (element.namespaceURI === SVG_NAMESPACE) {
892
701
  // The only way to switch from HTML namespace to SVG
893
702
  // is via <svg>. If it happens via any other tag, then
894
703
  // it should be killed.
895
704
  if (parent.namespaceURI === HTML_NAMESPACE) {
896
705
  return tagName === 'svg';
897
- } // The only way to switch from MathML to SVG is via`
706
+ }
707
+ // The only way to switch from MathML to SVG is via`
898
708
  // svg if parent is either <annotation-xml> or MathML
899
709
  // text integration points.
900
-
901
-
902
710
  if (parent.namespaceURI === MATHML_NAMESPACE) {
903
711
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
904
- } // We only allow elements that are defined in SVG
712
+ }
713
+ // We only allow elements that are defined in SVG
905
714
  // spec. All others are disallowed in SVG namespace.
906
-
907
-
908
715
  return Boolean(ALL_SVG_TAGS[tagName]);
909
716
  }
910
-
911
717
  if (element.namespaceURI === MATHML_NAMESPACE) {
912
718
  // The only way to switch from HTML namespace to MathML
913
719
  // is via <math>. If it happens via any other tag, then
914
720
  // it should be killed.
915
721
  if (parent.namespaceURI === HTML_NAMESPACE) {
916
722
  return tagName === 'math';
917
- } // The only way to switch from SVG to MathML is via
723
+ }
724
+ // The only way to switch from SVG to MathML is via
918
725
  // <math> and HTML integration points
919
-
920
-
921
726
  if (parent.namespaceURI === SVG_NAMESPACE) {
922
727
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
923
- } // We only allow elements that are defined in MathML
728
+ }
729
+ // We only allow elements that are defined in MathML
924
730
  // spec. All others are disallowed in MathML namespace.
925
-
926
-
927
731
  return Boolean(ALL_MATHML_TAGS[tagName]);
928
732
  }
929
-
930
733
  if (element.namespaceURI === HTML_NAMESPACE) {
931
734
  // The only way to switch from SVG to HTML is via
932
735
  // HTML integration points, and from MathML to HTML
@@ -934,77 +737,67 @@ function createDOMPurify() {
934
737
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
935
738
  return false;
936
739
  }
937
-
938
740
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
939
741
  return false;
940
- } // We disallow tags that are specific for MathML
742
+ }
743
+ // We disallow tags that are specific for MathML
941
744
  // or SVG and should never appear in HTML namespace
942
-
943
-
944
745
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
945
- } // For XHTML and XML documents that support custom namespaces
946
-
947
-
746
+ }
747
+ // For XHTML and XML documents that support custom namespaces
948
748
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
949
749
  return true;
950
- } // The code should never reach this place (this means
750
+ }
751
+ // The code should never reach this place (this means
951
752
  // that the element somehow got namespace that is not
952
753
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
953
754
  // Return false just in case.
954
-
955
-
956
755
  return false;
957
756
  };
958
757
  /**
959
758
  * _forceRemove
960
759
  *
961
- * @param {Node} node a DOM node
760
+ * @param node a DOM node
962
761
  */
963
-
964
-
965
- var _forceRemove = function _forceRemove(node) {
762
+ const _forceRemove = function _forceRemove(node) {
966
763
  arrayPush(DOMPurify.removed, {
967
764
  element: node
968
765
  });
969
-
970
766
  try {
971
767
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
972
- node.parentNode.removeChild(node);
768
+ getParentNode(node).removeChild(node);
973
769
  } catch (_) {
974
- node.remove();
770
+ remove(node);
975
771
  }
976
772
  };
977
773
  /**
978
774
  * _removeAttribute
979
775
  *
980
- * @param {String} name an Attribute name
981
- * @param {Node} node a DOM node
776
+ * @param name an Attribute name
777
+ * @param element a DOM node
982
778
  */
983
-
984
-
985
- var _removeAttribute = function _removeAttribute(name, node) {
779
+ const _removeAttribute = function _removeAttribute(name, element) {
986
780
  try {
987
781
  arrayPush(DOMPurify.removed, {
988
- attribute: node.getAttributeNode(name),
989
- from: node
782
+ attribute: element.getAttributeNode(name),
783
+ from: element
990
784
  });
991
785
  } catch (_) {
992
786
  arrayPush(DOMPurify.removed, {
993
787
  attribute: null,
994
- from: node
788
+ from: element
995
789
  });
996
790
  }
997
-
998
- node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
999
-
1000
- if (name === 'is' && !ALLOWED_ATTR[name]) {
791
+ element.removeAttribute(name);
792
+ // We void attribute values for unremovable "is" attributes
793
+ if (name === 'is') {
1001
794
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1002
795
  try {
1003
- _forceRemove(node);
796
+ _forceRemove(element);
1004
797
  } catch (_) {}
1005
798
  } else {
1006
799
  try {
1007
- node.setAttribute(name, '');
800
+ element.setAttribute(name, '');
1008
801
  } catch (_) {}
1009
802
  }
1010
803
  }
@@ -1012,217 +805,171 @@ function createDOMPurify() {
1012
805
  /**
1013
806
  * _initDocument
1014
807
  *
1015
- * @param {String} dirty a string of dirty markup
1016
- * @return {Document} a DOM, filled with the dirty markup
808
+ * @param dirty - a string of dirty markup
809
+ * @return a DOM, filled with the dirty markup
1017
810
  */
1018
-
1019
-
1020
- var _initDocument = function _initDocument(dirty) {
811
+ const _initDocument = function _initDocument(dirty) {
1021
812
  /* Create a HTML document */
1022
- var doc;
1023
- var leadingWhitespace;
1024
-
813
+ let doc = null;
814
+ let leadingWhitespace = null;
1025
815
  if (FORCE_BODY) {
1026
816
  dirty = '<remove></remove>' + dirty;
1027
817
  } else {
1028
818
  /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
1029
- var matches = stringMatch(dirty, /^[\r\n\t ]+/);
819
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1030
820
  leadingWhitespace = matches && matches[0];
1031
821
  }
1032
-
1033
822
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
1034
823
  // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1035
824
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
1036
825
  }
1037
-
1038
- var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
826
+ const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
1039
827
  /*
1040
828
  * Use the DOMParser API by default, fallback later if needs be
1041
829
  * DOMParser not work for svg when has multiple root element.
1042
830
  */
1043
-
1044
831
  if (NAMESPACE === HTML_NAMESPACE) {
1045
832
  try {
1046
833
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1047
834
  } catch (_) {}
1048
835
  }
1049
836
  /* Use createHTMLDocument in case DOMParser is not available */
1050
-
1051
-
1052
837
  if (!doc || !doc.documentElement) {
1053
838
  doc = implementation.createDocument(NAMESPACE, 'template', null);
1054
-
1055
839
  try {
1056
840
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1057
- } catch (_) {// Syntax error if dirtyPayload is invalid xml
841
+ } catch (_) {
842
+ // Syntax error if dirtyPayload is invalid xml
1058
843
  }
1059
844
  }
1060
-
1061
- var body = doc.body || doc.documentElement;
1062
-
845
+ const body = doc.body || doc.documentElement;
1063
846
  if (dirty && leadingWhitespace) {
1064
847
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1065
848
  }
1066
849
  /* Work on whole document or just its body */
1067
-
1068
-
1069
850
  if (NAMESPACE === HTML_NAMESPACE) {
1070
851
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
1071
852
  }
1072
-
1073
853
  return WHOLE_DOCUMENT ? doc.documentElement : body;
1074
854
  };
1075
855
  /**
1076
- * _createIterator
856
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
1077
857
  *
1078
- * @param {Document} root document/fragment to create iterator for
1079
- * @return {Iterator} iterator instance
858
+ * @param root The root element or node to start traversing on.
859
+ * @return The created NodeIterator
1080
860
  */
1081
-
1082
-
1083
- var _createIterator = function _createIterator(root) {
1084
- return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
1085
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
861
+ const _createNodeIterator = function _createNodeIterator(root) {
862
+ return createNodeIterator.call(root.ownerDocument || root, root,
863
+ // eslint-disable-next-line no-bitwise
864
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
1086
865
  };
1087
866
  /**
1088
867
  * _isClobbered
1089
868
  *
1090
- * @param {Node} elm element to check for clobbering attacks
1091
- * @return {Boolean} true if clobbered, false if safe
869
+ * @param element element to check for clobbering attacks
870
+ * @return true if clobbered, false if safe
1092
871
  */
1093
-
1094
-
1095
- var _isClobbered = function _isClobbered(elm) {
1096
- return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
872
+ const _isClobbered = function _isClobbered(element) {
873
+ 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');
1097
874
  };
1098
875
  /**
1099
- * _isNode
876
+ * Checks whether the given object is a DOM node.
1100
877
  *
1101
- * @param {Node} obj object to check whether it's a DOM node
1102
- * @return {Boolean} true is object is a DOM node
878
+ * @param value object to check whether it's a DOM node
879
+ * @return true is object is a DOM node
1103
880
  */
1104
-
1105
-
1106
- var _isNode = function _isNode(object) {
1107
- return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
881
+ const _isNode = function _isNode(value) {
882
+ return typeof Node === 'function' && value instanceof Node;
1108
883
  };
1109
- /**
1110
- * _executeHook
1111
- * Execute user configurable hooks
1112
- *
1113
- * @param {String} entryPoint Name of the hook's entry point
1114
- * @param {Node} currentNode node to work on with the hook
1115
- * @param {Object} data additional hook parameters
1116
- */
1117
-
1118
-
1119
- var _executeHook = function _executeHook(entryPoint, currentNode, data) {
1120
- if (!hooks[entryPoint]) {
1121
- return;
1122
- }
1123
-
1124
- arrayForEach(hooks[entryPoint], function (hook) {
884
+ function _executeHooks(hooks, currentNode, data) {
885
+ arrayForEach(hooks, hook => {
1125
886
  hook.call(DOMPurify, currentNode, data, CONFIG);
1126
887
  });
1127
- };
888
+ }
1128
889
  /**
1129
890
  * _sanitizeElements
1130
891
  *
1131
892
  * @protect nodeName
1132
893
  * @protect textContent
1133
894
  * @protect removeChild
1134
- *
1135
- * @param {Node} currentNode to check for permission to exist
1136
- * @return {Boolean} true if node was killed, false if left alive
895
+ * @param currentNode to check for permission to exist
896
+ * @return true if node was killed, false if left alive
1137
897
  */
1138
-
1139
-
1140
- var _sanitizeElements = function _sanitizeElements(currentNode) {
1141
- var content;
898
+ const _sanitizeElements = function _sanitizeElements(currentNode) {
899
+ let content = null;
1142
900
  /* Execute a hook if present */
1143
-
1144
- _executeHook('beforeSanitizeElements', currentNode, null);
901
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1145
902
  /* Check if element is clobbered or can clobber */
1146
-
1147
-
1148
903
  if (_isClobbered(currentNode)) {
1149
904
  _forceRemove(currentNode);
1150
-
1151
905
  return true;
1152
906
  }
1153
907
  /* Now let's check the element's type and name */
1154
-
1155
-
1156
- var tagName = transformCaseFunc(currentNode.nodeName);
908
+ const tagName = transformCaseFunc(currentNode.nodeName);
1157
909
  /* Execute a hook if present */
1158
-
1159
- _executeHook('uponSanitizeElement', currentNode, {
1160
- tagName: tagName,
910
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
911
+ tagName,
1161
912
  allowedTags: ALLOWED_TAGS
1162
913
  });
1163
914
  /* Detect mXSS attempts abusing namespace confusion */
1164
-
1165
-
1166
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
915
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
916
+ _forceRemove(currentNode);
917
+ return true;
918
+ }
919
+ /* Remove any occurrence of processing instructions */
920
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
921
+ _forceRemove(currentNode);
922
+ return true;
923
+ }
924
+ /* Remove any kind of possibly harmful comments */
925
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1167
926
  _forceRemove(currentNode);
1168
-
1169
927
  return true;
1170
928
  }
1171
929
  /* Remove element if anything forbids its presence */
1172
-
1173
-
1174
930
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1175
931
  /* Check if we have a custom element to handle */
1176
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1177
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1178
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
932
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
933
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
934
+ return false;
935
+ }
936
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
937
+ return false;
938
+ }
1179
939
  }
1180
940
  /* Keep content except for bad-listed elements */
1181
-
1182
-
1183
941
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1184
- var parentNode = getParentNode(currentNode) || currentNode.parentNode;
1185
- var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1186
-
942
+ const parentNode = getParentNode(currentNode) || currentNode.parentNode;
943
+ const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1187
944
  if (childNodes && parentNode) {
1188
- var childCount = childNodes.length;
1189
-
1190
- for (var i = childCount - 1; i >= 0; --i) {
1191
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
945
+ const childCount = childNodes.length;
946
+ for (let i = childCount - 1; i >= 0; --i) {
947
+ const childClone = cloneNode(childNodes[i], true);
948
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
949
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
1192
950
  }
1193
951
  }
1194
952
  }
1195
-
1196
953
  _forceRemove(currentNode);
1197
-
1198
954
  return true;
1199
955
  }
1200
956
  /* Check whether element has a valid namespace */
1201
-
1202
-
1203
957
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1204
958
  _forceRemove(currentNode);
1205
-
1206
959
  return true;
1207
960
  }
1208
- /* Make sure that older browsers don't get noscript mXSS */
1209
-
1210
-
1211
- if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
961
+ /* Make sure that older browsers don't get fallback-tag mXSS */
962
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1212
963
  _forceRemove(currentNode);
1213
-
1214
964
  return true;
1215
965
  }
1216
966
  /* Sanitize element content to be template-safe */
1217
-
1218
-
1219
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
967
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1220
968
  /* Get the element's text content */
1221
969
  content = currentNode.textContent;
1222
- content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
1223
- content = stringReplace(content, ERB_EXPR$1, ' ');
1224
- content = stringReplace(content, TMPLIT_EXPR$1, ' ');
1225
-
970
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
971
+ content = stringReplace(content, expr, ' ');
972
+ });
1226
973
  if (currentNode.textContent !== content) {
1227
974
  arrayPush(DOMPurify.removed, {
1228
975
  element: currentNode.cloneNode()
@@ -1231,24 +978,19 @@ function createDOMPurify() {
1231
978
  }
1232
979
  }
1233
980
  /* Execute a hook if present */
1234
-
1235
-
1236
- _executeHook('afterSanitizeElements', currentNode, null);
1237
-
981
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
1238
982
  return false;
1239
983
  };
1240
984
  /**
1241
985
  * _isValidAttribute
1242
986
  *
1243
- * @param {string} lcTag Lowercase tag name of containing element.
1244
- * @param {string} lcName Lowercase attribute name.
1245
- * @param {string} value Attribute value.
1246
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
987
+ * @param lcTag Lowercase tag name of containing element.
988
+ * @param lcName Lowercase attribute name.
989
+ * @param value Attribute value.
990
+ * @return Returns true if `value` is valid, otherwise false.
1247
991
  */
1248
992
  // eslint-disable-next-line complexity
1249
-
1250
-
1251
- var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
993
+ const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1252
994
  /* Make sure attribute cannot clobber */
1253
995
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1254
996
  return false;
@@ -1257,35 +999,33 @@ function createDOMPurify() {
1257
999
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1258
1000
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1259
1001
  We don't need to check the value; it's always URI safe. */
1260
-
1261
-
1262
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1263
- if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1002
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1003
+ if (
1004
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1264
1005
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1265
1006
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1266
- _basicCustomElementTest(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)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1007
+ _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)) ||
1008
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1267
1009
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1268
1010
  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 {
1269
1011
  return false;
1270
1012
  }
1271
1013
  /* Check value is safe. First, is attr inert? If so, is safe */
1272
-
1273
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; 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$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (!value) ; else {
1014
+ } 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) {
1274
1015
  return false;
1275
- }
1276
-
1016
+ } else ;
1277
1017
  return true;
1278
1018
  };
1279
1019
  /**
1280
- * _basicCustomElementCheck
1020
+ * _isBasicCustomElement
1281
1021
  * checks if at least one dash is included in tagName, and it's not the first char
1282
1022
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1283
- * @param {string} tagName name of the tag of the node to sanitize
1023
+ *
1024
+ * @param tagName name of the tag of the node to sanitize
1025
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1284
1026
  */
1285
-
1286
-
1287
- var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1288
- return tagName.indexOf('-') > 0;
1027
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1028
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1289
1029
  };
1290
1030
  /**
1291
1031
  * _sanitizeAttributes
@@ -1295,246 +1035,196 @@ function createDOMPurify() {
1295
1035
  * @protect removeAttribute
1296
1036
  * @protect setAttribute
1297
1037
  *
1298
- * @param {Node} currentNode to sanitize
1038
+ * @param currentNode to sanitize
1299
1039
  */
1300
-
1301
-
1302
- var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1303
- var attr;
1304
- var value;
1305
- var lcName;
1306
- var l;
1040
+ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1307
1041
  /* Execute a hook if present */
1308
-
1309
- _executeHook('beforeSanitizeAttributes', currentNode, null);
1310
-
1311
- var attributes = currentNode.attributes;
1042
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
1043
+ const {
1044
+ attributes
1045
+ } = currentNode;
1312
1046
  /* Check if we have attributes; if not we might have a text node */
1313
-
1314
- if (!attributes) {
1047
+ if (!attributes || _isClobbered(currentNode)) {
1315
1048
  return;
1316
1049
  }
1317
-
1318
- var hookEvent = {
1050
+ const hookEvent = {
1319
1051
  attrName: '',
1320
1052
  attrValue: '',
1321
1053
  keepAttr: true,
1322
- allowedAttributes: ALLOWED_ATTR
1054
+ allowedAttributes: ALLOWED_ATTR,
1055
+ forceKeepAttr: undefined
1323
1056
  };
1324
- l = attributes.length;
1057
+ let l = attributes.length;
1325
1058
  /* Go backwards over all attributes; safely remove bad ones */
1326
-
1327
1059
  while (l--) {
1328
- attr = attributes[l];
1329
- var _attr = attr,
1330
- name = _attr.name,
1331
- namespaceURI = _attr.namespaceURI;
1332
- value = name === 'value' ? attr.value : stringTrim(attr.value);
1333
- lcName = transformCaseFunc(name);
1060
+ const attr = attributes[l];
1061
+ const {
1062
+ name,
1063
+ namespaceURI,
1064
+ value: attrValue
1065
+ } = attr;
1066
+ const lcName = transformCaseFunc(name);
1067
+ const initValue = attrValue;
1068
+ let value = name === 'value' ? initValue : stringTrim(initValue);
1334
1069
  /* Execute a hook if present */
1335
-
1336
1070
  hookEvent.attrName = lcName;
1337
1071
  hookEvent.attrValue = value;
1338
1072
  hookEvent.keepAttr = true;
1339
1073
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1340
-
1341
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1342
-
1074
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
1343
1075
  value = hookEvent.attrValue;
1076
+ /* Full DOM Clobbering protection via namespace isolation,
1077
+ * Prefix id and name attributes with `user-content-`
1078
+ */
1079
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1080
+ // Remove the attribute with this value
1081
+ _removeAttribute(name, currentNode);
1082
+ // Prefix the value and later re-create the attribute with the sanitized value
1083
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
1084
+ }
1085
+ /* Work around a security issue with comments inside attributes */
1086
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title|textarea)/i, value)) {
1087
+ _removeAttribute(name, currentNode);
1088
+ continue;
1089
+ }
1090
+ /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
1091
+ if (lcName === 'attributename' && stringMatch(value, 'href')) {
1092
+ _removeAttribute(name, currentNode);
1093
+ continue;
1094
+ }
1344
1095
  /* Did the hooks approve of the attribute? */
1345
-
1346
1096
  if (hookEvent.forceKeepAttr) {
1347
1097
  continue;
1348
1098
  }
1349
- /* Remove attribute */
1350
-
1351
-
1352
- _removeAttribute(name, currentNode);
1353
1099
  /* Did the hooks approve of the attribute? */
1354
-
1355
-
1356
1100
  if (!hookEvent.keepAttr) {
1101
+ _removeAttribute(name, currentNode);
1357
1102
  continue;
1358
1103
  }
1359
1104
  /* Work around a security issue in jQuery 3.0 */
1360
-
1361
-
1362
1105
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1363
1106
  _removeAttribute(name, currentNode);
1364
-
1365
1107
  continue;
1366
1108
  }
1367
1109
  /* Sanitize attribute content to be template-safe */
1368
-
1369
-
1370
1110
  if (SAFE_FOR_TEMPLATES) {
1371
- value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1372
- value = stringReplace(value, ERB_EXPR$1, ' ');
1373
- value = stringReplace(value, TMPLIT_EXPR$1, ' ');
1111
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1112
+ value = stringReplace(value, expr, ' ');
1113
+ });
1374
1114
  }
1375
1115
  /* Is `value` valid for this attribute? */
1376
-
1377
-
1378
- var lcTag = transformCaseFunc(currentNode.nodeName);
1379
-
1116
+ const lcTag = transformCaseFunc(currentNode.nodeName);
1380
1117
  if (!_isValidAttribute(lcTag, lcName, value)) {
1118
+ _removeAttribute(name, currentNode);
1381
1119
  continue;
1382
1120
  }
1383
- /* Full DOM Clobbering protection via namespace isolation,
1384
- * Prefix id and name attributes with `user-content-`
1385
- */
1386
-
1387
-
1388
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1389
- // Remove the attribute with this value
1390
- _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1391
-
1392
-
1393
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
1394
- }
1395
1121
  /* Handle attributes that require Trusted Types */
1396
-
1397
-
1398
- if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1122
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1399
1123
  if (namespaceURI) ; else {
1400
1124
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1401
1125
  case 'TrustedHTML':
1402
- value = trustedTypesPolicy.createHTML(value);
1403
- break;
1404
-
1126
+ {
1127
+ value = trustedTypesPolicy.createHTML(value);
1128
+ break;
1129
+ }
1405
1130
  case 'TrustedScriptURL':
1406
- value = trustedTypesPolicy.createScriptURL(value);
1407
- break;
1131
+ {
1132
+ value = trustedTypesPolicy.createScriptURL(value);
1133
+ break;
1134
+ }
1408
1135
  }
1409
1136
  }
1410
1137
  }
1411
1138
  /* Handle invalid data-* attribute set by try-catching it */
1412
-
1413
-
1414
- try {
1415
- if (namespaceURI) {
1416
- currentNode.setAttributeNS(namespaceURI, name, value);
1417
- } else {
1418
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1419
- currentNode.setAttribute(name, value);
1139
+ if (value !== initValue) {
1140
+ try {
1141
+ if (namespaceURI) {
1142
+ currentNode.setAttributeNS(namespaceURI, name, value);
1143
+ } else {
1144
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1145
+ currentNode.setAttribute(name, value);
1146
+ }
1147
+ if (_isClobbered(currentNode)) {
1148
+ _forceRemove(currentNode);
1149
+ } else {
1150
+ arrayPop(DOMPurify.removed);
1151
+ }
1152
+ } catch (_) {
1153
+ _removeAttribute(name, currentNode);
1420
1154
  }
1421
-
1422
- arrayPop(DOMPurify.removed);
1423
- } catch (_) {}
1155
+ }
1424
1156
  }
1425
1157
  /* Execute a hook if present */
1426
-
1427
-
1428
- _executeHook('afterSanitizeAttributes', currentNode, null);
1158
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
1429
1159
  };
1430
1160
  /**
1431
1161
  * _sanitizeShadowDOM
1432
1162
  *
1433
- * @param {DocumentFragment} fragment to iterate over recursively
1163
+ * @param fragment to iterate over recursively
1434
1164
  */
1435
-
1436
-
1437
- var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1438
- var shadowNode;
1439
-
1440
- var shadowIterator = _createIterator(fragment);
1165
+ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1166
+ let shadowNode = null;
1167
+ const shadowIterator = _createNodeIterator(fragment);
1441
1168
  /* Execute a hook if present */
1442
-
1443
-
1444
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
1445
-
1169
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
1446
1170
  while (shadowNode = shadowIterator.nextNode()) {
1447
1171
  /* Execute a hook if present */
1448
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
1172
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
1449
1173
  /* Sanitize tags and elements */
1450
-
1451
-
1452
- if (_sanitizeElements(shadowNode)) {
1453
- continue;
1454
- }
1174
+ _sanitizeElements(shadowNode);
1175
+ /* Check attributes next */
1176
+ _sanitizeAttributes(shadowNode);
1455
1177
  /* Deep shadow DOM detected */
1456
-
1457
-
1458
1178
  if (shadowNode.content instanceof DocumentFragment) {
1459
1179
  _sanitizeShadowDOM(shadowNode.content);
1460
1180
  }
1461
- /* Check attributes, sanitize if necessary */
1462
-
1463
-
1464
- _sanitizeAttributes(shadowNode);
1465
1181
  }
1466
1182
  /* Execute a hook if present */
1467
-
1468
-
1469
- _executeHook('afterSanitizeShadowDOM', fragment, null);
1183
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
1470
1184
  };
1471
- /**
1472
- * Sanitize
1473
- * Public method providing core sanitation functionality
1474
- *
1475
- * @param {String|Node} dirty string or DOM node
1476
- * @param {Object} configuration object
1477
- */
1478
1185
  // eslint-disable-next-line complexity
1479
-
1480
-
1481
1186
  DOMPurify.sanitize = function (dirty) {
1482
- var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1483
- var body;
1484
- var importedNode;
1485
- var currentNode;
1486
- var returnNode;
1187
+ let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1188
+ let body = null;
1189
+ let importedNode = null;
1190
+ let currentNode = null;
1191
+ let returnNode = null;
1487
1192
  /* Make sure we have a string to sanitize.
1488
1193
  DO NOT return early, as this will return the wrong type if
1489
1194
  the user has requested a DOM object rather than a string */
1490
-
1491
1195
  IS_EMPTY_INPUT = !dirty;
1492
-
1493
1196
  if (IS_EMPTY_INPUT) {
1494
1197
  dirty = '<!-->';
1495
1198
  }
1496
1199
  /* Stringify, in case dirty is an object */
1497
-
1498
-
1499
1200
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1500
- // eslint-disable-next-line no-negated-condition
1501
- if (typeof dirty.toString !== 'function') {
1502
- throw typeErrorCreate('toString is not a function');
1503
- } else {
1201
+ if (typeof dirty.toString === 'function') {
1504
1202
  dirty = dirty.toString();
1505
-
1506
1203
  if (typeof dirty !== 'string') {
1507
1204
  throw typeErrorCreate('dirty is not a string, aborting');
1508
1205
  }
1206
+ } else {
1207
+ throw typeErrorCreate('toString is not a function');
1509
1208
  }
1510
1209
  }
1511
1210
  /* Return dirty HTML if DOMPurify cannot run */
1512
-
1513
-
1514
1211
  if (!DOMPurify.isSupported) {
1515
1212
  return dirty;
1516
1213
  }
1517
1214
  /* Assign config vars */
1518
-
1519
-
1520
1215
  if (!SET_CONFIG) {
1521
1216
  _parseConfig(cfg);
1522
1217
  }
1523
1218
  /* Clean up removed elements */
1524
-
1525
-
1526
1219
  DOMPurify.removed = [];
1527
1220
  /* Check if dirty is correctly typed for IN_PLACE */
1528
-
1529
1221
  if (typeof dirty === 'string') {
1530
1222
  IN_PLACE = false;
1531
1223
  }
1532
-
1533
1224
  if (IN_PLACE) {
1534
1225
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1535
1226
  if (dirty.nodeName) {
1536
- var tagName = transformCaseFunc(dirty.nodeName);
1537
-
1227
+ const tagName = transformCaseFunc(dirty.nodeName);
1538
1228
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1539
1229
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1540
1230
  }
@@ -1544,8 +1234,7 @@ function createDOMPurify() {
1544
1234
  elements being stripped by the parser */
1545
1235
  body = _initDocument('<!---->');
1546
1236
  importedNode = body.ownerDocument.importNode(dirty, true);
1547
-
1548
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1237
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1549
1238
  /* Node is already a body, use as is */
1550
1239
  body = importedNode;
1551
1240
  } else if (importedNode.nodeName === 'HTML') {
@@ -1556,62 +1245,43 @@ function createDOMPurify() {
1556
1245
  }
1557
1246
  } else {
1558
1247
  /* Exit directly if we have nothing to do */
1559
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1248
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1249
+ // eslint-disable-next-line unicorn/prefer-includes
1560
1250
  dirty.indexOf('<') === -1) {
1561
1251
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1562
1252
  }
1563
1253
  /* Initialize the document to work on */
1564
-
1565
-
1566
1254
  body = _initDocument(dirty);
1567
1255
  /* Check we have a DOM node from the data */
1568
-
1569
1256
  if (!body) {
1570
1257
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1571
1258
  }
1572
1259
  }
1573
1260
  /* Remove first element node (ours) if FORCE_BODY is set */
1574
-
1575
-
1576
1261
  if (body && FORCE_BODY) {
1577
1262
  _forceRemove(body.firstChild);
1578
1263
  }
1579
1264
  /* Get node iterator */
1580
-
1581
-
1582
- var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1265
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1583
1266
  /* Now start iterating over the created document */
1584
-
1585
-
1586
1267
  while (currentNode = nodeIterator.nextNode()) {
1587
1268
  /* Sanitize tags and elements */
1588
- if (_sanitizeElements(currentNode)) {
1589
- continue;
1590
- }
1269
+ _sanitizeElements(currentNode);
1270
+ /* Check attributes next */
1271
+ _sanitizeAttributes(currentNode);
1591
1272
  /* Shadow DOM detected, sanitize it */
1592
-
1593
-
1594
1273
  if (currentNode.content instanceof DocumentFragment) {
1595
1274
  _sanitizeShadowDOM(currentNode.content);
1596
1275
  }
1597
- /* Check attributes, sanitize if necessary */
1598
-
1599
-
1600
- _sanitizeAttributes(currentNode);
1601
1276
  }
1602
1277
  /* If we sanitized `dirty` in-place, return it. */
1603
-
1604
-
1605
1278
  if (IN_PLACE) {
1606
1279
  return dirty;
1607
1280
  }
1608
1281
  /* Return sanitized string or DOM */
1609
-
1610
-
1611
1282
  if (RETURN_DOM) {
1612
1283
  if (RETURN_DOM_FRAGMENT) {
1613
1284
  returnNode = createDocumentFragment.call(body.ownerDocument);
1614
-
1615
1285
  while (body.firstChild) {
1616
1286
  // eslint-disable-next-line unicorn/prefer-dom-node-append
1617
1287
  returnNode.appendChild(body.firstChild);
@@ -1619,8 +1289,7 @@ function createDOMPurify() {
1619
1289
  } else {
1620
1290
  returnNode = body;
1621
1291
  }
1622
-
1623
- if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
1292
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
1624
1293
  /*
1625
1294
  AdoptNode() is not used because internal state is not reset
1626
1295
  (e.g. the past names map of a HTMLFormElement), this is safe
@@ -1630,132 +1299,60 @@ function createDOMPurify() {
1630
1299
  */
1631
1300
  returnNode = importNode.call(originalDocument, returnNode, true);
1632
1301
  }
1633
-
1634
1302
  return returnNode;
1635
1303
  }
1636
-
1637
- var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1304
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1638
1305
  /* Serialize doctype if allowed */
1639
-
1640
1306
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1641
1307
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1642
1308
  }
1643
1309
  /* Sanitize final string template-safe */
1644
-
1645
-
1646
1310
  if (SAFE_FOR_TEMPLATES) {
1647
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
1648
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
1649
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
1311
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1312
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
1313
+ });
1650
1314
  }
1651
-
1652
1315
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1653
1316
  };
1654
- /**
1655
- * Public method to set the configuration once
1656
- * setConfig
1657
- *
1658
- * @param {Object} cfg configuration object
1659
- */
1660
-
1661
-
1662
- DOMPurify.setConfig = function (cfg) {
1317
+ DOMPurify.setConfig = function () {
1318
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1663
1319
  _parseConfig(cfg);
1664
-
1665
1320
  SET_CONFIG = true;
1666
1321
  };
1667
- /**
1668
- * Public method to remove the configuration
1669
- * clearConfig
1670
- *
1671
- */
1672
-
1673
-
1674
1322
  DOMPurify.clearConfig = function () {
1675
1323
  CONFIG = null;
1676
1324
  SET_CONFIG = false;
1677
1325
  };
1678
- /**
1679
- * Public method to check if an attribute value is valid.
1680
- * Uses last set config, if any. Otherwise, uses config defaults.
1681
- * isValidAttribute
1682
- *
1683
- * @param {string} tag Tag name of containing element.
1684
- * @param {string} attr Attribute name.
1685
- * @param {string} value Attribute value.
1686
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1687
- */
1688
-
1689
-
1690
1326
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1691
1327
  /* Initialize shared config vars if necessary. */
1692
1328
  if (!CONFIG) {
1693
1329
  _parseConfig({});
1694
1330
  }
1695
-
1696
- var lcTag = transformCaseFunc(tag);
1697
- var lcName = transformCaseFunc(attr);
1331
+ const lcTag = transformCaseFunc(tag);
1332
+ const lcName = transformCaseFunc(attr);
1698
1333
  return _isValidAttribute(lcTag, lcName, value);
1699
1334
  };
1700
- /**
1701
- * AddHook
1702
- * Public method to add DOMPurify hooks
1703
- *
1704
- * @param {String} entryPoint entry point for the hook to add
1705
- * @param {Function} hookFunction function to execute
1706
- */
1707
-
1708
-
1709
1335
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1710
1336
  if (typeof hookFunction !== 'function') {
1711
1337
  return;
1712
1338
  }
1713
-
1714
- hooks[entryPoint] = hooks[entryPoint] || [];
1715
1339
  arrayPush(hooks[entryPoint], hookFunction);
1716
1340
  };
1717
- /**
1718
- * RemoveHook
1719
- * Public method to remove a DOMPurify hook at a given entryPoint
1720
- * (pops it from the stack of hooks if more are present)
1721
- *
1722
- * @param {String} entryPoint entry point for the hook to remove
1723
- * @return {Function} removed(popped) hook
1724
- */
1725
-
1726
-
1727
- DOMPurify.removeHook = function (entryPoint) {
1728
- if (hooks[entryPoint]) {
1729
- return arrayPop(hooks[entryPoint]);
1341
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
1342
+ if (hookFunction !== undefined) {
1343
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
1344
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
1730
1345
  }
1346
+ return arrayPop(hooks[entryPoint]);
1731
1347
  };
1732
- /**
1733
- * RemoveHooks
1734
- * Public method to remove all DOMPurify hooks at a given entryPoint
1735
- *
1736
- * @param {String} entryPoint entry point for the hooks to remove
1737
- */
1738
-
1739
-
1740
1348
  DOMPurify.removeHooks = function (entryPoint) {
1741
- if (hooks[entryPoint]) {
1742
- hooks[entryPoint] = [];
1743
- }
1349
+ hooks[entryPoint] = [];
1744
1350
  };
1745
- /**
1746
- * RemoveAllHooks
1747
- * Public method to remove all DOMPurify hooks
1748
- *
1749
- */
1750
-
1751
-
1752
1351
  DOMPurify.removeAllHooks = function () {
1753
- hooks = {};
1352
+ hooks = _createHooksMap();
1754
1353
  };
1755
-
1756
1354
  return DOMPurify;
1757
1355
  }
1758
-
1759
1356
  var purify = createDOMPurify();
1760
1357
 
1761
1358
  module.exports = purify;