@bigbinary/neeto-commons-frontend 2.1.35 → 2.1.37

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/initializers.js CHANGED
@@ -5,7 +5,7 @@ import i18n from 'i18next';
5
5
  import { serializeKeysToSnakeCase, keysToCamelCase, matches, deepFreezeObject } from '@bigbinary/neeto-commons-frontend/pure';
6
6
  import { resetAuthTokens } from '@bigbinary/neeto-commons-frontend/utils';
7
7
  import { Toastr } from '@bigbinary/neetoui';
8
- import { evolve, omit, values, isEmpty, mergeDeepLeft } from 'ramda';
8
+ import { evolve, omit, values, mergeDeepLeft } from 'ramda';
9
9
  import { initReactI18next } from 'react-i18next';
10
10
  import Logger from 'js-logger';
11
11
 
@@ -55,7 +55,8 @@ var HEADERS_KEYS = {
55
55
  contentType: "Content-Type",
56
56
  accept: "Accept"
57
57
  };
58
- var ANY_CASE_STR = "__ANY_CASE__";
58
+ var LOWERCASED = "__LOWERCASED__";
59
+ var ANY_CASE = "anyCase";
59
60
 
60
61
  var shouldNot = function shouldNot(skip) {
61
62
  return _typeof$1(skip) === "object" || !skip;
@@ -243,2192 +244,2167 @@ function initializeGlobalProps() {
243
244
  deepFreezeObject(window.globalProps);
244
245
  }
245
246
 
246
- /*! @license DOMPurify 3.0.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.3/LICENSE */
247
-
248
- const {
249
- entries,
250
- setPrototypeOf,
251
- isFrozen,
252
- getPrototypeOf,
253
- getOwnPropertyDescriptor
254
- } = Object;
255
- let {
256
- freeze,
257
- seal,
258
- create
259
- } = Object; // eslint-disable-line import/no-mutable-exports
247
+ function _classCallCheck(instance, Constructor) {
248
+ if (!(instance instanceof Constructor)) {
249
+ throw new TypeError("Cannot call a class as a function");
250
+ }
251
+ }
260
252
 
261
- let {
262
- apply,
263
- construct
264
- } = typeof Reflect !== 'undefined' && Reflect;
253
+ function _typeof(obj) {
254
+ "@babel/helpers - typeof";
265
255
 
266
- if (!apply) {
267
- apply = function apply(fun, thisValue, args) {
268
- return fun.apply(thisValue, args);
269
- };
256
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
257
+ return typeof obj;
258
+ } : function (obj) {
259
+ return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
260
+ }, _typeof(obj);
270
261
  }
271
262
 
272
- if (!freeze) {
273
- freeze = function freeze(x) {
274
- return x;
275
- };
263
+ function _toPrimitive(input, hint) {
264
+ if (_typeof(input) !== "object" || input === null) return input;
265
+ var prim = input[Symbol.toPrimitive];
266
+ if (prim !== undefined) {
267
+ var res = prim.call(input, hint || "default");
268
+ if (_typeof(res) !== "object") return res;
269
+ throw new TypeError("@@toPrimitive must return a primitive value.");
270
+ }
271
+ return (hint === "string" ? String : Number)(input);
276
272
  }
277
273
 
278
- if (!seal) {
279
- seal = function seal(x) {
280
- return x;
281
- };
274
+ function _toPropertyKey(arg) {
275
+ var key = _toPrimitive(arg, "string");
276
+ return _typeof(key) === "symbol" ? key : String(key);
282
277
  }
283
278
 
284
- if (!construct) {
285
- construct = function construct(Func, args) {
286
- return new Func(...args);
287
- };
279
+ function _defineProperties(target, props) {
280
+ for (var i = 0; i < props.length; i++) {
281
+ var descriptor = props[i];
282
+ descriptor.enumerable = descriptor.enumerable || false;
283
+ descriptor.configurable = true;
284
+ if ("value" in descriptor) descriptor.writable = true;
285
+ Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
286
+ }
288
287
  }
289
-
290
- const arrayForEach = unapply(Array.prototype.forEach);
291
- const arrayPop = unapply(Array.prototype.pop);
292
- const arrayPush = unapply(Array.prototype.push);
293
- const stringToLowerCase = unapply(String.prototype.toLowerCase);
294
- const stringToString = unapply(String.prototype.toString);
295
- const stringMatch = unapply(String.prototype.match);
296
- const stringReplace = unapply(String.prototype.replace);
297
- const stringIndexOf = unapply(String.prototype.indexOf);
298
- const stringTrim = unapply(String.prototype.trim);
299
- const regExpTest = unapply(RegExp.prototype.test);
300
- const typeErrorCreate = unconstruct(TypeError);
301
- function unapply(func) {
302
- return function (thisArg) {
303
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
304
- args[_key - 1] = arguments[_key];
305
- }
306
-
307
- return apply(func, thisArg, args);
308
- };
288
+ function _createClass(Constructor, protoProps, staticProps) {
289
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
290
+ if (staticProps) _defineProperties(Constructor, staticProps);
291
+ Object.defineProperty(Constructor, "prototype", {
292
+ writable: false
293
+ });
294
+ return Constructor;
309
295
  }
310
- function unconstruct(func) {
311
- return function () {
312
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
313
- args[_key2] = arguments[_key2];
314
- }
315
296
 
316
- return construct(func, args);
317
- };
297
+ var arr = [];
298
+ var each = arr.forEach;
299
+ var slice = arr.slice;
300
+ function defaults(obj) {
301
+ each.call(slice.call(arguments, 1), function (source) {
302
+ if (source) {
303
+ for (var prop in source) {
304
+ if (obj[prop] === undefined) obj[prop] = source[prop];
305
+ }
306
+ }
307
+ });
308
+ return obj;
318
309
  }
319
- /* Add properties to a lookup table */
320
-
321
- function addToSet(set, array, transformCaseFunc) {
322
- var _transformCaseFunc;
323
310
 
324
- transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
325
-
326
- if (setPrototypeOf) {
327
- // Make 'in' and truthy checks like Boolean(set.constructor)
328
- // independent of any properties defined on Object.prototype.
329
- // Prevent prototype setters from intercepting set as a this value.
330
- setPrototypeOf(set, null);
311
+ // eslint-disable-next-line no-control-regex
312
+ var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
313
+ var serializeCookie = function serializeCookie(name, val, options) {
314
+ var opt = options || {};
315
+ opt.path = opt.path || '/';
316
+ var value = encodeURIComponent(val);
317
+ var str = "".concat(name, "=").concat(value);
318
+ if (opt.maxAge > 0) {
319
+ var maxAge = opt.maxAge - 0;
320
+ if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
321
+ str += "; Max-Age=".concat(Math.floor(maxAge));
331
322
  }
332
-
333
- let l = array.length;
334
-
335
- while (l--) {
336
- let element = array[l];
337
-
338
- if (typeof element === 'string') {
339
- const lcElement = transformCaseFunc(element);
340
-
341
- if (lcElement !== element) {
342
- // Config presets (e.g. tags.js, attrs.js) are immutable.
343
- if (!isFrozen(array)) {
344
- array[l] = lcElement;
345
- }
346
-
347
- element = lcElement;
348
- }
323
+ if (opt.domain) {
324
+ if (!fieldContentRegExp.test(opt.domain)) {
325
+ throw new TypeError('option domain is invalid');
349
326
  }
350
-
351
- set[element] = true;
327
+ str += "; Domain=".concat(opt.domain);
352
328
  }
353
-
354
- return set;
355
- }
356
- /* Shallow clone an object */
357
-
358
- function clone(object) {
359
- const newObject = create(null);
360
-
361
- for (const [property, value] of entries(object)) {
362
- newObject[property] = value;
329
+ if (opt.path) {
330
+ if (!fieldContentRegExp.test(opt.path)) {
331
+ throw new TypeError('option path is invalid');
332
+ }
333
+ str += "; Path=".concat(opt.path);
363
334
  }
364
-
365
- return newObject;
366
- }
367
- /* This method automatically checks if the prop is function
368
- * or getter and behaves accordingly. */
369
-
370
- function lookupGetter(object, prop) {
371
- while (object !== null) {
372
- const desc = getOwnPropertyDescriptor(object, prop);
373
-
374
- if (desc) {
375
- if (desc.get) {
376
- return unapply(desc.get);
377
- }
378
-
379
- if (typeof desc.value === 'function') {
380
- return unapply(desc.value);
381
- }
335
+ if (opt.expires) {
336
+ if (typeof opt.expires.toUTCString !== 'function') {
337
+ throw new TypeError('option expires is invalid');
382
338
  }
383
-
384
- object = getPrototypeOf(object);
339
+ str += "; Expires=".concat(opt.expires.toUTCString());
385
340
  }
386
-
387
- function fallbackValue(element) {
388
- console.warn('fallback value for', element);
341
+ if (opt.httpOnly) str += '; HttpOnly';
342
+ if (opt.secure) str += '; Secure';
343
+ if (opt.sameSite) {
344
+ var sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
345
+ switch (sameSite) {
346
+ case true:
347
+ str += '; SameSite=Strict';
348
+ break;
349
+ case 'lax':
350
+ str += '; SameSite=Lax';
351
+ break;
352
+ case 'strict':
353
+ str += '; SameSite=Strict';
354
+ break;
355
+ case 'none':
356
+ str += '; SameSite=None';
357
+ break;
358
+ default:
359
+ throw new TypeError('option sameSite is invalid');
360
+ }
361
+ }
362
+ return str;
363
+ };
364
+ var cookie = {
365
+ create: function create(name, value, minutes, domain) {
366
+ var cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
367
+ path: '/',
368
+ sameSite: 'strict'
369
+ };
370
+ if (minutes) {
371
+ cookieOptions.expires = new Date();
372
+ cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
373
+ }
374
+ if (domain) cookieOptions.domain = domain;
375
+ document.cookie = serializeCookie(name, encodeURIComponent(value), cookieOptions);
376
+ },
377
+ read: function read(name) {
378
+ var nameEQ = "".concat(name, "=");
379
+ var ca = document.cookie.split(';');
380
+ for (var i = 0; i < ca.length; i++) {
381
+ var c = ca[i];
382
+ while (c.charAt(0) === ' ') {
383
+ c = c.substring(1, c.length);
384
+ }
385
+ if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
386
+ }
389
387
  return null;
388
+ },
389
+ remove: function remove(name) {
390
+ this.create(name, '', -1);
390
391
  }
391
-
392
- return fallbackValue;
393
- }
394
-
395
- const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
396
-
397
- const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
398
- const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default.
399
- // We still need to know them so that we can do namespace
400
- // checks properly in case one wants to add them to
401
- // allow-list.
402
-
403
- const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
404
- const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']); // Similarly to SVG, we want to know all MathML elements,
405
- // even those that we disallow by default.
406
-
407
- const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
408
- const text = freeze(['#text']);
409
-
410
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
411
- const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
412
- const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
413
- const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
414
-
415
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
416
-
417
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
418
- const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
419
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
420
-
421
- const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
422
-
423
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
424
- );
425
- const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
426
- const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
427
- );
428
- const DOCTYPE_NAME = seal(/^html$/i);
429
-
430
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
431
- __proto__: null,
432
- MUSTACHE_EXPR: MUSTACHE_EXPR,
433
- ERB_EXPR: ERB_EXPR,
434
- TMPLIT_EXPR: TMPLIT_EXPR,
435
- DATA_ATTR: DATA_ATTR,
436
- ARIA_ATTR: ARIA_ATTR,
437
- IS_ALLOWED_URI: IS_ALLOWED_URI,
438
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
439
- ATTR_WHITESPACE: ATTR_WHITESPACE,
440
- DOCTYPE_NAME: DOCTYPE_NAME
441
- });
442
-
443
- const getGlobal = () => typeof window === 'undefined' ? null : window;
444
- /**
445
- * Creates a no-op policy for internal use only.
446
- * Don't export this function outside this module!
447
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
448
- * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
449
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
450
- * are not supported or creating the policy failed).
451
- */
452
-
453
-
454
- const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
455
- if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
456
- return null;
457
- } // Allow the callers to control the unique policy name
458
- // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
459
- // Policy creation with duplicate names throws in Trusted Types.
460
-
461
-
462
- let suffix = null;
463
- const ATTR_NAME = 'data-tt-policy-suffix';
464
-
465
- if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
466
- suffix = purifyHostElement.getAttribute(ATTR_NAME);
392
+ };
393
+ var cookie$1 = {
394
+ name: 'cookie',
395
+ lookup: function lookup(options) {
396
+ var found;
397
+ if (options.lookupCookie && typeof document !== 'undefined') {
398
+ var c = cookie.read(options.lookupCookie);
399
+ if (c) found = c;
400
+ }
401
+ return found;
402
+ },
403
+ cacheUserLanguage: function cacheUserLanguage(lng, options) {
404
+ if (options.lookupCookie && typeof document !== 'undefined') {
405
+ cookie.create(options.lookupCookie, lng, options.cookieMinutes, options.cookieDomain, options.cookieOptions);
406
+ }
467
407
  }
408
+ };
468
409
 
469
- const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
470
-
471
- try {
472
- return trustedTypes.createPolicy(policyName, {
473
- createHTML(html) {
474
- return html;
475
- },
476
-
477
- createScriptURL(scriptUrl) {
478
- return scriptUrl;
410
+ var querystring = {
411
+ name: 'querystring',
412
+ lookup: function lookup(options) {
413
+ var found;
414
+ if (typeof window !== 'undefined') {
415
+ var search = window.location.search;
416
+ if (!window.location.search && window.location.hash && window.location.hash.indexOf('?') > -1) {
417
+ search = window.location.hash.substring(window.location.hash.indexOf('?'));
479
418
  }
480
-
481
- });
482
- } catch (_) {
483
- // Policy creation failed (most likely another DOMPurify script has
484
- // already run). Skip creating the policy, as this will only cause errors
485
- // if TT are enforced.
486
- console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
487
- return null;
419
+ var query = search.substring(1);
420
+ var params = query.split('&');
421
+ for (var i = 0; i < params.length; i++) {
422
+ var pos = params[i].indexOf('=');
423
+ if (pos > 0) {
424
+ var key = params[i].substring(0, pos);
425
+ if (key === options.lookupQuerystring) {
426
+ found = params[i].substring(pos + 1);
427
+ }
428
+ }
429
+ }
430
+ }
431
+ return found;
488
432
  }
489
433
  };
490
434
 
491
- function createDOMPurify() {
492
- let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
493
-
494
- const DOMPurify = root => createDOMPurify(root);
495
- /**
496
- * Version label, exposed for easier checks
497
- * if DOMPurify is up to date or not
498
- */
499
-
500
-
501
- DOMPurify.version = '3.0.3';
502
- /**
503
- * Array of elements that DOMPurify removed during sanitation.
504
- * Empty if nothing was removed.
505
- */
506
-
507
- DOMPurify.removed = [];
508
-
509
- if (!window || !window.document || window.document.nodeType !== 9) {
510
- // Not running in a browser, provide a factory function
511
- // so that you can pass your own Window
512
- DOMPurify.isSupported = false;
513
- return DOMPurify;
435
+ var hasLocalStorageSupport = null;
436
+ var localStorageAvailable = function localStorageAvailable() {
437
+ if (hasLocalStorageSupport !== null) return hasLocalStorageSupport;
438
+ try {
439
+ hasLocalStorageSupport = window !== 'undefined' && window.localStorage !== null;
440
+ var testKey = 'i18next.translate.boo';
441
+ window.localStorage.setItem(testKey, 'foo');
442
+ window.localStorage.removeItem(testKey);
443
+ } catch (e) {
444
+ hasLocalStorageSupport = false;
514
445
  }
515
-
516
- const originalDocument = window.document;
517
- const currentScript = originalDocument.currentScript;
518
- let {
519
- document
520
- } = window;
521
- const {
522
- DocumentFragment,
523
- HTMLTemplateElement,
524
- Node,
525
- Element,
526
- NodeFilter,
527
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
528
- HTMLFormElement,
529
- DOMParser,
530
- trustedTypes
531
- } = window;
532
- const ElementPrototype = Element.prototype;
533
- const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
534
- const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
535
- const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
536
- const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
537
- // new document created via createHTMLDocument. As per the spec
538
- // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
539
- // a new empty registry is used when creating a template contents owner
540
- // document, so we use that as our parent document to ensure nothing
541
- // is inherited.
542
-
543
- if (typeof HTMLTemplateElement === 'function') {
544
- const template = document.createElement('template');
545
-
546
- if (template.content && template.content.ownerDocument) {
547
- document = template.content.ownerDocument;
446
+ return hasLocalStorageSupport;
447
+ };
448
+ var localStorage = {
449
+ name: 'localStorage',
450
+ lookup: function lookup(options) {
451
+ var found;
452
+ if (options.lookupLocalStorage && localStorageAvailable()) {
453
+ var lng = window.localStorage.getItem(options.lookupLocalStorage);
454
+ if (lng) found = lng;
455
+ }
456
+ return found;
457
+ },
458
+ cacheUserLanguage: function cacheUserLanguage(lng, options) {
459
+ if (options.lookupLocalStorage && localStorageAvailable()) {
460
+ window.localStorage.setItem(options.lookupLocalStorage, lng);
548
461
  }
549
462
  }
463
+ };
550
464
 
551
- let trustedTypesPolicy;
552
- let emptyHTML = '';
553
- const {
554
- implementation,
555
- createNodeIterator,
556
- createDocumentFragment,
557
- getElementsByTagName
558
- } = document;
559
- const {
560
- importNode
561
- } = originalDocument;
562
- let hooks = {};
563
- /**
564
- * Expose whether this browser supports running the full DOMPurify.
565
- */
566
-
567
- DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
568
- const {
569
- MUSTACHE_EXPR,
570
- ERB_EXPR,
571
- TMPLIT_EXPR,
572
- DATA_ATTR,
573
- ARIA_ATTR,
574
- IS_SCRIPT_OR_DATA,
575
- ATTR_WHITESPACE
576
- } = EXPRESSIONS;
577
- let {
578
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
579
- } = EXPRESSIONS;
580
- /**
581
- * We consider the elements and attributes below to be safe. Ideally
582
- * don't add any new ones but feel free to remove unwanted ones.
583
- */
584
-
585
- /* allowed element names */
586
-
587
- let ALLOWED_TAGS = null;
588
- const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
589
- /* Allowed attribute names */
590
-
591
- let ALLOWED_ATTR = null;
592
- const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
593
- /*
594
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
595
- * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
596
- * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
597
- * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
598
- */
599
-
600
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
601
- tagNameCheck: {
602
- writable: true,
603
- configurable: false,
604
- enumerable: true,
605
- value: null
606
- },
607
- attributeNameCheck: {
608
- writable: true,
609
- configurable: false,
610
- enumerable: true,
611
- value: null
612
- },
613
- allowCustomizedBuiltInElements: {
614
- writable: true,
615
- configurable: false,
616
- enumerable: true,
617
- value: false
465
+ var hasSessionStorageSupport = null;
466
+ var sessionStorageAvailable = function sessionStorageAvailable() {
467
+ if (hasSessionStorageSupport !== null) return hasSessionStorageSupport;
468
+ try {
469
+ hasSessionStorageSupport = window !== 'undefined' && window.sessionStorage !== null;
470
+ var testKey = 'i18next.translate.boo';
471
+ window.sessionStorage.setItem(testKey, 'foo');
472
+ window.sessionStorage.removeItem(testKey);
473
+ } catch (e) {
474
+ hasSessionStorageSupport = false;
475
+ }
476
+ return hasSessionStorageSupport;
477
+ };
478
+ var sessionStorage = {
479
+ name: 'sessionStorage',
480
+ lookup: function lookup(options) {
481
+ var found;
482
+ if (options.lookupSessionStorage && sessionStorageAvailable()) {
483
+ var lng = window.sessionStorage.getItem(options.lookupSessionStorage);
484
+ if (lng) found = lng;
618
485
  }
619
- }));
620
- /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
621
-
622
- let FORBID_TAGS = null;
623
- /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
624
-
625
- let FORBID_ATTR = null;
626
- /* Decide if ARIA attributes are okay */
627
-
628
- let ALLOW_ARIA_ATTR = true;
629
- /* Decide if custom data attributes are okay */
630
-
631
- let ALLOW_DATA_ATTR = true;
632
- /* Decide if unknown protocols are okay */
633
-
634
- let ALLOW_UNKNOWN_PROTOCOLS = false;
635
- /* Decide if self-closing tags in attributes are allowed.
636
- * Usually removed due to a mXSS issue in jQuery 3.0 */
637
-
638
- let ALLOW_SELF_CLOSE_IN_ATTR = true;
639
- /* Output should be safe for common template engines.
640
- * This means, DOMPurify removes data attributes, mustaches and ERB
641
- */
642
-
643
- let SAFE_FOR_TEMPLATES = false;
644
- /* Decide if document with <html>... should be returned */
486
+ return found;
487
+ },
488
+ cacheUserLanguage: function cacheUserLanguage(lng, options) {
489
+ if (options.lookupSessionStorage && sessionStorageAvailable()) {
490
+ window.sessionStorage.setItem(options.lookupSessionStorage, lng);
491
+ }
492
+ }
493
+ };
645
494
 
646
- let WHOLE_DOCUMENT = false;
647
- /* Track whether config is already set on this instance of DOMPurify. */
495
+ var navigator$1 = {
496
+ name: 'navigator',
497
+ lookup: function lookup(options) {
498
+ var found = [];
499
+ if (typeof navigator !== 'undefined') {
500
+ if (navigator.languages) {
501
+ // chrome only; not an array, so can't use .push.apply instead of iterating
502
+ for (var i = 0; i < navigator.languages.length; i++) {
503
+ found.push(navigator.languages[i]);
504
+ }
505
+ }
506
+ if (navigator.userLanguage) {
507
+ found.push(navigator.userLanguage);
508
+ }
509
+ if (navigator.language) {
510
+ found.push(navigator.language);
511
+ }
512
+ }
513
+ return found.length > 0 ? found : undefined;
514
+ }
515
+ };
648
516
 
649
- let SET_CONFIG = false;
650
- /* Decide if all elements (e.g. style, script) must be children of
651
- * document.body. By default, browsers might move them to document.head */
517
+ var htmlTag = {
518
+ name: 'htmlTag',
519
+ lookup: function lookup(options) {
520
+ var found;
521
+ var htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
522
+ if (htmlTag && typeof htmlTag.getAttribute === 'function') {
523
+ found = htmlTag.getAttribute('lang');
524
+ }
525
+ return found;
526
+ }
527
+ };
652
528
 
653
- let FORCE_BODY = false;
654
- /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
655
- * string (or a TrustedHTML object if Trusted Types are supported).
656
- * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
657
- */
529
+ var path = {
530
+ name: 'path',
531
+ lookup: function lookup(options) {
532
+ var found;
533
+ if (typeof window !== 'undefined') {
534
+ var language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
535
+ if (language instanceof Array) {
536
+ if (typeof options.lookupFromPathIndex === 'number') {
537
+ if (typeof language[options.lookupFromPathIndex] !== 'string') {
538
+ return undefined;
539
+ }
540
+ found = language[options.lookupFromPathIndex].replace('/', '');
541
+ } else {
542
+ found = language[0].replace('/', '');
543
+ }
544
+ }
545
+ }
546
+ return found;
547
+ }
548
+ };
658
549
 
659
- let RETURN_DOM = false;
660
- /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
661
- * string (or a TrustedHTML object if Trusted Types are supported) */
550
+ var subdomain = {
551
+ name: 'subdomain',
552
+ lookup: function lookup(options) {
553
+ // If given get the subdomain index else 1
554
+ var lookupFromSubdomainIndex = typeof options.lookupFromSubdomainIndex === 'number' ? options.lookupFromSubdomainIndex + 1 : 1;
555
+ // get all matches if window.location. is existing
556
+ // first item of match is the match itself and the second is the first group macht which sould be the first subdomain match
557
+ // is the hostname no public domain get the or option of localhost
558
+ var language = typeof window !== 'undefined' && window.location && window.location.hostname && window.location.hostname.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
662
559
 
663
- let RETURN_DOM_FRAGMENT = false;
664
- /* Try to return a Trusted Type object instead of a string, return a string in
665
- * case Trusted Types are not supported */
560
+ // if there is no match (null) return undefined
561
+ if (!language) return undefined;
562
+ // return the given group match
563
+ return language[lookupFromSubdomainIndex];
564
+ }
565
+ };
666
566
 
667
- let RETURN_TRUSTED_TYPE = false;
668
- /* Output should be free from DOM clobbering attacks?
669
- * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
670
- */
567
+ function getDefaults() {
568
+ return {
569
+ order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
570
+ lookupQuerystring: 'lng',
571
+ lookupCookie: 'i18next',
572
+ lookupLocalStorage: 'i18nextLng',
573
+ lookupSessionStorage: 'i18nextLng',
574
+ // cache user language
575
+ caches: ['localStorage'],
576
+ excludeCacheFor: ['cimode'],
577
+ // cookieMinutes: 10,
578
+ // cookieDomain: 'myDomain'
671
579
 
672
- let SANITIZE_DOM = true;
673
- /* Achieve full DOM Clobbering protection by isolating the namespace of named
674
- * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
675
- *
676
- * HTML/DOM spec rules that enable DOM Clobbering:
677
- * - Named Access on Window (§7.3.3)
678
- * - DOM Tree Accessors (§3.1.5)
679
- * - Form Element Parent-Child Relations (§4.10.3)
680
- * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
681
- * - HTMLCollection (§4.2.10.2)
682
- *
683
- * Namespace isolation is implemented by prefixing `id` and `name` attributes
684
- * with a constant string, i.e., `user-content-`
685
- */
580
+ convertDetectedLanguage: function convertDetectedLanguage(l) {
581
+ return l;
582
+ }
583
+ };
584
+ }
585
+ var Browser = /*#__PURE__*/function () {
586
+ function Browser(services) {
587
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
588
+ _classCallCheck(this, Browser);
589
+ this.type = 'languageDetector';
590
+ this.detectors = {};
591
+ this.init(services, options);
592
+ }
593
+ _createClass(Browser, [{
594
+ key: "init",
595
+ value: function init(services) {
596
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
597
+ var i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
598
+ this.services = services || {
599
+ languageUtils: {}
600
+ }; // this way the language detector can be used without i18next
601
+ this.options = defaults(options, this.options || {}, getDefaults());
602
+ if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
603
+ this.options.convertDetectedLanguage = function (l) {
604
+ return l.replace('-', '_');
605
+ };
606
+ }
686
607
 
687
- let SANITIZE_NAMED_PROPS = false;
688
- const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
689
- /* Keep element content when removing element? */
608
+ // backwards compatibility
609
+ if (this.options.lookupFromUrlIndex) this.options.lookupFromPathIndex = this.options.lookupFromUrlIndex;
610
+ this.i18nOptions = i18nOptions;
611
+ this.addDetector(cookie$1);
612
+ this.addDetector(querystring);
613
+ this.addDetector(localStorage);
614
+ this.addDetector(sessionStorage);
615
+ this.addDetector(navigator$1);
616
+ this.addDetector(htmlTag);
617
+ this.addDetector(path);
618
+ this.addDetector(subdomain);
619
+ }
620
+ }, {
621
+ key: "addDetector",
622
+ value: function addDetector(detector) {
623
+ this.detectors[detector.name] = detector;
624
+ }
625
+ }, {
626
+ key: "detect",
627
+ value: function detect(detectionOrder) {
628
+ var _this = this;
629
+ if (!detectionOrder) detectionOrder = this.options.order;
630
+ var detected = [];
631
+ detectionOrder.forEach(function (detectorName) {
632
+ if (_this.detectors[detectorName]) {
633
+ var lookup = _this.detectors[detectorName].lookup(_this.options);
634
+ if (lookup && typeof lookup === 'string') lookup = [lookup];
635
+ if (lookup) detected = detected.concat(lookup);
636
+ }
637
+ });
638
+ detected = detected.map(function (d) {
639
+ return _this.options.convertDetectedLanguage(d);
640
+ });
641
+ if (this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
642
+ return detected.length > 0 ? detected[0] : null; // a little backward compatibility
643
+ }
644
+ }, {
645
+ key: "cacheUserLanguage",
646
+ value: function cacheUserLanguage(lng, caches) {
647
+ var _this2 = this;
648
+ if (!caches) caches = this.options.caches;
649
+ if (!caches) return;
650
+ if (this.options.excludeCacheFor && this.options.excludeCacheFor.indexOf(lng) > -1) return;
651
+ caches.forEach(function (cacheName) {
652
+ if (_this2.detectors[cacheName]) _this2.detectors[cacheName].cacheUserLanguage(lng, _this2.options);
653
+ });
654
+ }
655
+ }]);
656
+ return Browser;
657
+ }();
658
+ Browser.type = 'languageDetector';
690
659
 
691
- let KEEP_CONTENT = true;
692
- /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
693
- * of importing it into a new Document and returning a sanitized copy */
660
+ function _arrayWithHoles(arr) {
661
+ if (Array.isArray(arr)) return arr;
662
+ }
694
663
 
695
- let IN_PLACE = false;
696
- /* Allow usage of profiles like html, svg and mathMl */
664
+ function _iterableToArrayLimit(arr, i) {
665
+ var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
666
+ if (null != _i) {
667
+ var _s,
668
+ _e,
669
+ _x,
670
+ _r,
671
+ _arr = [],
672
+ _n = !0,
673
+ _d = !1;
674
+ try {
675
+ if (_x = (_i = _i.call(arr)).next, 0 === i) {
676
+ if (Object(_i) !== _i) return;
677
+ _n = !1;
678
+ } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);
679
+ } catch (err) {
680
+ _d = !0, _e = err;
681
+ } finally {
682
+ try {
683
+ if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return;
684
+ } finally {
685
+ if (_d) throw _e;
686
+ }
687
+ }
688
+ return _arr;
689
+ }
690
+ }
697
691
 
698
- let USE_PROFILES = {};
699
- /* Tags to ignore content of when KEEP_CONTENT is true */
692
+ function _arrayLikeToArray(arr, len) {
693
+ if (len == null || len > arr.length) len = arr.length;
694
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
695
+ return arr2;
696
+ }
700
697
 
701
- let FORBID_CONTENTS = null;
702
- const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
703
- /* Tags that are safe for data: URIs */
698
+ function _unsupportedIterableToArray(o, minLen) {
699
+ if (!o) return;
700
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
701
+ var n = Object.prototype.toString.call(o).slice(8, -1);
702
+ if (n === "Object" && o.constructor) n = o.constructor.name;
703
+ if (n === "Map" || n === "Set") return Array.from(o);
704
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
705
+ }
704
706
 
705
- let DATA_URI_TAGS = null;
706
- const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
707
- /* Attributes safe for values like "javascript:" */
707
+ function _nonIterableRest() {
708
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
709
+ }
708
710
 
709
- let URI_SAFE_ATTRIBUTES = null;
710
- const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
711
- const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
712
- const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
713
- const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
714
- /* Document namespace */
711
+ function _slicedToArray(arr, i) {
712
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
713
+ }
715
714
 
716
- let NAMESPACE = HTML_NAMESPACE;
717
- let IS_EMPTY_INPUT = false;
718
- /* Allowed XHTML+XML namespaces */
715
+ var getter = function getter(key) {
716
+ return function () {
717
+ return i18n.t("taxonomyDefaultLabels.".concat(key));
718
+ };
719
+ };
720
+ var replaceNullValuesWithGetter = function replaceNullValuesWithGetter(inputObject) {
721
+ var parentKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
722
+ var result = {};
723
+ for (var _i = 0, _Object$entries = Object.entries(inputObject); _i < _Object$entries.length; _i++) {
724
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
725
+ key = _Object$entries$_i[0],
726
+ value = _Object$entries$_i[1];
727
+ var transKey = parentKey ? "".concat(parentKey, ".").concat(key) : key;
728
+ if (value === null) {
729
+ Object.defineProperty(result, key, {
730
+ get: getter(transKey)
731
+ });
732
+ } else if (_typeof$1(value) === "object") {
733
+ result[key] = replaceNullValuesWithGetter(value, transKey);
734
+ } else {
735
+ result[key] = value;
736
+ }
737
+ }
738
+ return result;
739
+ };
719
740
 
720
- let ALLOWED_NAMESPACES = null;
721
- const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
722
- /* Parsing of strict XHTML documents */
741
+ /*! @license DOMPurify 3.0.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.3/LICENSE */
723
742
 
724
- let PARSER_MEDIA_TYPE;
725
- const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
726
- const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
727
- let transformCaseFunc;
728
- /* Keep a reference to config to pass to hooks */
743
+ const {
744
+ entries,
745
+ setPrototypeOf,
746
+ isFrozen,
747
+ getPrototypeOf,
748
+ getOwnPropertyDescriptor
749
+ } = Object;
750
+ let {
751
+ freeze,
752
+ seal,
753
+ create
754
+ } = Object; // eslint-disable-line import/no-mutable-exports
729
755
 
730
- let CONFIG = null;
731
- /* Ideally, do not touch anything below this line */
756
+ let {
757
+ apply,
758
+ construct
759
+ } = typeof Reflect !== 'undefined' && Reflect;
732
760
 
733
- /* ______________________________________________ */
761
+ if (!apply) {
762
+ apply = function apply(fun, thisValue, args) {
763
+ return fun.apply(thisValue, args);
764
+ };
765
+ }
734
766
 
735
- const formElement = document.createElement('form');
767
+ if (!freeze) {
768
+ freeze = function freeze(x) {
769
+ return x;
770
+ };
771
+ }
736
772
 
737
- const isRegexOrFunction = function isRegexOrFunction(testValue) {
738
- return testValue instanceof RegExp || testValue instanceof Function;
773
+ if (!seal) {
774
+ seal = function seal(x) {
775
+ return x;
739
776
  };
740
- /**
741
- * _parseConfig
742
- *
743
- * @param {Object} cfg optional config literal
744
- */
745
- // eslint-disable-next-line complexity
777
+ }
746
778
 
779
+ if (!construct) {
780
+ construct = function construct(Func, args) {
781
+ return new Func(...args);
782
+ };
783
+ }
747
784
 
748
- const _parseConfig = function _parseConfig(cfg) {
749
- if (CONFIG && CONFIG === cfg) {
750
- return;
785
+ const arrayForEach = unapply(Array.prototype.forEach);
786
+ const arrayPop = unapply(Array.prototype.pop);
787
+ const arrayPush = unapply(Array.prototype.push);
788
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
789
+ const stringToString = unapply(String.prototype.toString);
790
+ const stringMatch = unapply(String.prototype.match);
791
+ const stringReplace = unapply(String.prototype.replace);
792
+ const stringIndexOf = unapply(String.prototype.indexOf);
793
+ const stringTrim = unapply(String.prototype.trim);
794
+ const regExpTest = unapply(RegExp.prototype.test);
795
+ const typeErrorCreate = unconstruct(TypeError);
796
+ function unapply(func) {
797
+ return function (thisArg) {
798
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
799
+ args[_key - 1] = arguments[_key];
751
800
  }
752
- /* Shield configuration object from tampering */
753
801
 
754
-
755
- if (!cfg || typeof cfg !== 'object') {
756
- cfg = {};
802
+ return apply(func, thisArg, args);
803
+ };
804
+ }
805
+ function unconstruct(func) {
806
+ return function () {
807
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
808
+ args[_key2] = arguments[_key2];
757
809
  }
758
- /* Shield configuration object from prototype pollution */
759
-
760
-
761
- cfg = clone(cfg);
762
- PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
763
- SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
764
-
765
- transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
766
- /* Set configuration parameters */
767
-
768
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
769
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
770
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
771
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
772
- cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
773
- transformCaseFunc // eslint-disable-line indent
774
- ) // eslint-disable-line indent
775
- : DEFAULT_URI_SAFE_ATTRIBUTES;
776
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
777
- cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
778
- transformCaseFunc // eslint-disable-line indent
779
- ) // eslint-disable-line indent
780
- : DEFAULT_DATA_URI_TAGS;
781
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
782
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
783
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
784
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
785
- ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
786
-
787
- ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
788
810
 
789
- ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
811
+ return construct(func, args);
812
+ };
813
+ }
814
+ /* Add properties to a lookup table */
790
815
 
791
- ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
816
+ function addToSet(set, array, transformCaseFunc) {
817
+ var _transformCaseFunc;
792
818
 
793
- SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
819
+ transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
794
820
 
795
- WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
821
+ if (setPrototypeOf) {
822
+ // Make 'in' and truthy checks like Boolean(set.constructor)
823
+ // independent of any properties defined on Object.prototype.
824
+ // Prevent prototype setters from intercepting set as a this value.
825
+ setPrototypeOf(set, null);
826
+ }
796
827
 
797
- RETURN_DOM = cfg.RETURN_DOM || false; // Default false
828
+ let l = array.length;
798
829
 
799
- RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
830
+ while (l--) {
831
+ let element = array[l];
800
832
 
801
- RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
833
+ if (typeof element === 'string') {
834
+ const lcElement = transformCaseFunc(element);
802
835
 
803
- FORCE_BODY = cfg.FORCE_BODY || false; // Default false
836
+ if (lcElement !== element) {
837
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
838
+ if (!isFrozen(array)) {
839
+ array[l] = lcElement;
840
+ }
804
841
 
805
- SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
842
+ element = lcElement;
843
+ }
844
+ }
806
845
 
807
- SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
846
+ set[element] = true;
847
+ }
808
848
 
809
- KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
849
+ return set;
850
+ }
851
+ /* Shallow clone an object */
810
852
 
811
- IN_PLACE = cfg.IN_PLACE || false; // Default false
853
+ function clone(object) {
854
+ const newObject = create(null);
812
855
 
813
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
814
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
815
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
856
+ for (const [property, value] of entries(object)) {
857
+ newObject[property] = value;
858
+ }
816
859
 
817
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
818
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
819
- }
860
+ return newObject;
861
+ }
862
+ /* This method automatically checks if the prop is function
863
+ * or getter and behaves accordingly. */
820
864
 
821
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
822
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
823
- }
865
+ function lookupGetter(object, prop) {
866
+ while (object !== null) {
867
+ const desc = getOwnPropertyDescriptor(object, prop);
824
868
 
825
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
826
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
827
- }
869
+ if (desc) {
870
+ if (desc.get) {
871
+ return unapply(desc.get);
872
+ }
828
873
 
829
- if (SAFE_FOR_TEMPLATES) {
830
- ALLOW_DATA_ATTR = false;
874
+ if (typeof desc.value === 'function') {
875
+ return unapply(desc.value);
876
+ }
831
877
  }
832
878
 
833
- if (RETURN_DOM_FRAGMENT) {
834
- RETURN_DOM = true;
835
- }
836
- /* Parse profile info */
879
+ object = getPrototypeOf(object);
880
+ }
837
881
 
882
+ function fallbackValue(element) {
883
+ console.warn('fallback value for', element);
884
+ return null;
885
+ }
838
886
 
839
- if (USE_PROFILES) {
840
- ALLOWED_TAGS = addToSet({}, [...text]);
841
- ALLOWED_ATTR = [];
887
+ return fallbackValue;
888
+ }
842
889
 
843
- if (USE_PROFILES.html === true) {
844
- addToSet(ALLOWED_TAGS, html$1);
845
- addToSet(ALLOWED_ATTR, html);
846
- }
890
+ const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
847
891
 
848
- if (USE_PROFILES.svg === true) {
849
- addToSet(ALLOWED_TAGS, svg$1);
850
- addToSet(ALLOWED_ATTR, svg);
851
- addToSet(ALLOWED_ATTR, xml);
852
- }
892
+ const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
893
+ const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default.
894
+ // We still need to know them so that we can do namespace
895
+ // checks properly in case one wants to add them to
896
+ // allow-list.
853
897
 
854
- if (USE_PROFILES.svgFilters === true) {
855
- addToSet(ALLOWED_TAGS, svgFilters);
856
- addToSet(ALLOWED_ATTR, svg);
857
- addToSet(ALLOWED_ATTR, xml);
858
- }
898
+ const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
899
+ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']); // Similarly to SVG, we want to know all MathML elements,
900
+ // even those that we disallow by default.
859
901
 
860
- if (USE_PROFILES.mathMl === true) {
861
- addToSet(ALLOWED_TAGS, mathMl$1);
862
- addToSet(ALLOWED_ATTR, mathMl);
863
- addToSet(ALLOWED_ATTR, xml);
864
- }
865
- }
866
- /* Merge configuration parameters */
902
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
903
+ const text = freeze(['#text']);
867
904
 
905
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
906
+ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
907
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
908
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
868
909
 
869
- if (cfg.ADD_TAGS) {
870
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
871
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
872
- }
910
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
873
911
 
874
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
875
- }
912
+ const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
913
+ const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
914
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
876
915
 
877
- if (cfg.ADD_ATTR) {
878
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
879
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
880
- }
916
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
881
917
 
882
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
883
- }
918
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
919
+ );
920
+ const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
921
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
922
+ );
923
+ const DOCTYPE_NAME = seal(/^html$/i);
884
924
 
885
- if (cfg.ADD_URI_SAFE_ATTR) {
886
- addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
887
- }
925
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
926
+ __proto__: null,
927
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
928
+ ERB_EXPR: ERB_EXPR,
929
+ TMPLIT_EXPR: TMPLIT_EXPR,
930
+ DATA_ATTR: DATA_ATTR,
931
+ ARIA_ATTR: ARIA_ATTR,
932
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
933
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
934
+ ATTR_WHITESPACE: ATTR_WHITESPACE,
935
+ DOCTYPE_NAME: DOCTYPE_NAME
936
+ });
888
937
 
889
- if (cfg.FORBID_CONTENTS) {
890
- if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
891
- FORBID_CONTENTS = clone(FORBID_CONTENTS);
892
- }
938
+ const getGlobal = () => typeof window === 'undefined' ? null : window;
939
+ /**
940
+ * Creates a no-op policy for internal use only.
941
+ * Don't export this function outside this module!
942
+ * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
943
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
944
+ * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
945
+ * are not supported or creating the policy failed).
946
+ */
893
947
 
894
- addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
895
- }
896
- /* Add #text in case KEEP_CONTENT is set to true */
897
948
 
949
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
950
+ if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
951
+ return null;
952
+ } // Allow the callers to control the unique policy name
953
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
954
+ // Policy creation with duplicate names throws in Trusted Types.
898
955
 
899
- if (KEEP_CONTENT) {
900
- ALLOWED_TAGS['#text'] = true;
901
- }
902
- /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
903
956
 
957
+ let suffix = null;
958
+ const ATTR_NAME = 'data-tt-policy-suffix';
904
959
 
905
- if (WHOLE_DOCUMENT) {
906
- addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
907
- }
908
- /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
960
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
961
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
962
+ }
909
963
 
964
+ const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
910
965
 
911
- if (ALLOWED_TAGS.table) {
912
- addToSet(ALLOWED_TAGS, ['tbody']);
913
- delete FORBID_TAGS.tbody;
914
- }
966
+ try {
967
+ return trustedTypes.createPolicy(policyName, {
968
+ createHTML(html) {
969
+ return html;
970
+ },
915
971
 
916
- if (cfg.TRUSTED_TYPES_POLICY) {
917
- if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
918
- throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
972
+ createScriptURL(scriptUrl) {
973
+ return scriptUrl;
919
974
  }
920
975
 
921
- if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
922
- throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
923
- } // Overwrite existing TrustedTypes policy.
976
+ });
977
+ } catch (_) {
978
+ // Policy creation failed (most likely another DOMPurify script has
979
+ // already run). Skip creating the policy, as this will only cause errors
980
+ // if TT are enforced.
981
+ console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
982
+ return null;
983
+ }
984
+ };
924
985
 
986
+ function createDOMPurify() {
987
+ let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
925
988
 
926
- trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
989
+ const DOMPurify = root => createDOMPurify(root);
990
+ /**
991
+ * Version label, exposed for easier checks
992
+ * if DOMPurify is up to date or not
993
+ */
927
994
 
928
- emptyHTML = trustedTypesPolicy.createHTML('');
929
- } else {
930
- // Uninitialized policy, attempt to initialize the internal dompurify policy.
931
- if (trustedTypesPolicy === undefined) {
932
- trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
933
- } // If creating the internal policy succeeded sign internal variables.
934
995
 
996
+ DOMPurify.version = '3.0.3';
997
+ /**
998
+ * Array of elements that DOMPurify removed during sanitation.
999
+ * Empty if nothing was removed.
1000
+ */
935
1001
 
936
- if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
937
- emptyHTML = trustedTypesPolicy.createHTML('');
938
- }
939
- } // Prevent further manipulation of configuration.
940
- // Not available in IE8, Safari 5, etc.
1002
+ DOMPurify.removed = [];
941
1003
 
1004
+ if (!window || !window.document || window.document.nodeType !== 9) {
1005
+ // Not running in a browser, provide a factory function
1006
+ // so that you can pass your own Window
1007
+ DOMPurify.isSupported = false;
1008
+ return DOMPurify;
1009
+ }
942
1010
 
943
- if (freeze) {
944
- freeze(cfg);
945
- }
1011
+ const originalDocument = window.document;
1012
+ const currentScript = originalDocument.currentScript;
1013
+ let {
1014
+ document
1015
+ } = window;
1016
+ const {
1017
+ DocumentFragment,
1018
+ HTMLTemplateElement,
1019
+ Node,
1020
+ Element,
1021
+ NodeFilter,
1022
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
1023
+ HTMLFormElement,
1024
+ DOMParser,
1025
+ trustedTypes
1026
+ } = window;
1027
+ const ElementPrototype = Element.prototype;
1028
+ const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
1029
+ const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
1030
+ const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
1031
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
1032
+ // new document created via createHTMLDocument. As per the spec
1033
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
1034
+ // a new empty registry is used when creating a template contents owner
1035
+ // document, so we use that as our parent document to ensure nothing
1036
+ // is inherited.
946
1037
 
947
- CONFIG = cfg;
948
- };
1038
+ if (typeof HTMLTemplateElement === 'function') {
1039
+ const template = document.createElement('template');
949
1040
 
950
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
951
- const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
952
- // namespace. We need to specify them explicitly
953
- // so that they don't get erroneously deleted from
954
- // HTML namespace.
1041
+ if (template.content && template.content.ownerDocument) {
1042
+ document = template.content.ownerDocument;
1043
+ }
1044
+ }
955
1045
 
956
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
957
- /* Keep track of all possible SVG and MathML tags
958
- * so that we can perform the namespace checks
959
- * correctly. */
1046
+ let trustedTypesPolicy;
1047
+ let emptyHTML = '';
1048
+ const {
1049
+ implementation,
1050
+ createNodeIterator,
1051
+ createDocumentFragment,
1052
+ getElementsByTagName
1053
+ } = document;
1054
+ const {
1055
+ importNode
1056
+ } = originalDocument;
1057
+ let hooks = {};
1058
+ /**
1059
+ * Expose whether this browser supports running the full DOMPurify.
1060
+ */
960
1061
 
961
- const ALL_SVG_TAGS = addToSet({}, svg$1);
962
- addToSet(ALL_SVG_TAGS, svgFilters);
963
- addToSet(ALL_SVG_TAGS, svgDisallowed);
964
- const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
965
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
1062
+ DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
1063
+ const {
1064
+ MUSTACHE_EXPR,
1065
+ ERB_EXPR,
1066
+ TMPLIT_EXPR,
1067
+ DATA_ATTR,
1068
+ ARIA_ATTR,
1069
+ IS_SCRIPT_OR_DATA,
1070
+ ATTR_WHITESPACE
1071
+ } = EXPRESSIONS;
1072
+ let {
1073
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
1074
+ } = EXPRESSIONS;
966
1075
  /**
967
- *
968
- *
969
- * @param {Element} element a DOM element whose namespace is being checked
970
- * @returns {boolean} Return false if the element has a
971
- * namespace that a spec-compliant parser would never
972
- * return. Return true otherwise.
1076
+ * We consider the elements and attributes below to be safe. Ideally
1077
+ * don't add any new ones but feel free to remove unwanted ones.
973
1078
  */
974
1079
 
975
- const _checkValidNamespace = function _checkValidNamespace(element) {
976
- let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
977
- // can be null. We just simulate parent in this case.
1080
+ /* allowed element names */
978
1081
 
979
- if (!parent || !parent.tagName) {
980
- parent = {
981
- namespaceURI: NAMESPACE,
982
- tagName: 'template'
983
- };
984
- }
1082
+ let ALLOWED_TAGS = null;
1083
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
1084
+ /* Allowed attribute names */
985
1085
 
986
- const tagName = stringToLowerCase(element.tagName);
987
- const parentTagName = stringToLowerCase(parent.tagName);
1086
+ let ALLOWED_ATTR = null;
1087
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
1088
+ /*
1089
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
1090
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
1091
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
1092
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
1093
+ */
988
1094
 
989
- if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
990
- return false;
1095
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
1096
+ tagNameCheck: {
1097
+ writable: true,
1098
+ configurable: false,
1099
+ enumerable: true,
1100
+ value: null
1101
+ },
1102
+ attributeNameCheck: {
1103
+ writable: true,
1104
+ configurable: false,
1105
+ enumerable: true,
1106
+ value: null
1107
+ },
1108
+ allowCustomizedBuiltInElements: {
1109
+ writable: true,
1110
+ configurable: false,
1111
+ enumerable: true,
1112
+ value: false
991
1113
  }
1114
+ }));
1115
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
992
1116
 
993
- if (element.namespaceURI === SVG_NAMESPACE) {
994
- // The only way to switch from HTML namespace to SVG
995
- // is via <svg>. If it happens via any other tag, then
996
- // it should be killed.
997
- if (parent.namespaceURI === HTML_NAMESPACE) {
998
- return tagName === 'svg';
999
- } // The only way to switch from MathML to SVG is via`
1000
- // svg if parent is either <annotation-xml> or MathML
1001
- // text integration points.
1117
+ let FORBID_TAGS = null;
1118
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
1119
+
1120
+ let FORBID_ATTR = null;
1121
+ /* Decide if ARIA attributes are okay */
1002
1122
 
1123
+ let ALLOW_ARIA_ATTR = true;
1124
+ /* Decide if custom data attributes are okay */
1125
+
1126
+ let ALLOW_DATA_ATTR = true;
1127
+ /* Decide if unknown protocols are okay */
1128
+
1129
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
1130
+ /* Decide if self-closing tags in attributes are allowed.
1131
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
1003
1132
 
1004
- if (parent.namespaceURI === MATHML_NAMESPACE) {
1005
- return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
1006
- } // We only allow elements that are defined in SVG
1007
- // spec. All others are disallowed in SVG namespace.
1133
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
1134
+ /* Output should be safe for common template engines.
1135
+ * This means, DOMPurify removes data attributes, mustaches and ERB
1136
+ */
1008
1137
 
1138
+ let SAFE_FOR_TEMPLATES = false;
1139
+ /* Decide if document with <html>... should be returned */
1009
1140
 
1010
- return Boolean(ALL_SVG_TAGS[tagName]);
1011
- }
1141
+ let WHOLE_DOCUMENT = false;
1142
+ /* Track whether config is already set on this instance of DOMPurify. */
1012
1143
 
1013
- if (element.namespaceURI === MATHML_NAMESPACE) {
1014
- // The only way to switch from HTML namespace to MathML
1015
- // is via <math>. If it happens via any other tag, then
1016
- // it should be killed.
1017
- if (parent.namespaceURI === HTML_NAMESPACE) {
1018
- return tagName === 'math';
1019
- } // The only way to switch from SVG to MathML is via
1020
- // <math> and HTML integration points
1144
+ let SET_CONFIG = false;
1145
+ /* Decide if all elements (e.g. style, script) must be children of
1146
+ * document.body. By default, browsers might move them to document.head */
1021
1147
 
1148
+ let FORCE_BODY = false;
1149
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
1150
+ * string (or a TrustedHTML object if Trusted Types are supported).
1151
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
1152
+ */
1022
1153
 
1023
- if (parent.namespaceURI === SVG_NAMESPACE) {
1024
- return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
1025
- } // We only allow elements that are defined in MathML
1026
- // spec. All others are disallowed in MathML namespace.
1154
+ let RETURN_DOM = false;
1155
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
1156
+ * string (or a TrustedHTML object if Trusted Types are supported) */
1027
1157
 
1158
+ let RETURN_DOM_FRAGMENT = false;
1159
+ /* Try to return a Trusted Type object instead of a string, return a string in
1160
+ * case Trusted Types are not supported */
1028
1161
 
1029
- return Boolean(ALL_MATHML_TAGS[tagName]);
1030
- }
1162
+ let RETURN_TRUSTED_TYPE = false;
1163
+ /* Output should be free from DOM clobbering attacks?
1164
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
1165
+ */
1031
1166
 
1032
- if (element.namespaceURI === HTML_NAMESPACE) {
1033
- // The only way to switch from SVG to HTML is via
1034
- // HTML integration points, and from MathML to HTML
1035
- // is via MathML text integration points
1036
- if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
1037
- return false;
1038
- }
1167
+ let SANITIZE_DOM = true;
1168
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
1169
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
1170
+ *
1171
+ * HTML/DOM spec rules that enable DOM Clobbering:
1172
+ * - Named Access on Window (§7.3.3)
1173
+ * - DOM Tree Accessors (§3.1.5)
1174
+ * - Form Element Parent-Child Relations (§4.10.3)
1175
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
1176
+ * - HTMLCollection (§4.2.10.2)
1177
+ *
1178
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
1179
+ * with a constant string, i.e., `user-content-`
1180
+ */
1039
1181
 
1040
- if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
1041
- return false;
1042
- } // We disallow tags that are specific for MathML
1043
- // or SVG and should never appear in HTML namespace
1182
+ let SANITIZE_NAMED_PROPS = false;
1183
+ const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
1184
+ /* Keep element content when removing element? */
1044
1185
 
1186
+ let KEEP_CONTENT = true;
1187
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
1188
+ * of importing it into a new Document and returning a sanitized copy */
1045
1189
 
1046
- return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1047
- } // For XHTML and XML documents that support custom namespaces
1190
+ let IN_PLACE = false;
1191
+ /* Allow usage of profiles like html, svg and mathMl */
1048
1192
 
1193
+ let USE_PROFILES = {};
1194
+ /* Tags to ignore content of when KEEP_CONTENT is true */
1049
1195
 
1050
- if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
1051
- return true;
1052
- } // The code should never reach this place (this means
1053
- // that the element somehow got namespace that is not
1054
- // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
1055
- // Return false just in case.
1196
+ let FORBID_CONTENTS = null;
1197
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
1198
+ /* Tags that are safe for data: URIs */
1056
1199
 
1200
+ let DATA_URI_TAGS = null;
1201
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
1202
+ /* Attributes safe for values like "javascript:" */
1057
1203
 
1058
- return false;
1059
- };
1060
- /**
1061
- * _forceRemove
1062
- *
1063
- * @param {Node} node a DOM node
1064
- */
1204
+ let URI_SAFE_ATTRIBUTES = null;
1205
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
1206
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
1207
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
1208
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
1209
+ /* Document namespace */
1065
1210
 
1211
+ let NAMESPACE = HTML_NAMESPACE;
1212
+ let IS_EMPTY_INPUT = false;
1213
+ /* Allowed XHTML+XML namespaces */
1066
1214
 
1067
- const _forceRemove = function _forceRemove(node) {
1068
- arrayPush(DOMPurify.removed, {
1069
- element: node
1070
- });
1215
+ let ALLOWED_NAMESPACES = null;
1216
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
1217
+ /* Parsing of strict XHTML documents */
1071
1218
 
1072
- try {
1073
- // eslint-disable-next-line unicorn/prefer-dom-node-remove
1074
- node.parentNode.removeChild(node);
1075
- } catch (_) {
1076
- node.remove();
1077
- }
1078
- };
1079
- /**
1080
- * _removeAttribute
1081
- *
1082
- * @param {String} name an Attribute name
1083
- * @param {Node} node a DOM node
1084
- */
1219
+ let PARSER_MEDIA_TYPE;
1220
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
1221
+ const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
1222
+ let transformCaseFunc;
1223
+ /* Keep a reference to config to pass to hooks */
1085
1224
 
1225
+ let CONFIG = null;
1226
+ /* Ideally, do not touch anything below this line */
1086
1227
 
1087
- const _removeAttribute = function _removeAttribute(name, node) {
1088
- try {
1089
- arrayPush(DOMPurify.removed, {
1090
- attribute: node.getAttributeNode(name),
1091
- from: node
1092
- });
1093
- } catch (_) {
1094
- arrayPush(DOMPurify.removed, {
1095
- attribute: null,
1096
- from: node
1097
- });
1098
- }
1228
+ /* ______________________________________________ */
1099
1229
 
1100
- node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
1230
+ const formElement = document.createElement('form');
1101
1231
 
1102
- if (name === 'is' && !ALLOWED_ATTR[name]) {
1103
- if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1104
- try {
1105
- _forceRemove(node);
1106
- } catch (_) {}
1107
- } else {
1108
- try {
1109
- node.setAttribute(name, '');
1110
- } catch (_) {}
1111
- }
1112
- }
1232
+ const isRegexOrFunction = function isRegexOrFunction(testValue) {
1233
+ return testValue instanceof RegExp || testValue instanceof Function;
1113
1234
  };
1114
1235
  /**
1115
- * _initDocument
1236
+ * _parseConfig
1116
1237
  *
1117
- * @param {String} dirty a string of dirty markup
1118
- * @return {Document} a DOM, filled with the dirty markup
1238
+ * @param {Object} cfg optional config literal
1119
1239
  */
1240
+ // eslint-disable-next-line complexity
1120
1241
 
1121
1242
 
1122
- const _initDocument = function _initDocument(dirty) {
1123
- /* Create a HTML document */
1124
- let doc;
1125
- let leadingWhitespace;
1126
-
1127
- if (FORCE_BODY) {
1128
- dirty = '<remove></remove>' + dirty;
1129
- } else {
1130
- /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
1131
- const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1132
- leadingWhitespace = matches && matches[0];
1133
- }
1134
-
1135
- if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
1136
- // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1137
- dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
1138
- }
1139
-
1140
- const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
1141
- /*
1142
- * Use the DOMParser API by default, fallback later if needs be
1143
- * DOMParser not work for svg when has multiple root element.
1144
- */
1145
-
1146
- if (NAMESPACE === HTML_NAMESPACE) {
1147
- try {
1148
- doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1149
- } catch (_) {}
1243
+ const _parseConfig = function _parseConfig(cfg) {
1244
+ if (CONFIG && CONFIG === cfg) {
1245
+ return;
1150
1246
  }
1151
- /* Use createHTMLDocument in case DOMParser is not available */
1152
-
1247
+ /* Shield configuration object from tampering */
1153
1248
 
1154
- if (!doc || !doc.documentElement) {
1155
- doc = implementation.createDocument(NAMESPACE, 'template', null);
1156
1249
 
1157
- try {
1158
- doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1159
- } catch (_) {// Syntax error if dirtyPayload is invalid xml
1160
- }
1250
+ if (!cfg || typeof cfg !== 'object') {
1251
+ cfg = {};
1161
1252
  }
1253
+ /* Shield configuration object from prototype pollution */
1162
1254
 
1163
- const body = doc.body || doc.documentElement;
1164
1255
 
1165
- if (dirty && leadingWhitespace) {
1166
- body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1167
- }
1168
- /* Work on whole document or just its body */
1256
+ cfg = clone(cfg);
1257
+ PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
1258
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
1169
1259
 
1260
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
1261
+ /* Set configuration parameters */
1170
1262
 
1171
- if (NAMESPACE === HTML_NAMESPACE) {
1172
- return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
1173
- }
1263
+ ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
1264
+ ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
1265
+ ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
1266
+ URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
1267
+ cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
1268
+ transformCaseFunc // eslint-disable-line indent
1269
+ ) // eslint-disable-line indent
1270
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
1271
+ DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
1272
+ cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
1273
+ transformCaseFunc // eslint-disable-line indent
1274
+ ) // eslint-disable-line indent
1275
+ : DEFAULT_DATA_URI_TAGS;
1276
+ FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
1277
+ FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
1278
+ FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
1279
+ USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
1280
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
1174
1281
 
1175
- return WHOLE_DOCUMENT ? doc.documentElement : body;
1176
- };
1177
- /**
1178
- * _createIterator
1179
- *
1180
- * @param {Document} root document/fragment to create iterator for
1181
- * @return {Iterator} iterator instance
1182
- */
1282
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
1183
1283
 
1284
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
1184
1285
 
1185
- const _createIterator = function _createIterator(root) {
1186
- return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
1187
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
1188
- };
1189
- /**
1190
- * _isClobbered
1191
- *
1192
- * @param {Node} elm element to check for clobbering attacks
1193
- * @return {Boolean} true if clobbered, false if safe
1194
- */
1286
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
1195
1287
 
1288
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
1196
1289
 
1197
- const _isClobbered = function _isClobbered(elm) {
1198
- return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
1199
- };
1200
- /**
1201
- * _isNode
1202
- *
1203
- * @param {Node} obj object to check whether it's a DOM node
1204
- * @return {Boolean} true is object is a DOM node
1205
- */
1290
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
1206
1291
 
1292
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
1207
1293
 
1208
- const _isNode = function _isNode(object) {
1209
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
1210
- };
1211
- /**
1212
- * _executeHook
1213
- * Execute user configurable hooks
1214
- *
1215
- * @param {String} entryPoint Name of the hook's entry point
1216
- * @param {Node} currentNode node to work on with the hook
1217
- * @param {Object} data additional hook parameters
1218
- */
1294
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
1219
1295
 
1296
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
1220
1297
 
1221
- const _executeHook = function _executeHook(entryPoint, currentNode, data) {
1222
- if (!hooks[entryPoint]) {
1223
- return;
1224
- }
1298
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
1225
1299
 
1226
- arrayForEach(hooks[entryPoint], hook => {
1227
- hook.call(DOMPurify, currentNode, data, CONFIG);
1228
- });
1229
- };
1230
- /**
1231
- * _sanitizeElements
1232
- *
1233
- * @protect nodeName
1234
- * @protect textContent
1235
- * @protect removeChild
1236
- *
1237
- * @param {Node} currentNode to check for permission to exist
1238
- * @return {Boolean} true if node was killed, false if left alive
1239
- */
1300
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
1240
1301
 
1302
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
1241
1303
 
1242
- const _sanitizeElements = function _sanitizeElements(currentNode) {
1243
- let content;
1244
- /* Execute a hook if present */
1304
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
1245
1305
 
1246
- _executeHook('beforeSanitizeElements', currentNode, null);
1247
- /* Check if element is clobbered or can clobber */
1306
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
1248
1307
 
1308
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
1309
+ NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
1310
+ CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
1249
1311
 
1250
- if (_isClobbered(currentNode)) {
1251
- _forceRemove(currentNode);
1312
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
1313
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
1314
+ }
1252
1315
 
1253
- return true;
1316
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
1317
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
1254
1318
  }
1255
- /* Now let's check the element's type and name */
1256
1319
 
1320
+ if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
1321
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
1322
+ }
1257
1323
 
1258
- const tagName = transformCaseFunc(currentNode.nodeName);
1259
- /* Execute a hook if present */
1324
+ if (SAFE_FOR_TEMPLATES) {
1325
+ ALLOW_DATA_ATTR = false;
1326
+ }
1260
1327
 
1261
- _executeHook('uponSanitizeElement', currentNode, {
1262
- tagName,
1263
- allowedTags: ALLOWED_TAGS
1264
- });
1265
- /* Detect mXSS attempts abusing namespace confusion */
1328
+ if (RETURN_DOM_FRAGMENT) {
1329
+ RETURN_DOM = true;
1330
+ }
1331
+ /* Parse profile info */
1266
1332
 
1267
1333
 
1268
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1269
- _forceRemove(currentNode);
1334
+ if (USE_PROFILES) {
1335
+ ALLOWED_TAGS = addToSet({}, [...text]);
1336
+ ALLOWED_ATTR = [];
1270
1337
 
1271
- return true;
1272
- }
1273
- /* Remove element if anything forbids its presence */
1338
+ if (USE_PROFILES.html === true) {
1339
+ addToSet(ALLOWED_TAGS, html$1);
1340
+ addToSet(ALLOWED_ATTR, html);
1341
+ }
1274
1342
 
1343
+ if (USE_PROFILES.svg === true) {
1344
+ addToSet(ALLOWED_TAGS, svg$1);
1345
+ addToSet(ALLOWED_ATTR, svg);
1346
+ addToSet(ALLOWED_ATTR, xml);
1347
+ }
1275
1348
 
1276
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1277
- /* Check if we have a custom element to handle */
1278
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1279
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1280
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1349
+ if (USE_PROFILES.svgFilters === true) {
1350
+ addToSet(ALLOWED_TAGS, svgFilters);
1351
+ addToSet(ALLOWED_ATTR, svg);
1352
+ addToSet(ALLOWED_ATTR, xml);
1281
1353
  }
1282
- /* Keep content except for bad-listed elements */
1354
+
1355
+ if (USE_PROFILES.mathMl === true) {
1356
+ addToSet(ALLOWED_TAGS, mathMl$1);
1357
+ addToSet(ALLOWED_ATTR, mathMl);
1358
+ addToSet(ALLOWED_ATTR, xml);
1359
+ }
1360
+ }
1361
+ /* Merge configuration parameters */
1283
1362
 
1284
1363
 
1285
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1286
- const parentNode = getParentNode(currentNode) || currentNode.parentNode;
1287
- const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1364
+ if (cfg.ADD_TAGS) {
1365
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
1366
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
1367
+ }
1288
1368
 
1289
- if (childNodes && parentNode) {
1290
- const childCount = childNodes.length;
1369
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
1370
+ }
1291
1371
 
1292
- for (let i = childCount - 1; i >= 0; --i) {
1293
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
1294
- }
1295
- }
1372
+ if (cfg.ADD_ATTR) {
1373
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
1374
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
1296
1375
  }
1297
1376
 
1298
- _forceRemove(currentNode);
1377
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
1378
+ }
1299
1379
 
1300
- return true;
1380
+ if (cfg.ADD_URI_SAFE_ATTR) {
1381
+ addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
1301
1382
  }
1302
- /* Check whether element has a valid namespace */
1303
1383
 
1384
+ if (cfg.FORBID_CONTENTS) {
1385
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
1386
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
1387
+ }
1304
1388
 
1305
- if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1306
- _forceRemove(currentNode);
1389
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
1390
+ }
1391
+ /* Add #text in case KEEP_CONTENT is set to true */
1307
1392
 
1308
- return true;
1393
+
1394
+ if (KEEP_CONTENT) {
1395
+ ALLOWED_TAGS['#text'] = true;
1309
1396
  }
1310
- /* Make sure that older browsers don't get noscript mXSS */
1397
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
1311
1398
 
1312
1399
 
1313
- if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
1314
- _forceRemove(currentNode);
1400
+ if (WHOLE_DOCUMENT) {
1401
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
1402
+ }
1403
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
1315
1404
 
1316
- return true;
1405
+
1406
+ if (ALLOWED_TAGS.table) {
1407
+ addToSet(ALLOWED_TAGS, ['tbody']);
1408
+ delete FORBID_TAGS.tbody;
1317
1409
  }
1318
- /* Sanitize element content to be template-safe */
1319
1410
 
1411
+ if (cfg.TRUSTED_TYPES_POLICY) {
1412
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
1413
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
1414
+ }
1320
1415
 
1321
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1322
- /* Get the element's text content */
1323
- content = currentNode.textContent;
1324
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
1325
- content = stringReplace(content, ERB_EXPR, ' ');
1326
- content = stringReplace(content, TMPLIT_EXPR, ' ');
1416
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
1417
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
1418
+ } // Overwrite existing TrustedTypes policy.
1327
1419
 
1328
- if (currentNode.textContent !== content) {
1329
- arrayPush(DOMPurify.removed, {
1330
- element: currentNode.cloneNode()
1331
- });
1332
- currentNode.textContent = content;
1420
+
1421
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
1422
+
1423
+ emptyHTML = trustedTypesPolicy.createHTML('');
1424
+ } else {
1425
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
1426
+ if (trustedTypesPolicy === undefined) {
1427
+ trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
1428
+ } // If creating the internal policy succeeded sign internal variables.
1429
+
1430
+
1431
+ if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
1432
+ emptyHTML = trustedTypesPolicy.createHTML('');
1333
1433
  }
1334
- }
1335
- /* Execute a hook if present */
1434
+ } // Prevent further manipulation of configuration.
1435
+ // Not available in IE8, Safari 5, etc.
1336
1436
 
1337
1437
 
1338
- _executeHook('afterSanitizeElements', currentNode, null);
1438
+ if (freeze) {
1439
+ freeze(cfg);
1440
+ }
1339
1441
 
1340
- return false;
1442
+ CONFIG = cfg;
1341
1443
  };
1444
+
1445
+ const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
1446
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
1447
+ // namespace. We need to specify them explicitly
1448
+ // so that they don't get erroneously deleted from
1449
+ // HTML namespace.
1450
+
1451
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
1452
+ /* Keep track of all possible SVG and MathML tags
1453
+ * so that we can perform the namespace checks
1454
+ * correctly. */
1455
+
1456
+ const ALL_SVG_TAGS = addToSet({}, svg$1);
1457
+ addToSet(ALL_SVG_TAGS, svgFilters);
1458
+ addToSet(ALL_SVG_TAGS, svgDisallowed);
1459
+ const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
1460
+ addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
1342
1461
  /**
1343
- * _isValidAttribute
1344
1462
  *
1345
- * @param {string} lcTag Lowercase tag name of containing element.
1346
- * @param {string} lcName Lowercase attribute name.
1347
- * @param {string} value Attribute value.
1348
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
1463
+ *
1464
+ * @param {Element} element a DOM element whose namespace is being checked
1465
+ * @returns {boolean} Return false if the element has a
1466
+ * namespace that a spec-compliant parser would never
1467
+ * return. Return true otherwise.
1349
1468
  */
1350
- // eslint-disable-next-line complexity
1351
1469
 
1470
+ const _checkValidNamespace = function _checkValidNamespace(element) {
1471
+ let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
1472
+ // can be null. We just simulate parent in this case.
1473
+
1474
+ if (!parent || !parent.tagName) {
1475
+ parent = {
1476
+ namespaceURI: NAMESPACE,
1477
+ tagName: 'template'
1478
+ };
1479
+ }
1480
+
1481
+ const tagName = stringToLowerCase(element.tagName);
1482
+ const parentTagName = stringToLowerCase(parent.tagName);
1352
1483
 
1353
- const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1354
- /* Make sure attribute cannot clobber */
1355
- if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1484
+ if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
1356
1485
  return false;
1357
1486
  }
1358
- /* Allow valid data-* attributes: At least one character after "-"
1359
- (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1360
- XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1361
- We don't need to check the value; it's always URI safe. */
1362
-
1363
1487
 
1364
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1365
- if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1366
- // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1367
- // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1368
- _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1369
- // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1370
- lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
1371
- return false;
1372
- }
1373
- /* Check value is safe. First, is attr inert? If so, is safe */
1488
+ if (element.namespaceURI === SVG_NAMESPACE) {
1489
+ // The only way to switch from HTML namespace to SVG
1490
+ // is via <svg>. If it happens via any other tag, then
1491
+ // it should be killed.
1492
+ if (parent.namespaceURI === HTML_NAMESPACE) {
1493
+ return tagName === 'svg';
1494
+ } // The only way to switch from MathML to SVG is via`
1495
+ // svg if parent is either <annotation-xml> or MathML
1496
+ // text integration points.
1374
1497
 
1375
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
1376
- return false;
1377
- } else ;
1378
1498
 
1379
- return true;
1380
- };
1381
- /**
1382
- * _basicCustomElementCheck
1383
- * checks if at least one dash is included in tagName, and it's not the first char
1384
- * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1385
- * @param {string} tagName name of the tag of the node to sanitize
1386
- */
1499
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
1500
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
1501
+ } // We only allow elements that are defined in SVG
1502
+ // spec. All others are disallowed in SVG namespace.
1387
1503
 
1388
1504
 
1389
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1390
- return tagName.indexOf('-') > 0;
1391
- };
1392
- /**
1393
- * _sanitizeAttributes
1394
- *
1395
- * @protect attributes
1396
- * @protect nodeName
1397
- * @protect removeAttribute
1398
- * @protect setAttribute
1399
- *
1400
- * @param {Node} currentNode to sanitize
1401
- */
1505
+ return Boolean(ALL_SVG_TAGS[tagName]);
1506
+ }
1402
1507
 
1508
+ if (element.namespaceURI === MATHML_NAMESPACE) {
1509
+ // The only way to switch from HTML namespace to MathML
1510
+ // is via <math>. If it happens via any other tag, then
1511
+ // it should be killed.
1512
+ if (parent.namespaceURI === HTML_NAMESPACE) {
1513
+ return tagName === 'math';
1514
+ } // The only way to switch from SVG to MathML is via
1515
+ // <math> and HTML integration points
1403
1516
 
1404
- const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1405
- let attr;
1406
- let value;
1407
- let lcName;
1408
- let l;
1409
- /* Execute a hook if present */
1410
1517
 
1411
- _executeHook('beforeSanitizeAttributes', currentNode, null);
1518
+ if (parent.namespaceURI === SVG_NAMESPACE) {
1519
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
1520
+ } // We only allow elements that are defined in MathML
1521
+ // spec. All others are disallowed in MathML namespace.
1412
1522
 
1413
- const {
1414
- attributes
1415
- } = currentNode;
1416
- /* Check if we have attributes; if not we might have a text node */
1417
1523
 
1418
- if (!attributes) {
1419
- return;
1524
+ return Boolean(ALL_MATHML_TAGS[tagName]);
1420
1525
  }
1421
1526
 
1422
- const hookEvent = {
1423
- attrName: '',
1424
- attrValue: '',
1425
- keepAttr: true,
1426
- allowedAttributes: ALLOWED_ATTR
1427
- };
1428
- l = attributes.length;
1429
- /* Go backwards over all attributes; safely remove bad ones */
1527
+ if (element.namespaceURI === HTML_NAMESPACE) {
1528
+ // The only way to switch from SVG to HTML is via
1529
+ // HTML integration points, and from MathML to HTML
1530
+ // is via MathML text integration points
1531
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
1532
+ return false;
1533
+ }
1430
1534
 
1431
- while (l--) {
1432
- attr = attributes[l];
1433
- const {
1434
- name,
1435
- namespaceURI
1436
- } = attr;
1437
- value = name === 'value' ? attr.value : stringTrim(attr.value);
1438
- lcName = transformCaseFunc(name);
1439
- /* Execute a hook if present */
1535
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
1536
+ return false;
1537
+ } // We disallow tags that are specific for MathML
1538
+ // or SVG and should never appear in HTML namespace
1440
1539
 
1441
- hookEvent.attrName = lcName;
1442
- hookEvent.attrValue = value;
1443
- hookEvent.keepAttr = true;
1444
- hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1445
1540
 
1446
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1541
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1542
+ } // For XHTML and XML documents that support custom namespaces
1447
1543
 
1448
- value = hookEvent.attrValue;
1449
- /* Did the hooks approve of the attribute? */
1450
1544
 
1451
- if (hookEvent.forceKeepAttr) {
1452
- continue;
1453
- }
1454
- /* Remove attribute */
1545
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
1546
+ return true;
1547
+ } // The code should never reach this place (this means
1548
+ // that the element somehow got namespace that is not
1549
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
1550
+ // Return false just in case.
1455
1551
 
1456
1552
 
1457
- _removeAttribute(name, currentNode);
1458
- /* Did the hooks approve of the attribute? */
1553
+ return false;
1554
+ };
1555
+ /**
1556
+ * _forceRemove
1557
+ *
1558
+ * @param {Node} node a DOM node
1559
+ */
1459
1560
 
1460
1561
 
1461
- if (!hookEvent.keepAttr) {
1462
- continue;
1463
- }
1464
- /* Work around a security issue in jQuery 3.0 */
1562
+ const _forceRemove = function _forceRemove(node) {
1563
+ arrayPush(DOMPurify.removed, {
1564
+ element: node
1565
+ });
1465
1566
 
1567
+ try {
1568
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
1569
+ node.parentNode.removeChild(node);
1570
+ } catch (_) {
1571
+ node.remove();
1572
+ }
1573
+ };
1574
+ /**
1575
+ * _removeAttribute
1576
+ *
1577
+ * @param {String} name an Attribute name
1578
+ * @param {Node} node a DOM node
1579
+ */
1466
1580
 
1467
- if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1468
- _removeAttribute(name, currentNode);
1469
1581
 
1470
- continue;
1471
- }
1472
- /* Sanitize attribute content to be template-safe */
1582
+ const _removeAttribute = function _removeAttribute(name, node) {
1583
+ try {
1584
+ arrayPush(DOMPurify.removed, {
1585
+ attribute: node.getAttributeNode(name),
1586
+ from: node
1587
+ });
1588
+ } catch (_) {
1589
+ arrayPush(DOMPurify.removed, {
1590
+ attribute: null,
1591
+ from: node
1592
+ });
1593
+ }
1473
1594
 
1595
+ node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
1474
1596
 
1475
- if (SAFE_FOR_TEMPLATES) {
1476
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
1477
- value = stringReplace(value, ERB_EXPR, ' ');
1478
- value = stringReplace(value, TMPLIT_EXPR, ' ');
1597
+ if (name === 'is' && !ALLOWED_ATTR[name]) {
1598
+ if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1599
+ try {
1600
+ _forceRemove(node);
1601
+ } catch (_) {}
1602
+ } else {
1603
+ try {
1604
+ node.setAttribute(name, '');
1605
+ } catch (_) {}
1479
1606
  }
1480
- /* Is `value` valid for this attribute? */
1481
-
1607
+ }
1608
+ };
1609
+ /**
1610
+ * _initDocument
1611
+ *
1612
+ * @param {String} dirty a string of dirty markup
1613
+ * @return {Document} a DOM, filled with the dirty markup
1614
+ */
1482
1615
 
1483
- const lcTag = transformCaseFunc(currentNode.nodeName);
1484
1616
 
1485
- if (!_isValidAttribute(lcTag, lcName, value)) {
1486
- continue;
1487
- }
1488
- /* Full DOM Clobbering protection via namespace isolation,
1489
- * Prefix id and name attributes with `user-content-`
1490
- */
1617
+ const _initDocument = function _initDocument(dirty) {
1618
+ /* Create a HTML document */
1619
+ let doc;
1620
+ let leadingWhitespace;
1491
1621
 
1622
+ if (FORCE_BODY) {
1623
+ dirty = '<remove></remove>' + dirty;
1624
+ } else {
1625
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
1626
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1627
+ leadingWhitespace = matches && matches[0];
1628
+ }
1492
1629
 
1493
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1494
- // Remove the attribute with this value
1495
- _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1630
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
1631
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1632
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
1633
+ }
1496
1634
 
1635
+ const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
1636
+ /*
1637
+ * Use the DOMParser API by default, fallback later if needs be
1638
+ * DOMParser not work for svg when has multiple root element.
1639
+ */
1497
1640
 
1498
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
1499
- }
1500
- /* Handle attributes that require Trusted Types */
1641
+ if (NAMESPACE === HTML_NAMESPACE) {
1642
+ try {
1643
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1644
+ } catch (_) {}
1645
+ }
1646
+ /* Use createHTMLDocument in case DOMParser is not available */
1501
1647
 
1502
1648
 
1503
- if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1504
- if (namespaceURI) ; else {
1505
- switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1506
- case 'TrustedHTML':
1507
- {
1508
- value = trustedTypesPolicy.createHTML(value);
1509
- break;
1510
- }
1649
+ if (!doc || !doc.documentElement) {
1650
+ doc = implementation.createDocument(NAMESPACE, 'template', null);
1511
1651
 
1512
- case 'TrustedScriptURL':
1513
- {
1514
- value = trustedTypesPolicy.createScriptURL(value);
1515
- break;
1516
- }
1517
- }
1518
- }
1652
+ try {
1653
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1654
+ } catch (_) {// Syntax error if dirtyPayload is invalid xml
1519
1655
  }
1520
- /* Handle invalid data-* attribute set by try-catching it */
1656
+ }
1521
1657
 
1658
+ const body = doc.body || doc.documentElement;
1522
1659
 
1523
- try {
1524
- if (namespaceURI) {
1525
- currentNode.setAttributeNS(namespaceURI, name, value);
1526
- } else {
1527
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1528
- currentNode.setAttribute(name, value);
1529
- }
1660
+ if (dirty && leadingWhitespace) {
1661
+ body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1662
+ }
1663
+ /* Work on whole document or just its body */
1530
1664
 
1531
- arrayPop(DOMPurify.removed);
1532
- } catch (_) {}
1665
+
1666
+ if (NAMESPACE === HTML_NAMESPACE) {
1667
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
1533
1668
  }
1534
- /* Execute a hook if present */
1535
1669
 
1670
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
1671
+ };
1672
+ /**
1673
+ * _createIterator
1674
+ *
1675
+ * @param {Document} root document/fragment to create iterator for
1676
+ * @return {Iterator} iterator instance
1677
+ */
1536
1678
 
1537
- _executeHook('afterSanitizeAttributes', currentNode, null);
1679
+
1680
+ const _createIterator = function _createIterator(root) {
1681
+ return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
1682
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
1538
1683
  };
1539
1684
  /**
1540
- * _sanitizeShadowDOM
1685
+ * _isClobbered
1541
1686
  *
1542
- * @param {DocumentFragment} fragment to iterate over recursively
1687
+ * @param {Node} elm element to check for clobbering attacks
1688
+ * @return {Boolean} true if clobbered, false if safe
1543
1689
  */
1544
1690
 
1545
1691
 
1546
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1547
- let shadowNode;
1692
+ const _isClobbered = function _isClobbered(elm) {
1693
+ return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
1694
+ };
1695
+ /**
1696
+ * _isNode
1697
+ *
1698
+ * @param {Node} obj object to check whether it's a DOM node
1699
+ * @return {Boolean} true is object is a DOM node
1700
+ */
1548
1701
 
1549
- const shadowIterator = _createIterator(fragment);
1550
- /* Execute a hook if present */
1702
+
1703
+ const _isNode = function _isNode(object) {
1704
+ return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
1705
+ };
1706
+ /**
1707
+ * _executeHook
1708
+ * Execute user configurable hooks
1709
+ *
1710
+ * @param {String} entryPoint Name of the hook's entry point
1711
+ * @param {Node} currentNode node to work on with the hook
1712
+ * @param {Object} data additional hook parameters
1713
+ */
1551
1714
 
1552
1715
 
1553
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
1716
+ const _executeHook = function _executeHook(entryPoint, currentNode, data) {
1717
+ if (!hooks[entryPoint]) {
1718
+ return;
1719
+ }
1554
1720
 
1555
- while (shadowNode = shadowIterator.nextNode()) {
1556
- /* Execute a hook if present */
1557
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
1558
- /* Sanitize tags and elements */
1721
+ arrayForEach(hooks[entryPoint], hook => {
1722
+ hook.call(DOMPurify, currentNode, data, CONFIG);
1723
+ });
1724
+ };
1725
+ /**
1726
+ * _sanitizeElements
1727
+ *
1728
+ * @protect nodeName
1729
+ * @protect textContent
1730
+ * @protect removeChild
1731
+ *
1732
+ * @param {Node} currentNode to check for permission to exist
1733
+ * @return {Boolean} true if node was killed, false if left alive
1734
+ */
1559
1735
 
1560
1736
 
1561
- if (_sanitizeElements(shadowNode)) {
1562
- continue;
1563
- }
1564
- /* Deep shadow DOM detected */
1737
+ const _sanitizeElements = function _sanitizeElements(currentNode) {
1738
+ let content;
1739
+ /* Execute a hook if present */
1565
1740
 
1741
+ _executeHook('beforeSanitizeElements', currentNode, null);
1742
+ /* Check if element is clobbered or can clobber */
1566
1743
 
1567
- if (shadowNode.content instanceof DocumentFragment) {
1568
- _sanitizeShadowDOM(shadowNode.content);
1569
- }
1570
- /* Check attributes, sanitize if necessary */
1571
1744
 
1745
+ if (_isClobbered(currentNode)) {
1746
+ _forceRemove(currentNode);
1572
1747
 
1573
- _sanitizeAttributes(shadowNode);
1748
+ return true;
1574
1749
  }
1575
- /* Execute a hook if present */
1750
+ /* Now let's check the element's type and name */
1576
1751
 
1577
1752
 
1578
- _executeHook('afterSanitizeShadowDOM', fragment, null);
1579
- };
1580
- /**
1581
- * Sanitize
1582
- * Public method providing core sanitation functionality
1583
- *
1584
- * @param {String|Node} dirty string or DOM node
1585
- * @param {Object} configuration object
1586
- */
1587
- // eslint-disable-next-line complexity
1753
+ const tagName = transformCaseFunc(currentNode.nodeName);
1754
+ /* Execute a hook if present */
1588
1755
 
1756
+ _executeHook('uponSanitizeElement', currentNode, {
1757
+ tagName,
1758
+ allowedTags: ALLOWED_TAGS
1759
+ });
1760
+ /* Detect mXSS attempts abusing namespace confusion */
1589
1761
 
1590
- DOMPurify.sanitize = function (dirty) {
1591
- let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1592
- let body;
1593
- let importedNode;
1594
- let currentNode;
1595
- let returnNode;
1596
- /* Make sure we have a string to sanitize.
1597
- DO NOT return early, as this will return the wrong type if
1598
- the user has requested a DOM object rather than a string */
1599
1762
 
1600
- IS_EMPTY_INPUT = !dirty;
1763
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1764
+ _forceRemove(currentNode);
1601
1765
 
1602
- if (IS_EMPTY_INPUT) {
1603
- dirty = '<!-->';
1766
+ return true;
1604
1767
  }
1605
- /* Stringify, in case dirty is an object */
1768
+ /* Remove element if anything forbids its presence */
1606
1769
 
1607
1770
 
1608
- if (typeof dirty !== 'string' && !_isNode(dirty)) {
1609
- if (typeof dirty.toString === 'function') {
1610
- dirty = dirty.toString();
1771
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1772
+ /* Check if we have a custom element to handle */
1773
+ if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1774
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1775
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1776
+ }
1777
+ /* Keep content except for bad-listed elements */
1611
1778
 
1612
- if (typeof dirty !== 'string') {
1613
- throw typeErrorCreate('dirty is not a string, aborting');
1779
+
1780
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1781
+ const parentNode = getParentNode(currentNode) || currentNode.parentNode;
1782
+ const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1783
+
1784
+ if (childNodes && parentNode) {
1785
+ const childCount = childNodes.length;
1786
+
1787
+ for (let i = childCount - 1; i >= 0; --i) {
1788
+ parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
1789
+ }
1614
1790
  }
1615
- } else {
1616
- throw typeErrorCreate('toString is not a function');
1617
1791
  }
1618
- }
1619
- /* Return dirty HTML if DOMPurify cannot run */
1620
1792
 
1793
+ _forceRemove(currentNode);
1621
1794
 
1622
- if (!DOMPurify.isSupported) {
1623
- return dirty;
1795
+ return true;
1624
1796
  }
1625
- /* Assign config vars */
1797
+ /* Check whether element has a valid namespace */
1626
1798
 
1627
1799
 
1628
- if (!SET_CONFIG) {
1629
- _parseConfig(cfg);
1800
+ if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1801
+ _forceRemove(currentNode);
1802
+
1803
+ return true;
1630
1804
  }
1631
- /* Clean up removed elements */
1805
+ /* Make sure that older browsers don't get noscript mXSS */
1632
1806
 
1633
1807
 
1634
- DOMPurify.removed = [];
1635
- /* Check if dirty is correctly typed for IN_PLACE */
1808
+ if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
1809
+ _forceRemove(currentNode);
1636
1810
 
1637
- if (typeof dirty === 'string') {
1638
- IN_PLACE = false;
1811
+ return true;
1639
1812
  }
1813
+ /* Sanitize element content to be template-safe */
1640
1814
 
1641
- if (IN_PLACE) {
1642
- /* Do some early pre-sanitization to avoid unsafe root nodes */
1643
- if (dirty.nodeName) {
1644
- const tagName = transformCaseFunc(dirty.nodeName);
1645
1815
 
1646
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1647
- throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1648
- }
1649
- }
1650
- } else if (dirty instanceof Node) {
1651
- /* If dirty is a DOM element, append to an empty document to avoid
1652
- elements being stripped by the parser */
1653
- body = _initDocument('<!---->');
1654
- importedNode = body.ownerDocument.importNode(dirty, true);
1816
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1817
+ /* Get the element's text content */
1818
+ content = currentNode.textContent;
1819
+ content = stringReplace(content, MUSTACHE_EXPR, ' ');
1820
+ content = stringReplace(content, ERB_EXPR, ' ');
1821
+ content = stringReplace(content, TMPLIT_EXPR, ' ');
1655
1822
 
1656
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1657
- /* Node is already a body, use as is */
1658
- body = importedNode;
1659
- } else if (importedNode.nodeName === 'HTML') {
1660
- body = importedNode;
1661
- } else {
1662
- // eslint-disable-next-line unicorn/prefer-dom-node-append
1663
- body.appendChild(importedNode);
1664
- }
1665
- } else {
1666
- /* Exit directly if we have nothing to do */
1667
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1668
- dirty.indexOf('<') === -1) {
1669
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1823
+ if (currentNode.textContent !== content) {
1824
+ arrayPush(DOMPurify.removed, {
1825
+ element: currentNode.cloneNode()
1826
+ });
1827
+ currentNode.textContent = content;
1670
1828
  }
1671
- /* Initialize the document to work on */
1829
+ }
1830
+ /* Execute a hook if present */
1672
1831
 
1673
1832
 
1674
- body = _initDocument(dirty);
1675
- /* Check we have a DOM node from the data */
1833
+ _executeHook('afterSanitizeElements', currentNode, null);
1834
+
1835
+ return false;
1836
+ };
1837
+ /**
1838
+ * _isValidAttribute
1839
+ *
1840
+ * @param {string} lcTag Lowercase tag name of containing element.
1841
+ * @param {string} lcName Lowercase attribute name.
1842
+ * @param {string} value Attribute value.
1843
+ * @return {Boolean} Returns true if `value` is valid, otherwise false.
1844
+ */
1845
+ // eslint-disable-next-line complexity
1676
1846
 
1677
- if (!body) {
1678
- return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1679
- }
1847
+
1848
+ const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1849
+ /* Make sure attribute cannot clobber */
1850
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1851
+ return false;
1680
1852
  }
1681
- /* Remove first element node (ours) if FORCE_BODY is set */
1853
+ /* Allow valid data-* attributes: At least one character after "-"
1854
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1855
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1856
+ We don't need to check the value; it's always URI safe. */
1682
1857
 
1683
1858
 
1684
- if (body && FORCE_BODY) {
1685
- _forceRemove(body.firstChild);
1686
- }
1687
- /* Get node iterator */
1859
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1860
+ if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1861
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1862
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1863
+ _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1864
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1865
+ lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
1866
+ return false;
1867
+ }
1868
+ /* Check value is safe. First, is attr inert? If so, is safe */
1688
1869
 
1870
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
1871
+ return false;
1872
+ } else ;
1689
1873
 
1690
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1691
- /* Now start iterating over the created document */
1874
+ return true;
1875
+ };
1876
+ /**
1877
+ * _basicCustomElementCheck
1878
+ * checks if at least one dash is included in tagName, and it's not the first char
1879
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1880
+ * @param {string} tagName name of the tag of the node to sanitize
1881
+ */
1692
1882
 
1693
1883
 
1694
- while (currentNode = nodeIterator.nextNode()) {
1695
- /* Sanitize tags and elements */
1696
- if (_sanitizeElements(currentNode)) {
1697
- continue;
1698
- }
1699
- /* Shadow DOM detected, sanitize it */
1884
+ const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1885
+ return tagName.indexOf('-') > 0;
1886
+ };
1887
+ /**
1888
+ * _sanitizeAttributes
1889
+ *
1890
+ * @protect attributes
1891
+ * @protect nodeName
1892
+ * @protect removeAttribute
1893
+ * @protect setAttribute
1894
+ *
1895
+ * @param {Node} currentNode to sanitize
1896
+ */
1700
1897
 
1701
1898
 
1702
- if (currentNode.content instanceof DocumentFragment) {
1703
- _sanitizeShadowDOM(currentNode.content);
1704
- }
1705
- /* Check attributes, sanitize if necessary */
1899
+ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1900
+ let attr;
1901
+ let value;
1902
+ let lcName;
1903
+ let l;
1904
+ /* Execute a hook if present */
1706
1905
 
1906
+ _executeHook('beforeSanitizeAttributes', currentNode, null);
1707
1907
 
1708
- _sanitizeAttributes(currentNode);
1908
+ const {
1909
+ attributes
1910
+ } = currentNode;
1911
+ /* Check if we have attributes; if not we might have a text node */
1912
+
1913
+ if (!attributes) {
1914
+ return;
1709
1915
  }
1710
- /* If we sanitized `dirty` in-place, return it. */
1711
1916
 
1917
+ const hookEvent = {
1918
+ attrName: '',
1919
+ attrValue: '',
1920
+ keepAttr: true,
1921
+ allowedAttributes: ALLOWED_ATTR
1922
+ };
1923
+ l = attributes.length;
1924
+ /* Go backwards over all attributes; safely remove bad ones */
1712
1925
 
1713
- if (IN_PLACE) {
1714
- return dirty;
1715
- }
1716
- /* Return sanitized string or DOM */
1926
+ while (l--) {
1927
+ attr = attributes[l];
1928
+ const {
1929
+ name,
1930
+ namespaceURI
1931
+ } = attr;
1932
+ value = name === 'value' ? attr.value : stringTrim(attr.value);
1933
+ lcName = transformCaseFunc(name);
1934
+ /* Execute a hook if present */
1717
1935
 
1936
+ hookEvent.attrName = lcName;
1937
+ hookEvent.attrValue = value;
1938
+ hookEvent.keepAttr = true;
1939
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1718
1940
 
1719
- if (RETURN_DOM) {
1720
- if (RETURN_DOM_FRAGMENT) {
1721
- returnNode = createDocumentFragment.call(body.ownerDocument);
1941
+ _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1722
1942
 
1723
- while (body.firstChild) {
1724
- // eslint-disable-next-line unicorn/prefer-dom-node-append
1725
- returnNode.appendChild(body.firstChild);
1726
- }
1727
- } else {
1728
- returnNode = body;
1729
- }
1943
+ value = hookEvent.attrValue;
1944
+ /* Did the hooks approve of the attribute? */
1730
1945
 
1731
- if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
1732
- /*
1733
- AdoptNode() is not used because internal state is not reset
1734
- (e.g. the past names map of a HTMLFormElement), this is safe
1735
- in theory but we would rather not risk another attack vector.
1736
- The state that is cloned by importNode() is explicitly defined
1737
- by the specs.
1738
- */
1739
- returnNode = importNode.call(originalDocument, returnNode, true);
1946
+ if (hookEvent.forceKeepAttr) {
1947
+ continue;
1740
1948
  }
1949
+ /* Remove attribute */
1741
1950
 
1742
- return returnNode;
1743
- }
1744
1951
 
1745
- let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1746
- /* Serialize doctype if allowed */
1952
+ _removeAttribute(name, currentNode);
1953
+ /* Did the hooks approve of the attribute? */
1747
1954
 
1748
- if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1749
- serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1750
- }
1751
- /* Sanitize final string template-safe */
1752
1955
 
1956
+ if (!hookEvent.keepAttr) {
1957
+ continue;
1958
+ }
1959
+ /* Work around a security issue in jQuery 3.0 */
1753
1960
 
1754
- if (SAFE_FOR_TEMPLATES) {
1755
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
1756
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
1757
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
1758
- }
1759
1961
 
1760
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1761
- };
1762
- /**
1763
- * Public method to set the configuration once
1764
- * setConfig
1765
- *
1766
- * @param {Object} cfg configuration object
1767
- */
1962
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1963
+ _removeAttribute(name, currentNode);
1964
+
1965
+ continue;
1966
+ }
1967
+ /* Sanitize attribute content to be template-safe */
1768
1968
 
1769
1969
 
1770
- DOMPurify.setConfig = function (cfg) {
1771
- _parseConfig(cfg);
1970
+ if (SAFE_FOR_TEMPLATES) {
1971
+ value = stringReplace(value, MUSTACHE_EXPR, ' ');
1972
+ value = stringReplace(value, ERB_EXPR, ' ');
1973
+ value = stringReplace(value, TMPLIT_EXPR, ' ');
1974
+ }
1975
+ /* Is `value` valid for this attribute? */
1772
1976
 
1773
- SET_CONFIG = true;
1774
- };
1775
- /**
1776
- * Public method to remove the configuration
1777
- * clearConfig
1778
- *
1779
- */
1780
1977
 
1978
+ const lcTag = transformCaseFunc(currentNode.nodeName);
1781
1979
 
1782
- DOMPurify.clearConfig = function () {
1783
- CONFIG = null;
1784
- SET_CONFIG = false;
1785
- };
1786
- /**
1787
- * Public method to check if an attribute value is valid.
1788
- * Uses last set config, if any. Otherwise, uses config defaults.
1789
- * isValidAttribute
1790
- *
1791
- * @param {string} tag Tag name of containing element.
1792
- * @param {string} attr Attribute name.
1793
- * @param {string} value Attribute value.
1794
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1795
- */
1980
+ if (!_isValidAttribute(lcTag, lcName, value)) {
1981
+ continue;
1982
+ }
1983
+ /* Full DOM Clobbering protection via namespace isolation,
1984
+ * Prefix id and name attributes with `user-content-`
1985
+ */
1796
1986
 
1797
1987
 
1798
- DOMPurify.isValidAttribute = function (tag, attr, value) {
1799
- /* Initialize shared config vars if necessary. */
1800
- if (!CONFIG) {
1801
- _parseConfig({});
1802
- }
1988
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1989
+ // Remove the attribute with this value
1990
+ _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1803
1991
 
1804
- const lcTag = transformCaseFunc(tag);
1805
- const lcName = transformCaseFunc(attr);
1806
- return _isValidAttribute(lcTag, lcName, value);
1807
- };
1808
- /**
1809
- * AddHook
1810
- * Public method to add DOMPurify hooks
1811
- *
1812
- * @param {String} entryPoint entry point for the hook to add
1813
- * @param {Function} hookFunction function to execute
1814
- */
1815
1992
 
1993
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
1994
+ }
1995
+ /* Handle attributes that require Trusted Types */
1816
1996
 
1817
- DOMPurify.addHook = function (entryPoint, hookFunction) {
1818
- if (typeof hookFunction !== 'function') {
1819
- return;
1820
- }
1821
1997
 
1822
- hooks[entryPoint] = hooks[entryPoint] || [];
1823
- arrayPush(hooks[entryPoint], hookFunction);
1824
- };
1825
- /**
1826
- * RemoveHook
1827
- * Public method to remove a DOMPurify hook at a given entryPoint
1828
- * (pops it from the stack of hooks if more are present)
1829
- *
1830
- * @param {String} entryPoint entry point for the hook to remove
1831
- * @return {Function} removed(popped) hook
1832
- */
1998
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1999
+ if (namespaceURI) ; else {
2000
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
2001
+ case 'TrustedHTML':
2002
+ {
2003
+ value = trustedTypesPolicy.createHTML(value);
2004
+ break;
2005
+ }
2006
+
2007
+ case 'TrustedScriptURL':
2008
+ {
2009
+ value = trustedTypesPolicy.createScriptURL(value);
2010
+ break;
2011
+ }
2012
+ }
2013
+ }
2014
+ }
2015
+ /* Handle invalid data-* attribute set by try-catching it */
2016
+
1833
2017
 
2018
+ try {
2019
+ if (namespaceURI) {
2020
+ currentNode.setAttributeNS(namespaceURI, name, value);
2021
+ } else {
2022
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
2023
+ currentNode.setAttribute(name, value);
2024
+ }
1834
2025
 
1835
- DOMPurify.removeHook = function (entryPoint) {
1836
- if (hooks[entryPoint]) {
1837
- return arrayPop(hooks[entryPoint]);
2026
+ arrayPop(DOMPurify.removed);
2027
+ } catch (_) {}
1838
2028
  }
1839
- };
1840
- /**
1841
- * RemoveHooks
1842
- * Public method to remove all DOMPurify hooks at a given entryPoint
1843
- *
1844
- * @param {String} entryPoint entry point for the hooks to remove
1845
- */
2029
+ /* Execute a hook if present */
1846
2030
 
1847
2031
 
1848
- DOMPurify.removeHooks = function (entryPoint) {
1849
- if (hooks[entryPoint]) {
1850
- hooks[entryPoint] = [];
1851
- }
2032
+ _executeHook('afterSanitizeAttributes', currentNode, null);
1852
2033
  };
1853
2034
  /**
1854
- * RemoveAllHooks
1855
- * Public method to remove all DOMPurify hooks
2035
+ * _sanitizeShadowDOM
1856
2036
  *
2037
+ * @param {DocumentFragment} fragment to iterate over recursively
1857
2038
  */
1858
2039
 
1859
2040
 
1860
- DOMPurify.removeAllHooks = function () {
1861
- hooks = {};
1862
- };
1863
-
1864
- return DOMPurify;
1865
- }
2041
+ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
2042
+ let shadowNode;
1866
2043
 
1867
- var purify = createDOMPurify();
2044
+ const shadowIterator = _createIterator(fragment);
2045
+ /* Execute a hook if present */
1868
2046
 
1869
- function _classCallCheck(instance, Constructor) {
1870
- if (!(instance instanceof Constructor)) {
1871
- throw new TypeError("Cannot call a class as a function");
1872
- }
1873
- }
1874
2047
 
1875
- function _typeof(obj) {
1876
- "@babel/helpers - typeof";
2048
+ _executeHook('beforeSanitizeShadowDOM', fragment, null);
1877
2049
 
1878
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
1879
- return typeof obj;
1880
- } : function (obj) {
1881
- return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
1882
- }, _typeof(obj);
1883
- }
2050
+ while (shadowNode = shadowIterator.nextNode()) {
2051
+ /* Execute a hook if present */
2052
+ _executeHook('uponSanitizeShadowNode', shadowNode, null);
2053
+ /* Sanitize tags and elements */
1884
2054
 
1885
- function _toPrimitive(input, hint) {
1886
- if (_typeof(input) !== "object" || input === null) return input;
1887
- var prim = input[Symbol.toPrimitive];
1888
- if (prim !== undefined) {
1889
- var res = prim.call(input, hint || "default");
1890
- if (_typeof(res) !== "object") return res;
1891
- throw new TypeError("@@toPrimitive must return a primitive value.");
1892
- }
1893
- return (hint === "string" ? String : Number)(input);
1894
- }
1895
2055
 
1896
- function _toPropertyKey(arg) {
1897
- var key = _toPrimitive(arg, "string");
1898
- return _typeof(key) === "symbol" ? key : String(key);
1899
- }
2056
+ if (_sanitizeElements(shadowNode)) {
2057
+ continue;
2058
+ }
2059
+ /* Deep shadow DOM detected */
1900
2060
 
1901
- function _defineProperties(target, props) {
1902
- for (var i = 0; i < props.length; i++) {
1903
- var descriptor = props[i];
1904
- descriptor.enumerable = descriptor.enumerable || false;
1905
- descriptor.configurable = true;
1906
- if ("value" in descriptor) descriptor.writable = true;
1907
- Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
1908
- }
1909
- }
1910
- function _createClass(Constructor, protoProps, staticProps) {
1911
- if (protoProps) _defineProperties(Constructor.prototype, protoProps);
1912
- if (staticProps) _defineProperties(Constructor, staticProps);
1913
- Object.defineProperty(Constructor, "prototype", {
1914
- writable: false
1915
- });
1916
- return Constructor;
1917
- }
1918
2061
 
1919
- var arr = [];
1920
- var each = arr.forEach;
1921
- var slice = arr.slice;
1922
- function defaults(obj) {
1923
- each.call(slice.call(arguments, 1), function (source) {
1924
- if (source) {
1925
- for (var prop in source) {
1926
- if (obj[prop] === undefined) obj[prop] = source[prop];
2062
+ if (shadowNode.content instanceof DocumentFragment) {
2063
+ _sanitizeShadowDOM(shadowNode.content);
1927
2064
  }
1928
- }
1929
- });
1930
- return obj;
1931
- }
2065
+ /* Check attributes, sanitize if necessary */
1932
2066
 
1933
- // eslint-disable-next-line no-control-regex
1934
- var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
1935
- var serializeCookie = function serializeCookie(name, val, options) {
1936
- var opt = options || {};
1937
- opt.path = opt.path || '/';
1938
- var value = encodeURIComponent(val);
1939
- var str = "".concat(name, "=").concat(value);
1940
- if (opt.maxAge > 0) {
1941
- var maxAge = opt.maxAge - 0;
1942
- if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
1943
- str += "; Max-Age=".concat(Math.floor(maxAge));
1944
- }
1945
- if (opt.domain) {
1946
- if (!fieldContentRegExp.test(opt.domain)) {
1947
- throw new TypeError('option domain is invalid');
1948
- }
1949
- str += "; Domain=".concat(opt.domain);
1950
- }
1951
- if (opt.path) {
1952
- if (!fieldContentRegExp.test(opt.path)) {
1953
- throw new TypeError('option path is invalid');
1954
- }
1955
- str += "; Path=".concat(opt.path);
1956
- }
1957
- if (opt.expires) {
1958
- if (typeof opt.expires.toUTCString !== 'function') {
1959
- throw new TypeError('option expires is invalid');
1960
- }
1961
- str += "; Expires=".concat(opt.expires.toUTCString());
1962
- }
1963
- if (opt.httpOnly) str += '; HttpOnly';
1964
- if (opt.secure) str += '; Secure';
1965
- if (opt.sameSite) {
1966
- var sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
1967
- switch (sameSite) {
1968
- case true:
1969
- str += '; SameSite=Strict';
1970
- break;
1971
- case 'lax':
1972
- str += '; SameSite=Lax';
1973
- break;
1974
- case 'strict':
1975
- str += '; SameSite=Strict';
1976
- break;
1977
- case 'none':
1978
- str += '; SameSite=None';
1979
- break;
1980
- default:
1981
- throw new TypeError('option sameSite is invalid');
1982
- }
1983
- }
1984
- return str;
1985
- };
1986
- var cookie = {
1987
- create: function create(name, value, minutes, domain) {
1988
- var cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
1989
- path: '/',
1990
- sameSite: 'strict'
1991
- };
1992
- if (minutes) {
1993
- cookieOptions.expires = new Date();
1994
- cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
1995
- }
1996
- if (domain) cookieOptions.domain = domain;
1997
- document.cookie = serializeCookie(name, encodeURIComponent(value), cookieOptions);
1998
- },
1999
- read: function read(name) {
2000
- var nameEQ = "".concat(name, "=");
2001
- var ca = document.cookie.split(';');
2002
- for (var i = 0; i < ca.length; i++) {
2003
- var c = ca[i];
2004
- while (c.charAt(0) === ' ') {
2005
- c = c.substring(1, c.length);
2006
- }
2007
- if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
2008
- }
2009
- return null;
2010
- },
2011
- remove: function remove(name) {
2012
- this.create(name, '', -1);
2013
- }
2014
- };
2015
- var cookie$1 = {
2016
- name: 'cookie',
2017
- lookup: function lookup(options) {
2018
- var found;
2019
- if (options.lookupCookie && typeof document !== 'undefined') {
2020
- var c = cookie.read(options.lookupCookie);
2021
- if (c) found = c;
2067
+
2068
+ _sanitizeAttributes(shadowNode);
2022
2069
  }
2023
- return found;
2024
- },
2025
- cacheUserLanguage: function cacheUserLanguage(lng, options) {
2026
- if (options.lookupCookie && typeof document !== 'undefined') {
2027
- cookie.create(options.lookupCookie, lng, options.cookieMinutes, options.cookieDomain, options.cookieOptions);
2070
+ /* Execute a hook if present */
2071
+
2072
+
2073
+ _executeHook('afterSanitizeShadowDOM', fragment, null);
2074
+ };
2075
+ /**
2076
+ * Sanitize
2077
+ * Public method providing core sanitation functionality
2078
+ *
2079
+ * @param {String|Node} dirty string or DOM node
2080
+ * @param {Object} configuration object
2081
+ */
2082
+ // eslint-disable-next-line complexity
2083
+
2084
+
2085
+ DOMPurify.sanitize = function (dirty) {
2086
+ let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2087
+ let body;
2088
+ let importedNode;
2089
+ let currentNode;
2090
+ let returnNode;
2091
+ /* Make sure we have a string to sanitize.
2092
+ DO NOT return early, as this will return the wrong type if
2093
+ the user has requested a DOM object rather than a string */
2094
+
2095
+ IS_EMPTY_INPUT = !dirty;
2096
+
2097
+ if (IS_EMPTY_INPUT) {
2098
+ dirty = '<!-->';
2028
2099
  }
2029
- }
2030
- };
2100
+ /* Stringify, in case dirty is an object */
2031
2101
 
2032
- var querystring = {
2033
- name: 'querystring',
2034
- lookup: function lookup(options) {
2035
- var found;
2036
- if (typeof window !== 'undefined') {
2037
- var search = window.location.search;
2038
- if (!window.location.search && window.location.hash && window.location.hash.indexOf('?') > -1) {
2039
- search = window.location.hash.substring(window.location.hash.indexOf('?'));
2040
- }
2041
- var query = search.substring(1);
2042
- var params = query.split('&');
2043
- for (var i = 0; i < params.length; i++) {
2044
- var pos = params[i].indexOf('=');
2045
- if (pos > 0) {
2046
- var key = params[i].substring(0, pos);
2047
- if (key === options.lookupQuerystring) {
2048
- found = params[i].substring(pos + 1);
2049
- }
2102
+
2103
+ if (typeof dirty !== 'string' && !_isNode(dirty)) {
2104
+ if (typeof dirty.toString === 'function') {
2105
+ dirty = dirty.toString();
2106
+
2107
+ if (typeof dirty !== 'string') {
2108
+ throw typeErrorCreate('dirty is not a string, aborting');
2050
2109
  }
2110
+ } else {
2111
+ throw typeErrorCreate('toString is not a function');
2051
2112
  }
2052
2113
  }
2053
- return found;
2054
- }
2055
- };
2114
+ /* Return dirty HTML if DOMPurify cannot run */
2056
2115
 
2057
- var hasLocalStorageSupport = null;
2058
- var localStorageAvailable = function localStorageAvailable() {
2059
- if (hasLocalStorageSupport !== null) return hasLocalStorageSupport;
2060
- try {
2061
- hasLocalStorageSupport = window !== 'undefined' && window.localStorage !== null;
2062
- var testKey = 'i18next.translate.boo';
2063
- window.localStorage.setItem(testKey, 'foo');
2064
- window.localStorage.removeItem(testKey);
2065
- } catch (e) {
2066
- hasLocalStorageSupport = false;
2067
- }
2068
- return hasLocalStorageSupport;
2069
- };
2070
- var localStorage = {
2071
- name: 'localStorage',
2072
- lookup: function lookup(options) {
2073
- var found;
2074
- if (options.lookupLocalStorage && localStorageAvailable()) {
2075
- var lng = window.localStorage.getItem(options.lookupLocalStorage);
2076
- if (lng) found = lng;
2077
- }
2078
- return found;
2079
- },
2080
- cacheUserLanguage: function cacheUserLanguage(lng, options) {
2081
- if (options.lookupLocalStorage && localStorageAvailable()) {
2082
- window.localStorage.setItem(options.lookupLocalStorage, lng);
2116
+
2117
+ if (!DOMPurify.isSupported) {
2118
+ return dirty;
2083
2119
  }
2084
- }
2085
- };
2120
+ /* Assign config vars */
2086
2121
 
2087
- var hasSessionStorageSupport = null;
2088
- var sessionStorageAvailable = function sessionStorageAvailable() {
2089
- if (hasSessionStorageSupport !== null) return hasSessionStorageSupport;
2090
- try {
2091
- hasSessionStorageSupport = window !== 'undefined' && window.sessionStorage !== null;
2092
- var testKey = 'i18next.translate.boo';
2093
- window.sessionStorage.setItem(testKey, 'foo');
2094
- window.sessionStorage.removeItem(testKey);
2095
- } catch (e) {
2096
- hasSessionStorageSupport = false;
2097
- }
2098
- return hasSessionStorageSupport;
2099
- };
2100
- var sessionStorage = {
2101
- name: 'sessionStorage',
2102
- lookup: function lookup(options) {
2103
- var found;
2104
- if (options.lookupSessionStorage && sessionStorageAvailable()) {
2105
- var lng = window.sessionStorage.getItem(options.lookupSessionStorage);
2106
- if (lng) found = lng;
2122
+
2123
+ if (!SET_CONFIG) {
2124
+ _parseConfig(cfg);
2107
2125
  }
2108
- return found;
2109
- },
2110
- cacheUserLanguage: function cacheUserLanguage(lng, options) {
2111
- if (options.lookupSessionStorage && sessionStorageAvailable()) {
2112
- window.sessionStorage.setItem(options.lookupSessionStorage, lng);
2126
+ /* Clean up removed elements */
2127
+
2128
+
2129
+ DOMPurify.removed = [];
2130
+ /* Check if dirty is correctly typed for IN_PLACE */
2131
+
2132
+ if (typeof dirty === 'string') {
2133
+ IN_PLACE = false;
2113
2134
  }
2114
- }
2115
- };
2116
2135
 
2117
- var navigator$1 = {
2118
- name: 'navigator',
2119
- lookup: function lookup(options) {
2120
- var found = [];
2121
- if (typeof navigator !== 'undefined') {
2122
- if (navigator.languages) {
2123
- // chrome only; not an array, so can't use .push.apply instead of iterating
2124
- for (var i = 0; i < navigator.languages.length; i++) {
2125
- found.push(navigator.languages[i]);
2136
+ if (IN_PLACE) {
2137
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
2138
+ if (dirty.nodeName) {
2139
+ const tagName = transformCaseFunc(dirty.nodeName);
2140
+
2141
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2142
+ throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
2126
2143
  }
2127
2144
  }
2128
- if (navigator.userLanguage) {
2129
- found.push(navigator.userLanguage);
2145
+ } else if (dirty instanceof Node) {
2146
+ /* If dirty is a DOM element, append to an empty document to avoid
2147
+ elements being stripped by the parser */
2148
+ body = _initDocument('<!---->');
2149
+ importedNode = body.ownerDocument.importNode(dirty, true);
2150
+
2151
+ if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
2152
+ /* Node is already a body, use as is */
2153
+ body = importedNode;
2154
+ } else if (importedNode.nodeName === 'HTML') {
2155
+ body = importedNode;
2156
+ } else {
2157
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
2158
+ body.appendChild(importedNode);
2130
2159
  }
2131
- if (navigator.language) {
2132
- found.push(navigator.language);
2160
+ } else {
2161
+ /* Exit directly if we have nothing to do */
2162
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
2163
+ dirty.indexOf('<') === -1) {
2164
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
2165
+ }
2166
+ /* Initialize the document to work on */
2167
+
2168
+
2169
+ body = _initDocument(dirty);
2170
+ /* Check we have a DOM node from the data */
2171
+
2172
+ if (!body) {
2173
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
2133
2174
  }
2134
2175
  }
2135
- return found.length > 0 ? found : undefined;
2136
- }
2137
- };
2176
+ /* Remove first element node (ours) if FORCE_BODY is set */
2138
2177
 
2139
- var htmlTag = {
2140
- name: 'htmlTag',
2141
- lookup: function lookup(options) {
2142
- var found;
2143
- var htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
2144
- if (htmlTag && typeof htmlTag.getAttribute === 'function') {
2145
- found = htmlTag.getAttribute('lang');
2178
+
2179
+ if (body && FORCE_BODY) {
2180
+ _forceRemove(body.firstChild);
2146
2181
  }
2147
- return found;
2148
- }
2149
- };
2182
+ /* Get node iterator */
2150
2183
 
2151
- var path = {
2152
- name: 'path',
2153
- lookup: function lookup(options) {
2154
- var found;
2155
- if (typeof window !== 'undefined') {
2156
- var language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
2157
- if (language instanceof Array) {
2158
- if (typeof options.lookupFromPathIndex === 'number') {
2159
- if (typeof language[options.lookupFromPathIndex] !== 'string') {
2160
- return undefined;
2161
- }
2162
- found = language[options.lookupFromPathIndex].replace('/', '');
2163
- } else {
2164
- found = language[0].replace('/', '');
2184
+
2185
+ const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
2186
+ /* Now start iterating over the created document */
2187
+
2188
+
2189
+ while (currentNode = nodeIterator.nextNode()) {
2190
+ /* Sanitize tags and elements */
2191
+ if (_sanitizeElements(currentNode)) {
2192
+ continue;
2193
+ }
2194
+ /* Shadow DOM detected, sanitize it */
2195
+
2196
+
2197
+ if (currentNode.content instanceof DocumentFragment) {
2198
+ _sanitizeShadowDOM(currentNode.content);
2199
+ }
2200
+ /* Check attributes, sanitize if necessary */
2201
+
2202
+
2203
+ _sanitizeAttributes(currentNode);
2204
+ }
2205
+ /* If we sanitized `dirty` in-place, return it. */
2206
+
2207
+
2208
+ if (IN_PLACE) {
2209
+ return dirty;
2210
+ }
2211
+ /* Return sanitized string or DOM */
2212
+
2213
+
2214
+ if (RETURN_DOM) {
2215
+ if (RETURN_DOM_FRAGMENT) {
2216
+ returnNode = createDocumentFragment.call(body.ownerDocument);
2217
+
2218
+ while (body.firstChild) {
2219
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
2220
+ returnNode.appendChild(body.firstChild);
2165
2221
  }
2222
+ } else {
2223
+ returnNode = body;
2224
+ }
2225
+
2226
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
2227
+ /*
2228
+ AdoptNode() is not used because internal state is not reset
2229
+ (e.g. the past names map of a HTMLFormElement), this is safe
2230
+ in theory but we would rather not risk another attack vector.
2231
+ The state that is cloned by importNode() is explicitly defined
2232
+ by the specs.
2233
+ */
2234
+ returnNode = importNode.call(originalDocument, returnNode, true);
2166
2235
  }
2236
+
2237
+ return returnNode;
2167
2238
  }
2168
- return found;
2169
- }
2170
- };
2171
2239
 
2172
- var subdomain = {
2173
- name: 'subdomain',
2174
- lookup: function lookup(options) {
2175
- // If given get the subdomain index else 1
2176
- var lookupFromSubdomainIndex = typeof options.lookupFromSubdomainIndex === 'number' ? options.lookupFromSubdomainIndex + 1 : 1;
2177
- // get all matches if window.location. is existing
2178
- // first item of match is the match itself and the second is the first group macht which sould be the first subdomain match
2179
- // is the hostname no public domain get the or option of localhost
2180
- var language = typeof window !== 'undefined' && window.location && window.location.hostname && window.location.hostname.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
2240
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
2241
+ /* Serialize doctype if allowed */
2242
+
2243
+ if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
2244
+ serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
2245
+ }
2246
+ /* Sanitize final string template-safe */
2247
+
2248
+
2249
+ if (SAFE_FOR_TEMPLATES) {
2250
+ serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
2251
+ serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
2252
+ serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
2253
+ }
2254
+
2255
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
2256
+ };
2257
+ /**
2258
+ * Public method to set the configuration once
2259
+ * setConfig
2260
+ *
2261
+ * @param {Object} cfg configuration object
2262
+ */
2263
+
2264
+
2265
+ DOMPurify.setConfig = function (cfg) {
2266
+ _parseConfig(cfg);
2267
+
2268
+ SET_CONFIG = true;
2269
+ };
2270
+ /**
2271
+ * Public method to remove the configuration
2272
+ * clearConfig
2273
+ *
2274
+ */
2181
2275
 
2182
- // if there is no match (null) return undefined
2183
- if (!language) return undefined;
2184
- // return the given group match
2185
- return language[lookupFromSubdomainIndex];
2186
- }
2187
- };
2188
2276
 
2189
- function getDefaults() {
2190
- return {
2191
- order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
2192
- lookupQuerystring: 'lng',
2193
- lookupCookie: 'i18next',
2194
- lookupLocalStorage: 'i18nextLng',
2195
- lookupSessionStorage: 'i18nextLng',
2196
- // cache user language
2197
- caches: ['localStorage'],
2198
- excludeCacheFor: ['cimode'],
2199
- // cookieMinutes: 10,
2200
- // cookieDomain: 'myDomain'
2277
+ DOMPurify.clearConfig = function () {
2278
+ CONFIG = null;
2279
+ SET_CONFIG = false;
2280
+ };
2281
+ /**
2282
+ * Public method to check if an attribute value is valid.
2283
+ * Uses last set config, if any. Otherwise, uses config defaults.
2284
+ * isValidAttribute
2285
+ *
2286
+ * @param {string} tag Tag name of containing element.
2287
+ * @param {string} attr Attribute name.
2288
+ * @param {string} value Attribute value.
2289
+ * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
2290
+ */
2201
2291
 
2202
- convertDetectedLanguage: function convertDetectedLanguage(l) {
2203
- return l;
2292
+
2293
+ DOMPurify.isValidAttribute = function (tag, attr, value) {
2294
+ /* Initialize shared config vars if necessary. */
2295
+ if (!CONFIG) {
2296
+ _parseConfig({});
2204
2297
  }
2298
+
2299
+ const lcTag = transformCaseFunc(tag);
2300
+ const lcName = transformCaseFunc(attr);
2301
+ return _isValidAttribute(lcTag, lcName, value);
2205
2302
  };
2206
- }
2207
- var Browser = /*#__PURE__*/function () {
2208
- function Browser(services) {
2209
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2210
- _classCallCheck(this, Browser);
2211
- this.type = 'languageDetector';
2212
- this.detectors = {};
2213
- this.init(services, options);
2214
- }
2215
- _createClass(Browser, [{
2216
- key: "init",
2217
- value: function init(services) {
2218
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2219
- var i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2220
- this.services = services || {
2221
- languageUtils: {}
2222
- }; // this way the language detector can be used without i18next
2223
- this.options = defaults(options, this.options || {}, getDefaults());
2224
- if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
2225
- this.options.convertDetectedLanguage = function (l) {
2226
- return l.replace('-', '_');
2227
- };
2228
- }
2303
+ /**
2304
+ * AddHook
2305
+ * Public method to add DOMPurify hooks
2306
+ *
2307
+ * @param {String} entryPoint entry point for the hook to add
2308
+ * @param {Function} hookFunction function to execute
2309
+ */
2229
2310
 
2230
- // backwards compatibility
2231
- if (this.options.lookupFromUrlIndex) this.options.lookupFromPathIndex = this.options.lookupFromUrlIndex;
2232
- this.i18nOptions = i18nOptions;
2233
- this.addDetector(cookie$1);
2234
- this.addDetector(querystring);
2235
- this.addDetector(localStorage);
2236
- this.addDetector(sessionStorage);
2237
- this.addDetector(navigator$1);
2238
- this.addDetector(htmlTag);
2239
- this.addDetector(path);
2240
- this.addDetector(subdomain);
2241
- }
2242
- }, {
2243
- key: "addDetector",
2244
- value: function addDetector(detector) {
2245
- this.detectors[detector.name] = detector;
2246
- }
2247
- }, {
2248
- key: "detect",
2249
- value: function detect(detectionOrder) {
2250
- var _this = this;
2251
- if (!detectionOrder) detectionOrder = this.options.order;
2252
- var detected = [];
2253
- detectionOrder.forEach(function (detectorName) {
2254
- if (_this.detectors[detectorName]) {
2255
- var lookup = _this.detectors[detectorName].lookup(_this.options);
2256
- if (lookup && typeof lookup === 'string') lookup = [lookup];
2257
- if (lookup) detected = detected.concat(lookup);
2258
- }
2259
- });
2260
- detected = detected.map(function (d) {
2261
- return _this.options.convertDetectedLanguage(d);
2262
- });
2263
- if (this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
2264
- return detected.length > 0 ? detected[0] : null; // a little backward compatibility
2311
+
2312
+ DOMPurify.addHook = function (entryPoint, hookFunction) {
2313
+ if (typeof hookFunction !== 'function') {
2314
+ return;
2265
2315
  }
2266
- }, {
2267
- key: "cacheUserLanguage",
2268
- value: function cacheUserLanguage(lng, caches) {
2269
- var _this2 = this;
2270
- if (!caches) caches = this.options.caches;
2271
- if (!caches) return;
2272
- if (this.options.excludeCacheFor && this.options.excludeCacheFor.indexOf(lng) > -1) return;
2273
- caches.forEach(function (cacheName) {
2274
- if (_this2.detectors[cacheName]) _this2.detectors[cacheName].cacheUserLanguage(lng, _this2.options);
2275
- });
2316
+
2317
+ hooks[entryPoint] = hooks[entryPoint] || [];
2318
+ arrayPush(hooks[entryPoint], hookFunction);
2319
+ };
2320
+ /**
2321
+ * RemoveHook
2322
+ * Public method to remove a DOMPurify hook at a given entryPoint
2323
+ * (pops it from the stack of hooks if more are present)
2324
+ *
2325
+ * @param {String} entryPoint entry point for the hook to remove
2326
+ * @return {Function} removed(popped) hook
2327
+ */
2328
+
2329
+
2330
+ DOMPurify.removeHook = function (entryPoint) {
2331
+ if (hooks[entryPoint]) {
2332
+ return arrayPop(hooks[entryPoint]);
2276
2333
  }
2277
- }]);
2278
- return Browser;
2279
- }();
2280
- Browser.type = 'languageDetector';
2334
+ };
2335
+ /**
2336
+ * RemoveHooks
2337
+ * Public method to remove all DOMPurify hooks at a given entryPoint
2338
+ *
2339
+ * @param {String} entryPoint entry point for the hooks to remove
2340
+ */
2281
2341
 
2282
- function _arrayWithHoles(arr) {
2283
- if (Array.isArray(arr)) return arr;
2284
- }
2285
2342
 
2286
- function _iterableToArrayLimit(arr, i) {
2287
- var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
2288
- if (null != _i) {
2289
- var _s,
2290
- _e,
2291
- _x,
2292
- _r,
2293
- _arr = [],
2294
- _n = !0,
2295
- _d = !1;
2296
- try {
2297
- if (_x = (_i = _i.call(arr)).next, 0 === i) {
2298
- if (Object(_i) !== _i) return;
2299
- _n = !1;
2300
- } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);
2301
- } catch (err) {
2302
- _d = !0, _e = err;
2303
- } finally {
2304
- try {
2305
- if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return;
2306
- } finally {
2307
- if (_d) throw _e;
2308
- }
2343
+ DOMPurify.removeHooks = function (entryPoint) {
2344
+ if (hooks[entryPoint]) {
2345
+ hooks[entryPoint] = [];
2309
2346
  }
2310
- return _arr;
2311
- }
2312
- }
2347
+ };
2348
+ /**
2349
+ * RemoveAllHooks
2350
+ * Public method to remove all DOMPurify hooks
2351
+ *
2352
+ */
2313
2353
 
2314
- function _arrayLikeToArray(arr, len) {
2315
- if (len == null || len > arr.length) len = arr.length;
2316
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
2317
- return arr2;
2318
- }
2319
2354
 
2320
- function _unsupportedIterableToArray(o, minLen) {
2321
- if (!o) return;
2322
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
2323
- var n = Object.prototype.toString.call(o).slice(8, -1);
2324
- if (n === "Object" && o.constructor) n = o.constructor.name;
2325
- if (n === "Map" || n === "Set") return Array.from(o);
2326
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
2327
- }
2355
+ DOMPurify.removeAllHooks = function () {
2356
+ hooks = {};
2357
+ };
2328
2358
 
2329
- function _nonIterableRest() {
2330
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2359
+ return DOMPurify;
2331
2360
  }
2332
2361
 
2333
- function _slicedToArray(arr, i) {
2334
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
2335
- }
2362
+ var purify = createDOMPurify();
2336
2363
 
2337
- var getter = function getter(key) {
2338
- return function () {
2339
- return i18n.t("taxonomyDefaultLabels.".concat(key));
2340
- };
2341
- };
2342
- var replaceNullValuesWithGetter = function replaceNullValuesWithGetter(inputObject) {
2343
- var parentKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
2344
- var result = {};
2345
- for (var _i = 0, _Object$entries = Object.entries(inputObject); _i < _Object$entries.length; _i++) {
2346
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
2347
- key = _Object$entries$_i[0],
2348
- value = _Object$entries$_i[1];
2349
- var transKey = parentKey ? "".concat(parentKey, ".").concat(key) : key;
2350
- if (value === null) {
2351
- Object.defineProperty(result, key, {
2352
- get: getter(transKey)
2353
- });
2354
- } else if (_typeof$1(value) === "object") {
2355
- result[key] = replaceNullValuesWithGetter(value, transKey);
2356
- } else {
2357
- result[key] = value;
2364
+ var sanitizer = function sanitizer(array) {
2365
+ return purify.sanitize(array, {
2366
+ USE_PROFILES: {
2367
+ html: true
2358
2368
  }
2359
- }
2360
- return result;
2361
- };
2362
-
2363
- /*
2364
- For some reason dynamic values available via options param doesn't have formatter 'anyCase' applied.
2365
- So here we are just re applying.
2366
- */
2367
- var wrapAnyCaseIfApplicable = function wrapAnyCaseIfApplicable(value, dynamic) {
2368
- return Object.keys(dynamic).map(function (item) {
2369
- var format = anyCaseFormatter(dynamic[item]);
2370
- if (value.includes(format)) return format;
2371
- return dynamic[item];
2372
2369
  });
2373
2370
  };
2374
- var isAnyCaseStr = function isAnyCaseStr(str) {
2375
- return str.startsWith(ANY_CASE_STR) && str.endsWith(ANY_CASE_STR);
2371
+ var cacheStore = new Map();
2372
+ var fetchCachedOrInvoke = function fetchCachedOrInvoke(func, lng, options) {
2373
+ var _cacheStore$get;
2374
+ var cache = (_cacheStore$get = cacheStore.get(lng)) === null || _cacheStore$get === void 0 ? void 0 : _cacheStore$get.get(options);
2375
+ if (cache) return cache;
2376
+ cache = func(lng, options);
2377
+ var lngCache = cacheStore.get(lng);
2378
+ if (lngCache) lngCache.set(options, cache);else cacheStore.set(lng, new Map([[options, cache]]));
2379
+ return cache;
2376
2380
  };
2377
- var lowerCaseDynamicText = function lowerCaseDynamicText(value, dynamic) {
2378
- /*
2379
- Example:
2380
- translation: {
2381
- create: "Create {{entity1}}, {{entity2, anyCase}} and {{entity3}} and apply COLOR"
2382
- }
2383
- // ...
2384
- t("create", {entity1: "Car", entity2: 'BuS', enity3: "Truck"});
2385
- */
2386
-
2387
- // example: ['Car', '__ANY_CASE__BuS__ANY_CASE__', 'Truck']
2388
- var dynamicArr = wrapAnyCaseIfApplicable(value, dynamic);
2389
- //example pattern: (Car)|(__ANY_CASE__BuS__ANY_CASE__)|(Bus)
2390
- var regexPattern = dynamicArr.map(function (delimiter) {
2391
- return "(".concat(delimiter, ")");
2392
- }).join("|");
2393
- /* Generated array
2394
- [
2395
- 'Create ', // static
2396
- 'Car', // dynamic
2397
- ', ', // static
2398
- '__ANY_CASE__BuS__ANY_CASE__', // dynamic
2399
- ' and ', // static
2400
- 'Truck', // dynamic
2401
- ' and apply COLOR' // static
2402
- ]
2403
- */
2404
- var splitArr = value.split(new RegExp(regexPattern)).filter(Boolean);
2405
- return splitArr.map(function (item, index) {
2406
- if (dynamicArr.includes(item)) {
2407
- if (isAnyCaseStr(item)) {
2408
- return item.replaceAll(ANY_CASE_STR, "");
2409
- } else if (index === 0) {
2410
- // Don't lower case if it is first word.
2411
- return item;
2412
- }
2413
- return item.toLocaleLowerCase();
2414
- }
2415
- return item;
2416
- }).join("");
2381
+ var lowerCaseDynamicTextFormatter = function lowerCaseDynamicTextFormatter(value, format) {
2382
+ if (!value || format === ANY_CASE || typeof value !== "string") return value;
2383
+ return LOWERCASED + value.toLocaleLowerCase();
2384
+ };
2385
+ var boldListFormatter = function boldListFormatter(array, lng, options) {
2386
+ var formatter = fetchCachedOrInvoke(function (lng, options) {
2387
+ return new Intl.ListFormat(lng, options);
2388
+ }, lng, options);
2389
+ var boldItems = array.map(function (item) {
2390
+ return "<strong>".concat(item, "</strong>");
2391
+ });
2392
+ var sanitizedItems = sanitizer(boldItems).split(",");
2393
+ return formatter.format(sanitizedItems);
2394
+ };
2395
+
2396
+ var sentenceCase = function sentenceCase(value) {
2397
+ return value.charAt(0).toLocaleUpperCase() + value.slice(1);
2417
2398
  };
2418
2399
  var sentenceCaseProcessor = {
2419
2400
  type: "postProcessor",
2420
2401
  name: "sentenceCaseProcessor",
2421
- process: function process(value, _, options) {
2422
- var dynamic = omit(["lng", "lngs", "ns"], options); // removes default args
2423
- if (isEmpty(dynamic)) return value; // Do nothing if no dynamic values
2424
-
2425
- return lowerCaseDynamicText(value, dynamic);
2402
+ process: function process(value) {
2403
+ var shouldSentenceCase = value.startsWith(LOWERCASED);
2404
+ value = value.replaceAll(LOWERCASED, "");
2405
+ return shouldSentenceCase ? sentenceCase(value) : value;
2426
2406
  }
2427
2407
  };
2428
- var anyCaseFormatter = function anyCaseFormatter(value) {
2429
- if (value) return ANY_CASE_STR + value + ANY_CASE_STR;
2430
- return value;
2431
- };
2432
2408
 
2433
2409
  var generic = {
2434
2410
  error: "Something went wrong. Please try again later."
@@ -2548,7 +2524,7 @@ var commonsEn = {
2548
2524
  // eslint-disable-next-line import/no-mutable-exports
2549
2525
  var taxonomies = {};
2550
2526
  var initializeI18n = function initializeI18n(hostTranslations) {
2551
- var _window$globalProps, _i18n$services$format, _i18n$services$format2;
2527
+ var _window$globalProps;
2552
2528
  var packageTranslations = preval.require("./configs/scripts/getPkgTranslations.js");
2553
2529
  var commonsTranslations = {
2554
2530
  en: {
@@ -2573,9 +2549,17 @@ var initializeI18n = function initializeI18n(hostTranslations) {
2573
2549
  taxonomies: taxonomies
2574
2550
  },
2575
2551
  escapeValue: false,
2576
- skipOnVariables: false
2552
+ skipOnVariables: false,
2553
+ alwaysFormat: true,
2554
+ format: function format(value, _format, lng, options) {
2555
+ var newValue = value;
2556
+ if (_format === "boldList") {
2557
+ newValue = boldListFormatter(newValue, lng, options);
2558
+ }
2559
+ return lowerCaseDynamicTextFormatter(newValue, _format);
2560
+ }
2577
2561
  },
2578
- postProcess: ["sentenceCaseProcessor"],
2562
+ postProcess: [sentenceCaseProcessor.name],
2579
2563
  detection: {
2580
2564
  order: ["querystring", "cookie", "navigator", "path"],
2581
2565
  caches: ["cookie"],
@@ -2583,24 +2567,6 @@ var initializeI18n = function initializeI18n(hostTranslations) {
2583
2567
  lookupCookie: "lang"
2584
2568
  }
2585
2569
  });
2586
- (_i18n$services$format = i18n.services.formatter) === null || _i18n$services$format === void 0 ? void 0 : _i18n$services$format.addCached("boldList", function (language, options) {
2587
- var formatter = new Intl.ListFormat(language, options);
2588
- var sanitizer = function sanitizer(array) {
2589
- return purify.sanitize(array, {
2590
- USE_PROFILES: {
2591
- html: true
2592
- }
2593
- });
2594
- };
2595
- return function (array) {
2596
- var boldItems = array.map(function (item) {
2597
- return "<strong>".concat(item, "</strong>");
2598
- });
2599
- var sanitizedItems = sanitizer(boldItems).split(",");
2600
- return formatter.format(sanitizedItems);
2601
- };
2602
- });
2603
- (_i18n$services$format2 = i18n.services.formatter) === null || _i18n$services$format2 === void 0 ? void 0 : _i18n$services$format2.add("anyCase", anyCaseFormatter);
2604
2570
  };
2605
2571
 
2606
2572
  function initializeLogger() {