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/README.md +18 -10
- package/dist/igv.esm.js +518 -165
- package/dist/igv.esm.min.js +9 -9
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +518 -165
- package/dist/igv.min.js +9 -9
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
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
|
|
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 (
|
|
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'
|
|
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', '
|
|
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
|
-
|
|
10814
|
-
const
|
|
10815
|
-
const
|
|
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.
|
|
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
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
NamedNodeMap
|
|
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
|
-
|
|
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
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
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
|
|
11189
|
-
NAMESPACE = cfg.NAMESPACE
|
|
11190
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
11191
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
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 (
|
|
11197
|
-
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 (
|
|
11200
|
-
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
|
-
/*
|
|
11233
|
-
|
|
11234
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
11728
|
+
* @return true if value is a DOM node from any realm
|
|
11522
11729
|
*/
|
|
11523
11730
|
const _isNode = function _isNode(value) {
|
|
11524
|
-
|
|
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)) &&
|
|
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)
|
|
11585
|
-
const childNodes = getChildNodes(currentNode)
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
11709
|
-
|
|
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
|
|
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
|
-
|
|
11825
|
-
|
|
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
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
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
|
-
|
|
11873
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11920
|
-
|
|
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.
|
|
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
|
|
78684
|
+
function createRegionKey(chr, start, end) {
|
|
78332
78685
|
return `${chr}-${start}-${end}`
|
|
78333
78686
|
}
|
|
78334
78687
|
|
|
78335
|
-
function parseRegionKey
|
|
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
|
|
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
|
|
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
|
|
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
|
|
78893
|
+
const { locus } = parseRegionKey(el.dataset.region);
|
|
78541
78894
|
loci.push(locus);
|
|
78542
78895
|
}
|
|
78543
78896
|
|