@bigbinary/neeto-commons-frontend 2.1.35 → 2.1.36

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