igv 3.8.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/igv.esm.js CHANGED
@@ -10606,24 +10606,65 @@ function didSelectSingleTrackType(types) {
10606
10606
  return 1 === unique.length
10607
10607
  }
10608
10608
 
10609
- /*! @license DOMPurify 3.3.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.3.3/LICENSE */
10609
+ /*! @license DOMPurify 3.4.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.7/LICENSE */
10610
10610
 
10611
- const {
10612
- entries,
10613
- setPrototypeOf,
10614
- isFrozen,
10615
- getPrototypeOf,
10616
- getOwnPropertyDescriptor
10617
- } = Object;
10618
- let {
10619
- freeze,
10620
- seal,
10621
- create
10622
- } = Object; // eslint-disable-line import/no-mutable-exports
10623
- let {
10624
- apply,
10625
- construct
10626
- } = typeof Reflect !== 'undefined' && Reflect;
10611
+ function _arrayLikeToArray(r, a) {
10612
+ (null == a || a > r.length) && (a = r.length);
10613
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10614
+ return n;
10615
+ }
10616
+ function _arrayWithHoles(r) {
10617
+ if (Array.isArray(r)) return r;
10618
+ }
10619
+ function _iterableToArrayLimit(r, l) {
10620
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
10621
+ if (null != t) {
10622
+ var e,
10623
+ n,
10624
+ i,
10625
+ u,
10626
+ a = [],
10627
+ f = true,
10628
+ o = false;
10629
+ try {
10630
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
10631
+ } catch (r) {
10632
+ o = true, n = r;
10633
+ } finally {
10634
+ try {
10635
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
10636
+ } finally {
10637
+ if (o) throw n;
10638
+ }
10639
+ }
10640
+ return a;
10641
+ }
10642
+ }
10643
+ function _nonIterableRest() {
10644
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
10645
+ }
10646
+ function _slicedToArray(r, e) {
10647
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
10648
+ }
10649
+ function _unsupportedIterableToArray(r, a) {
10650
+ if (r) {
10651
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
10652
+ var t = {}.toString.call(r).slice(8, -1);
10653
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
10654
+ }
10655
+ }
10656
+
10657
+ const entries = Object.entries,
10658
+ setPrototypeOf = Object.setPrototypeOf,
10659
+ isFrozen = Object.isFrozen,
10660
+ getPrototypeOf = Object.getPrototypeOf,
10661
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
10662
+ let freeze = Object.freeze,
10663
+ seal = Object.seal,
10664
+ create = Object.create; // eslint-disable-line import/no-mutable-exports
10665
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
10666
+ apply = _ref.apply,
10667
+ construct = _ref.construct;
10627
10668
  if (!freeze) {
10628
10669
  freeze = function freeze(x) {
10629
10670
  return x;
@@ -10655,13 +10696,19 @@ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
10655
10696
  const arrayPop = unapply(Array.prototype.pop);
10656
10697
  const arrayPush = unapply(Array.prototype.push);
10657
10698
  const arraySplice = unapply(Array.prototype.splice);
10699
+ const arrayIsArray = Array.isArray;
10658
10700
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
10659
10701
  const stringToString = unapply(String.prototype.toString);
10660
10702
  const stringMatch = unapply(String.prototype.match);
10661
10703
  const stringReplace = unapply(String.prototype.replace);
10662
10704
  const stringIndexOf = unapply(String.prototype.indexOf);
10663
10705
  const stringTrim = unapply(String.prototype.trim);
10706
+ const numberToString = unapply(Number.prototype.toString);
10707
+ const booleanToString = unapply(Boolean.prototype.toString);
10708
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
10709
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
10664
10710
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
10711
+ const objectToString = unapply(Object.prototype.toString);
10665
10712
  const regExpTest = unapply(RegExp.prototype.test);
10666
10713
  const typeErrorCreate = unconstruct(TypeError);
10667
10714
  /**
@@ -10711,6 +10758,9 @@ function addToSet(set, array) {
10711
10758
  // Prevent prototype setters from intercepting set as a this value.
10712
10759
  setPrototypeOf(set, null);
10713
10760
  }
10761
+ if (!arrayIsArray(array)) {
10762
+ return set;
10763
+ }
10714
10764
  let l = array.length;
10715
10765
  while (l--) {
10716
10766
  let element = array[l];
@@ -10751,10 +10801,13 @@ function cleanArray(array) {
10751
10801
  */
10752
10802
  function clone(object) {
10753
10803
  const newObject = create(null);
10754
- for (const [property, value] of entries(object)) {
10804
+ for (const _ref2 of entries(object)) {
10805
+ var _ref3 = _slicedToArray(_ref2, 2);
10806
+ const property = _ref3[0];
10807
+ const value = _ref3[1];
10755
10808
  const isPropertyExist = objectHasOwnProperty(object, property);
10756
10809
  if (isPropertyExist) {
10757
- if (Array.isArray(value)) {
10810
+ if (arrayIsArray(value)) {
10758
10811
  newObject[property] = cleanArray(value);
10759
10812
  } else if (value && typeof value === 'object' && value.constructor === Object) {
10760
10813
  newObject[property] = clone(value);
@@ -10765,6 +10818,58 @@ function clone(object) {
10765
10818
  }
10766
10819
  return newObject;
10767
10820
  }
10821
+ /**
10822
+ * Convert non-node values into strings without depending on direct property access.
10823
+ *
10824
+ * @param value - The value to stringify.
10825
+ * @returns A string representation of the provided value.
10826
+ */
10827
+ function stringifyValue(value) {
10828
+ switch (typeof value) {
10829
+ case 'string':
10830
+ {
10831
+ return value;
10832
+ }
10833
+ case 'number':
10834
+ {
10835
+ return numberToString(value);
10836
+ }
10837
+ case 'boolean':
10838
+ {
10839
+ return booleanToString(value);
10840
+ }
10841
+ case 'bigint':
10842
+ {
10843
+ return bigintToString ? bigintToString(value) : '0';
10844
+ }
10845
+ case 'symbol':
10846
+ {
10847
+ return symbolToString ? symbolToString(value) : 'Symbol()';
10848
+ }
10849
+ case 'undefined':
10850
+ {
10851
+ return objectToString(value);
10852
+ }
10853
+ case 'function':
10854
+ case 'object':
10855
+ {
10856
+ if (value === null) {
10857
+ return objectToString(value);
10858
+ }
10859
+ const valueAsRecord = value;
10860
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
10861
+ if (typeof valueToString === 'function') {
10862
+ const stringified = valueToString(valueAsRecord);
10863
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
10864
+ }
10865
+ return objectToString(value);
10866
+ }
10867
+ default:
10868
+ {
10869
+ return objectToString(value);
10870
+ }
10871
+ }
10872
+ }
10768
10873
  /**
10769
10874
  * This method automatically checks if the prop is function or getter and behaves accordingly.
10770
10875
  *
@@ -10790,6 +10895,14 @@ function lookupGetter(object, prop) {
10790
10895
  }
10791
10896
  return fallbackValue;
10792
10897
  }
10898
+ function isRegex(value) {
10899
+ try {
10900
+ regExpTest(value, '');
10901
+ return true;
10902
+ } catch (_unused) {
10903
+ return false;
10904
+ }
10905
+ }
10793
10906
 
10794
10907
  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', 'search', 'section', 'select', 'shadow', 'slot', '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']);
10795
10908
  const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
@@ -10805,15 +10918,14 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
10805
10918
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
10806
10919
  const text = freeze(['#text']);
10807
10920
 
10808
- 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', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', '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', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
10921
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', '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', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
10809
10922
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', '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', 'exponent', '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', 'intercept', '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', 'mask-type', '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', 'slope', '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', 'tablevalues', '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']);
10810
- 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']);
10923
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', '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']);
10811
10924
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
10812
10925
 
10813
- // eslint-disable-next-line unicorn/better-regex
10814
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
10815
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
10816
- const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
10926
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
10927
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
10928
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
10817
10929
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
10818
10930
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
10819
10931
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
@@ -10824,20 +10936,6 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
10824
10936
  const DOCTYPE_NAME = seal(/^html$/i);
10825
10937
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
10826
10938
 
10827
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
10828
- __proto__: null,
10829
- ARIA_ATTR: ARIA_ATTR,
10830
- ATTR_WHITESPACE: ATTR_WHITESPACE,
10831
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
10832
- DATA_ATTR: DATA_ATTR,
10833
- DOCTYPE_NAME: DOCTYPE_NAME,
10834
- ERB_EXPR: ERB_EXPR,
10835
- IS_ALLOWED_URI: IS_ALLOWED_URI,
10836
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
10837
- MUSTACHE_EXPR: MUSTACHE_EXPR,
10838
- TMPLIT_EXPR: TMPLIT_EXPR
10839
- });
10840
-
10841
10939
  /* eslint-disable @typescript-eslint/indent */
10842
10940
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
10843
10941
  const NODE_TYPE = {
@@ -10913,7 +11011,7 @@ const _createHooksMap = function _createHooksMap() {
10913
11011
  function createDOMPurify() {
10914
11012
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
10915
11013
  const DOMPurify = root => createDOMPurify(root);
10916
- DOMPurify.version = '3.3.3';
11014
+ DOMPurify.version = '3.4.7';
10917
11015
  DOMPurify.removed = [];
10918
11016
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
10919
11017
  // Not running in a browser, provide a factory function
@@ -10921,28 +11019,29 @@ function createDOMPurify() {
10921
11019
  DOMPurify.isSupported = false;
10922
11020
  return DOMPurify;
10923
11021
  }
10924
- let {
10925
- document
10926
- } = window;
11022
+ let document = window.document;
10927
11023
  const originalDocument = document;
10928
11024
  const currentScript = originalDocument.currentScript;
10929
- const {
10930
- DocumentFragment,
10931
- HTMLTemplateElement,
10932
- Node,
10933
- Element,
10934
- NodeFilter,
10935
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
10936
- HTMLFormElement,
10937
- DOMParser,
10938
- trustedTypes
10939
- } = window;
11025
+ window.DocumentFragment;
11026
+ const HTMLTemplateElement = window.HTMLTemplateElement,
11027
+ Node = window.Node,
11028
+ Element = window.Element,
11029
+ NodeFilter = window.NodeFilter,
11030
+ _window$NamedNodeMap = window.NamedNodeMap;
11031
+ _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
11032
+ window.HTMLFormElement;
11033
+ const DOMParser = window.DOMParser,
11034
+ trustedTypes = window.trustedTypes;
10940
11035
  const ElementPrototype = Element.prototype;
10941
11036
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
10942
11037
  const remove = lookupGetter(ElementPrototype, 'remove');
10943
11038
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
10944
11039
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
10945
11040
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
11041
+ const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
11042
+ const getAttributes = lookupGetter(ElementPrototype, 'attributes');
11043
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
11044
+ const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
10946
11045
  // As per issue #47, the web-components registry is inherited by a
10947
11046
  // new document created via createHTMLDocument. As per the spec
10948
11047
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -10957,33 +11056,26 @@ function createDOMPurify() {
10957
11056
  }
10958
11057
  let trustedTypesPolicy;
10959
11058
  let emptyHTML = '';
10960
- const {
10961
- implementation,
10962
- createNodeIterator,
10963
- createDocumentFragment,
10964
- getElementsByTagName
10965
- } = document;
10966
- const {
10967
- importNode
10968
- } = originalDocument;
11059
+ const _document = document,
11060
+ implementation = _document.implementation,
11061
+ createNodeIterator = _document.createNodeIterator,
11062
+ createDocumentFragment = _document.createDocumentFragment,
11063
+ getElementsByTagName = _document.getElementsByTagName;
11064
+ const importNode = originalDocument.importNode;
10969
11065
  let hooks = _createHooksMap();
10970
11066
  /**
10971
11067
  * Expose whether this browser supports running the full DOMPurify.
10972
11068
  */
10973
11069
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
10974
- const {
10975
- MUSTACHE_EXPR,
10976
- ERB_EXPR,
10977
- TMPLIT_EXPR,
10978
- DATA_ATTR,
10979
- ARIA_ATTR,
10980
- IS_SCRIPT_OR_DATA,
10981
- ATTR_WHITESPACE,
10982
- CUSTOM_ELEMENT
10983
- } = EXPRESSIONS;
10984
- let {
10985
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
10986
- } = EXPRESSIONS;
11070
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
11071
+ ERB_EXPR$1 = ERB_EXPR,
11072
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
11073
+ DATA_ATTR$1 = DATA_ATTR,
11074
+ ARIA_ATTR$1 = ARIA_ATTR,
11075
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
11076
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
11077
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
11078
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
10987
11079
  /**
10988
11080
  * We consider the elements and attributes below to be safe. Ideally
10989
11081
  * don't add any new ones but feel free to remove unwanted ones.
@@ -11161,15 +11253,15 @@ function createDOMPurify() {
11161
11253
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
11162
11254
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
11163
11255
  /* Set configuration parameters */
11164
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
11165
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
11166
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
11167
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
11168
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
11169
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
11170
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
11171
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
11172
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
11256
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
11257
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
11258
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
11259
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
11260
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
11261
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
11262
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
11263
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
11264
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
11173
11265
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
11174
11266
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
11175
11267
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -11185,19 +11277,20 @@ function createDOMPurify() {
11185
11277
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
11186
11278
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
11187
11279
  IN_PLACE = cfg.IN_PLACE || false; // Default false
11188
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
11189
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
11190
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
11191
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
11192
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
11193
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
11194
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
11280
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
11281
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
11282
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
11283
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
11284
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
11285
+ CUSTOM_ELEMENT_HANDLING = create(null);
11286
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
11287
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
11195
11288
  }
11196
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
11197
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
11289
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
11290
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
11198
11291
  }
11199
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
11200
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
11292
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
11293
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
11201
11294
  }
11202
11295
  if (SAFE_FOR_TEMPLATES) {
11203
11296
  ALLOW_DATA_ATTR = false;
@@ -11229,44 +11322,41 @@ function createDOMPurify() {
11229
11322
  addToSet(ALLOWED_ATTR, xml);
11230
11323
  }
11231
11324
  }
11232
- /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
11233
- if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
11234
- EXTRA_ELEMENT_HANDLING.tagCheck = null;
11235
- }
11236
- if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
11237
- EXTRA_ELEMENT_HANDLING.attributeCheck = null;
11238
- }
11325
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
11326
+ * leaking across calls when switching from function to array config */
11327
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
11328
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
11239
11329
  /* Merge configuration parameters */
11240
- if (cfg.ADD_TAGS) {
11330
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
11241
11331
  if (typeof cfg.ADD_TAGS === 'function') {
11242
11332
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
11243
- } else {
11333
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
11244
11334
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11245
11335
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
11246
11336
  }
11247
11337
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
11248
11338
  }
11249
11339
  }
11250
- if (cfg.ADD_ATTR) {
11340
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
11251
11341
  if (typeof cfg.ADD_ATTR === 'function') {
11252
11342
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
11253
- } else {
11343
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
11254
11344
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11255
11345
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
11256
11346
  }
11257
11347
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
11258
11348
  }
11259
11349
  }
11260
- if (cfg.ADD_URI_SAFE_ATTR) {
11350
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
11261
11351
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
11262
11352
  }
11263
- if (cfg.FORBID_CONTENTS) {
11353
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
11264
11354
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
11265
11355
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
11266
11356
  }
11267
11357
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
11268
11358
  }
11269
- if (cfg.ADD_FORBID_CONTENTS) {
11359
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
11270
11360
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
11271
11361
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
11272
11362
  }
@@ -11306,6 +11396,21 @@ function createDOMPurify() {
11306
11396
  emptyHTML = trustedTypesPolicy.createHTML('');
11307
11397
  }
11308
11398
  }
11399
+ /*
11400
+ * Mirror the clone-before-mutate pattern already applied above for
11401
+ * cfg.ADD_TAGS / cfg.ADD_ATTR: if any uponSanitize* hook is
11402
+ * registered AND the set still points at the default constant,
11403
+ * clone it. The hook then mutates the clone (in-call widening
11404
+ * still works exactly as documented) and the next default-cfg
11405
+ * call rebinds to the untouched original via the reassignment at
11406
+ * the top of this function.
11407
+ */
11408
+ if ((hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) && ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
11409
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
11410
+ }
11411
+ if (hooks.uponSanitizeAttribute.length > 0 && ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
11412
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
11413
+ }
11309
11414
  // Prevent further manipulation of configuration.
11310
11415
  // Not available in IE8, Safari 5, etc.
11311
11416
  if (freeze) {
@@ -11505,23 +11610,132 @@ function createDOMPurify() {
11505
11610
  // eslint-disable-next-line no-bitwise
11506
11611
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
11507
11612
  };
11613
+ /**
11614
+ * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
11615
+ * character data of an element subtree. Used as the final safety net for
11616
+ * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
11617
+ * which only form after text-node normalization (e.g. fragments split across
11618
+ * stripped elements) cannot survive into a template-evaluating framework.
11619
+ *
11620
+ * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
11621
+ * in place rather than round-tripping through innerHTML. This preserves
11622
+ * descendant node references (important for IN_PLACE callers), avoids a
11623
+ * serialize/reparse cycle, and reads literal character data — which means
11624
+ * `<%...%>` in text content matches the ERB regex against its real bytes
11625
+ * instead of the HTML-entity-escaped form innerHTML would produce.
11626
+ *
11627
+ * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
11628
+ * attributes is performed during the per-node `_sanitizeAttributes` pass.
11629
+ *
11630
+ * @param node The root element whose character data should be scrubbed.
11631
+ */
11632
+ const _scrubTemplateExpressions = function _scrubTemplateExpressions(node) {
11633
+ node.normalize();
11634
+ const walker = createNodeIterator.call(node.ownerDocument || node, node,
11635
+ // eslint-disable-next-line no-bitwise
11636
+ NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
11637
+ let currentNode = walker.nextNode();
11638
+ while (currentNode) {
11639
+ let data = currentNode.data;
11640
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
11641
+ data = stringReplace(data, expr, ' ');
11642
+ });
11643
+ currentNode.data = data;
11644
+ currentNode = walker.nextNode();
11645
+ }
11646
+ };
11508
11647
  /**
11509
11648
  * _isClobbered
11510
11649
  *
11650
+ * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
11651
+ * interface with [LegacyOverrideBuiltIns]; a descendant element with a
11652
+ * `name` attribute matching a prototype property shadows that property
11653
+ * on direct reads. We use this check at the IN_PLACE entry-point and
11654
+ * during attribute sanitization to refuse clobbered forms.
11655
+ *
11511
11656
  * @param element element to check for clobbering attacks
11512
11657
  * @return true if clobbered, false if safe
11513
11658
  */
11514
11659
  const _isClobbered = function _isClobbered(element) {
11515
- return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
11660
+ // Realm-independent tag-name probe. If we can't determine the tag
11661
+ // name at all, we can't reason about clobbering — return false
11662
+ // (the caller's other defences still apply).
11663
+ const realTagName = getNodeName ? getNodeName(element) : null;
11664
+ if (typeof realTagName !== 'string') {
11665
+ return false;
11666
+ }
11667
+ if (transformCaseFunc(realTagName) !== 'form') {
11668
+ return false;
11669
+ }
11670
+ return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
11671
+ // Realm-safe NamedNodeMap detection: equality against the cached
11672
+ // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
11673
+ // makes the direct read diverge from the cached read; a clean form
11674
+ // (same-realm OR foreign-realm) has both reads pointing at the same
11675
+ // canonical NamedNodeMap.
11676
+ element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
11677
+ // NodeType clobbering probe. Cached Node.prototype.nodeType getter
11678
+ // returns the integer 1 for any Element regardless of realm; direct
11679
+ // read on a clobbered form (e.g. <input name="nodeType">) returns
11680
+ // the named child element. Cheap addition — nodeType is read from
11681
+ // an internal slot, no serialization cost — and removes a residual
11682
+ // clobbering surface used by several mXSS / PI / comment branches
11683
+ // in _sanitizeElements that compare currentNode.nodeType directly.
11684
+ element.nodeType !== getNodeType(element) ||
11685
+ // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
11686
+ // "childNodes" shadows the prototype getter. Direct reads of
11687
+ // form.childNodes from a clobbered form return the named child
11688
+ // instead of the real NodeList, so any walk that reads it directly
11689
+ // skips the form's real children. Compare the direct read to the
11690
+ // cached Node.prototype getter — when the form's named-property
11691
+ // getter intercepts the read, the two values differ and we flag
11692
+ // the form. This catches every clobbering child type (input,
11693
+ // select, etc.) regardless of whether the named child happens to
11694
+ // carry a numeric .length, which a typeof-based probe would miss
11695
+ // (e.g. HTMLSelectElement.length is a defined unsigned-long).
11696
+ element.childNodes !== getChildNodes(element);
11697
+ };
11698
+ /**
11699
+ * Checks whether the given value is a DocumentFragment from any realm.
11700
+ *
11701
+ * The realm-independent replacement reads `nodeType` through the cached
11702
+ * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
11703
+ * constant (11). nodeType is a numeric value resolved from the node's
11704
+ * internal slot, identical across realms for the same kind of node.
11705
+ *
11706
+ * @param value object to check
11707
+ * @return true if value is a DocumentFragment-shaped node from any realm
11708
+ */
11709
+ const _isDocumentFragment = function _isDocumentFragment(value) {
11710
+ if (!getNodeType || typeof value !== 'object' || value === null) {
11711
+ return false;
11712
+ }
11713
+ try {
11714
+ return getNodeType(value) === NODE_TYPE.documentFragment;
11715
+ } catch (_) {
11716
+ return false;
11717
+ }
11516
11718
  };
11517
11719
  /**
11518
- * Checks whether the given object is a DOM node.
11720
+ * Checks whether the given object is a DOM node, including nodes that
11721
+ * originate from a different window/realm (e.g. an iframe's
11722
+ * contentDocument). The previous `value instanceof Node` check was
11723
+ * realm-bound: nodes from a different window failed it, causing
11724
+ * sanitize() to silently stringify them and reset IN_PLACE to false,
11725
+ * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
11519
11726
  *
11520
11727
  * @param value object to check whether it's a DOM node
11521
- * @return true is object is a DOM node
11728
+ * @return true if value is a DOM node from any realm
11522
11729
  */
11523
11730
  const _isNode = function _isNode(value) {
11524
- return typeof Node === 'function' && value instanceof Node;
11731
+ if (!getNodeType || typeof value !== 'object' || value === null) {
11732
+ return false;
11733
+ }
11734
+ try {
11735
+ return typeof getNodeType(value) === 'number';
11736
+ } catch (_) {
11737
+ return false;
11738
+ }
11525
11739
  };
11526
11740
  function _executeHooks(hooks, currentNode, data) {
11527
11741
  arrayForEach(hooks, hook => {
@@ -11558,6 +11772,11 @@ function createDOMPurify() {
11558
11772
  _forceRemove(currentNode);
11559
11773
  return true;
11560
11774
  }
11775
+ /* Remove risky CSS construction leading to mXSS */
11776
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
11777
+ _forceRemove(currentNode);
11778
+ return true;
11779
+ }
11561
11780
  /* Remove any occurrence of processing instructions */
11562
11781
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
11563
11782
  _forceRemove(currentNode);
@@ -11569,7 +11788,7 @@ function createDOMPurify() {
11569
11788
  return true;
11570
11789
  }
11571
11790
  /* Remove element if anything forbids its presence */
11572
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
11791
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
11573
11792
  /* Check if we have a custom element to handle */
11574
11793
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
11575
11794
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -11579,15 +11798,21 @@ function createDOMPurify() {
11579
11798
  return false;
11580
11799
  }
11581
11800
  }
11582
- /* Keep content except for bad-listed elements */
11801
+ /* Keep content except for bad-listed elements.
11802
+ Use the cached prototype getters exclusively — the previous code
11803
+ had `|| currentNode.parentNode` / `|| currentNode.childNodes`
11804
+ fallbacks, but the cached getters always return the canonical
11805
+ value (or null for a real parent-less node), so the fallback
11806
+ path was dead in safe cases and a clobbering surface in unsafe
11807
+ ones. Falsy cached results stay falsy; the `if (childNodes &&
11808
+ parentNode)` check already gates correctly. */
11583
11809
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
11584
- const parentNode = getParentNode(currentNode) || currentNode.parentNode;
11585
- const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
11810
+ const parentNode = getParentNode(currentNode);
11811
+ const childNodes = getChildNodes(currentNode);
11586
11812
  if (childNodes && parentNode) {
11587
11813
  const childCount = childNodes.length;
11588
11814
  for (let i = childCount - 1; i >= 0; --i) {
11589
11815
  const childClone = cloneNode(childNodes[i], true);
11590
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
11591
11816
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
11592
11817
  }
11593
11818
  }
@@ -11595,8 +11820,14 @@ function createDOMPurify() {
11595
11820
  _forceRemove(currentNode);
11596
11821
  return true;
11597
11822
  }
11598
- /* Check whether element has a valid namespace */
11599
- if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
11823
+ /* Check whether element has a valid namespace.
11824
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
11825
+ nodeType getter rather than `instanceof Element`, which is realm-
11826
+ bound and short-circuits to false for any node minted in a different
11827
+ realm — letting a foreign-realm element with a forbidden namespace
11828
+ slip past the namespace check entirely. */
11829
+ const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
11830
+ if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
11600
11831
  _forceRemove(currentNode);
11601
11832
  return true;
11602
11833
  }
@@ -11609,7 +11840,7 @@ function createDOMPurify() {
11609
11840
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
11610
11841
  /* Get the element's text content */
11611
11842
  content = currentNode.textContent;
11612
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
11843
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
11613
11844
  content = stringReplace(content, expr, ' ');
11614
11845
  });
11615
11846
  if (currentNode.textContent !== content) {
@@ -11641,11 +11872,12 @@ function createDOMPurify() {
11641
11872
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
11642
11873
  return false;
11643
11874
  }
11875
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
11644
11876
  /* Allow valid data-* attributes: At least one character after "-"
11645
11877
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
11646
11878
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
11647
11879
  We don't need to check the value; it's always URI safe. */
11648
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
11880
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
11649
11881
  if (
11650
11882
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
11651
11883
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -11657,11 +11889,15 @@ function createDOMPurify() {
11657
11889
  return false;
11658
11890
  }
11659
11891
  /* Check value is safe. First, is attr inert? If so, is safe */
11660
- } 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) {
11892
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
11661
11893
  return false;
11662
11894
  } else ;
11663
11895
  return true;
11664
11896
  };
11897
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
11898
+ * never be treated as basic custom elements even when a permissive
11899
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
11900
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
11665
11901
  /**
11666
11902
  * _isBasicCustomElement
11667
11903
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -11671,7 +11907,7 @@ function createDOMPurify() {
11671
11907
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
11672
11908
  */
11673
11909
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
11674
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
11910
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
11675
11911
  };
11676
11912
  /**
11677
11913
  * _sanitizeAttributes
@@ -11686,9 +11922,7 @@ function createDOMPurify() {
11686
11922
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
11687
11923
  /* Execute a hook if present */
11688
11924
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
11689
- const {
11690
- attributes
11691
- } = currentNode;
11925
+ const attributes = currentNode.attributes;
11692
11926
  /* Check if we have attributes; if not we might have a text node */
11693
11927
  if (!attributes || _isClobbered(currentNode)) {
11694
11928
  return;
@@ -11704,11 +11938,9 @@ function createDOMPurify() {
11704
11938
  /* Go backwards over all attributes; safely remove bad ones */
11705
11939
  while (l--) {
11706
11940
  const attr = attributes[l];
11707
- const {
11708
- name,
11709
- namespaceURI,
11710
- value: attrValue
11711
- } = attr;
11941
+ const name = attr.name,
11942
+ namespaceURI = attr.namespaceURI,
11943
+ attrValue = attr.value;
11712
11944
  const lcName = transformCaseFunc(name);
11713
11945
  const initValue = attrValue;
11714
11946
  let value = name === 'value' ? initValue : stringTrim(initValue);
@@ -11722,12 +11954,14 @@ function createDOMPurify() {
11722
11954
  /* Full DOM Clobbering protection via namespace isolation,
11723
11955
  * Prefix id and name attributes with `user-content-`
11724
11956
  */
11725
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
11957
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
11726
11958
  // Remove the attribute with this value
11727
11959
  _removeAttribute(name, currentNode);
11728
11960
  // Prefix the value and later re-create the attribute with the sanitized value
11729
11961
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
11730
11962
  }
11963
+ // Else: already prefixed, leave the attribute alone — the prefix is
11964
+ // itself the clobbering protection, and re-applying it is incorrect.
11731
11965
  /* Work around a security issue with comments inside attributes */
11732
11966
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
11733
11967
  _removeAttribute(name, currentNode);
@@ -11754,7 +11988,7 @@ function createDOMPurify() {
11754
11988
  }
11755
11989
  /* Sanitize attribute content to be template-safe */
11756
11990
  if (SAFE_FOR_TEMPLATES) {
11757
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
11991
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
11758
11992
  value = stringReplace(value, expr, ' ');
11759
11993
  });
11760
11994
  }
@@ -11808,7 +12042,7 @@ function createDOMPurify() {
11808
12042
  *
11809
12043
  * @param fragment to iterate over recursively
11810
12044
  */
11811
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
12045
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
11812
12046
  let shadowNode = null;
11813
12047
  const shadowIterator = _createNodeIterator(fragment);
11814
12048
  /* Execute a hook if present */
@@ -11820,14 +12054,98 @@ function createDOMPurify() {
11820
12054
  _sanitizeElements(shadowNode);
11821
12055
  /* Check attributes next */
11822
12056
  _sanitizeAttributes(shadowNode);
11823
- /* Deep shadow DOM detected */
11824
- if (shadowNode.content instanceof DocumentFragment) {
11825
- _sanitizeShadowDOM(shadowNode.content);
12057
+ /* Deep shadow DOM detected.
12058
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
12059
+ DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
12060
+ recurse into <template>.content from foreign realms too. */
12061
+ if (_isDocumentFragment(shadowNode.content)) {
12062
+ _sanitizeShadowDOM2(shadowNode.content);
12063
+ }
12064
+ /* An element iterated here may itself host an attached
12065
+ shadow root. The default NodeIterator does not enter shadow
12066
+ trees, so a shadow root nested inside template.content was
12067
+ previously reached by no walk at all (the pre-pass at
12068
+ _sanitizeAttachedShadowRoots descends via childNodes, which
12069
+ doesn't enter template.content; the template-content recursion
12070
+ above iterates the content but never inspected shadowRoot).
12071
+ Walk it explicitly. The nodeType guard avoids reading
12072
+ shadowRoot off text / comment / CDATA / PI nodes that the
12073
+ iterator also surfaces. */
12074
+ const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
12075
+ if (shadowNodeType === NODE_TYPE.element) {
12076
+ const innerSr = getShadowRoot ? getShadowRoot(shadowNode) : shadowNode.shadowRoot;
12077
+ if (_isDocumentFragment(innerSr)) {
12078
+ _sanitizeAttachedShadowRoots2(innerSr);
12079
+ _sanitizeShadowDOM2(innerSr);
12080
+ }
11826
12081
  }
11827
12082
  }
11828
12083
  /* Execute a hook if present */
11829
12084
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
11830
12085
  };
12086
+ /**
12087
+ * _sanitizeAttachedShadowRoots
12088
+ *
12089
+ * Walks `root` and feeds every attached shadow root we encounter into
12090
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
12091
+ * does not descend into shadow trees, so nodes inside an attached
12092
+ * shadow root would otherwise be skipped entirely.
12093
+ *
12094
+ * Two real input paths put attached shadow roots in front of us:
12095
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
12096
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
12097
+ * shadow root because it was created with `clonable: true`.
12098
+ *
12099
+ * This pass runs once, up front, so the main iteration loop (and the
12100
+ * existing _sanitizeShadowDOM template-content recursion) stay
12101
+ * untouched — string-input paths are not affected.
12102
+ *
12103
+ * @param root the subtree root to walk for attached shadow roots
12104
+ */
12105
+ const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
12106
+ const nodeType = getNodeType ? getNodeType(root) : root.nodeType;
12107
+ if (nodeType === NODE_TYPE.element) {
12108
+ const sr = getShadowRoot ? getShadowRoot(root) : root.shadowRoot;
12109
+ // Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType-based
12110
+ // detection rather than `instanceof DocumentFragment`, which is
12111
+ // realm-bound and silently skipped shadow roots whose host element
12112
+ // belonged to a foreign realm (e.g. iframe.contentDocument
12113
+ // attachShadow). A foreign-realm ShadowRoot extends the foreign
12114
+ // realm's DocumentFragment, not ours, so the old instanceof check
12115
+ // returned false and the shadow subtree was never walked.
12116
+ if (_isDocumentFragment(sr)) {
12117
+ // Recurse first so that nested shadow roots are reached even if
12118
+ // _sanitizeShadowDOM removes hosts at this level.
12119
+ _sanitizeAttachedShadowRoots2(sr);
12120
+ _sanitizeShadowDOM2(sr);
12121
+ }
12122
+ }
12123
+ // Snapshot children before recursing. Sanitization of one subtree
12124
+ // (e.g. via an uponSanitizeShadowNode hook) may detach siblings,
12125
+ // and naive nextSibling traversal would silently skip the rest of
12126
+ // the list once a node is detached.
12127
+ const childNodes = getChildNodes ? getChildNodes(root) : root.childNodes;
12128
+ if (!childNodes) {
12129
+ return;
12130
+ }
12131
+ const snapshot = [];
12132
+ arrayForEach(childNodes, child => {
12133
+ arrayPush(snapshot, child);
12134
+ });
12135
+ for (const child of snapshot) {
12136
+ _sanitizeAttachedShadowRoots2(child);
12137
+ }
12138
+ /* When the root is a <template>, also descend into root.content */
12139
+ if (nodeType === NODE_TYPE.element) {
12140
+ const rootName = getNodeName ? getNodeName(root) : null;
12141
+ if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
12142
+ const content = root.content;
12143
+ if (_isDocumentFragment(content)) {
12144
+ _sanitizeAttachedShadowRoots2(content);
12145
+ }
12146
+ }
12147
+ }
12148
+ };
11831
12149
  // eslint-disable-next-line complexity
11832
12150
  DOMPurify.sanitize = function (dirty) {
11833
12151
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -11844,13 +12162,9 @@ function createDOMPurify() {
11844
12162
  }
11845
12163
  /* Stringify, in case dirty is an object */
11846
12164
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
11847
- if (typeof dirty.toString === 'function') {
11848
- dirty = dirty.toString();
11849
- if (typeof dirty !== 'string') {
11850
- throw typeErrorCreate('dirty is not a string, aborting');
11851
- }
11852
- } else {
11853
- throw typeErrorCreate('toString is not a function');
12165
+ dirty = stringifyValue(dirty);
12166
+ if (typeof dirty !== 'string') {
12167
+ throw typeErrorCreate('dirty is not a string, aborting');
11854
12168
  }
11855
12169
  }
11856
12170
  /* Return dirty HTML if DOMPurify cannot run */
@@ -11868,14 +12182,35 @@ function createDOMPurify() {
11868
12182
  IN_PLACE = false;
11869
12183
  }
11870
12184
  if (IN_PLACE) {
11871
- /* Do some early pre-sanitization to avoid unsafe root nodes */
11872
- if (dirty.nodeName) {
11873
- const tagName = transformCaseFunc(dirty.nodeName);
12185
+ /* Do some early pre-sanitization to avoid unsafe root nodes.
12186
+ Read nodeName through the cached prototype getter — a clobbering
12187
+ child named "nodeName" on the form root would otherwise shadow
12188
+ the property and let this check skip the root-allowlist
12189
+ validation entirely. */
12190
+ const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
12191
+ if (typeof nn === 'string') {
12192
+ const tagName = transformCaseFunc(nn);
11874
12193
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
11875
12194
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
11876
12195
  }
11877
12196
  }
11878
- } else if (dirty instanceof Node) {
12197
+ /* Pre-flight the root through _isClobbered. The iterator-driven
12198
+ removal path can not detach a parent-less root: _forceRemove
12199
+ falls through to Element.prototype.remove(), which per spec
12200
+ is a no-op on a node with no parent. A clobbered root would
12201
+ then survive the main loop with its attributes uninspected,
12202
+ because _sanitizeAttributes early-returns on _isClobbered. The
12203
+ result would be an attacker-controlled form, complete with any
12204
+ event-handler attributes the caller passed in, handed back to
12205
+ the application unsanitized. Refuse to sanitize such a root
12206
+ the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
12207
+ if (_isClobbered(dirty)) {
12208
+ throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
12209
+ }
12210
+ /* Sanitize attached shadow roots before the main iterator runs.
12211
+ The iterator does not descend into shadow trees. */
12212
+ _sanitizeAttachedShadowRoots2(dirty);
12213
+ } else if (_isNode(dirty)) {
11879
12214
  /* If dirty is a DOM element, append to an empty document to avoid
11880
12215
  elements being stripped by the parser */
11881
12216
  body = _initDocument('<!---->');
@@ -11889,6 +12224,12 @@ function createDOMPurify() {
11889
12224
  // eslint-disable-next-line unicorn/prefer-dom-node-append
11890
12225
  body.appendChild(importedNode);
11891
12226
  }
12227
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
12228
+ them before the main iterator runs, since the iterator does not
12229
+ descend into shadow trees. The walk routes every read through a
12230
+ cached prototype getter so clobbering descendants on a form root
12231
+ cannot hide a shadow host from this pass. */
12232
+ _sanitizeAttachedShadowRoots2(importedNode);
11892
12233
  } else {
11893
12234
  /* Exit directly if we have nothing to do */
11894
12235
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
@@ -11915,17 +12256,26 @@ function createDOMPurify() {
11915
12256
  _sanitizeElements(currentNode);
11916
12257
  /* Check attributes next */
11917
12258
  _sanitizeAttributes(currentNode);
11918
- /* Shadow DOM detected, sanitize it */
11919
- if (currentNode.content instanceof DocumentFragment) {
11920
- _sanitizeShadowDOM(currentNode.content);
12259
+ /* Shadow DOM detected, sanitize it.
12260
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
12261
+ instead of instanceof, so foreign-realm <template>.content is
12262
+ walked correctly. */
12263
+ if (_isDocumentFragment(currentNode.content)) {
12264
+ _sanitizeShadowDOM2(currentNode.content);
11921
12265
  }
11922
12266
  }
11923
12267
  /* If we sanitized `dirty` in-place, return it. */
11924
12268
  if (IN_PLACE) {
12269
+ if (SAFE_FOR_TEMPLATES) {
12270
+ _scrubTemplateExpressions(dirty);
12271
+ }
11925
12272
  return dirty;
11926
12273
  }
11927
12274
  /* Return sanitized string or DOM */
11928
12275
  if (RETURN_DOM) {
12276
+ if (SAFE_FOR_TEMPLATES) {
12277
+ _scrubTemplateExpressions(body);
12278
+ }
11929
12279
  if (RETURN_DOM_FRAGMENT) {
11930
12280
  returnNode = createDocumentFragment.call(body.ownerDocument);
11931
12281
  while (body.firstChild) {
@@ -11954,7 +12304,7 @@ function createDOMPurify() {
11954
12304
  }
11955
12305
  /* Sanitize final string template-safe */
11956
12306
  if (SAFE_FOR_TEMPLATES) {
11957
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
12307
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
11958
12308
  serializedHTML = stringReplace(serializedHTML, expr, ' ');
11959
12309
  });
11960
12310
  }
@@ -46226,6 +46576,7 @@ class WigTrack extends TrackBase {
46226
46576
  logScale: false,
46227
46577
  windowFunction: 'mean',
46228
46578
  graphType: 'bar',
46579
+ lineWidth: 2,
46229
46580
  normalize: undefined,
46230
46581
  scaleFactor: undefined,
46231
46582
  overflowColor: `rgb(255, 32, 255)`,
@@ -46277,6 +46628,8 @@ class WigTrack extends TrackBase {
46277
46628
  if ("heatmap" === config.graphType && !config.height) {
46278
46629
  this.height = 20;
46279
46630
  }
46631
+
46632
+ this.lineWidth = config.lineWidth || WigTrack.defaults.lineWidth; // Set lineWidth from config
46280
46633
  }
46281
46634
 
46282
46635
  async postInit() {
@@ -46498,11 +46851,11 @@ class WigTrack extends TrackBase {
46498
46851
  const color = options.alpha ? IGVColor.addAlpha(this.getColorForFeature(f), options.alpha) : this.getColorForFeature(f);
46499
46852
 
46500
46853
  if (this.graphType === "line") {
46854
+ const lineWidth = this.lineWidth;
46855
+ const properties = {"fillStyle": color, "strokeStyle": color, "lineWidth": lineWidth};
46856
+
46501
46857
  if (lastY !== undefined) {
46502
- IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, {
46503
- "fillStyle": color,
46504
- "strokeStyle": color
46505
- });
46858
+ IGVGraphics.strokeLine(ctx, lastPixelEnd, lastY, x, y, properties);
46506
46859
  }
46507
46860
  IGVGraphics.strokeLine(ctx, x, y, x + width, y, {"fillStyle": color, "strokeStyle": color});
46508
46861
  } else if (this.graphType === "points") {
@@ -76701,7 +77054,7 @@ function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, v
76701
77054
  })
76702
77055
  }
76703
77056
 
76704
- const _version = "3.8.0";
77057
+ const _version = "3.8.1";
76705
77058
  function version() {
76706
77059
  return _version
76707
77060
  }
@@ -78328,11 +78681,11 @@ class ROIMenu {
78328
78681
 
78329
78682
  }
78330
78683
 
78331
- function createRegionKey$1(chr, start, end) {
78684
+ function createRegionKey(chr, start, end) {
78332
78685
  return `${chr}-${start}-${end}`
78333
78686
  }
78334
78687
 
78335
- function parseRegionKey$1(regionKey) {
78688
+ function parseRegionKey(regionKey) {
78336
78689
  let regionParts = regionKey.split('-');
78337
78690
  let ee = parseInt(regionParts.pop());
78338
78691
  let ss = parseInt(regionParts.pop());
@@ -78362,7 +78715,7 @@ class ROITable extends RegionTableBase {
78362
78715
  const dom = div({ class: 'igv-roi-table-row' });
78363
78716
 
78364
78717
  const { setName, feature } = record;
78365
- dom.dataset.region = createRegionKey$1(feature.chr, feature.start, feature.end);
78718
+ dom.dataset.region = createRegionKey(feature.chr, feature.start, feature.end);
78366
78719
 
78367
78720
  let strings =
78368
78721
  [
@@ -78434,7 +78787,7 @@ class ROITable extends RegionTableBase {
78434
78787
 
78435
78788
  const loci = [];
78436
78789
  for (let el of selected) {
78437
- const { locus } = parseRegionKey$1(el.dataset.region);
78790
+ const { locus } = parseRegionKey(el.dataset.region);
78438
78791
  loci.push(locus);
78439
78792
  }
78440
78793
 
@@ -78488,7 +78841,7 @@ class ROITable extends RegionTableBase {
78488
78841
  const selected = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected');
78489
78842
 
78490
78843
  if (selected.length > 0 && selected.length < 2) {
78491
- const { locus } = parseRegionKey$1(selected[ 0 ].dataset.region);
78844
+ const { locus } = parseRegionKey(selected[ 0 ].dataset.region);
78492
78845
  const { chr, start, end } = parseLocusString(locus, this.browser.isSoftclipped());
78493
78846
  enableButton(this.copySequenceButton, (end - start) < 1e6);
78494
78847
  } else {
@@ -78537,7 +78890,7 @@ class ROITable extends RegionTableBase {
78537
78890
  const selected = this.tableDOM.querySelectorAll('.igv-roi-table-row-selected');
78538
78891
  const loci = [];
78539
78892
  for (let el of selected) {
78540
- const { locus } = parseRegionKey$1(el.dataset.region);
78893
+ const { locus } = parseRegionKey(el.dataset.region);
78541
78894
  loci.push(locus);
78542
78895
  }
78543
78896