@bigbinary/neeto-commons-frontend 2.1.34 → 2.1.36

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