dompurify 2.0.8 → 2.0.12

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,1256 +1,1218 @@
1
+ /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.0.8/LICENSE */
2
+
1
3
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
- typeof define === 'function' && define.amd ? define(factory) :
4
- (global.DOMPurify = factory());
5
- }(this, (function () { 'use strict';
6
-
7
- 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); } }
8
-
9
- var hasOwnProperty = Object.hasOwnProperty;
10
- var setPrototypeOf = Object.setPrototypeOf;
11
- var isFrozen = Object.isFrozen;
12
- var objectKeys = Object.keys;
13
- var freeze = Object.freeze;
14
- var seal = Object.seal; // eslint-disable-line import/no-mutable-exports
15
-
16
- var _ref = typeof Reflect !== 'undefined' && Reflect;
17
- var apply = _ref.apply;
18
- var construct = _ref.construct;
19
-
20
- if (!apply) {
21
- apply = function apply(fun, thisValue, args) {
22
- return fun.apply(thisValue, args);
23
- };
24
- }
4
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
5
+ typeof define === 'function' && define.amd ? define(factory) :
6
+ (global = global || self, global.DOMPurify = factory());
7
+ }(this, function () { 'use strict';
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); } }
10
+
11
+ var hasOwnProperty = Object.hasOwnProperty,
12
+ setPrototypeOf = Object.setPrototypeOf,
13
+ isFrozen = Object.isFrozen,
14
+ objectKeys = Object.keys;
15
+ var freeze = Object.freeze,
16
+ seal = Object.seal; // eslint-disable-line import/no-mutable-exports
17
+
18
+ var _ref = typeof Reflect !== 'undefined' && Reflect,
19
+ apply = _ref.apply,
20
+ construct = _ref.construct;
21
+
22
+ if (!apply) {
23
+ apply = function apply(fun, thisValue, args) {
24
+ return fun.apply(thisValue, args);
25
+ };
26
+ }
25
27
 
26
- if (!freeze) {
27
- freeze = function freeze(x) {
28
- return x;
29
- };
30
- }
28
+ if (!freeze) {
29
+ freeze = function freeze(x) {
30
+ return x;
31
+ };
32
+ }
31
33
 
32
- if (!seal) {
33
- seal = function seal(x) {
34
- return x;
35
- };
36
- }
34
+ if (!seal) {
35
+ seal = function seal(x) {
36
+ return x;
37
+ };
38
+ }
37
39
 
38
- if (!construct) {
39
- construct = function construct(Func, args) {
40
- return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray$1(args))))();
41
- };
42
- }
43
-
44
- var arrayForEach = unapply(Array.prototype.forEach);
45
- var arrayIndexOf = unapply(Array.prototype.indexOf);
46
- var arrayJoin = unapply(Array.prototype.join);
47
- var arrayPop = unapply(Array.prototype.pop);
48
- var arrayPush = unapply(Array.prototype.push);
49
- var arraySlice = unapply(Array.prototype.slice);
50
-
51
- var stringToLowerCase = unapply(String.prototype.toLowerCase);
52
- var stringMatch = unapply(String.prototype.match);
53
- var stringReplace = unapply(String.prototype.replace);
54
- var stringIndexOf = unapply(String.prototype.indexOf);
55
- var stringTrim = unapply(String.prototype.trim);
56
-
57
- var regExpTest = unapply(RegExp.prototype.test);
58
- var regExpCreate = unconstruct(RegExp);
59
-
60
- var typeErrorCreate = unconstruct(TypeError);
61
-
62
- function unapply(func) {
63
- return function (thisArg) {
64
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
65
- args[_key - 1] = arguments[_key];
66
- }
40
+ if (!construct) {
41
+ construct = function construct(Func, args) {
42
+ return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
43
+ };
44
+ }
67
45
 
68
- return apply(func, thisArg, args);
69
- };
70
- }
46
+ var arrayForEach = unapply(Array.prototype.forEach);
47
+ var arrayIndexOf = unapply(Array.prototype.indexOf);
48
+ var arrayJoin = unapply(Array.prototype.join);
49
+ var arrayPop = unapply(Array.prototype.pop);
50
+ var arrayPush = unapply(Array.prototype.push);
51
+ var arraySlice = unapply(Array.prototype.slice);
71
52
 
72
- function unconstruct(func) {
73
- return function () {
74
- for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
75
- args[_key2] = arguments[_key2];
76
- }
53
+ var stringToLowerCase = unapply(String.prototype.toLowerCase);
54
+ var stringMatch = unapply(String.prototype.match);
55
+ var stringReplace = unapply(String.prototype.replace);
56
+ var stringIndexOf = unapply(String.prototype.indexOf);
57
+ var stringTrim = unapply(String.prototype.trim);
77
58
 
78
- return construct(func, args);
79
- };
80
- }
81
-
82
- /* Add properties to a lookup table */
83
- function addToSet(set, array) {
84
- if (setPrototypeOf) {
85
- // Make 'in' and truthy checks like Boolean(set.constructor)
86
- // independent of any properties defined on Object.prototype.
87
- // Prevent prototype setters from intercepting set as a this value.
88
- setPrototypeOf(set, null);
89
- }
59
+ var regExpTest = unapply(RegExp.prototype.test);
60
+ var regExpCreate = unconstruct(RegExp);
90
61
 
91
- var l = array.length;
92
- while (l--) {
93
- var element = array[l];
94
- if (typeof element === 'string') {
95
- var lcElement = stringToLowerCase(element);
96
- if (lcElement !== element) {
97
- // Config presets (e.g. tags.js, attrs.js) are immutable.
98
- if (!isFrozen(array)) {
99
- array[l] = lcElement;
100
- }
62
+ var typeErrorCreate = unconstruct(TypeError);
101
63
 
102
- element = lcElement;
64
+ function unapply(func) {
65
+ return function (thisArg) {
66
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
67
+ args[_key - 1] = arguments[_key];
103
68
  }
104
- }
105
69
 
106
- set[element] = true;
70
+ return apply(func, thisArg, args);
71
+ };
107
72
  }
108
73
 
109
- return set;
110
- }
111
-
112
- /* Shallow clone an object */
113
- function clone(object) {
114
- var newObject = {};
74
+ function unconstruct(func) {
75
+ return function () {
76
+ for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
77
+ args[_key2] = arguments[_key2];
78
+ }
115
79
 
116
- var property = void 0;
117
- for (property in object) {
118
- if (apply(hasOwnProperty, object, [property])) {
119
- newObject[property] = object[property];
120
- }
80
+ return construct(func, args);
81
+ };
121
82
  }
122
83
 
123
- return newObject;
124
- }
84
+ /* Add properties to a lookup table */
85
+ function addToSet(set, array) {
86
+ if (setPrototypeOf) {
87
+ // Make 'in' and truthy checks like Boolean(set.constructor)
88
+ // independent of any properties defined on Object.prototype.
89
+ // Prevent prototype setters from intercepting set as a this value.
90
+ setPrototypeOf(set, null);
91
+ }
125
92
 
126
- 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', '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']);
93
+ var l = array.length;
94
+ while (l--) {
95
+ var element = array[l];
96
+ if (typeof element === 'string') {
97
+ var lcElement = stringToLowerCase(element);
98
+ if (lcElement !== element) {
99
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
100
+ if (!isFrozen(array)) {
101
+ array[l] = lcElement;
102
+ }
103
+
104
+ element = lcElement;
105
+ }
106
+ }
127
107
 
128
- // SVG
129
- var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', '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', 'video', 'view', 'vkern']);
108
+ set[element] = true;
109
+ }
130
110
 
131
- var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
111
+ return set;
112
+ }
132
113
 
133
- 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']);
114
+ /* Shallow clone an object */
115
+ function clone(object) {
116
+ var newObject = {};
134
117
 
135
- var text = freeze(['#text']);
118
+ var property = void 0;
119
+ for (property in object) {
120
+ if (apply(hasOwnProperty, object, [property])) {
121
+ newObject[property] = object[property];
122
+ }
123
+ }
136
124
 
137
- var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocomplete', 'background', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'coords', 'crossorigin', 'datetime', 'default', 'dir', 'disabled', 'download', 'enctype', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'integrity', 'ismap', 'label', 'lang', 'list', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', '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', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']);
125
+ return newObject;
126
+ }
138
127
 
139
- var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'tabindex', 'targetx', 'targety', 'transform', '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']);
128
+ 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', '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']);
140
129
 
141
- 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']);
130
+ // SVG
131
+ var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', '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', 'video', 'view', 'vkern']);
142
132
 
143
- var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
133
+ var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
144
134
 
145
- var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
146
- var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
147
- var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
148
- var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
149
- 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
150
- );
151
- var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
152
- var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g // eslint-disable-line no-control-regex
153
- );
135
+ 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']);
154
136
 
155
- 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; };
137
+ var text = freeze(['#text']);
156
138
 
157
- 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); } }
139
+ 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', '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']);
158
140
 
159
- var getGlobal = function getGlobal() {
160
- return typeof window === 'undefined' ? null : window;
161
- };
141
+ var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', 'tabindex', 'targetx', 'targety', 'transform', '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']);
162
142
 
163
- /**
164
- * Creates a no-op policy for internal use only.
165
- * Don't export this function outside this module!
166
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
167
- * @param {Document} document The document object (to determine policy name suffix)
168
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
169
- * are not supported).
170
- */
171
- var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
172
- if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
173
- return null;
174
- }
143
+ 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']);
175
144
 
176
- // Allow the callers to control the unique policy name
177
- // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
178
- // Policy creation with duplicate names throws in Trusted Types.
179
- var suffix = null;
180
- var ATTR_NAME = 'data-tt-policy-suffix';
181
- if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
182
- suffix = document.currentScript.getAttribute(ATTR_NAME);
183
- }
145
+ var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
184
146
 
185
- var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
147
+ // eslint-disable-next-line unicorn/better-regex
148
+ var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
149
+ var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
150
+ var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
151
+ var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
152
+ 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
153
+ );
154
+ var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
155
+ var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g // eslint-disable-line no-control-regex
156
+ );
186
157
 
187
- try {
188
- return trustedTypes.createPolicy(policyName, {
189
- createHTML: function createHTML(html$$1) {
190
- return html$$1;
191
- }
192
- });
193
- } catch (error) {
194
- // Policy creation failed (most likely another DOMPurify script has
195
- // already run). Skip creating the policy, as this will only cause errors
196
- // if TT are enforced.
197
- console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
198
- return null;
199
- }
200
- };
158
+ 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; };
201
159
 
202
- function createDOMPurify() {
203
- var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
160
+ 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); } }
204
161
 
205
- var DOMPurify = function DOMPurify(root) {
206
- return createDOMPurify(root);
162
+ var getGlobal = function getGlobal() {
163
+ return typeof window === 'undefined' ? null : window;
207
164
  };
208
165
 
209
166
  /**
210
- * Version label, exposed for easier checks
211
- * if DOMPurify is up to date or not
167
+ * Creates a no-op policy for internal use only.
168
+ * Don't export this function outside this module!
169
+ * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
170
+ * @param {Document} document The document object (to determine policy name suffix)
171
+ * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
172
+ * are not supported).
212
173
  */
213
- DOMPurify.version = '2.0.8';
214
-
215
- /**
216
- * Array of elements that DOMPurify removed during sanitation.
217
- * Empty if nothing was removed.
218
- */
219
- DOMPurify.removed = [];
174
+ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
175
+ if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
176
+ return null;
177
+ }
220
178
 
221
- if (!window || !window.document || window.document.nodeType !== 9) {
222
- // Not running in a browser, provide a factory function
223
- // so that you can pass your own Window
224
- DOMPurify.isSupported = false;
179
+ // Allow the callers to control the unique policy name
180
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
181
+ // Policy creation with duplicate names throws in Trusted Types.
182
+ var suffix = null;
183
+ var ATTR_NAME = 'data-tt-policy-suffix';
184
+ if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
185
+ suffix = document.currentScript.getAttribute(ATTR_NAME);
186
+ }
225
187
 
226
- return DOMPurify;
227
- }
188
+ var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
228
189
 
229
- var originalDocument = window.document;
230
- var useDOMParser = false;
231
- var removeTitle = false;
232
-
233
- var document = window.document;
234
- var DocumentFragment = window.DocumentFragment,
235
- HTMLTemplateElement = window.HTMLTemplateElement,
236
- Node = window.Node,
237
- NodeFilter = window.NodeFilter,
238
- _window$NamedNodeMap = window.NamedNodeMap,
239
- NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
240
- Text = window.Text,
241
- Comment = window.Comment,
242
- DOMParser = window.DOMParser,
243
- trustedTypes = window.trustedTypes;
244
-
245
- // As per issue #47, the web-components registry is inherited by a
246
- // new document created via createHTMLDocument. As per the spec
247
- // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
248
- // a new empty registry is used when creating a template contents owner
249
- // document, so we use that as our parent document to ensure nothing
250
- // is inherited.
251
-
252
- if (typeof HTMLTemplateElement === 'function') {
253
- var template = document.createElement('template');
254
- if (template.content && template.content.ownerDocument) {
255
- document = template.content.ownerDocument;
190
+ try {
191
+ return trustedTypes.createPolicy(policyName, {
192
+ createHTML: function createHTML(html$$1) {
193
+ return html$$1;
194
+ }
195
+ });
196
+ } catch (_) {
197
+ // Policy creation failed (most likely another DOMPurify script has
198
+ // already run). Skip creating the policy, as this will only cause errors
199
+ // if TT are enforced.
200
+ console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
201
+ return null;
256
202
  }
257
- }
203
+ };
258
204
 
259
- var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
260
- var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
205
+ function createDOMPurify() {
206
+ var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
261
207
 
262
- var _document = document,
263
- implementation = _document.implementation,
264
- createNodeIterator = _document.createNodeIterator,
265
- getElementsByTagName = _document.getElementsByTagName,
266
- createDocumentFragment = _document.createDocumentFragment;
267
- var importNode = originalDocument.importNode;
208
+ var DOMPurify = function DOMPurify(root) {
209
+ return createDOMPurify(root);
210
+ };
268
211
 
212
+ /**
213
+ * Version label, exposed for easier checks
214
+ * if DOMPurify is up to date or not
215
+ */
216
+ DOMPurify.version = '2.0.12';
269
217
 
270
- var hooks = {};
218
+ /**
219
+ * Array of elements that DOMPurify removed during sanitation.
220
+ * Empty if nothing was removed.
221
+ */
222
+ DOMPurify.removed = [];
271
223
 
272
- /**
273
- * Expose whether this browser supports running the full DOMPurify.
274
- */
275
- DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && document.documentMode !== 9;
224
+ if (!window || !window.document || window.document.nodeType !== 9) {
225
+ // Not running in a browser, provide a factory function
226
+ // so that you can pass your own Window
227
+ DOMPurify.isSupported = false;
276
228
 
277
- var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
278
- ERB_EXPR$$1 = ERB_EXPR,
279
- DATA_ATTR$$1 = DATA_ATTR,
280
- ARIA_ATTR$$1 = ARIA_ATTR,
281
- IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
282
- ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
283
- var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
229
+ return DOMPurify;
230
+ }
284
231
 
285
- /**
286
- * We consider the elements and attributes below to be safe. Ideally
287
- * don't add any new ones but feel free to remove unwanted ones.
288
- */
232
+ var originalDocument = window.document;
233
+ var removeTitle = false;
234
+
235
+ var document = window.document;
236
+ var DocumentFragment = window.DocumentFragment,
237
+ HTMLTemplateElement = window.HTMLTemplateElement,
238
+ Node = window.Node,
239
+ NodeFilter = window.NodeFilter,
240
+ _window$NamedNodeMap = window.NamedNodeMap,
241
+ NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
242
+ Text = window.Text,
243
+ Comment = window.Comment,
244
+ DOMParser = window.DOMParser,
245
+ trustedTypes = window.trustedTypes;
246
+
247
+ // As per issue #47, the web-components registry is inherited by a
248
+ // new document created via createHTMLDocument. As per the spec
249
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
250
+ // a new empty registry is used when creating a template contents owner
251
+ // document, so we use that as our parent document to ensure nothing
252
+ // is inherited.
253
+
254
+ if (typeof HTMLTemplateElement === 'function') {
255
+ var template = document.createElement('template');
256
+ if (template.content && template.content.ownerDocument) {
257
+ document = template.content.ownerDocument;
258
+ }
259
+ }
289
260
 
290
- /* allowed element names */
261
+ var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
262
+ var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
291
263
 
292
- var ALLOWED_TAGS = null;
293
- var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(svgFilters), _toConsumableArray(mathMl), _toConsumableArray(text)));
264
+ var _document = document,
265
+ implementation = _document.implementation,
266
+ createNodeIterator = _document.createNodeIterator,
267
+ getElementsByTagName = _document.getElementsByTagName,
268
+ createDocumentFragment = _document.createDocumentFragment;
269
+ var importNode = originalDocument.importNode;
294
270
 
295
- /* Allowed attribute names */
296
- var ALLOWED_ATTR = null;
297
- var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(mathMl$1), _toConsumableArray(xml)));
298
271
 
299
- /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
300
- var FORBID_TAGS = null;
272
+ var hooks = {};
301
273
 
302
- /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
303
- var FORBID_ATTR = null;
274
+ /**
275
+ * Expose whether this browser supports running the full DOMPurify.
276
+ */
277
+ DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && document.documentMode !== 9;
304
278
 
305
- /* Decide if ARIA attributes are okay */
306
- var ALLOW_ARIA_ATTR = true;
279
+ var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
280
+ ERB_EXPR$$1 = ERB_EXPR,
281
+ DATA_ATTR$$1 = DATA_ATTR,
282
+ ARIA_ATTR$$1 = ARIA_ATTR,
283
+ IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
284
+ ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
285
+ var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
307
286
 
308
- /* Decide if custom data attributes are okay */
309
- var ALLOW_DATA_ATTR = true;
287
+ /**
288
+ * We consider the elements and attributes below to be safe. Ideally
289
+ * don't add any new ones but feel free to remove unwanted ones.
290
+ */
310
291
 
311
- /* Decide if unknown protocols are okay */
312
- var ALLOW_UNKNOWN_PROTOCOLS = false;
292
+ /* allowed element names */
313
293
 
314
- /* Output should be safe for jQuery's $() factory? */
315
- var SAFE_FOR_JQUERY = false;
294
+ var ALLOWED_TAGS = null;
295
+ var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));
316
296
 
317
- /* Output should be safe for common template engines.
318
- * This means, DOMPurify removes data attributes, mustaches and ERB
319
- */
320
- var SAFE_FOR_TEMPLATES = false;
297
+ /* Allowed attribute names */
298
+ var ALLOWED_ATTR = null;
299
+ var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
321
300
 
322
- /* Decide if document with <html>... should be returned */
323
- var WHOLE_DOCUMENT = false;
301
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
302
+ var FORBID_TAGS = null;
324
303
 
325
- /* Track whether config is already set on this instance of DOMPurify. */
326
- var SET_CONFIG = false;
304
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
305
+ var FORBID_ATTR = null;
327
306
 
328
- /* Decide if all elements (e.g. style, script) must be children of
329
- * document.body. By default, browsers might move them to document.head */
330
- var FORCE_BODY = false;
307
+ /* Decide if ARIA attributes are okay */
308
+ var ALLOW_ARIA_ATTR = true;
331
309
 
332
- /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
333
- * string (or a TrustedHTML object if Trusted Types are supported).
334
- * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
335
- */
336
- var RETURN_DOM = false;
310
+ /* Decide if custom data attributes are okay */
311
+ var ALLOW_DATA_ATTR = true;
337
312
 
338
- /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
339
- * string (or a TrustedHTML object if Trusted Types are supported) */
340
- var RETURN_DOM_FRAGMENT = false;
313
+ /* Decide if unknown protocols are okay */
314
+ var ALLOW_UNKNOWN_PROTOCOLS = false;
341
315
 
342
- /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
343
- * `Node` is imported into the current `Document`. If this flag is not enabled the
344
- * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
345
- * DOMPurify. */
346
- var RETURN_DOM_IMPORT = false;
316
+ /* Output should be safe for jQuery's $() factory? */
317
+ var SAFE_FOR_JQUERY = false;
347
318
 
348
- /* Try to return a Trusted Type object instead of a string, retrun a string in
349
- * case Trusted Types are not supported */
350
- var RETURN_TRUSTED_TYPE = false;
319
+ /* Output should be safe for common template engines.
320
+ * This means, DOMPurify removes data attributes, mustaches and ERB
321
+ */
322
+ var SAFE_FOR_TEMPLATES = false;
351
323
 
352
- /* Output should be free from DOM clobbering attacks? */
353
- var SANITIZE_DOM = true;
324
+ /* Decide if document with <html>... should be returned */
325
+ var WHOLE_DOCUMENT = false;
354
326
 
355
- /* Keep element content when removing element? */
356
- var KEEP_CONTENT = true;
327
+ /* Track whether config is already set on this instance of DOMPurify. */
328
+ var SET_CONFIG = false;
357
329
 
358
- /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
359
- * of importing it into a new Document and returning a sanitized copy */
360
- var IN_PLACE = false;
330
+ /* Decide if all elements (e.g. style, script) must be children of
331
+ * document.body. By default, browsers might move them to document.head */
332
+ var FORCE_BODY = false;
361
333
 
362
- /* Allow usage of profiles like html, svg and mathMl */
363
- var USE_PROFILES = {};
334
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
335
+ * string (or a TrustedHTML object if Trusted Types are supported).
336
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
337
+ */
338
+ var RETURN_DOM = false;
364
339
 
365
- /* Tags to ignore content of when KEEP_CONTENT is true */
366
- var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
340
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
341
+ * string (or a TrustedHTML object if Trusted Types are supported) */
342
+ var RETURN_DOM_FRAGMENT = false;
367
343
 
368
- /* Tags that are safe for data: URIs */
369
- var DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image']);
344
+ /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
345
+ * `Node` is imported into the current `Document`. If this flag is not enabled the
346
+ * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
347
+ * DOMPurify. */
348
+ var RETURN_DOM_IMPORT = false;
370
349
 
371
- /* Attributes safe for values like "javascript:" */
372
- var URI_SAFE_ATTRIBUTES = null;
373
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
350
+ /* Try to return a Trusted Type object instead of a string, retrun a string in
351
+ * case Trusted Types are not supported */
352
+ var RETURN_TRUSTED_TYPE = false;
374
353
 
375
- /* Keep a reference to config to pass to hooks */
376
- var CONFIG = null;
354
+ /* Output should be free from DOM clobbering attacks? */
355
+ var SANITIZE_DOM = true;
377
356
 
378
- /* Ideally, do not touch anything below this line */
379
- /* ______________________________________________ */
357
+ /* Keep element content when removing element? */
358
+ var KEEP_CONTENT = true;
380
359
 
381
- var formElement = document.createElement('form');
360
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
361
+ * of importing it into a new Document and returning a sanitized copy */
362
+ var IN_PLACE = false;
382
363
 
383
- /**
384
- * _parseConfig
385
- *
386
- * @param {Object} cfg optional config literal
387
- */
388
- // eslint-disable-next-line complexity
389
- var _parseConfig = function _parseConfig(cfg) {
390
- if (CONFIG && CONFIG === cfg) {
391
- return;
392
- }
364
+ /* Allow usage of profiles like html, svg and mathMl */
365
+ var USE_PROFILES = {};
393
366
 
394
- /* Shield configuration object from tampering */
395
- if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
396
- cfg = {};
397
- }
367
+ /* Tags to ignore content of when KEEP_CONTENT is true */
368
+ var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
398
369
 
399
- /* Set configuration parameters */
400
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
401
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
402
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
403
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
404
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
405
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
406
- ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
407
- ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
408
- ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
409
- SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false
410
- SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
411
- WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
412
- RETURN_DOM = cfg.RETURN_DOM || false; // Default false
413
- RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
414
- RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false
415
- RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
416
- FORCE_BODY = cfg.FORCE_BODY || false; // Default false
417
- SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
418
- KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
419
- IN_PLACE = cfg.IN_PLACE || false; // Default false
420
- IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
421
- if (SAFE_FOR_TEMPLATES) {
422
- ALLOW_DATA_ATTR = false;
423
- }
370
+ /* Tags that are safe for data: URIs */
371
+ var DATA_URI_TAGS = null;
372
+ var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
424
373
 
425
- if (RETURN_DOM_FRAGMENT) {
426
- RETURN_DOM = true;
427
- }
374
+ /* Attributes safe for values like "javascript:" */
375
+ var URI_SAFE_ATTRIBUTES = null;
376
+ var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
428
377
 
429
- /* Parse profile info */
430
- if (USE_PROFILES) {
431
- ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(text)));
432
- ALLOWED_ATTR = [];
433
- if (USE_PROFILES.html === true) {
434
- addToSet(ALLOWED_TAGS, html);
435
- addToSet(ALLOWED_ATTR, html$1);
436
- }
378
+ /* Keep a reference to config to pass to hooks */
379
+ var CONFIG = null;
380
+
381
+ /* Ideally, do not touch anything below this line */
382
+ /* ______________________________________________ */
437
383
 
438
- if (USE_PROFILES.svg === true) {
439
- addToSet(ALLOWED_TAGS, svg);
440
- addToSet(ALLOWED_ATTR, svg$1);
441
- addToSet(ALLOWED_ATTR, xml);
384
+ var formElement = document.createElement('form');
385
+
386
+ /**
387
+ * _parseConfig
388
+ *
389
+ * @param {Object} cfg optional config literal
390
+ */
391
+ // eslint-disable-next-line complexity
392
+ var _parseConfig = function _parseConfig(cfg) {
393
+ if (CONFIG && CONFIG === cfg) {
394
+ return;
442
395
  }
443
396
 
444
- if (USE_PROFILES.svgFilters === true) {
445
- addToSet(ALLOWED_TAGS, svgFilters);
446
- addToSet(ALLOWED_ATTR, svg$1);
447
- addToSet(ALLOWED_ATTR, xml);
397
+ /* Shield configuration object from tampering */
398
+ if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
399
+ cfg = {};
448
400
  }
449
401
 
450
- if (USE_PROFILES.mathMl === true) {
451
- addToSet(ALLOWED_TAGS, mathMl);
452
- addToSet(ALLOWED_ATTR, mathMl$1);
453
- addToSet(ALLOWED_ATTR, xml);
402
+ /* Set configuration parameters */
403
+ ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
404
+ ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
405
+ URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
406
+ DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
407
+ FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
408
+ FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
409
+ USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
410
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
411
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
412
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
413
+ SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false
414
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
415
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
416
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
417
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
418
+ RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false
419
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
420
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
421
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
422
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
423
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
424
+ IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
425
+ if (SAFE_FOR_TEMPLATES) {
426
+ ALLOW_DATA_ATTR = false;
454
427
  }
455
- }
456
428
 
457
- /* Merge configuration parameters */
458
- if (cfg.ADD_TAGS) {
459
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
460
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
429
+ if (RETURN_DOM_FRAGMENT) {
430
+ RETURN_DOM = true;
461
431
  }
462
432
 
463
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
464
- }
433
+ /* Parse profile info */
434
+ if (USE_PROFILES) {
435
+ ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
436
+ ALLOWED_ATTR = [];
437
+ if (USE_PROFILES.html === true) {
438
+ addToSet(ALLOWED_TAGS, html);
439
+ addToSet(ALLOWED_ATTR, html$1);
440
+ }
441
+
442
+ if (USE_PROFILES.svg === true) {
443
+ addToSet(ALLOWED_TAGS, svg);
444
+ addToSet(ALLOWED_ATTR, svg$1);
445
+ addToSet(ALLOWED_ATTR, xml);
446
+ }
447
+
448
+ if (USE_PROFILES.svgFilters === true) {
449
+ addToSet(ALLOWED_TAGS, svgFilters);
450
+ addToSet(ALLOWED_ATTR, svg$1);
451
+ addToSet(ALLOWED_ATTR, xml);
452
+ }
465
453
 
466
- if (cfg.ADD_ATTR) {
467
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
468
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
454
+ if (USE_PROFILES.mathMl === true) {
455
+ addToSet(ALLOWED_TAGS, mathMl);
456
+ addToSet(ALLOWED_ATTR, mathMl$1);
457
+ addToSet(ALLOWED_ATTR, xml);
458
+ }
469
459
  }
470
460
 
471
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
472
- }
461
+ /* Merge configuration parameters */
462
+ if (cfg.ADD_TAGS) {
463
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
464
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
465
+ }
473
466
 
474
- if (cfg.ADD_URI_SAFE_ATTR) {
475
- addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
476
- }
467
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
468
+ }
477
469
 
478
- /* Add #text in case KEEP_CONTENT is set to true */
479
- if (KEEP_CONTENT) {
480
- ALLOWED_TAGS['#text'] = true;
481
- }
470
+ if (cfg.ADD_ATTR) {
471
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
472
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
473
+ }
482
474
 
483
- /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
484
- if (WHOLE_DOCUMENT) {
485
- addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
486
- }
475
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
476
+ }
487
477
 
488
- /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
489
- if (ALLOWED_TAGS.table) {
490
- addToSet(ALLOWED_TAGS, ['tbody']);
491
- delete FORBID_TAGS.tbody;
492
- }
478
+ if (cfg.ADD_URI_SAFE_ATTR) {
479
+ addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
480
+ }
493
481
 
494
- // Prevent further manipulation of configuration.
495
- // Not available in IE8, Safari 5, etc.
496
- if (freeze) {
497
- freeze(cfg);
498
- }
482
+ /* Add #text in case KEEP_CONTENT is set to true */
483
+ if (KEEP_CONTENT) {
484
+ ALLOWED_TAGS['#text'] = true;
485
+ }
499
486
 
500
- CONFIG = cfg;
501
- };
487
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
488
+ if (WHOLE_DOCUMENT) {
489
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
490
+ }
502
491
 
503
- /**
504
- * _forceRemove
505
- *
506
- * @param {Node} node a DOM node
507
- */
508
- var _forceRemove = function _forceRemove(node) {
509
- arrayPush(DOMPurify.removed, { element: node });
510
- try {
511
- node.parentNode.removeChild(node);
512
- } catch (error) {
513
- node.outerHTML = emptyHTML;
514
- }
515
- };
492
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
493
+ if (ALLOWED_TAGS.table) {
494
+ addToSet(ALLOWED_TAGS, ['tbody']);
495
+ delete FORBID_TAGS.tbody;
496
+ }
516
497
 
517
- /**
518
- * _removeAttribute
519
- *
520
- * @param {String} name an Attribute name
521
- * @param {Node} node a DOM node
522
- */
523
- var _removeAttribute = function _removeAttribute(name, node) {
524
- try {
525
- arrayPush(DOMPurify.removed, {
526
- attribute: node.getAttributeNode(name),
527
- from: node
528
- });
529
- } catch (error) {
530
- arrayPush(DOMPurify.removed, {
531
- attribute: null,
532
- from: node
533
- });
534
- }
498
+ // Prevent further manipulation of configuration.
499
+ // Not available in IE8, Safari 5, etc.
500
+ if (freeze) {
501
+ freeze(cfg);
502
+ }
535
503
 
536
- node.removeAttribute(name);
537
- };
504
+ CONFIG = cfg;
505
+ };
538
506
 
539
- /**
540
- * _initDocument
541
- *
542
- * @param {String} dirty a string of dirty markup
543
- * @return {Document} a DOM, filled with the dirty markup
544
- */
545
- var _initDocument = function _initDocument(dirty) {
546
- /* Create a HTML document */
547
- var doc = void 0;
548
- var leadingWhitespace = void 0;
549
-
550
- if (FORCE_BODY) {
551
- dirty = '<remove></remove>' + dirty;
552
- } else {
553
- /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
554
- var matches = stringMatch(dirty, /^[\s]+/);
555
- leadingWhitespace = matches && matches[0];
556
- }
507
+ /**
508
+ * _forceRemove
509
+ *
510
+ * @param {Node} node a DOM node
511
+ */
512
+ var _forceRemove = function _forceRemove(node) {
513
+ arrayPush(DOMPurify.removed, { element: node });
514
+ try {
515
+ // eslint-disable-next-line unicorn/prefer-node-remove
516
+ node.parentNode.removeChild(node);
517
+ } catch (_) {
518
+ node.outerHTML = emptyHTML;
519
+ }
520
+ };
557
521
 
558
- var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
559
- /* Use DOMParser to workaround Firefox bug (see comment below) */
560
- if (useDOMParser) {
522
+ /**
523
+ * _removeAttribute
524
+ *
525
+ * @param {String} name an Attribute name
526
+ * @param {Node} node a DOM node
527
+ */
528
+ var _removeAttribute = function _removeAttribute(name, node) {
561
529
  try {
562
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
563
- } catch (error) {}
564
- }
530
+ arrayPush(DOMPurify.removed, {
531
+ attribute: node.getAttributeNode(name),
532
+ from: node
533
+ });
534
+ } catch (_) {
535
+ arrayPush(DOMPurify.removed, {
536
+ attribute: null,
537
+ from: node
538
+ });
539
+ }
565
540
 
566
- /* Remove title to fix a mXSS bug in older MS Edge */
567
- if (removeTitle) {
568
- addToSet(FORBID_TAGS, ['title']);
569
- }
541
+ node.removeAttribute(name);
542
+ };
570
543
 
571
- /* Otherwise use createHTMLDocument, because DOMParser is unsafe in
572
- Safari (see comment below) */
573
- if (!doc || !doc.documentElement) {
574
- doc = implementation.createHTMLDocument('');
575
- var _doc = doc,
576
- body = _doc.body;
544
+ /**
545
+ * _initDocument
546
+ *
547
+ * @param {String} dirty a string of dirty markup
548
+ * @return {Document} a DOM, filled with the dirty markup
549
+ */
550
+ var _initDocument = function _initDocument(dirty) {
551
+ /* Create a HTML document */
552
+ var doc = void 0;
553
+ var leadingWhitespace = void 0;
554
+
555
+ if (FORCE_BODY) {
556
+ dirty = '<remove></remove>' + dirty;
557
+ } else {
558
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
559
+ var matches = stringMatch(dirty, /^[\r\n\t ]+/);
560
+ leadingWhitespace = matches && matches[0];
561
+ }
577
562
 
578
- body.parentNode.removeChild(body.parentNode.firstElementChild);
579
- body.outerHTML = dirtyPayload;
580
- }
563
+ var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
564
+ /* Use the DOMParser API by default, fallback later if needs be */
565
+ try {
566
+ doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
567
+ } catch (_) {}
581
568
 
582
- if (dirty && leadingWhitespace) {
583
- doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
584
- }
569
+ /* Remove title to fix a mXSS bug in older MS Edge */
570
+ if (removeTitle) {
571
+ addToSet(FORBID_TAGS, ['title']);
572
+ }
585
573
 
586
- /* Work on whole document or just its body */
587
- return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
588
- };
574
+ /* Use createHTMLDocument in case DOMParser is not available */
575
+ if (!doc || !doc.documentElement) {
576
+ doc = implementation.createHTMLDocument('');
577
+ var _doc = doc,
578
+ body = _doc.body;
589
579
 
590
- // Firefox uses a different parser for innerHTML rather than
591
- // DOMParser (see https://bugzilla.mozilla.org/show_bug.cgi?id=1205631)
592
- // which means that you *must* use DOMParser, otherwise the output may
593
- // not be safe if used in a document.write context later.
594
- //
595
- // So we feature detect the Firefox bug and use the DOMParser if necessary.
596
- //
597
- // Chrome 77 and other versions ship an mXSS bug that caused a bypass to
598
- // happen. We now check for the mXSS trigger and react accordingly.
599
- if (DOMPurify.isSupported) {
600
- (function () {
601
- try {
602
- var doc = _initDocument('<svg><p><textarea><img src="</textarea><img src=x abc=1//">');
603
- if (doc.querySelector('svg img')) {
604
- useDOMParser = true;
605
- }
606
- } catch (error) {}
607
- })();
580
+ body.parentNode.removeChild(body.parentNode.firstElementChild);
581
+ body.outerHTML = dirtyPayload;
582
+ }
608
583
 
609
- (function () {
610
- try {
611
- var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
612
- if (regExpTest(/<\/title/, doc.querySelector('title').innerHTML)) {
613
- removeTitle = true;
614
- }
615
- } catch (error) {}
616
- })();
617
- }
584
+ if (dirty && leadingWhitespace) {
585
+ doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
586
+ }
618
587
 
619
- /**
620
- * _createIterator
621
- *
622
- * @param {Document} root document/fragment to create iterator for
623
- * @return {Iterator} iterator instance
624
- */
625
- var _createIterator = function _createIterator(root) {
626
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
627
- return NodeFilter.FILTER_ACCEPT;
628
- }, false);
629
- };
588
+ /* Work on whole document or just its body */
589
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
590
+ };
630
591
 
631
- /**
632
- * _isClobbered
633
- *
634
- * @param {Node} elm element to check for clobbering attacks
635
- * @return {Boolean} true if clobbered, false if safe
636
- */
637
- var _isClobbered = function _isClobbered(elm) {
638
- if (elm instanceof Text || elm instanceof Comment) {
639
- return false;
592
+ /* Here we test for a broken feature in Edge that might cause mXSS */
593
+ if (DOMPurify.isSupported) {
594
+ (function () {
595
+ try {
596
+ var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
597
+ if (regExpTest(/<\/title/, doc.querySelector('title').innerHTML)) {
598
+ removeTitle = true;
599
+ }
600
+ } catch (_) {}
601
+ })();
640
602
  }
641
603
 
642
- if (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') {
643
- return true;
644
- }
604
+ /**
605
+ * _createIterator
606
+ *
607
+ * @param {Document} root document/fragment to create iterator for
608
+ * @return {Iterator} iterator instance
609
+ */
610
+ var _createIterator = function _createIterator(root) {
611
+ return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
612
+ return NodeFilter.FILTER_ACCEPT;
613
+ }, false);
614
+ };
645
615
 
646
- return false;
647
- };
616
+ /**
617
+ * _isClobbered
618
+ *
619
+ * @param {Node} elm element to check for clobbering attacks
620
+ * @return {Boolean} true if clobbered, false if safe
621
+ */
622
+ var _isClobbered = function _isClobbered(elm) {
623
+ if (elm instanceof Text || elm instanceof Comment) {
624
+ return false;
625
+ }
648
626
 
649
- /**
650
- * _isNode
651
- *
652
- * @param {Node} obj object to check whether it's a DOM node
653
- * @return {Boolean} true is object is a DOM node
654
- */
655
- var _isNode = function _isNode(obj) {
656
- return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? obj instanceof Node : obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string';
657
- };
627
+ if (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') {
628
+ return true;
629
+ }
658
630
 
659
- /**
660
- * _executeHook
661
- * Execute user configurable hooks
662
- *
663
- * @param {String} entryPoint Name of the hook's entry point
664
- * @param {Node} currentNode node to work on with the hook
665
- * @param {Object} data additional hook parameters
666
- */
667
- var _executeHook = function _executeHook(entryPoint, currentNode, data) {
668
- if (!hooks[entryPoint]) {
669
- return;
670
- }
631
+ return false;
632
+ };
671
633
 
672
- arrayForEach(hooks[entryPoint], function (hook) {
673
- hook.call(DOMPurify, currentNode, data, CONFIG);
674
- });
675
- };
634
+ /**
635
+ * _isNode
636
+ *
637
+ * @param {Node} obj object to check whether it's a DOM node
638
+ * @return {Boolean} true is object is a DOM node
639
+ */
640
+ var _isNode = function _isNode(object) {
641
+ 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';
642
+ };
676
643
 
677
- /**
678
- * _sanitizeElements
679
- *
680
- * @protect nodeName
681
- * @protect textContent
682
- * @protect removeChild
683
- *
684
- * @param {Node} currentNode to check for permission to exist
685
- * @return {Boolean} true if node was killed, false if left alive
686
- */
687
- // eslint-disable-next-line complexity
688
- var _sanitizeElements = function _sanitizeElements(currentNode) {
689
- var content = void 0;
644
+ /**
645
+ * _executeHook
646
+ * Execute user configurable hooks
647
+ *
648
+ * @param {String} entryPoint Name of the hook's entry point
649
+ * @param {Node} currentNode node to work on with the hook
650
+ * @param {Object} data additional hook parameters
651
+ */
652
+ var _executeHook = function _executeHook(entryPoint, currentNode, data) {
653
+ if (!hooks[entryPoint]) {
654
+ return;
655
+ }
656
+
657
+ arrayForEach(hooks[entryPoint], function (hook) {
658
+ hook.call(DOMPurify, currentNode, data, CONFIG);
659
+ });
660
+ };
690
661
 
691
- /* Execute a hook if present */
692
- _executeHook('beforeSanitizeElements', currentNode, null);
662
+ /**
663
+ * _sanitizeElements
664
+ *
665
+ * @protect nodeName
666
+ * @protect textContent
667
+ * @protect removeChild
668
+ *
669
+ * @param {Node} currentNode to check for permission to exist
670
+ * @return {Boolean} true if node was killed, false if left alive
671
+ */
672
+ // eslint-disable-next-line complexity
673
+ var _sanitizeElements = function _sanitizeElements(currentNode) {
674
+ var content = void 0;
693
675
 
694
- /* Check if element is clobbered or can clobber */
695
- if (_isClobbered(currentNode)) {
696
- _forceRemove(currentNode);
697
- return true;
698
- }
676
+ /* Execute a hook if present */
677
+ _executeHook('beforeSanitizeElements', currentNode, null);
699
678
 
700
- /* Now let's check the element's type and name */
701
- var tagName = stringToLowerCase(currentNode.nodeName);
679
+ /* Check if element is clobbered or can clobber */
680
+ if (_isClobbered(currentNode)) {
681
+ _forceRemove(currentNode);
682
+ return true;
683
+ }
702
684
 
703
- /* Execute a hook if present */
704
- _executeHook('uponSanitizeElement', currentNode, {
705
- tagName: tagName,
706
- allowedTags: ALLOWED_TAGS
707
- });
685
+ /* Now let's check the element's type and name */
686
+ var tagName = stringToLowerCase(currentNode.nodeName);
708
687
 
709
- /* Take care of an mXSS pattern using p, br inside svg, math */
710
- if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br').length !== 0) {
711
- _forceRemove(currentNode);
712
- return true;
713
- }
688
+ /* Execute a hook if present */
689
+ _executeHook('uponSanitizeElement', currentNode, {
690
+ tagName: tagName,
691
+ allowedTags: ALLOWED_TAGS
692
+ });
714
693
 
715
- /* Remove element if anything forbids its presence */
716
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
717
- /* Keep content except for black-listed elements */
718
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
719
- try {
720
- var htmlToInsert = currentNode.innerHTML;
721
- currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
722
- } catch (error) {}
694
+ /* Take care of an mXSS pattern using p, br inside svg, math */
695
+ if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br').length !== 0) {
696
+ _forceRemove(currentNode);
697
+ return true;
723
698
  }
724
699
 
725
- _forceRemove(currentNode);
726
- return true;
727
- }
700
+ /* Remove element if anything forbids its presence */
701
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
702
+ /* Keep content except for bad-listed elements */
703
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
704
+ try {
705
+ var htmlToInsert = currentNode.innerHTML;
706
+ currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
707
+ } catch (_) {}
708
+ }
728
709
 
729
- /* Remove in case a noscript/noembed XSS is suspected */
730
- if (tagName === 'noscript' && regExpTest(/<\/noscript/i, currentNode.innerHTML)) {
731
- _forceRemove(currentNode);
732
- return true;
733
- }
710
+ _forceRemove(currentNode);
711
+ return true;
712
+ }
734
713
 
735
- if (tagName === 'noembed' && regExpTest(/<\/noembed/i, currentNode.innerHTML)) {
736
- _forceRemove(currentNode);
737
- return true;
738
- }
714
+ /* Remove in case a noscript/noembed XSS is suspected */
715
+ if (tagName === 'noscript' && regExpTest(/<\/noscript/i, currentNode.innerHTML)) {
716
+ _forceRemove(currentNode);
717
+ return true;
718
+ }
739
719
 
740
- /* Convert markup to cover jQuery behavior */
741
- if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && (!currentNode.content || !currentNode.content.firstElementChild) && regExpTest(/</g, currentNode.textContent)) {
742
- arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
743
- if (currentNode.innerHTML) {
744
- currentNode.innerHTML = stringReplace(currentNode.innerHTML, /</g, '&lt;');
745
- } else {
746
- currentNode.innerHTML = stringReplace(currentNode.textContent, /</g, '&lt;');
720
+ if (tagName === 'noembed' && regExpTest(/<\/noembed/i, currentNode.innerHTML)) {
721
+ _forceRemove(currentNode);
722
+ return true;
747
723
  }
748
- }
749
724
 
750
- /* Sanitize element content to be template-safe */
751
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
752
- /* Get the element's text content */
753
- content = currentNode.textContent;
754
- content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
755
- content = stringReplace(content, ERB_EXPR$$1, ' ');
756
- if (currentNode.textContent !== content) {
725
+ /* Convert markup to cover jQuery behavior */
726
+ if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && (!currentNode.content || !currentNode.content.firstElementChild) && regExpTest(/</g, currentNode.textContent)) {
757
727
  arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
758
- currentNode.textContent = content;
728
+ if (currentNode.innerHTML) {
729
+ currentNode.innerHTML = stringReplace(currentNode.innerHTML, /</g, '&lt;');
730
+ } else {
731
+ currentNode.innerHTML = stringReplace(currentNode.textContent, /</g, '&lt;');
732
+ }
759
733
  }
760
- }
761
-
762
- /* Execute a hook if present */
763
- _executeHook('afterSanitizeElements', currentNode, null);
764
734
 
765
- return false;
766
- };
767
-
768
- /**
769
- * _isValidAttribute
770
- *
771
- * @param {string} lcTag Lowercase tag name of containing element.
772
- * @param {string} lcName Lowercase attribute name.
773
- * @param {string} value Attribute value.
774
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
775
- */
776
- // eslint-disable-next-line complexity
777
- var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
778
- /* Make sure attribute cannot clobber */
779
- if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
780
- return false;
781
- }
735
+ /* Sanitize element content to be template-safe */
736
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
737
+ /* Get the element's text content */
738
+ content = currentNode.textContent;
739
+ content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
740
+ content = stringReplace(content, ERB_EXPR$$1, ' ');
741
+ if (currentNode.textContent !== content) {
742
+ arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
743
+ currentNode.textContent = content;
744
+ }
745
+ }
782
746
 
783
- /* Allow valid data-* attributes: At least one character after "-"
784
- (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
785
- XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
786
- We don't need to check the value; it's always URI safe. */
787
- if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) {
788
- // This attribute is safe
789
- } else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) {
790
- // This attribute is safe
791
- /* Otherwise, check the name is permitted */
792
- } else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
793
- return false;
747
+ /* Execute a hook if present */
748
+ _executeHook('afterSanitizeElements', currentNode, null);
794
749
 
795
- /* Check value is safe. First, is attr inert? If so, is safe */
796
- } else if (URI_SAFE_ATTRIBUTES[lcName]) {
797
- // This attribute is safe
798
- /* Check no script, data or unknown possibly unsafe URI
799
- unless we know URI values are safe for that attribute */
800
- } else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) {
801
- // This attribute is safe
802
- /* Keep image data URIs alive if src/xlink:href is allowed */
803
- /* Further prevent gadget XSS for dynamically built script tags */
804
- } else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) {
805
- // This attribute is safe
806
- /* Allow unknown protocols: This provides support for links that
807
- are handled by protocol handlers which may be unknown ahead of
808
- time, e.g. fb:, spotify: */
809
- } else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) {
810
- // This attribute is safe
811
- /* Check for binary attributes */
812
- // eslint-disable-next-line no-negated-condition
813
- } else if (!value) {
814
- // Binary attributes are safe at this point
815
- /* Anything else, presume unsafe, do not add it back */
816
- } else {
817
750
  return false;
818
- }
819
-
820
- return true;
821
- };
822
-
823
- /**
824
- * _sanitizeAttributes
825
- *
826
- * @protect attributes
827
- * @protect nodeName
828
- * @protect removeAttribute
829
- * @protect setAttribute
830
- *
831
- * @param {Node} currentNode to sanitize
832
- */
833
- // eslint-disable-next-line complexity
834
- var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
835
- var attr = void 0;
836
- var value = void 0;
837
- var lcName = void 0;
838
- var idAttr = void 0;
839
- var l = void 0;
840
- /* Execute a hook if present */
841
- _executeHook('beforeSanitizeAttributes', currentNode, null);
842
-
843
- var attributes = currentNode.attributes;
844
-
845
- /* Check if we have attributes; if not we might have a text node */
846
-
847
- if (!attributes) {
848
- return;
849
- }
850
-
851
- var hookEvent = {
852
- attrName: '',
853
- attrValue: '',
854
- keepAttr: true,
855
- allowedAttributes: ALLOWED_ATTR
856
751
  };
857
- l = attributes.length;
858
-
859
- /* Go backwards over all attributes; safely remove bad ones */
860
- while (l--) {
861
- attr = attributes[l];
862
- var _attr = attr,
863
- name = _attr.name,
864
- namespaceURI = _attr.namespaceURI;
865
-
866
- value = stringTrim(attr.value);
867
- lcName = stringToLowerCase(name);
868
752
 
869
- /* Execute a hook if present */
870
- hookEvent.attrName = lcName;
871
- hookEvent.attrValue = value;
872
- hookEvent.keepAttr = true;
873
- hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
874
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
875
- value = hookEvent.attrValue;
876
- /* Did the hooks approve of the attribute? */
877
- if (hookEvent.forceKeepAttr) {
878
- continue;
753
+ /**
754
+ * _isValidAttribute
755
+ *
756
+ * @param {string} lcTag Lowercase tag name of containing element.
757
+ * @param {string} lcName Lowercase attribute name.
758
+ * @param {string} value Attribute value.
759
+ * @return {Boolean} Returns true if `value` is valid, otherwise false.
760
+ */
761
+ // eslint-disable-next-line complexity
762
+ var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
763
+ /* Make sure attribute cannot clobber */
764
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
765
+ return false;
879
766
  }
880
767
 
881
- /* Remove attribute */
882
- // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
883
- // remove a "name" attribute from an <img> tag that has an "id"
884
- // attribute at the time.
885
- if (lcName === 'name' && currentNode.nodeName === 'IMG' && attributes.id) {
886
- idAttr = attributes.id;
887
- attributes = arraySlice(attributes, []);
888
- _removeAttribute('id', currentNode);
889
- _removeAttribute(name, currentNode);
890
- if (arrayIndexOf(attributes, idAttr) > l) {
891
- currentNode.setAttribute('id', idAttr.value);
892
- }
893
- } else if (
894
- // This works around a bug in Safari, where input[type=file]
895
- // cannot be dynamically set after type has been removed
896
- currentNode.nodeName === 'INPUT' && lcName === 'type' && value === 'file' && hookEvent.keepAttr && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
897
- continue;
898
- } else {
899
- // This avoids a crash in Safari v9.0 with double-ids.
900
- // The trick is to first set the id to be empty and then to
901
- // remove the attribute
902
- if (name === 'id') {
903
- currentNode.setAttribute(name, '');
904
- }
768
+ /* Allow valid data-* attributes: At least one character after "-"
769
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
770
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
771
+ We don't need to check the value; it's always URI safe. */
772
+ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
773
+ return false;
905
774
 
906
- _removeAttribute(name, currentNode);
775
+ /* Check value is safe. First, is attr inert? If so, is safe */
776
+ } 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 {
777
+ return false;
907
778
  }
908
779
 
909
- /* Did the hooks approve of the attribute? */
910
- if (!hookEvent.keepAttr) {
911
- continue;
912
- }
780
+ return true;
781
+ };
913
782
 
914
- /* Work around a security issue in jQuery 3.0 */
915
- if (SAFE_FOR_JQUERY && regExpTest(/\/>/i, value)) {
916
- _removeAttribute(name, currentNode);
917
- continue;
918
- }
783
+ /**
784
+ * _sanitizeAttributes
785
+ *
786
+ * @protect attributes
787
+ * @protect nodeName
788
+ * @protect removeAttribute
789
+ * @protect setAttribute
790
+ *
791
+ * @param {Node} currentNode to sanitize
792
+ */
793
+ // eslint-disable-next-line complexity
794
+ var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
795
+ var attr = void 0;
796
+ var value = void 0;
797
+ var lcName = void 0;
798
+ var idAttr = void 0;
799
+ var l = void 0;
800
+ /* Execute a hook if present */
801
+ _executeHook('beforeSanitizeAttributes', currentNode, null);
919
802
 
920
- /* Take care of an mXSS pattern using namespace switches */
921
- if (regExpTest(/svg|math/i, currentNode.namespaceURI) && regExpTest(regExpCreate('</(' + arrayJoin(objectKeys(FORBID_CONTENTS), '|') + ')', 'i'), value)) {
922
- _removeAttribute(name, currentNode);
923
- continue;
924
- }
803
+ var attributes = currentNode.attributes;
925
804
 
926
- /* Sanitize attribute content to be template-safe */
927
- if (SAFE_FOR_TEMPLATES) {
928
- value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
929
- value = stringReplace(value, ERB_EXPR$$1, ' ');
930
- }
805
+ /* Check if we have attributes; if not we might have a text node */
931
806
 
932
- /* Is `value` valid for this attribute? */
933
- var lcTag = currentNode.nodeName.toLowerCase();
934
- if (!_isValidAttribute(lcTag, lcName, value)) {
935
- continue;
807
+ if (!attributes) {
808
+ return;
936
809
  }
937
810
 
938
- /* Handle invalid data-* attribute set by try-catching it */
939
- try {
940
- if (namespaceURI) {
941
- currentNode.setAttributeNS(namespaceURI, name, value);
811
+ var hookEvent = {
812
+ attrName: '',
813
+ attrValue: '',
814
+ keepAttr: true,
815
+ allowedAttributes: ALLOWED_ATTR
816
+ };
817
+ l = attributes.length;
818
+
819
+ /* Go backwards over all attributes; safely remove bad ones */
820
+ while (l--) {
821
+ attr = attributes[l];
822
+ var _attr = attr,
823
+ name = _attr.name,
824
+ namespaceURI = _attr.namespaceURI;
825
+
826
+ value = stringTrim(attr.value);
827
+ lcName = stringToLowerCase(name);
828
+
829
+ /* Execute a hook if present */
830
+ hookEvent.attrName = lcName;
831
+ hookEvent.attrValue = value;
832
+ hookEvent.keepAttr = true;
833
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
834
+ _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
835
+ value = hookEvent.attrValue;
836
+ /* Did the hooks approve of the attribute? */
837
+ if (hookEvent.forceKeepAttr) {
838
+ continue;
839
+ }
840
+
841
+ /* Remove attribute */
842
+ // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
843
+ // remove a "name" attribute from an <img> tag that has an "id"
844
+ // attribute at the time.
845
+ if (lcName === 'name' && currentNode.nodeName === 'IMG' && attributes.id) {
846
+ idAttr = attributes.id;
847
+ attributes = arraySlice(attributes, []);
848
+ _removeAttribute('id', currentNode);
849
+ _removeAttribute(name, currentNode);
850
+ if (arrayIndexOf(attributes, idAttr) > l) {
851
+ currentNode.setAttribute('id', idAttr.value);
852
+ }
853
+ } else if (
854
+ // This works around a bug in Safari, where input[type=file]
855
+ // cannot be dynamically set after type has been removed
856
+ currentNode.nodeName === 'INPUT' && lcName === 'type' && value === 'file' && hookEvent.keepAttr && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
857
+ continue;
942
858
  } else {
943
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
944
- currentNode.setAttribute(name, value);
859
+ // This avoids a crash in Safari v9.0 with double-ids.
860
+ // The trick is to first set the id to be empty and then to
861
+ // remove the attribute
862
+ if (name === 'id') {
863
+ currentNode.setAttribute(name, '');
864
+ }
865
+
866
+ _removeAttribute(name, currentNode);
945
867
  }
946
868
 
947
- arrayPop(DOMPurify.removed);
948
- } catch (error) {}
949
- }
869
+ /* Did the hooks approve of the attribute? */
870
+ if (!hookEvent.keepAttr) {
871
+ continue;
872
+ }
950
873
 
951
- /* Execute a hook if present */
952
- _executeHook('afterSanitizeAttributes', currentNode, null);
953
- };
874
+ /* Work around a security issue in jQuery 3.0 */
875
+ if (SAFE_FOR_JQUERY && regExpTest(/\/>/i, value)) {
876
+ _removeAttribute(name, currentNode);
877
+ continue;
878
+ }
954
879
 
955
- /**
956
- * _sanitizeShadowDOM
957
- *
958
- * @param {DocumentFragment} fragment to iterate over recursively
959
- */
960
- var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
961
- var shadowNode = void 0;
962
- var shadowIterator = _createIterator(fragment);
880
+ /* Take care of an mXSS pattern using namespace switches */
881
+ if (regExpTest(/svg|math/i, currentNode.namespaceURI) && regExpTest(regExpCreate('</(' + arrayJoin(objectKeys(FORBID_CONTENTS), '|') + ')', 'i'), value)) {
882
+ _removeAttribute(name, currentNode);
883
+ continue;
884
+ }
963
885
 
964
- /* Execute a hook if present */
965
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
886
+ /* Sanitize attribute content to be template-safe */
887
+ if (SAFE_FOR_TEMPLATES) {
888
+ value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
889
+ value = stringReplace(value, ERB_EXPR$$1, ' ');
890
+ }
966
891
 
967
- while (shadowNode = shadowIterator.nextNode()) {
968
- /* Execute a hook if present */
969
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
892
+ /* Is `value` valid for this attribute? */
893
+ var lcTag = currentNode.nodeName.toLowerCase();
894
+ if (!_isValidAttribute(lcTag, lcName, value)) {
895
+ continue;
896
+ }
970
897
 
971
- /* Sanitize tags and elements */
972
- if (_sanitizeElements(shadowNode)) {
973
- continue;
898
+ /* Handle invalid data-* attribute set by try-catching it */
899
+ try {
900
+ if (namespaceURI) {
901
+ currentNode.setAttributeNS(namespaceURI, name, value);
902
+ } else {
903
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
904
+ currentNode.setAttribute(name, value);
905
+ }
906
+
907
+ arrayPop(DOMPurify.removed);
908
+ } catch (_) {}
974
909
  }
975
910
 
976
- /* Deep shadow DOM detected */
977
- if (shadowNode.content instanceof DocumentFragment) {
978
- _sanitizeShadowDOM(shadowNode.content);
979
- }
911
+ /* Execute a hook if present */
912
+ _executeHook('afterSanitizeAttributes', currentNode, null);
913
+ };
980
914
 
981
- /* Check attributes, sanitize if necessary */
982
- _sanitizeAttributes(shadowNode);
983
- }
915
+ /**
916
+ * _sanitizeShadowDOM
917
+ *
918
+ * @param {DocumentFragment} fragment to iterate over recursively
919
+ */
920
+ var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
921
+ var shadowNode = void 0;
922
+ var shadowIterator = _createIterator(fragment);
984
923
 
985
- /* Execute a hook if present */
986
- _executeHook('afterSanitizeShadowDOM', fragment, null);
987
- };
924
+ /* Execute a hook if present */
925
+ _executeHook('beforeSanitizeShadowDOM', fragment, null);
988
926
 
989
- /**
990
- * Sanitize
991
- * Public method providing core sanitation functionality
992
- *
993
- * @param {String|Node} dirty string or DOM node
994
- * @param {Object} configuration object
995
- */
996
- // eslint-disable-next-line complexity
997
- DOMPurify.sanitize = function (dirty, cfg) {
998
- var body = void 0;
999
- var importedNode = void 0;
1000
- var currentNode = void 0;
1001
- var oldNode = void 0;
1002
- var returnNode = void 0;
1003
- /* Make sure we have a string to sanitize.
1004
- DO NOT return early, as this will return the wrong type if
1005
- the user has requested a DOM object rather than a string */
1006
- if (!dirty) {
1007
- dirty = '<!-->';
1008
- }
927
+ while (shadowNode = shadowIterator.nextNode()) {
928
+ /* Execute a hook if present */
929
+ _executeHook('uponSanitizeShadowNode', shadowNode, null);
1009
930
 
1010
- /* Stringify, in case dirty is an object */
1011
- if (typeof dirty !== 'string' && !_isNode(dirty)) {
1012
- // eslint-disable-next-line no-negated-condition
1013
- if (typeof dirty.toString !== 'function') {
1014
- throw typeErrorCreate('toString is not a function');
1015
- } else {
1016
- dirty = dirty.toString();
1017
- if (typeof dirty !== 'string') {
1018
- throw typeErrorCreate('dirty is not a string, aborting');
931
+ /* Sanitize tags and elements */
932
+ if (_sanitizeElements(shadowNode)) {
933
+ continue;
1019
934
  }
1020
- }
1021
- }
1022
935
 
1023
- /* Check we can run. Otherwise fall back or ignore */
1024
- if (!DOMPurify.isSupported) {
1025
- if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
1026
- if (typeof dirty === 'string') {
1027
- return window.toStaticHTML(dirty);
936
+ /* Deep shadow DOM detected */
937
+ if (shadowNode.content instanceof DocumentFragment) {
938
+ _sanitizeShadowDOM(shadowNode.content);
1028
939
  }
1029
940
 
1030
- if (_isNode(dirty)) {
1031
- return window.toStaticHTML(dirty.outerHTML);
1032
- }
941
+ /* Check attributes, sanitize if necessary */
942
+ _sanitizeAttributes(shadowNode);
1033
943
  }
1034
944
 
1035
- return dirty;
1036
- }
945
+ /* Execute a hook if present */
946
+ _executeHook('afterSanitizeShadowDOM', fragment, null);
947
+ };
1037
948
 
1038
- /* Assign config vars */
1039
- if (!SET_CONFIG) {
1040
- _parseConfig(cfg);
1041
- }
949
+ /**
950
+ * Sanitize
951
+ * Public method providing core sanitation functionality
952
+ *
953
+ * @param {String|Node} dirty string or DOM node
954
+ * @param {Object} configuration object
955
+ */
956
+ // eslint-disable-next-line complexity
957
+ DOMPurify.sanitize = function (dirty, cfg) {
958
+ var body = void 0;
959
+ var importedNode = void 0;
960
+ var currentNode = void 0;
961
+ var oldNode = void 0;
962
+ var returnNode = void 0;
963
+ /* Make sure we have a string to sanitize.
964
+ DO NOT return early, as this will return the wrong type if
965
+ the user has requested a DOM object rather than a string */
966
+ if (!dirty) {
967
+ dirty = '<!-->';
968
+ }
1042
969
 
1043
- /* Clean up removed elements */
1044
- DOMPurify.removed = [];
970
+ /* Stringify, in case dirty is an object */
971
+ if (typeof dirty !== 'string' && !_isNode(dirty)) {
972
+ // eslint-disable-next-line no-negated-condition
973
+ if (typeof dirty.toString !== 'function') {
974
+ throw typeErrorCreate('toString is not a function');
975
+ } else {
976
+ dirty = dirty.toString();
977
+ if (typeof dirty !== 'string') {
978
+ throw typeErrorCreate('dirty is not a string, aborting');
979
+ }
980
+ }
981
+ }
1045
982
 
1046
- /* Check if dirty is correctly typed for IN_PLACE */
1047
- if (typeof dirty === 'string') {
1048
- IN_PLACE = false;
1049
- }
983
+ /* Check we can run. Otherwise fall back or ignore */
984
+ if (!DOMPurify.isSupported) {
985
+ if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
986
+ if (typeof dirty === 'string') {
987
+ return window.toStaticHTML(dirty);
988
+ }
1050
989
 
1051
- if (IN_PLACE) {
1052
- /* No special handling necessary for in-place sanitization */
1053
- } else if (dirty instanceof Node) {
1054
- /* If dirty is a DOM element, append to an empty document to avoid
1055
- elements being stripped by the parser */
1056
- body = _initDocument('<!-->');
1057
- importedNode = body.ownerDocument.importNode(dirty, true);
1058
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1059
- /* Node is already a body, use as is */
1060
- body = importedNode;
1061
- } else if (importedNode.nodeName === 'HTML') {
1062
- body = importedNode;
1063
- } else {
1064
- // eslint-disable-next-line unicorn/prefer-node-append
1065
- body.appendChild(importedNode);
990
+ if (_isNode(dirty)) {
991
+ return window.toStaticHTML(dirty.outerHTML);
992
+ }
993
+ }
994
+
995
+ return dirty;
1066
996
  }
1067
- } else {
1068
- /* Exit directly if we have nothing to do */
1069
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && RETURN_TRUSTED_TYPE && dirty.indexOf('<') === -1) {
1070
- return trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
997
+
998
+ /* Assign config vars */
999
+ if (!SET_CONFIG) {
1000
+ _parseConfig(cfg);
1071
1001
  }
1072
1002
 
1073
- /* Initialize the document to work on */
1074
- body = _initDocument(dirty);
1003
+ /* Clean up removed elements */
1004
+ DOMPurify.removed = [];
1075
1005
 
1076
- /* Check we have a DOM node from the data */
1077
- if (!body) {
1078
- return RETURN_DOM ? null : emptyHTML;
1006
+ /* Check if dirty is correctly typed for IN_PLACE */
1007
+ if (typeof dirty === 'string') {
1008
+ IN_PLACE = false;
1079
1009
  }
1080
- }
1081
1010
 
1082
- /* Remove first element node (ours) if FORCE_BODY is set */
1083
- if (body && FORCE_BODY) {
1084
- _forceRemove(body.firstChild);
1085
- }
1086
-
1087
- /* Get node iterator */
1088
- var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1011
+ if (IN_PLACE) ; else if (dirty instanceof Node) {
1012
+ /* If dirty is a DOM element, append to an empty document to avoid
1013
+ elements being stripped by the parser */
1014
+ body = _initDocument('<!-->');
1015
+ importedNode = body.ownerDocument.importNode(dirty, true);
1016
+ if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1017
+ /* Node is already a body, use as is */
1018
+ body = importedNode;
1019
+ } else if (importedNode.nodeName === 'HTML') {
1020
+ body = importedNode;
1021
+ } else {
1022
+ // eslint-disable-next-line unicorn/prefer-node-append
1023
+ body.appendChild(importedNode);
1024
+ }
1025
+ } else {
1026
+ /* Exit directly if we have nothing to do */
1027
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1028
+ // eslint-disable-next-line unicorn/prefer-includes
1029
+ dirty.indexOf('<') === -1) {
1030
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1031
+ }
1089
1032
 
1090
- /* Now start iterating over the created document */
1091
- while (currentNode = nodeIterator.nextNode()) {
1092
- /* Fix IE's strange behavior with manipulated textNodes #89 */
1093
- if (currentNode.nodeType === 3 && currentNode === oldNode) {
1094
- continue;
1095
- }
1033
+ /* Initialize the document to work on */
1034
+ body = _initDocument(dirty);
1096
1035
 
1097
- /* Sanitize tags and elements */
1098
- if (_sanitizeElements(currentNode)) {
1099
- continue;
1036
+ /* Check we have a DOM node from the data */
1037
+ if (!body) {
1038
+ return RETURN_DOM ? null : emptyHTML;
1039
+ }
1100
1040
  }
1101
1041
 
1102
- /* Shadow DOM detected, sanitize it */
1103
- if (currentNode.content instanceof DocumentFragment) {
1104
- _sanitizeShadowDOM(currentNode.content);
1042
+ /* Remove first element node (ours) if FORCE_BODY is set */
1043
+ if (body && FORCE_BODY) {
1044
+ _forceRemove(body.firstChild);
1105
1045
  }
1106
1046
 
1107
- /* Check attributes, sanitize if necessary */
1108
- _sanitizeAttributes(currentNode);
1047
+ /* Get node iterator */
1048
+ var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1109
1049
 
1110
- oldNode = currentNode;
1111
- }
1050
+ /* Now start iterating over the created document */
1051
+ while (currentNode = nodeIterator.nextNode()) {
1052
+ /* Fix IE's strange behavior with manipulated textNodes #89 */
1053
+ if (currentNode.nodeType === 3 && currentNode === oldNode) {
1054
+ continue;
1055
+ }
1112
1056
 
1113
- oldNode = null;
1057
+ /* Sanitize tags and elements */
1058
+ if (_sanitizeElements(currentNode)) {
1059
+ continue;
1060
+ }
1114
1061
 
1115
- /* If we sanitized `dirty` in-place, return it. */
1116
- if (IN_PLACE) {
1117
- return dirty;
1118
- }
1062
+ /* Shadow DOM detected, sanitize it */
1063
+ if (currentNode.content instanceof DocumentFragment) {
1064
+ _sanitizeShadowDOM(currentNode.content);
1065
+ }
1119
1066
 
1120
- /* Return sanitized string or DOM */
1121
- if (RETURN_DOM) {
1122
- if (RETURN_DOM_FRAGMENT) {
1123
- returnNode = createDocumentFragment.call(body.ownerDocument);
1067
+ /* Check attributes, sanitize if necessary */
1068
+ _sanitizeAttributes(currentNode);
1124
1069
 
1125
- while (body.firstChild) {
1126
- // eslint-disable-next-line unicorn/prefer-node-append
1127
- returnNode.appendChild(body.firstChild);
1128
- }
1129
- } else {
1130
- returnNode = body;
1070
+ oldNode = currentNode;
1131
1071
  }
1132
1072
 
1133
- if (RETURN_DOM_IMPORT) {
1134
- /* AdoptNode() is not used because internal state is not reset
1135
- (e.g. the past names map of a HTMLFormElement), this is safe
1136
- in theory but we would rather not risk another attack vector.
1137
- The state that is cloned by importNode() is explicitly defined
1138
- by the specs. */
1139
- returnNode = importNode.call(originalDocument, returnNode, true);
1073
+ oldNode = null;
1074
+
1075
+ /* If we sanitized `dirty` in-place, return it. */
1076
+ if (IN_PLACE) {
1077
+ return dirty;
1140
1078
  }
1141
1079
 
1142
- return returnNode;
1143
- }
1080
+ /* Return sanitized string or DOM */
1081
+ if (RETURN_DOM) {
1082
+ if (RETURN_DOM_FRAGMENT) {
1083
+ returnNode = createDocumentFragment.call(body.ownerDocument);
1144
1084
 
1145
- var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1085
+ while (body.firstChild) {
1086
+ // eslint-disable-next-line unicorn/prefer-node-append
1087
+ returnNode.appendChild(body.firstChild);
1088
+ }
1089
+ } else {
1090
+ returnNode = body;
1091
+ }
1146
1092
 
1147
- /* Sanitize final string template-safe */
1148
- if (SAFE_FOR_TEMPLATES) {
1149
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
1150
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
1151
- }
1093
+ if (RETURN_DOM_IMPORT) {
1094
+ /*
1095
+ AdoptNode() is not used because internal state is not reset
1096
+ (e.g. the past names map of a HTMLFormElement), this is safe
1097
+ in theory but we would rather not risk another attack vector.
1098
+ The state that is cloned by importNode() is explicitly defined
1099
+ by the specs.
1100
+ */
1101
+ returnNode = importNode.call(originalDocument, returnNode, true);
1102
+ }
1152
1103
 
1153
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1154
- };
1104
+ return returnNode;
1105
+ }
1155
1106
 
1156
- /**
1157
- * Public method to set the configuration once
1158
- * setConfig
1159
- *
1160
- * @param {Object} cfg configuration object
1161
- */
1162
- DOMPurify.setConfig = function (cfg) {
1163
- _parseConfig(cfg);
1164
- SET_CONFIG = true;
1165
- };
1107
+ var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1166
1108
 
1167
- /**
1168
- * Public method to remove the configuration
1169
- * clearConfig
1170
- *
1171
- */
1172
- DOMPurify.clearConfig = function () {
1173
- CONFIG = null;
1174
- SET_CONFIG = false;
1175
- };
1109
+ /* Sanitize final string template-safe */
1110
+ if (SAFE_FOR_TEMPLATES) {
1111
+ serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
1112
+ serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
1113
+ }
1176
1114
 
1177
- /**
1178
- * Public method to check if an attribute value is valid.
1179
- * Uses last set config, if any. Otherwise, uses config defaults.
1180
- * isValidAttribute
1181
- *
1182
- * @param {string} tag Tag name of containing element.
1183
- * @param {string} attr Attribute name.
1184
- * @param {string} value Attribute value.
1185
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1186
- */
1187
- DOMPurify.isValidAttribute = function (tag, attr, value) {
1188
- /* Initialize shared config vars if necessary. */
1189
- if (!CONFIG) {
1190
- _parseConfig({});
1191
- }
1115
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1116
+ };
1192
1117
 
1193
- var lcTag = stringToLowerCase(tag);
1194
- var lcName = stringToLowerCase(attr);
1195
- return _isValidAttribute(lcTag, lcName, value);
1196
- };
1118
+ /**
1119
+ * Public method to set the configuration once
1120
+ * setConfig
1121
+ *
1122
+ * @param {Object} cfg configuration object
1123
+ */
1124
+ DOMPurify.setConfig = function (cfg) {
1125
+ _parseConfig(cfg);
1126
+ SET_CONFIG = true;
1127
+ };
1197
1128
 
1198
- /**
1199
- * AddHook
1200
- * Public method to add DOMPurify hooks
1201
- *
1202
- * @param {String} entryPoint entry point for the hook to add
1203
- * @param {Function} hookFunction function to execute
1204
- */
1205
- DOMPurify.addHook = function (entryPoint, hookFunction) {
1206
- if (typeof hookFunction !== 'function') {
1207
- return;
1208
- }
1129
+ /**
1130
+ * Public method to remove the configuration
1131
+ * clearConfig
1132
+ *
1133
+ */
1134
+ DOMPurify.clearConfig = function () {
1135
+ CONFIG = null;
1136
+ SET_CONFIG = false;
1137
+ };
1209
1138
 
1210
- hooks[entryPoint] = hooks[entryPoint] || [];
1211
- arrayPush(hooks[entryPoint], hookFunction);
1212
- };
1139
+ /**
1140
+ * Public method to check if an attribute value is valid.
1141
+ * Uses last set config, if any. Otherwise, uses config defaults.
1142
+ * isValidAttribute
1143
+ *
1144
+ * @param {string} tag Tag name of containing element.
1145
+ * @param {string} attr Attribute name.
1146
+ * @param {string} value Attribute value.
1147
+ * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1148
+ */
1149
+ DOMPurify.isValidAttribute = function (tag, attr, value) {
1150
+ /* Initialize shared config vars if necessary. */
1151
+ if (!CONFIG) {
1152
+ _parseConfig({});
1153
+ }
1213
1154
 
1214
- /**
1215
- * RemoveHook
1216
- * Public method to remove a DOMPurify hook at a given entryPoint
1217
- * (pops it from the stack of hooks if more are present)
1218
- *
1219
- * @param {String} entryPoint entry point for the hook to remove
1220
- */
1221
- DOMPurify.removeHook = function (entryPoint) {
1222
- if (hooks[entryPoint]) {
1223
- arrayPop(hooks[entryPoint]);
1224
- }
1225
- };
1155
+ var lcTag = stringToLowerCase(tag);
1156
+ var lcName = stringToLowerCase(attr);
1157
+ return _isValidAttribute(lcTag, lcName, value);
1158
+ };
1226
1159
 
1227
- /**
1228
- * RemoveHooks
1229
- * Public method to remove all DOMPurify hooks at a given entryPoint
1230
- *
1231
- * @param {String} entryPoint entry point for the hooks to remove
1232
- */
1233
- DOMPurify.removeHooks = function (entryPoint) {
1234
- if (hooks[entryPoint]) {
1235
- hooks[entryPoint] = [];
1236
- }
1237
- };
1160
+ /**
1161
+ * AddHook
1162
+ * Public method to add DOMPurify hooks
1163
+ *
1164
+ * @param {String} entryPoint entry point for the hook to add
1165
+ * @param {Function} hookFunction function to execute
1166
+ */
1167
+ DOMPurify.addHook = function (entryPoint, hookFunction) {
1168
+ if (typeof hookFunction !== 'function') {
1169
+ return;
1170
+ }
1238
1171
 
1239
- /**
1240
- * RemoveAllHooks
1241
- * Public method to remove all DOMPurify hooks
1242
- *
1243
- */
1244
- DOMPurify.removeAllHooks = function () {
1245
- hooks = {};
1246
- };
1172
+ hooks[entryPoint] = hooks[entryPoint] || [];
1173
+ arrayPush(hooks[entryPoint], hookFunction);
1174
+ };
1247
1175
 
1248
- return DOMPurify;
1249
- }
1176
+ /**
1177
+ * RemoveHook
1178
+ * Public method to remove a DOMPurify hook at a given entryPoint
1179
+ * (pops it from the stack of hooks if more are present)
1180
+ *
1181
+ * @param {String} entryPoint entry point for the hook to remove
1182
+ */
1183
+ DOMPurify.removeHook = function (entryPoint) {
1184
+ if (hooks[entryPoint]) {
1185
+ arrayPop(hooks[entryPoint]);
1186
+ }
1187
+ };
1188
+
1189
+ /**
1190
+ * RemoveHooks
1191
+ * Public method to remove all DOMPurify hooks at a given entryPoint
1192
+ *
1193
+ * @param {String} entryPoint entry point for the hooks to remove
1194
+ */
1195
+ DOMPurify.removeHooks = function (entryPoint) {
1196
+ if (hooks[entryPoint]) {
1197
+ hooks[entryPoint] = [];
1198
+ }
1199
+ };
1200
+
1201
+ /**
1202
+ * RemoveAllHooks
1203
+ * Public method to remove all DOMPurify hooks
1204
+ *
1205
+ */
1206
+ DOMPurify.removeAllHooks = function () {
1207
+ hooks = {};
1208
+ };
1209
+
1210
+ return DOMPurify;
1211
+ }
1250
1212
 
1251
- var purify = createDOMPurify();
1213
+ var purify = createDOMPurify();
1252
1214
 
1253
- return purify;
1215
+ return purify;
1254
1216
 
1255
- })));
1217
+ }));
1256
1218
  //# sourceMappingURL=purify.js.map