git.fiddlerwoaroof.com
Browse code

chore: upgrade DOMPurify

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