dompurify 2.3.6 → 2.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/purify.js CHANGED
@@ -1,12 +1,92 @@
1
- /*! @license DOMPurify 2.3.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.6/LICENSE */
1
+ /*! @license DOMPurify 2.3.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.9/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
5
5
  typeof define === 'function' && define.amd ? define(factory) :
6
- (global = global || self, global.DOMPurify = factory());
7
- }(this, function () { 'use strict';
6
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DOMPurify = factory());
7
+ })(this, (function () { 'use strict';
8
8
 
9
- function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
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;
44
+ } else {
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
+ };
53
+ }
54
+
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
+ }
10
90
 
11
91
  var hasOwnProperty = Object.hasOwnProperty,
12
92
  setPrototypeOf = Object.setPrototypeOf,
@@ -41,46 +121,43 @@
41
121
 
42
122
  if (!construct) {
43
123
  construct = function construct(Func, args) {
44
- return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
124
+ return _construct(Func, _toConsumableArray(args));
45
125
  };
46
126
  }
47
127
 
48
128
  var arrayForEach = unapply(Array.prototype.forEach);
49
129
  var arrayPop = unapply(Array.prototype.pop);
50
130
  var arrayPush = unapply(Array.prototype.push);
51
-
52
131
  var stringToLowerCase = unapply(String.prototype.toLowerCase);
53
132
  var stringMatch = unapply(String.prototype.match);
54
133
  var stringReplace = unapply(String.prototype.replace);
55
134
  var stringIndexOf = unapply(String.prototype.indexOf);
56
135
  var stringTrim = unapply(String.prototype.trim);
57
-
58
136
  var regExpTest = unapply(RegExp.prototype.test);
59
-
60
137
  var typeErrorCreate = unconstruct(TypeError);
61
-
62
138
  function unapply(func) {
63
139
  return function (thisArg) {
64
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
140
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
65
141
  args[_key - 1] = arguments[_key];
66
142
  }
67
143
 
68
144
  return apply(func, thisArg, args);
69
145
  };
70
146
  }
71
-
72
147
  function unconstruct(func) {
73
148
  return function () {
74
- for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
149
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
75
150
  args[_key2] = arguments[_key2];
76
151
  }
77
152
 
78
153
  return construct(func, args);
79
154
  };
80
155
  }
81
-
82
156
  /* Add properties to a lookup table */
83
- function addToSet(set, array) {
157
+
158
+ function addToSet(set, array, transformCaseFunc) {
159
+ transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
160
+
84
161
  if (setPrototypeOf) {
85
162
  // Make 'in' and truthy checks like Boolean(set.constructor)
86
163
  // independent of any properties defined on Object.prototype.
@@ -89,10 +166,13 @@
89
166
  }
90
167
 
91
168
  var l = array.length;
169
+
92
170
  while (l--) {
93
171
  var element = array[l];
172
+
94
173
  if (typeof element === 'string') {
95
- var lcElement = stringToLowerCase(element);
174
+ var lcElement = transformCaseFunc(element);
175
+
96
176
  if (lcElement !== element) {
97
177
  // Config presets (e.g. tags.js, attrs.js) are immutable.
98
178
  if (!isFrozen(array)) {
@@ -108,12 +188,12 @@
108
188
 
109
189
  return set;
110
190
  }
111
-
112
191
  /* Shallow clone an object */
192
+
113
193
  function clone(object) {
114
194
  var newObject = create(null);
195
+ var property;
115
196
 
116
- var property = void 0;
117
197
  for (property in object) {
118
198
  if (apply(hasOwnProperty, object, [property])) {
119
199
  newObject[property] = object[property];
@@ -122,14 +202,15 @@
122
202
 
123
203
  return newObject;
124
204
  }
125
-
126
205
  /* IE10 doesn't support __lookupGetter__ so lets'
127
206
  * simulate it. It also automatically checks
128
207
  * if the prop is function or getter and behaves
129
208
  * accordingly. */
209
+
130
210
  function lookupGetter(object, prop) {
131
211
  while (object !== null) {
132
212
  var desc = getOwnPropertyDescriptor(object, prop);
213
+
133
214
  if (desc) {
134
215
  if (desc.get) {
135
216
  return unapply(desc.get);
@@ -151,40 +232,33 @@
151
232
  return fallbackValue;
152
233
  }
153
234
 
154
- var html = 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']);
155
-
156
- // SVG
157
- var svg = 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']);
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
158
236
 
159
- 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']);
160
-
161
- // List of SVG elements that are disallowed by default.
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.
162
239
  // We still need to know them so that we can do namespace
163
240
  // checks properly in case one wants to add them to
164
241
  // allow-list.
165
- 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']);
166
242
 
167
- var mathMl = 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']);
168
-
169
- // Similarly to SVG, we want to know all MathML elements,
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,
170
245
  // even those that we disallow by default.
171
- var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
172
246
 
247
+ var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
173
248
  var text = freeze(['#text']);
174
249
 
175
- var html$1 = 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']);
176
-
177
- var svg$1 = 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']);
178
-
179
- var mathMl$1 = 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']);
180
-
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']);
181
253
  var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
182
254
 
183
- // eslint-disable-next-line unicorn/better-regex
184
- var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
185
- var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
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);
186
258
  var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
259
+
187
260
  var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
261
+
188
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
189
263
  );
190
264
  var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
@@ -192,14 +266,9 @@
192
266
  );
193
267
  var DOCTYPE_NAME = seal(/^html$/i);
194
268
 
195
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
196
-
197
- function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
198
-
199
269
  var getGlobal = function getGlobal() {
200
270
  return typeof window === 'undefined' ? null : window;
201
271
  };
202
-
203
272
  /**
204
273
  * Creates a no-op policy for internal use only.
205
274
  * Don't export this function outside this module!
@@ -208,16 +277,19 @@
208
277
  * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
209
278
  * are not supported).
210
279
  */
280
+
281
+
211
282
  var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
212
- if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
283
+ if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
213
284
  return null;
214
- }
215
-
216
- // Allow the callers to control the unique policy name
285
+ } // Allow the callers to control the unique policy name
217
286
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
218
287
  // Policy creation with duplicate names throws in Trusted Types.
288
+
289
+
219
290
  var suffix = null;
220
291
  var ATTR_NAME = 'data-tt-policy-suffix';
292
+
221
293
  if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
222
294
  suffix = document.currentScript.getAttribute(ATTR_NAME);
223
295
  }
@@ -226,8 +298,8 @@
226
298
 
227
299
  try {
228
300
  return trustedTypes.createPolicy(policyName, {
229
- createHTML: function createHTML(html$$1) {
230
- return html$$1;
301
+ createHTML: function createHTML(html) {
302
+ return html;
231
303
  }
232
304
  });
233
305
  } catch (_) {
@@ -245,29 +317,28 @@
245
317
  var DOMPurify = function DOMPurify(root) {
246
318
  return createDOMPurify(root);
247
319
  };
248
-
249
320
  /**
250
321
  * Version label, exposed for easier checks
251
322
  * if DOMPurify is up to date or not
252
323
  */
253
- DOMPurify.version = '2.3.6';
254
324
 
325
+
326
+ DOMPurify.version = '2.3.9';
255
327
  /**
256
328
  * Array of elements that DOMPurify removed during sanitation.
257
329
  * Empty if nothing was removed.
258
330
  */
331
+
259
332
  DOMPurify.removed = [];
260
333
 
261
334
  if (!window || !window.document || window.document.nodeType !== 9) {
262
335
  // Not running in a browser, provide a factory function
263
336
  // so that you can pass your own Window
264
337
  DOMPurify.isSupported = false;
265
-
266
338
  return DOMPurify;
267
339
  }
268
340
 
269
341
  var originalDocument = window.document;
270
-
271
342
  var document = window.document;
272
343
  var DocumentFragment = window.DocumentFragment,
273
344
  HTMLTemplateElement = window.HTMLTemplateElement,
@@ -275,63 +346,57 @@
275
346
  Element = window.Element,
276
347
  NodeFilter = window.NodeFilter,
277
348
  _window$NamedNodeMap = window.NamedNodeMap,
278
- NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
349
+ NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
279
350
  HTMLFormElement = window.HTMLFormElement,
280
351
  DOMParser = window.DOMParser,
281
352
  trustedTypes = window.trustedTypes;
282
-
283
-
284
353
  var ElementPrototype = Element.prototype;
285
-
286
354
  var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
287
355
  var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
288
356
  var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
289
- var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
290
-
291
- // As per issue #47, the web-components registry is inherited by a
357
+ var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
292
358
  // new document created via createHTMLDocument. As per the spec
293
359
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
294
360
  // a new empty registry is used when creating a template contents owner
295
361
  // document, so we use that as our parent document to ensure nothing
296
362
  // is inherited.
363
+
297
364
  if (typeof HTMLTemplateElement === 'function') {
298
365
  var template = document.createElement('template');
366
+
299
367
  if (template.content && template.content.ownerDocument) {
300
368
  document = template.content.ownerDocument;
301
369
  }
302
370
  }
303
371
 
304
372
  var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
305
- var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
306
373
 
374
+ var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
307
375
  var _document = document,
308
376
  implementation = _document.implementation,
309
377
  createNodeIterator = _document.createNodeIterator,
310
378
  createDocumentFragment = _document.createDocumentFragment,
311
379
  getElementsByTagName = _document.getElementsByTagName;
312
380
  var importNode = originalDocument.importNode;
313
-
314
-
315
381
  var documentMode = {};
382
+
316
383
  try {
317
384
  documentMode = clone(document).documentMode ? document.documentMode : {};
318
385
  } catch (_) {}
319
386
 
320
387
  var hooks = {};
321
-
322
388
  /**
323
389
  * Expose whether this browser supports running the full DOMPurify.
324
390
  */
325
- DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
326
-
327
- var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
328
- ERB_EXPR$$1 = ERB_EXPR,
329
- DATA_ATTR$$1 = DATA_ATTR,
330
- ARIA_ATTR$$1 = ARIA_ATTR,
331
- IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
332
- ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
333
- var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
334
391
 
392
+ DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
393
+ var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
394
+ ERB_EXPR$1 = ERB_EXPR,
395
+ DATA_ATTR$1 = DATA_ATTR,
396
+ ARIA_ATTR$1 = ARIA_ATTR,
397
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
398
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
399
+ var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
335
400
  /**
336
401
  * We consider the elements and attributes below to be safe. Ideally
337
402
  * don't add any new ones but feel free to remove unwanted ones.
@@ -340,18 +405,18 @@
340
405
  /* allowed element names */
341
406
 
342
407
  var ALLOWED_TAGS = null;
343
- var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));
344
-
408
+ var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
345
409
  /* Allowed attribute names */
346
- var ALLOWED_ATTR = null;
347
- var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
348
410
 
411
+ var ALLOWED_ATTR = null;
412
+ var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
349
413
  /*
350
414
  * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
351
415
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
352
416
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
353
417
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
354
418
  */
419
+
355
420
  var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
356
421
  tagNameCheck: {
357
422
  writable: true,
@@ -372,93 +437,93 @@
372
437
  value: false
373
438
  }
374
439
  }));
375
-
376
440
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
377
- var FORBID_TAGS = null;
378
441
 
442
+ var FORBID_TAGS = null;
379
443
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
380
- var FORBID_ATTR = null;
381
444
 
445
+ var FORBID_ATTR = null;
382
446
  /* Decide if ARIA attributes are okay */
383
- var ALLOW_ARIA_ATTR = true;
384
447
 
448
+ var ALLOW_ARIA_ATTR = true;
385
449
  /* Decide if custom data attributes are okay */
386
- var ALLOW_DATA_ATTR = true;
387
450
 
451
+ var ALLOW_DATA_ATTR = true;
388
452
  /* Decide if unknown protocols are okay */
389
- var ALLOW_UNKNOWN_PROTOCOLS = false;
390
453
 
454
+ var ALLOW_UNKNOWN_PROTOCOLS = false;
391
455
  /* Output should be safe for common template engines.
392
456
  * This means, DOMPurify removes data attributes, mustaches and ERB
393
457
  */
394
- var SAFE_FOR_TEMPLATES = false;
395
458
 
459
+ var SAFE_FOR_TEMPLATES = false;
396
460
  /* Decide if document with <html>... should be returned */
397
- var WHOLE_DOCUMENT = false;
398
461
 
462
+ var WHOLE_DOCUMENT = false;
399
463
  /* Track whether config is already set on this instance of DOMPurify. */
400
- var SET_CONFIG = false;
401
464
 
465
+ var SET_CONFIG = false;
402
466
  /* Decide if all elements (e.g. style, script) must be children of
403
467
  * document.body. By default, browsers might move them to document.head */
404
- var FORCE_BODY = false;
405
468
 
469
+ var FORCE_BODY = false;
406
470
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
407
471
  * string (or a TrustedHTML object if Trusted Types are supported).
408
472
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
409
473
  */
410
- var RETURN_DOM = false;
411
474
 
475
+ var RETURN_DOM = false;
412
476
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
413
477
  * string (or a TrustedHTML object if Trusted Types are supported) */
414
- var RETURN_DOM_FRAGMENT = false;
415
478
 
479
+ var RETURN_DOM_FRAGMENT = false;
416
480
  /* Try to return a Trusted Type object instead of a string, return a string in
417
481
  * case Trusted Types are not supported */
418
- var RETURN_TRUSTED_TYPE = false;
419
482
 
483
+ var RETURN_TRUSTED_TYPE = false;
420
484
  /* Output should be free from DOM clobbering attacks? */
421
- var SANITIZE_DOM = true;
422
485
 
486
+ var SANITIZE_DOM = true;
423
487
  /* Keep element content when removing element? */
424
- var KEEP_CONTENT = true;
425
488
 
489
+ var KEEP_CONTENT = true;
426
490
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
427
491
  * of importing it into a new Document and returning a sanitized copy */
428
- var IN_PLACE = false;
429
492
 
493
+ var IN_PLACE = false;
430
494
  /* Allow usage of profiles like html, svg and mathMl */
431
- var USE_PROFILES = {};
432
495
 
496
+ var USE_PROFILES = {};
433
497
  /* Tags to ignore content of when KEEP_CONTENT is true */
498
+
434
499
  var FORBID_CONTENTS = null;
435
500
  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']);
436
-
437
501
  /* Tags that are safe for data: URIs */
502
+
438
503
  var DATA_URI_TAGS = null;
439
504
  var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
440
-
441
505
  /* Attributes safe for values like "javascript:" */
506
+
442
507
  var URI_SAFE_ATTRIBUTES = null;
443
508
  var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
444
-
445
509
  var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
446
510
  var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
447
511
  var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
448
512
  /* Document namespace */
513
+
449
514
  var NAMESPACE = HTML_NAMESPACE;
450
515
  var IS_EMPTY_INPUT = false;
451
-
452
516
  /* Parsing of strict XHTML documents */
453
- var PARSER_MEDIA_TYPE = void 0;
517
+
518
+ var PARSER_MEDIA_TYPE;
454
519
  var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
455
520
  var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
456
- var transformCaseFunc = void 0;
457
-
521
+ var transformCaseFunc;
458
522
  /* Keep a reference to config to pass to hooks */
459
- var CONFIG = null;
460
523
 
524
+ var CONFIG = null;
461
525
  /* Ideally, do not touch anything below this line */
526
+
462
527
  /* ______________________________________________ */
463
528
 
464
529
  var formElement = document.createElement('form');
@@ -466,49 +531,79 @@
466
531
  var isRegexOrFunction = function isRegexOrFunction(testValue) {
467
532
  return testValue instanceof RegExp || testValue instanceof Function;
468
533
  };
469
-
470
534
  /**
471
535
  * _parseConfig
472
536
  *
473
537
  * @param {Object} cfg optional config literal
474
538
  */
475
539
  // eslint-disable-next-line complexity
540
+
541
+
476
542
  var _parseConfig = function _parseConfig(cfg) {
477
543
  if (CONFIG && CONFIG === cfg) {
478
544
  return;
479
545
  }
480
-
481
546
  /* Shield configuration object from tampering */
482
- if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
547
+
548
+
549
+ if (!cfg || _typeof(cfg) !== 'object') {
483
550
  cfg = {};
484
551
  }
485
-
486
552
  /* Shield configuration object from prototype pollution */
553
+
554
+
487
555
  cfg = clone(cfg);
556
+ PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
557
+ 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.
488
558
 
559
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
560
+ return x;
561
+ } : stringToLowerCase;
489
562
  /* Set configuration parameters */
490
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
491
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
492
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
493
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
494
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
495
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
496
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
563
+
564
+ ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
565
+ ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
566
+ URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
567
+ cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
568
+ transformCaseFunc // eslint-disable-line indent
569
+ ) // eslint-disable-line indent
570
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
571
+ DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
572
+ cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
573
+ transformCaseFunc // eslint-disable-line indent
574
+ ) // eslint-disable-line indent
575
+ : DEFAULT_DATA_URI_TAGS;
576
+ FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
577
+ FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
578
+ FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
497
579
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
498
580
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
581
+
499
582
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
583
+
500
584
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
585
+
501
586
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
587
+
502
588
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
589
+
503
590
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
591
+
504
592
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
593
+
505
594
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
595
+
506
596
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
597
+
507
598
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
599
+
508
600
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
601
+
509
602
  IN_PLACE = cfg.IN_PLACE || false; // Default false
510
- IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
603
+
604
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
511
605
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
606
+
512
607
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
513
608
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
514
609
  }
@@ -521,15 +616,6 @@
521
616
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
522
617
  }
523
618
 
524
- PARSER_MEDIA_TYPE =
525
- // eslint-disable-next-line unicorn/prefer-includes
526
- SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
527
-
528
- // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
529
- transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
530
- return x;
531
- } : stringToLowerCase;
532
-
533
619
  if (SAFE_FOR_TEMPLATES) {
534
620
  ALLOW_DATA_ATTR = false;
535
621
  }
@@ -537,42 +623,45 @@
537
623
  if (RETURN_DOM_FRAGMENT) {
538
624
  RETURN_DOM = true;
539
625
  }
540
-
541
626
  /* Parse profile info */
627
+
628
+
542
629
  if (USE_PROFILES) {
543
- ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
630
+ ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
544
631
  ALLOWED_ATTR = [];
632
+
545
633
  if (USE_PROFILES.html === true) {
546
- addToSet(ALLOWED_TAGS, html);
547
- addToSet(ALLOWED_ATTR, html$1);
634
+ addToSet(ALLOWED_TAGS, html$1);
635
+ addToSet(ALLOWED_ATTR, html);
548
636
  }
549
637
 
550
638
  if (USE_PROFILES.svg === true) {
551
- addToSet(ALLOWED_TAGS, svg);
552
- addToSet(ALLOWED_ATTR, svg$1);
639
+ addToSet(ALLOWED_TAGS, svg$1);
640
+ addToSet(ALLOWED_ATTR, svg);
553
641
  addToSet(ALLOWED_ATTR, xml);
554
642
  }
555
643
 
556
644
  if (USE_PROFILES.svgFilters === true) {
557
645
  addToSet(ALLOWED_TAGS, svgFilters);
558
- addToSet(ALLOWED_ATTR, svg$1);
646
+ addToSet(ALLOWED_ATTR, svg);
559
647
  addToSet(ALLOWED_ATTR, xml);
560
648
  }
561
649
 
562
650
  if (USE_PROFILES.mathMl === true) {
563
- addToSet(ALLOWED_TAGS, mathMl);
564
- addToSet(ALLOWED_ATTR, mathMl$1);
651
+ addToSet(ALLOWED_TAGS, mathMl$1);
652
+ addToSet(ALLOWED_ATTR, mathMl);
565
653
  addToSet(ALLOWED_ATTR, xml);
566
654
  }
567
655
  }
568
-
569
656
  /* Merge configuration parameters */
657
+
658
+
570
659
  if (cfg.ADD_TAGS) {
571
660
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
572
661
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
573
662
  }
574
663
 
575
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
664
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
576
665
  }
577
666
 
578
667
  if (cfg.ADD_ATTR) {
@@ -580,11 +669,11 @@
580
669
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
581
670
  }
582
671
 
583
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
672
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
584
673
  }
585
674
 
586
675
  if (cfg.ADD_URI_SAFE_ATTR) {
587
- addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
676
+ addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
588
677
  }
589
678
 
590
679
  if (cfg.FORBID_CONTENTS) {
@@ -592,27 +681,30 @@
592
681
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
593
682
  }
594
683
 
595
- addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
684
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
596
685
  }
597
-
598
686
  /* Add #text in case KEEP_CONTENT is set to true */
687
+
688
+
599
689
  if (KEEP_CONTENT) {
600
690
  ALLOWED_TAGS['#text'] = true;
601
691
  }
602
-
603
692
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
693
+
694
+
604
695
  if (WHOLE_DOCUMENT) {
605
696
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
606
697
  }
607
-
608
698
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
699
+
700
+
609
701
  if (ALLOWED_TAGS.table) {
610
702
  addToSet(ALLOWED_TAGS, ['tbody']);
611
703
  delete FORBID_TAGS.tbody;
612
- }
613
-
614
- // Prevent further manipulation of configuration.
704
+ } // Prevent further manipulation of configuration.
615
705
  // Not available in IE8, Safari 5, etc.
706
+
707
+
616
708
  if (freeze) {
617
709
  freeze(cfg);
618
710
  }
@@ -621,19 +713,21 @@
621
713
  };
622
714
 
623
715
  var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
716
+ var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
717
+ // namespace. We need to specify them explicitly
718
+ // so that they don't get erroneously deleted from
719
+ // HTML namespace.
624
720
 
625
- var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
626
-
721
+ var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
627
722
  /* Keep track of all possible SVG and MathML tags
628
723
  * so that we can perform the namespace checks
629
724
  * correctly. */
630
- var ALL_SVG_TAGS = addToSet({}, svg);
725
+
726
+ var ALL_SVG_TAGS = addToSet({}, svg$1);
631
727
  addToSet(ALL_SVG_TAGS, svgFilters);
632
728
  addToSet(ALL_SVG_TAGS, svgDisallowed);
633
-
634
- var ALL_MATHML_TAGS = addToSet({}, mathMl);
729
+ var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
635
730
  addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
636
-
637
731
  /**
638
732
  *
639
733
  *
@@ -642,11 +736,11 @@
642
736
  * namespace that a spec-compliant parser would never
643
737
  * return. Return true otherwise.
644
738
  */
645
- var _checkValidNamespace = function _checkValidNamespace(element) {
646
- var parent = getParentNode(element);
647
739
 
648
- // In JSDOM, if we're inside shadow DOM, then parentNode
740
+ var _checkValidNamespace = function _checkValidNamespace(element) {
741
+ var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
649
742
  // can be null. We just simulate parent in this case.
743
+
650
744
  if (!parent || !parent.tagName) {
651
745
  parent = {
652
746
  namespaceURI: HTML_NAMESPACE,
@@ -663,17 +757,17 @@
663
757
  // it should be killed.
664
758
  if (parent.namespaceURI === HTML_NAMESPACE) {
665
759
  return tagName === 'svg';
666
- }
667
-
668
- // The only way to switch from MathML to SVG is via
760
+ } // The only way to switch from MathML to SVG is via
669
761
  // svg if parent is either <annotation-xml> or MathML
670
762
  // text integration points.
763
+
764
+
671
765
  if (parent.namespaceURI === MATHML_NAMESPACE) {
672
766
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
673
- }
674
-
675
- // We only allow elements that are defined in SVG
767
+ } // We only allow elements that are defined in SVG
676
768
  // spec. All others are disallowed in SVG namespace.
769
+
770
+
677
771
  return Boolean(ALL_SVG_TAGS[tagName]);
678
772
  }
679
773
 
@@ -683,16 +777,16 @@
683
777
  // it should be killed.
684
778
  if (parent.namespaceURI === HTML_NAMESPACE) {
685
779
  return tagName === 'math';
686
- }
687
-
688
- // The only way to switch from SVG to MathML is via
780
+ } // The only way to switch from SVG to MathML is via
689
781
  // <math> and HTML integration points
782
+
783
+
690
784
  if (parent.namespaceURI === SVG_NAMESPACE) {
691
785
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
692
- }
693
-
694
- // We only allow elements that are defined in MathML
786
+ } // We only allow elements that are defined in MathML
695
787
  // spec. All others are disallowed in MathML namespace.
788
+
789
+
696
790
  return Boolean(ALL_MATHML_TAGS[tagName]);
697
791
  }
698
792
 
@@ -706,32 +800,30 @@
706
800
 
707
801
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
708
802
  return false;
709
- }
710
-
711
- // Certain elements are allowed in both SVG and HTML
712
- // namespace. We need to specify them explicitly
713
- // so that they don't get erronously deleted from
714
- // HTML namespace.
715
- var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
716
-
717
- // We disallow tags that are specific for MathML
803
+ } // We disallow tags that are specific for MathML
718
804
  // or SVG and should never appear in HTML namespace
719
- return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
720
- }
721
805
 
722
- // The code should never reach this place (this means
806
+
807
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
808
+ } // The code should never reach this place (this means
723
809
  // that the element somehow got namespace that is not
724
810
  // HTML, SVG or MathML). Return false just in case.
811
+
812
+
725
813
  return false;
726
814
  };
727
-
728
815
  /**
729
816
  * _forceRemove
730
817
  *
731
818
  * @param {Node} node a DOM node
732
819
  */
820
+
821
+
733
822
  var _forceRemove = function _forceRemove(node) {
734
- arrayPush(DOMPurify.removed, { element: node });
823
+ arrayPush(DOMPurify.removed, {
824
+ element: node
825
+ });
826
+
735
827
  try {
736
828
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
737
829
  node.parentNode.removeChild(node);
@@ -743,13 +835,14 @@
743
835
  }
744
836
  }
745
837
  };
746
-
747
838
  /**
748
839
  * _removeAttribute
749
840
  *
750
841
  * @param {String} name an Attribute name
751
842
  * @param {Node} node a DOM node
752
843
  */
844
+
845
+
753
846
  var _removeAttribute = function _removeAttribute(name, node) {
754
847
  try {
755
848
  arrayPush(DOMPurify.removed, {
@@ -763,9 +856,8 @@
763
856
  });
764
857
  }
765
858
 
766
- node.removeAttribute(name);
859
+ node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
767
860
 
768
- // We void attribute values for unremovable "is"" attributes
769
861
  if (name === 'is' && !ALLOWED_ATTR[name]) {
770
862
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
771
863
  try {
@@ -778,17 +870,18 @@
778
870
  }
779
871
  }
780
872
  };
781
-
782
873
  /**
783
874
  * _initDocument
784
875
  *
785
876
  * @param {String} dirty a string of dirty markup
786
877
  * @return {Document} a DOM, filled with the dirty markup
787
878
  */
879
+
880
+
788
881
  var _initDocument = function _initDocument(dirty) {
789
882
  /* Create a HTML document */
790
- var doc = void 0;
791
- var leadingWhitespace = void 0;
883
+ var doc;
884
+ var leadingWhitespace;
792
885
 
793
886
  if (FORCE_BODY) {
794
887
  dirty = '<remove></remove>' + dirty;
@@ -808,19 +901,21 @@
808
901
  * Use the DOMParser API by default, fallback later if needs be
809
902
  * DOMParser not work for svg when has multiple root element.
810
903
  */
904
+
811
905
  if (NAMESPACE === HTML_NAMESPACE) {
812
906
  try {
813
907
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
814
908
  } catch (_) {}
815
909
  }
816
-
817
910
  /* Use createHTMLDocument in case DOMParser is not available */
911
+
912
+
818
913
  if (!doc || !doc.documentElement) {
819
914
  doc = implementation.createDocument(NAMESPACE, 'template', null);
915
+
820
916
  try {
821
917
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
822
- } catch (_) {
823
- // Syntax error if dirtyPayload is invalid xml
918
+ } catch (_) {// Syntax error if dirtyPayload is invalid xml
824
919
  }
825
920
  }
826
921
 
@@ -829,47 +924,49 @@
829
924
  if (dirty && leadingWhitespace) {
830
925
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
831
926
  }
832
-
833
927
  /* Work on whole document or just its body */
928
+
929
+
834
930
  if (NAMESPACE === HTML_NAMESPACE) {
835
931
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
836
932
  }
837
933
 
838
934
  return WHOLE_DOCUMENT ? doc.documentElement : body;
839
935
  };
840
-
841
936
  /**
842
937
  * _createIterator
843
938
  *
844
939
  * @param {Document} root document/fragment to create iterator for
845
940
  * @return {Iterator} iterator instance
846
941
  */
942
+
943
+
847
944
  var _createIterator = function _createIterator(root) {
848
- return createNodeIterator.call(root.ownerDocument || root, root,
849
- // eslint-disable-next-line no-bitwise
945
+ return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
850
946
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
851
947
  };
852
-
853
948
  /**
854
949
  * _isClobbered
855
950
  *
856
951
  * @param {Node} elm element to check for clobbering attacks
857
952
  * @return {Boolean} true if clobbered, false if safe
858
953
  */
954
+
955
+
859
956
  var _isClobbered = function _isClobbered(elm) {
860
957
  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');
861
958
  };
862
-
863
959
  /**
864
960
  * _isNode
865
961
  *
866
962
  * @param {Node} obj object to check whether it's a DOM node
867
963
  * @return {Boolean} true is object is a DOM node
868
964
  */
965
+
966
+
869
967
  var _isNode = function _isNode(object) {
870
- return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
968
+ return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
871
969
  };
872
-
873
970
  /**
874
971
  * _executeHook
875
972
  * Execute user configurable hooks
@@ -878,6 +975,8 @@
878
975
  * @param {Node} currentNode node to work on with the hook
879
976
  * @param {Object} data additional hook parameters
880
977
  */
978
+
979
+
881
980
  var _executeHook = function _executeHook(entryPoint, currentNode, data) {
882
981
  if (!hooks[entryPoint]) {
883
982
  return;
@@ -887,7 +986,6 @@
887
986
  hook.call(DOMPurify, currentNode, data, CONFIG);
888
987
  });
889
988
  };
890
-
891
989
  /**
892
990
  * _sanitizeElements
893
991
  *
@@ -898,54 +996,67 @@
898
996
  * @param {Node} currentNode to check for permission to exist
899
997
  * @return {Boolean} true if node was killed, false if left alive
900
998
  */
901
- var _sanitizeElements = function _sanitizeElements(currentNode) {
902
- var content = void 0;
903
999
 
1000
+
1001
+ var _sanitizeElements = function _sanitizeElements(currentNode) {
1002
+ var content;
904
1003
  /* Execute a hook if present */
905
- _executeHook('beforeSanitizeElements', currentNode, null);
906
1004
 
1005
+ _executeHook('beforeSanitizeElements', currentNode, null);
907
1006
  /* Check if element is clobbered or can clobber */
1007
+
1008
+
908
1009
  if (_isClobbered(currentNode)) {
909
1010
  _forceRemove(currentNode);
1011
+
910
1012
  return true;
911
1013
  }
912
-
913
1014
  /* Check if tagname contains Unicode */
914
- if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
1015
+
1016
+
1017
+ if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
915
1018
  _forceRemove(currentNode);
1019
+
916
1020
  return true;
917
1021
  }
918
-
919
1022
  /* Now let's check the element's type and name */
920
- var tagName = transformCaseFunc(currentNode.nodeName);
921
1023
 
1024
+
1025
+ var tagName = transformCaseFunc(currentNode.nodeName);
922
1026
  /* Execute a hook if present */
1027
+
923
1028
  _executeHook('uponSanitizeElement', currentNode, {
924
1029
  tagName: tagName,
925
1030
  allowedTags: ALLOWED_TAGS
926
1031
  });
927
-
928
1032
  /* Detect mXSS attempts abusing namespace confusion */
929
- if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1033
+
1034
+
1035
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
930
1036
  _forceRemove(currentNode);
1037
+
931
1038
  return true;
932
1039
  }
933
-
934
1040
  /* Mitigate a problem with templates inside select */
1041
+
1042
+
935
1043
  if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
936
1044
  _forceRemove(currentNode);
1045
+
937
1046
  return true;
938
1047
  }
939
-
940
1048
  /* Remove element if anything forbids its presence */
1049
+
1050
+
941
1051
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
942
1052
  /* Check if we have a custom element to handle */
943
1053
  if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
944
1054
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
945
1055
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
946
1056
  }
947
-
948
1057
  /* Keep content except for bad-listed elements */
1058
+
1059
+
949
1060
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
950
1061
  var parentNode = getParentNode(currentNode) || currentNode.parentNode;
951
1062
  var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
@@ -960,38 +1071,46 @@
960
1071
  }
961
1072
 
962
1073
  _forceRemove(currentNode);
1074
+
963
1075
  return true;
964
1076
  }
965
-
966
1077
  /* Check whether element has a valid namespace */
1078
+
1079
+
967
1080
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
968
1081
  _forceRemove(currentNode);
1082
+
969
1083
  return true;
970
1084
  }
971
1085
 
972
1086
  if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
973
1087
  _forceRemove(currentNode);
1088
+
974
1089
  return true;
975
1090
  }
976
-
977
1091
  /* Sanitize element content to be template-safe */
1092
+
1093
+
978
1094
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
979
1095
  /* Get the element's text content */
980
1096
  content = currentNode.textContent;
981
- content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
982
- content = stringReplace(content, ERB_EXPR$$1, ' ');
1097
+ content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
1098
+ content = stringReplace(content, ERB_EXPR$1, ' ');
1099
+
983
1100
  if (currentNode.textContent !== content) {
984
- arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
1101
+ arrayPush(DOMPurify.removed, {
1102
+ element: currentNode.cloneNode()
1103
+ });
985
1104
  currentNode.textContent = content;
986
1105
  }
987
1106
  }
988
-
989
1107
  /* Execute a hook if present */
1108
+
1109
+
990
1110
  _executeHook('afterSanitizeElements', currentNode, null);
991
1111
 
992
1112
  return false;
993
1113
  };
994
-
995
1114
  /**
996
1115
  * _isValidAttribute
997
1116
  *
@@ -1001,45 +1120,47 @@
1001
1120
  * @return {Boolean} Returns true if `value` is valid, otherwise false.
1002
1121
  */
1003
1122
  // eslint-disable-next-line complexity
1123
+
1124
+
1004
1125
  var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1005
1126
  /* Make sure attribute cannot clobber */
1006
1127
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1007
1128
  return false;
1008
1129
  }
1009
-
1010
1130
  /* Allow valid data-* attributes: At least one character after "-"
1011
1131
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1012
1132
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1013
1133
  We don't need to check the value; it's always URI safe. */
1014
- 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]) {
1015
- if (
1016
- // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1134
+
1135
+
1136
+ 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]) {
1137
+ if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1017
1138
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1018
1139
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1019
- _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)) ||
1020
- // Alternative, second condition checks if it's an `is`-attribute, AND
1140
+ _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
1021
1141
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1022
1142
  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 {
1023
1143
  return false;
1024
1144
  }
1025
1145
  /* Check value is safe. First, is attr inert? If so, is safe */
1026
- } 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 {
1146
+
1147
+ } 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 {
1027
1148
  return false;
1028
1149
  }
1029
1150
 
1030
1151
  return true;
1031
1152
  };
1032
-
1033
1153
  /**
1034
1154
  * _basicCustomElementCheck
1035
1155
  * checks if at least one dash is included in tagName, and it's not the first char
1036
1156
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1037
1157
  * @param {string} tagName name of the tag of the node to sanitize
1038
1158
  */
1159
+
1160
+
1039
1161
  var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1040
1162
  return tagName.indexOf('-') > 0;
1041
1163
  };
1042
-
1043
1164
  /**
1044
1165
  * _sanitizeAttributes
1045
1166
  *
@@ -1050,16 +1171,18 @@
1050
1171
  *
1051
1172
  * @param {Node} currentNode to sanitize
1052
1173
  */
1174
+
1175
+
1053
1176
  var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1054
- var attr = void 0;
1055
- var value = void 0;
1056
- var lcName = void 0;
1057
- var l = void 0;
1177
+ var attr;
1178
+ var value;
1179
+ var lcName;
1180
+ var l;
1058
1181
  /* Execute a hook if present */
1182
+
1059
1183
  _executeHook('beforeSanitizeAttributes', currentNode, null);
1060
1184
 
1061
1185
  var attributes = currentNode.attributes;
1062
-
1063
1186
  /* Check if we have attributes; if not we might have a text node */
1064
1187
 
1065
1188
  if (!attributes) {
@@ -1073,56 +1196,66 @@
1073
1196
  allowedAttributes: ALLOWED_ATTR
1074
1197
  };
1075
1198
  l = attributes.length;
1076
-
1077
1199
  /* Go backwards over all attributes; safely remove bad ones */
1200
+
1078
1201
  while (l--) {
1079
1202
  attr = attributes[l];
1080
1203
  var _attr = attr,
1081
1204
  name = _attr.name,
1082
1205
  namespaceURI = _attr.namespaceURI;
1083
-
1084
- value = stringTrim(attr.value);
1206
+ value = name === 'value' ? attr.value : stringTrim(attr.value);
1085
1207
  lcName = transformCaseFunc(name);
1086
-
1087
1208
  /* Execute a hook if present */
1209
+
1088
1210
  hookEvent.attrName = lcName;
1089
1211
  hookEvent.attrValue = value;
1090
1212
  hookEvent.keepAttr = true;
1091
1213
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1214
+
1092
1215
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1216
+
1093
1217
  value = hookEvent.attrValue;
1094
1218
  /* Did the hooks approve of the attribute? */
1219
+
1095
1220
  if (hookEvent.forceKeepAttr) {
1096
1221
  continue;
1097
1222
  }
1098
-
1099
1223
  /* Remove attribute */
1100
- _removeAttribute(name, currentNode);
1101
1224
 
1225
+
1226
+ _removeAttribute(name, currentNode);
1102
1227
  /* Did the hooks approve of the attribute? */
1228
+
1229
+
1103
1230
  if (!hookEvent.keepAttr) {
1104
1231
  continue;
1105
1232
  }
1106
-
1107
1233
  /* Work around a security issue in jQuery 3.0 */
1234
+
1235
+
1108
1236
  if (regExpTest(/\/>/i, value)) {
1109
1237
  _removeAttribute(name, currentNode);
1238
+
1110
1239
  continue;
1111
1240
  }
1112
-
1113
1241
  /* Sanitize attribute content to be template-safe */
1242
+
1243
+
1114
1244
  if (SAFE_FOR_TEMPLATES) {
1115
- value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
1116
- value = stringReplace(value, ERB_EXPR$$1, ' ');
1245
+ value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1246
+ value = stringReplace(value, ERB_EXPR$1, ' ');
1117
1247
  }
1118
-
1119
1248
  /* Is `value` valid for this attribute? */
1249
+
1250
+
1120
1251
  var lcTag = transformCaseFunc(currentNode.nodeName);
1252
+
1121
1253
  if (!_isValidAttribute(lcTag, lcName, value)) {
1122
1254
  continue;
1123
1255
  }
1124
-
1125
1256
  /* Handle invalid data-* attribute set by try-catching it */
1257
+
1258
+
1126
1259
  try {
1127
1260
  if (namespaceURI) {
1128
1261
  currentNode.setAttributeNS(namespaceURI, name, value);
@@ -1134,45 +1267,52 @@
1134
1267
  arrayPop(DOMPurify.removed);
1135
1268
  } catch (_) {}
1136
1269
  }
1137
-
1138
1270
  /* Execute a hook if present */
1271
+
1272
+
1139
1273
  _executeHook('afterSanitizeAttributes', currentNode, null);
1140
1274
  };
1141
-
1142
1275
  /**
1143
1276
  * _sanitizeShadowDOM
1144
1277
  *
1145
1278
  * @param {DocumentFragment} fragment to iterate over recursively
1146
1279
  */
1280
+
1281
+
1147
1282
  var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1148
- var shadowNode = void 0;
1149
- var shadowIterator = _createIterator(fragment);
1283
+ var shadowNode;
1150
1284
 
1285
+ var shadowIterator = _createIterator(fragment);
1151
1286
  /* Execute a hook if present */
1287
+
1288
+
1152
1289
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
1153
1290
 
1154
1291
  while (shadowNode = shadowIterator.nextNode()) {
1155
1292
  /* Execute a hook if present */
1156
1293
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
1157
-
1158
1294
  /* Sanitize tags and elements */
1295
+
1296
+
1159
1297
  if (_sanitizeElements(shadowNode)) {
1160
1298
  continue;
1161
1299
  }
1162
-
1163
1300
  /* Deep shadow DOM detected */
1301
+
1302
+
1164
1303
  if (shadowNode.content instanceof DocumentFragment) {
1165
1304
  _sanitizeShadowDOM(shadowNode.content);
1166
1305
  }
1167
-
1168
1306
  /* Check attributes, sanitize if necessary */
1307
+
1308
+
1169
1309
  _sanitizeAttributes(shadowNode);
1170
1310
  }
1171
-
1172
1311
  /* Execute a hook if present */
1312
+
1313
+
1173
1314
  _executeHook('afterSanitizeShadowDOM', fragment, null);
1174
1315
  };
1175
-
1176
1316
  /**
1177
1317
  * Sanitize
1178
1318
  * Public method providing core sanitation functionality
@@ -1181,34 +1321,41 @@
1181
1321
  * @param {Object} configuration object
1182
1322
  */
1183
1323
  // eslint-disable-next-line complexity
1324
+
1325
+
1184
1326
  DOMPurify.sanitize = function (dirty, cfg) {
1185
- var body = void 0;
1186
- var importedNode = void 0;
1187
- var currentNode = void 0;
1188
- var oldNode = void 0;
1189
- var returnNode = void 0;
1327
+ var body;
1328
+ var importedNode;
1329
+ var currentNode;
1330
+ var oldNode;
1331
+ var returnNode;
1190
1332
  /* Make sure we have a string to sanitize.
1191
1333
  DO NOT return early, as this will return the wrong type if
1192
1334
  the user has requested a DOM object rather than a string */
1335
+
1193
1336
  IS_EMPTY_INPUT = !dirty;
1337
+
1194
1338
  if (IS_EMPTY_INPUT) {
1195
1339
  dirty = '<!-->';
1196
1340
  }
1197
-
1198
1341
  /* Stringify, in case dirty is an object */
1342
+
1343
+
1199
1344
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1200
1345
  // eslint-disable-next-line no-negated-condition
1201
1346
  if (typeof dirty.toString !== 'function') {
1202
1347
  throw typeErrorCreate('toString is not a function');
1203
1348
  } else {
1204
1349
  dirty = dirty.toString();
1350
+
1205
1351
  if (typeof dirty !== 'string') {
1206
1352
  throw typeErrorCreate('dirty is not a string, aborting');
1207
1353
  }
1208
1354
  }
1209
1355
  }
1210
-
1211
1356
  /* Check we can run. Otherwise fall back or ignore */
1357
+
1358
+
1212
1359
  if (!DOMPurify.isSupported) {
1213
1360
  if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
1214
1361
  if (typeof dirty === 'string') {
@@ -1222,16 +1369,18 @@
1222
1369
 
1223
1370
  return dirty;
1224
1371
  }
1225
-
1226
1372
  /* Assign config vars */
1373
+
1374
+
1227
1375
  if (!SET_CONFIG) {
1228
1376
  _parseConfig(cfg);
1229
1377
  }
1230
-
1231
1378
  /* Clean up removed elements */
1232
- DOMPurify.removed = [];
1233
1379
 
1380
+
1381
+ DOMPurify.removed = [];
1234
1382
  /* Check if dirty is correctly typed for IN_PLACE */
1383
+
1235
1384
  if (typeof dirty === 'string') {
1236
1385
  IN_PLACE = false;
1237
1386
  }
@@ -1240,6 +1389,7 @@
1240
1389
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1241
1390
  if (dirty.nodeName) {
1242
1391
  var tagName = transformCaseFunc(dirty.nodeName);
1392
+
1243
1393
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1244
1394
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1245
1395
  }
@@ -1249,6 +1399,7 @@
1249
1399
  elements being stripped by the parser */
1250
1400
  body = _initDocument('<!---->');
1251
1401
  importedNode = body.ownerDocument.importNode(dirty, true);
1402
+
1252
1403
  if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1253
1404
  /* Node is already a body, use as is */
1254
1405
  body = importedNode;
@@ -1260,60 +1411,67 @@
1260
1411
  }
1261
1412
  } else {
1262
1413
  /* Exit directly if we have nothing to do */
1263
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1264
- // eslint-disable-next-line unicorn/prefer-includes
1414
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1265
1415
  dirty.indexOf('<') === -1) {
1266
1416
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1267
1417
  }
1268
-
1269
1418
  /* Initialize the document to work on */
1270
- body = _initDocument(dirty);
1271
1419
 
1420
+
1421
+ body = _initDocument(dirty);
1272
1422
  /* Check we have a DOM node from the data */
1423
+
1273
1424
  if (!body) {
1274
1425
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1275
1426
  }
1276
1427
  }
1277
-
1278
1428
  /* Remove first element node (ours) if FORCE_BODY is set */
1429
+
1430
+
1279
1431
  if (body && FORCE_BODY) {
1280
1432
  _forceRemove(body.firstChild);
1281
1433
  }
1282
-
1283
1434
  /* Get node iterator */
1284
- var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1285
1435
 
1436
+
1437
+ var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1286
1438
  /* Now start iterating over the created document */
1439
+
1440
+
1287
1441
  while (currentNode = nodeIterator.nextNode()) {
1288
1442
  /* Fix IE's strange behavior with manipulated textNodes #89 */
1289
1443
  if (currentNode.nodeType === 3 && currentNode === oldNode) {
1290
1444
  continue;
1291
1445
  }
1292
-
1293
1446
  /* Sanitize tags and elements */
1447
+
1448
+
1294
1449
  if (_sanitizeElements(currentNode)) {
1295
1450
  continue;
1296
1451
  }
1297
-
1298
1452
  /* Shadow DOM detected, sanitize it */
1453
+
1454
+
1299
1455
  if (currentNode.content instanceof DocumentFragment) {
1300
1456
  _sanitizeShadowDOM(currentNode.content);
1301
1457
  }
1302
-
1303
1458
  /* Check attributes, sanitize if necessary */
1459
+
1460
+
1304
1461
  _sanitizeAttributes(currentNode);
1305
1462
 
1306
1463
  oldNode = currentNode;
1307
1464
  }
1308
1465
 
1309
1466
  oldNode = null;
1310
-
1311
1467
  /* If we sanitized `dirty` in-place, return it. */
1468
+
1312
1469
  if (IN_PLACE) {
1313
1470
  return dirty;
1314
1471
  }
1315
-
1316
1472
  /* Return sanitized string or DOM */
1473
+
1474
+
1317
1475
  if (RETURN_DOM) {
1318
1476
  if (RETURN_DOM_FRAGMENT) {
1319
1477
  returnNode = createDocumentFragment.call(body.ownerDocument);
@@ -1341,42 +1499,45 @@
1341
1499
  }
1342
1500
 
1343
1501
  var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1344
-
1345
1502
  /* Serialize doctype if allowed */
1503
+
1346
1504
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1347
1505
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1348
1506
  }
1349
-
1350
1507
  /* Sanitize final string template-safe */
1508
+
1509
+
1351
1510
  if (SAFE_FOR_TEMPLATES) {
1352
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
1353
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
1511
+ serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
1512
+ serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
1354
1513
  }
1355
1514
 
1356
1515
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1357
1516
  };
1358
-
1359
1517
  /**
1360
1518
  * Public method to set the configuration once
1361
1519
  * setConfig
1362
1520
  *
1363
1521
  * @param {Object} cfg configuration object
1364
1522
  */
1523
+
1524
+
1365
1525
  DOMPurify.setConfig = function (cfg) {
1366
1526
  _parseConfig(cfg);
1527
+
1367
1528
  SET_CONFIG = true;
1368
1529
  };
1369
-
1370
1530
  /**
1371
1531
  * Public method to remove the configuration
1372
1532
  * clearConfig
1373
1533
  *
1374
1534
  */
1535
+
1536
+
1375
1537
  DOMPurify.clearConfig = function () {
1376
1538
  CONFIG = null;
1377
1539
  SET_CONFIG = false;
1378
1540
  };
1379
-
1380
1541
  /**
1381
1542
  * Public method to check if an attribute value is valid.
1382
1543
  * Uses last set config, if any. Otherwise, uses config defaults.
@@ -1387,6 +1548,8 @@
1387
1548
  * @param {string} value Attribute value.
1388
1549
  * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1389
1550
  */
1551
+
1552
+
1390
1553
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1391
1554
  /* Initialize shared config vars if necessary. */
1392
1555
  if (!CONFIG) {
@@ -1397,7 +1560,6 @@
1397
1560
  var lcName = transformCaseFunc(attr);
1398
1561
  return _isValidAttribute(lcTag, lcName, value);
1399
1562
  };
1400
-
1401
1563
  /**
1402
1564
  * AddHook
1403
1565
  * Public method to add DOMPurify hooks
@@ -1405,6 +1567,8 @@
1405
1567
  * @param {String} entryPoint entry point for the hook to add
1406
1568
  * @param {Function} hookFunction function to execute
1407
1569
  */
1570
+
1571
+
1408
1572
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1409
1573
  if (typeof hookFunction !== 'function') {
1410
1574
  return;
@@ -1413,37 +1577,41 @@
1413
1577
  hooks[entryPoint] = hooks[entryPoint] || [];
1414
1578
  arrayPush(hooks[entryPoint], hookFunction);
1415
1579
  };
1416
-
1417
1580
  /**
1418
1581
  * RemoveHook
1419
1582
  * Public method to remove a DOMPurify hook at a given entryPoint
1420
1583
  * (pops it from the stack of hooks if more are present)
1421
1584
  *
1422
1585
  * @param {String} entryPoint entry point for the hook to remove
1586
+ * @return {Function} removed(popped) hook
1423
1587
  */
1588
+
1589
+
1424
1590
  DOMPurify.removeHook = function (entryPoint) {
1425
1591
  if (hooks[entryPoint]) {
1426
- arrayPop(hooks[entryPoint]);
1592
+ return arrayPop(hooks[entryPoint]);
1427
1593
  }
1428
1594
  };
1429
-
1430
1595
  /**
1431
1596
  * RemoveHooks
1432
1597
  * Public method to remove all DOMPurify hooks at a given entryPoint
1433
1598
  *
1434
1599
  * @param {String} entryPoint entry point for the hooks to remove
1435
1600
  */
1601
+
1602
+
1436
1603
  DOMPurify.removeHooks = function (entryPoint) {
1437
1604
  if (hooks[entryPoint]) {
1438
1605
  hooks[entryPoint] = [];
1439
1606
  }
1440
1607
  };
1441
-
1442
1608
  /**
1443
1609
  * RemoveAllHooks
1444
1610
  * Public method to remove all DOMPurify hooks
1445
1611
  *
1446
1612
  */
1613
+
1614
+
1447
1615
  DOMPurify.removeAllHooks = function () {
1448
1616
  hooks = {};
1449
1617
  };