action_text-trix 2.1.16 → 2.1.19

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ca2e4a01cb56e8a1717695d200a41f1ddf2194ce9ea6efce2a900a5f9d4d1cb
4
- data.tar.gz: 707557bec2365a589b5be41a5d859040f1a3229f44551f8f801cd84b33352665
3
+ metadata.gz: 14985d71593b521f0ddc7c0488ad00483db8a3423b58f1dac7fd19c5568e2d78
4
+ data.tar.gz: 6d2fc6d9de5e0b992b66e1a17d77c572e1b695c084fedf3bc79aaf6cd5b56107
5
5
  SHA512:
6
- metadata.gz: 51ada2877432316f6d1fe08c480d3b36343eab134e066f53517f07990f990defdeb20d2b213069d3a540a6b971c2e55cf8bfa3a95669048b851f7875ace1b60c
7
- data.tar.gz: 7c1780754b4763b4a163129f77347f18c7fc944d6d12f4606ce8fdd53c385842b48f49936dba9b83876f0b3a9d0745c90c81a799e300c2ece06fa206407b0b32
6
+ metadata.gz: 6a48e8c1dcdf076d11a930df88d45180fef46c42584522ddb01a58ecf5e4d2afa767e2985363ceae427779373237d7196760d150f93ea26485b3661d00a3fe29
7
+ data.tar.gz: 0f4305effe253c9a6f7aaf3c68324a477fbd3e7451d3f4479ca1f13f7777e1af9e93c2304e3120b48f70e703f73731cc2c7b6c8ef42541e87a0106f2f7c6f31e
@@ -1,6 +1,6 @@
1
1
  /*
2
- Trix 2.1.16
3
- Copyright © 2025 37signals, LLC
2
+ Trix 2.1.19
3
+ Copyright © 2026 37signals, LLC
4
4
  */
5
5
  (function (global, factory) {
6
6
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -9,7 +9,7 @@ Copyright © 2025 37signals, LLC
9
9
  })(this, (function () { 'use strict';
10
10
 
11
11
  var name = "trix";
12
- var version = "2.1.16";
12
+ var version = "2.1.19";
13
13
  var description = "A rich text editor for everyday writing";
14
14
  var main = "dist/trix.umd.min.js";
15
15
  var module = "dist/trix.esm.min.js";
@@ -43,22 +43,22 @@ Copyright © 2025 37signals, LLC
43
43
  "@rollup/plugin-json": "^4.1.0",
44
44
  "@rollup/plugin-node-resolve": "^13.3.0",
45
45
  "@web/dev-server": "^0.1.34",
46
+ "@web/test-runner": "^0.20.2",
47
+ "@web/test-runner-playwright": "^0.11.1",
48
+ "@web/test-runner-webdriver": "^0.9.0",
46
49
  "babel-eslint": "^10.1.0",
47
50
  chokidar: "^4.0.2",
48
51
  concurrently: "^7.4.0",
49
52
  eslint: "^7.32.0",
50
53
  esm: "^3.2.25",
51
54
  idiomorph: "^0.7.3",
52
- karma: "6.4.1",
53
- "karma-chrome-launcher": "3.2.0",
54
- "karma-qunit": "^4.1.2",
55
- "karma-sauce-launcher": "^4.3.6",
56
55
  qunit: "2.19.1",
57
56
  rangy: "^1.3.0",
58
57
  rollup: "^2.56.3",
59
58
  "rollup-plugin-includepaths": "^0.2.4",
60
59
  "rollup-plugin-terser": "^7.0.2",
61
60
  sass: "^1.83.0",
61
+ "source-map": "^0.7.6",
62
62
  svgo: "^2.8.0",
63
63
  webdriverio: "^7.19.5"
64
64
  };
@@ -74,7 +74,8 @@ Copyright © 2025 37signals, LLC
74
74
  watch: "rollup -c -w",
75
75
  lint: "eslint .",
76
76
  pretest: "yarn run lint && yarn run build",
77
- test: "karma start",
77
+ test: "web-test-runner",
78
+ "test:watch": "web-test-runner --watch",
78
79
  version: "yarn build && git add action_text-trix",
79
80
  prerelease: "yarn version && yarn test",
80
81
  "release-npm": "npm adduser && npm publish",
@@ -1764,7 +1765,7 @@ $\
1764
1765
  }
1765
1766
  }
1766
1767
 
1767
- /*! @license DOMPurify 3.2.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.2.7/LICENSE */
1768
+ /*! @license DOMPurify 3.4.2 | (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.2/LICENSE */
1768
1769
 
1769
1770
  const {
1770
1771
  entries,
@@ -1813,13 +1814,19 @@ $\
1813
1814
  const arrayPop = unapply(Array.prototype.pop);
1814
1815
  const arrayPush = unapply(Array.prototype.push);
1815
1816
  const arraySplice = unapply(Array.prototype.splice);
1817
+ const arrayIsArray = Array.isArray;
1816
1818
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
1817
1819
  const stringToString = unapply(String.prototype.toString);
1818
1820
  const stringMatch = unapply(String.prototype.match);
1819
1821
  const stringReplace = unapply(String.prototype.replace);
1820
1822
  const stringIndexOf = unapply(String.prototype.indexOf);
1821
1823
  const stringTrim = unapply(String.prototype.trim);
1824
+ const numberToString = unapply(Number.prototype.toString);
1825
+ const booleanToString = unapply(Boolean.prototype.toString);
1826
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
1827
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
1822
1828
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
1829
+ const objectToString = unapply(Object.prototype.toString);
1823
1830
  const regExpTest = unapply(RegExp.prototype.test);
1824
1831
  const typeErrorCreate = unconstruct(TypeError);
1825
1832
  /**
@@ -1869,6 +1876,9 @@ $\
1869
1876
  // Prevent prototype setters from intercepting set as a this value.
1870
1877
  setPrototypeOf(set, null);
1871
1878
  }
1879
+ if (!arrayIsArray(array)) {
1880
+ return set;
1881
+ }
1872
1882
  let l = array.length;
1873
1883
  while (l--) {
1874
1884
  let element = array[l];
@@ -1912,7 +1922,7 @@ $\
1912
1922
  for (const [property, value] of entries(object)) {
1913
1923
  const isPropertyExist = objectHasOwnProperty(object, property);
1914
1924
  if (isPropertyExist) {
1915
- if (Array.isArray(value)) {
1925
+ if (arrayIsArray(value)) {
1916
1926
  newObject[property] = cleanArray(value);
1917
1927
  } else if (value && typeof value === 'object' && value.constructor === Object) {
1918
1928
  newObject[property] = clone(value);
@@ -1923,6 +1933,58 @@ $\
1923
1933
  }
1924
1934
  return newObject;
1925
1935
  }
1936
+ /**
1937
+ * Convert non-node values into strings without depending on direct property access.
1938
+ *
1939
+ * @param value - The value to stringify.
1940
+ * @returns A string representation of the provided value.
1941
+ */
1942
+ function stringifyValue(value) {
1943
+ switch (typeof value) {
1944
+ case 'string':
1945
+ {
1946
+ return value;
1947
+ }
1948
+ case 'number':
1949
+ {
1950
+ return numberToString(value);
1951
+ }
1952
+ case 'boolean':
1953
+ {
1954
+ return booleanToString(value);
1955
+ }
1956
+ case 'bigint':
1957
+ {
1958
+ return bigintToString ? bigintToString(value) : '0';
1959
+ }
1960
+ case 'symbol':
1961
+ {
1962
+ return symbolToString ? symbolToString(value) : 'Symbol()';
1963
+ }
1964
+ case 'undefined':
1965
+ {
1966
+ return objectToString(value);
1967
+ }
1968
+ case 'function':
1969
+ case 'object':
1970
+ {
1971
+ if (value === null) {
1972
+ return objectToString(value);
1973
+ }
1974
+ const valueAsRecord = value;
1975
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
1976
+ if (typeof valueToString === 'function') {
1977
+ const stringified = valueToString(valueAsRecord);
1978
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
1979
+ }
1980
+ return objectToString(value);
1981
+ }
1982
+ default:
1983
+ {
1984
+ return objectToString(value);
1985
+ }
1986
+ }
1987
+ }
1926
1988
  /**
1927
1989
  * This method automatically checks if the prop is function or getter and behaves accordingly.
1928
1990
  *
@@ -1948,8 +2010,16 @@ $\
1948
2010
  }
1949
2011
  return fallbackValue;
1950
2012
  }
2013
+ function isRegex(value) {
2014
+ try {
2015
+ regExpTest(value, '');
2016
+ return true;
2017
+ } catch (_unused) {
2018
+ return false;
2019
+ }
2020
+ }
1951
2021
  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']);
1952
- 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', 'slot', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
2022
+ 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']);
1953
2023
  const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
1954
2024
  // List of SVG elements that are disallowed by default.
1955
2025
  // We still need to know them so that we can do namespace
@@ -1961,9 +2031,9 @@ $\
1961
2031
  // even those that we disallow by default.
1962
2032
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
1963
2033
  const text = freeze(['#text']);
1964
- 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']);
1965
- 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', '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']);
1966
- 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']);
2034
+ 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']);
2035
+ 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']);
2036
+ 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']);
1967
2037
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
1968
2038
 
1969
2039
  // eslint-disable-next-line unicorn/better-regex
@@ -1997,19 +2067,11 @@ $\
1997
2067
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
1998
2068
  const NODE_TYPE = {
1999
2069
  element: 1,
2000
- attribute: 2,
2001
2070
  text: 3,
2002
- cdataSection: 4,
2003
- entityReference: 5,
2004
- // Deprecated
2005
- entityNode: 6,
2006
2071
  // Deprecated
2007
2072
  progressingInstruction: 7,
2008
2073
  comment: 8,
2009
- document: 9,
2010
- documentType: 10,
2011
- documentFragment: 11,
2012
- notation: 12 // Deprecated
2074
+ document: 9
2013
2075
  };
2014
2076
  const getGlobal = function getGlobal() {
2015
2077
  return typeof window === 'undefined' ? null : window;
@@ -2068,7 +2130,7 @@ $\
2068
2130
  function createDOMPurify() {
2069
2131
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
2070
2132
  const DOMPurify = root => createDOMPurify(root);
2071
- DOMPurify.version = '3.2.7';
2133
+ DOMPurify.version = '3.4.2';
2072
2134
  DOMPurify.removed = [];
2073
2135
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
2074
2136
  // Not running in a browser, provide a factory function
@@ -2179,6 +2241,21 @@ $\
2179
2241
  let FORBID_TAGS = null;
2180
2242
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
2181
2243
  let FORBID_ATTR = null;
2244
+ /* Config object to store ADD_TAGS/ADD_ATTR functions (when used as functions) */
2245
+ const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
2246
+ tagCheck: {
2247
+ writable: true,
2248
+ configurable: false,
2249
+ enumerable: true,
2250
+ value: null
2251
+ },
2252
+ attributeCheck: {
2253
+ writable: true,
2254
+ configurable: false,
2255
+ enumerable: true,
2256
+ value: null
2257
+ }
2258
+ }));
2182
2259
  /* Decide if ARIA attributes are okay */
2183
2260
  let ALLOW_ARIA_ATTR = true;
2184
2261
  /* Decide if custom data attributes are okay */
@@ -2301,15 +2378,15 @@ $\
2301
2378
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
2302
2379
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
2303
2380
  /* Set configuration parameters */
2304
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
2305
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
2306
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
2307
- 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;
2308
- 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;
2309
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
2310
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
2311
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
2312
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
2381
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
2382
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
2383
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
2384
+ 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;
2385
+ 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;
2386
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
2387
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
2388
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
2389
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
2313
2390
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
2314
2391
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
2315
2392
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -2325,19 +2402,20 @@ $\
2325
2402
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
2326
2403
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
2327
2404
  IN_PLACE = cfg.IN_PLACE || false; // Default false
2328
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
2329
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
2330
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
2331
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
2332
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
2333
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
2334
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
2405
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
2406
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
2407
+ 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
2408
+ 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
2409
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
2410
+ CUSTOM_ELEMENT_HANDLING = create(null);
2411
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
2412
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
2335
2413
  }
2336
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
2337
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
2414
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
2415
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
2338
2416
  }
2339
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
2340
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
2417
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
2418
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
2341
2419
  }
2342
2420
  if (SAFE_FOR_TEMPLATES) {
2343
2421
  ALLOW_DATA_ATTR = false;
@@ -2348,7 +2426,7 @@ $\
2348
2426
  /* Parse profile info */
2349
2427
  if (USE_PROFILES) {
2350
2428
  ALLOWED_TAGS = addToSet({}, text);
2351
- ALLOWED_ATTR = [];
2429
+ ALLOWED_ATTR = create(null);
2352
2430
  if (USE_PROFILES.html === true) {
2353
2431
  addToSet(ALLOWED_TAGS, html$1);
2354
2432
  addToSet(ALLOWED_ATTR, html);
@@ -2369,28 +2447,46 @@ $\
2369
2447
  addToSet(ALLOWED_ATTR, xml);
2370
2448
  }
2371
2449
  }
2450
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
2451
+ * leaking across calls when switching from function to array config */
2452
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
2453
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
2372
2454
  /* Merge configuration parameters */
2373
- if (cfg.ADD_TAGS) {
2374
- if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
2375
- ALLOWED_TAGS = clone(ALLOWED_TAGS);
2455
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
2456
+ if (typeof cfg.ADD_TAGS === 'function') {
2457
+ EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
2458
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
2459
+ if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
2460
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
2461
+ }
2462
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
2376
2463
  }
2377
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
2378
2464
  }
2379
- if (cfg.ADD_ATTR) {
2380
- if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
2381
- ALLOWED_ATTR = clone(ALLOWED_ATTR);
2465
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
2466
+ if (typeof cfg.ADD_ATTR === 'function') {
2467
+ EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
2468
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
2469
+ if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
2470
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
2471
+ }
2472
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
2382
2473
  }
2383
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
2384
2474
  }
2385
- if (cfg.ADD_URI_SAFE_ATTR) {
2475
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
2386
2476
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
2387
2477
  }
2388
- if (cfg.FORBID_CONTENTS) {
2478
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
2389
2479
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
2390
2480
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
2391
2481
  }
2392
2482
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
2393
2483
  }
2484
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
2485
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
2486
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
2487
+ }
2488
+ addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
2489
+ }
2394
2490
  /* Add #text in case KEEP_CONTENT is set to true */
2395
2491
  if (KEEP_CONTENT) {
2396
2492
  ALLOWED_TAGS['#text'] = true;
@@ -2677,6 +2773,11 @@ $\
2677
2773
  _forceRemove(currentNode);
2678
2774
  return true;
2679
2775
  }
2776
+ /* Remove risky CSS construction leading to mXSS */
2777
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
2778
+ _forceRemove(currentNode);
2779
+ return true;
2780
+ }
2680
2781
  /* Remove any occurrence of processing instructions */
2681
2782
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
2682
2783
  _forceRemove(currentNode);
@@ -2688,7 +2789,7 @@ $\
2688
2789
  return true;
2689
2790
  }
2690
2791
  /* Remove element if anything forbids its presence */
2691
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2792
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
2692
2793
  /* Check if we have a custom element to handle */
2693
2794
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
2694
2795
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -2706,7 +2807,6 @@ $\
2706
2807
  const childCount = childNodes.length;
2707
2808
  for (let i = childCount - 1; i >= 0; --i) {
2708
2809
  const childClone = cloneNode(childNodes[i], true);
2709
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
2710
2810
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
2711
2811
  }
2712
2812
  }
@@ -2752,15 +2852,20 @@ $\
2752
2852
  */
2753
2853
  // eslint-disable-next-line complexity
2754
2854
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
2855
+ /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
2856
+ if (FORBID_ATTR[lcName]) {
2857
+ return false;
2858
+ }
2755
2859
  /* Make sure attribute cannot clobber */
2756
2860
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
2757
2861
  return false;
2758
2862
  }
2863
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
2759
2864
  /* Allow valid data-* attributes: At least one character after "-"
2760
2865
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
2761
2866
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
2762
2867
  We don't need to check the value; it's always URI safe. */
2763
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ;else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ;else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
2868
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ;else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ;else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
2764
2869
  if (
2765
2870
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
2766
2871
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -2777,6 +2882,10 @@ $\
2777
2882
  } else ;
2778
2883
  return true;
2779
2884
  };
2885
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
2886
+ * never be treated as basic custom elements even when a permissive
2887
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
2888
+ 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']);
2780
2889
  /**
2781
2890
  * _isBasicCustomElement
2782
2891
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -2786,7 +2895,7 @@ $\
2786
2895
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
2787
2896
  */
2788
2897
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
2789
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
2898
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT, tagName);
2790
2899
  };
2791
2900
  /**
2792
2901
  * _sanitizeAttributes
@@ -2837,14 +2946,16 @@ $\
2837
2946
  /* Full DOM Clobbering protection via namespace isolation,
2838
2947
  * Prefix id and name attributes with `user-content-`
2839
2948
  */
2840
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
2949
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
2841
2950
  // Remove the attribute with this value
2842
2951
  _removeAttribute(name, currentNode);
2843
2952
  // Prefix the value and later re-create the attribute with the sanitized value
2844
2953
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
2845
2954
  }
2955
+ // Else: already prefixed, leave the attribute alone — the prefix is
2956
+ // itself the clobbering protection, and re-applying it is incorrect.
2846
2957
  /* Work around a security issue with comments inside attributes */
2847
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title|textarea)/i, value)) {
2958
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
2848
2959
  _removeAttribute(name, currentNode);
2849
2960
  continue;
2850
2961
  }
@@ -2923,7 +3034,7 @@ $\
2923
3034
  *
2924
3035
  * @param fragment to iterate over recursively
2925
3036
  */
2926
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
3037
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
2927
3038
  let shadowNode = null;
2928
3039
  const shadowIterator = _createNodeIterator(fragment);
2929
3040
  /* Execute a hook if present */
@@ -2937,7 +3048,7 @@ $\
2937
3048
  _sanitizeAttributes(shadowNode);
2938
3049
  /* Deep shadow DOM detected */
2939
3050
  if (shadowNode.content instanceof DocumentFragment) {
2940
- _sanitizeShadowDOM(shadowNode.content);
3051
+ _sanitizeShadowDOM2(shadowNode.content);
2941
3052
  }
2942
3053
  }
2943
3054
  /* Execute a hook if present */
@@ -2959,13 +3070,9 @@ $\
2959
3070
  }
2960
3071
  /* Stringify, in case dirty is an object */
2961
3072
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
2962
- if (typeof dirty.toString === 'function') {
2963
- dirty = dirty.toString();
2964
- if (typeof dirty !== 'string') {
2965
- throw typeErrorCreate('dirty is not a string, aborting');
2966
- }
2967
- } else {
2968
- throw typeErrorCreate('toString is not a function');
3073
+ dirty = stringifyValue(dirty);
3074
+ if (typeof dirty !== 'string') {
3075
+ throw typeErrorCreate('dirty is not a string, aborting');
2969
3076
  }
2970
3077
  }
2971
3078
  /* Return dirty HTML if DOMPurify cannot run */
@@ -2984,8 +3091,9 @@ $\
2984
3091
  }
2985
3092
  if (IN_PLACE) {
2986
3093
  /* Do some early pre-sanitization to avoid unsafe root nodes */
2987
- if (dirty.nodeName) {
2988
- const tagName = transformCaseFunc(dirty.nodeName);
3094
+ const nn = dirty.nodeName;
3095
+ if (typeof nn === 'string') {
3096
+ const tagName = transformCaseFunc(nn);
2989
3097
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2990
3098
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
2991
3099
  }
@@ -3032,7 +3140,7 @@ $\
3032
3140
  _sanitizeAttributes(currentNode);
3033
3141
  /* Shadow DOM detected, sanitize it */
3034
3142
  if (currentNode.content instanceof DocumentFragment) {
3035
- _sanitizeShadowDOM(currentNode.content);
3143
+ _sanitizeShadowDOM2(currentNode.content);
3036
3144
  }
3037
3145
  }
3038
3146
  /* If we sanitized `dirty` in-place, return it. */
@@ -3041,6 +3149,14 @@ $\
3041
3149
  }
3042
3150
  /* Return sanitized string or DOM */
3043
3151
  if (RETURN_DOM) {
3152
+ if (SAFE_FOR_TEMPLATES) {
3153
+ body.normalize();
3154
+ let html = body.innerHTML;
3155
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
3156
+ html = stringReplace(html, expr, ' ');
3157
+ });
3158
+ body.innerHTML = html;
3159
+ }
3044
3160
  if (RETURN_DOM_FRAGMENT) {
3045
3161
  returnNode = createDocumentFragment.call(body.ownerDocument);
3046
3162
  while (body.firstChild) {
@@ -3117,6 +3233,10 @@ $\
3117
3233
  var purify = createDOMPurify();
3118
3234
 
3119
3235
  purify.addHook("uponSanitizeAttribute", function (node, data) {
3236
+ if (data.attrName === "data-trix-serialized-attributes") {
3237
+ data.keepAttr = false;
3238
+ return;
3239
+ }
3120
3240
  const allowedAttributePattern = /^data-trix-/;
3121
3241
  if (allowedAttributePattern.test(data.attrName)) {
3122
3242
  data.forceKeepAttr = true;
@@ -6857,7 +6977,13 @@ $\
6857
6977
 
6858
6978
  class StringPiece extends Piece {
6859
6979
  static fromJSON(pieceJSON) {
6860
- return new this(pieceJSON.string, pieceJSON.attributes);
6980
+ const attributes = {
6981
+ ...pieceJSON.attributes
6982
+ };
6983
+ if (attributes.href && !purify.isValidAttribute("a", "href", attributes.href)) {
6984
+ delete attributes.href;
6985
+ }
6986
+ return new this(pieceJSON.string, attributes);
6861
6987
  }
6862
6988
  constructor(string) {
6863
6989
  super(...arguments);
@@ -10043,6 +10169,15 @@ $\
10043
10169
  } else {
10044
10170
  if (node.parentNode === container) {
10045
10171
  if (childIndex++ === offset) {
10172
+ if (!strict && nodeIsBlockStart(node, {
10173
+ strict
10174
+ })) {
10175
+ if (foundBlock) {
10176
+ location.index++;
10177
+ }
10178
+ location.offset = 0;
10179
+ foundBlock = true;
10180
+ }
10046
10181
  break;
10047
10182
  }
10048
10183
  } else if (!elementContainsNode(container, node)) {
@@ -1,3 +1,3 @@
1
1
  module Trix
2
- VERSION = "2.1.16"
2
+ VERSION = "2.1.19"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_text-trix
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.16
4
+ version: 2.1.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - 37signals, LLC
@@ -55,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
57
  requirements: []
58
- rubygems_version: 4.0.3
58
+ rubygems_version: 4.0.6
59
59
  specification_version: 4
60
60
  summary: A rich text editor for everyday writing
61
61
  test_files: []