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