tinymce-rails 7.3.0 → 7.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/app/assets/source/tinymce/tinymce.js +1056 -980
  4. data/lib/tinymce/rails/version.rb +2 -2
  5. data/vendor/assets/javascripts/tinymce/icons/default/icons.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/models/dom/model.js +2 -2
  7. data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +2 -2
  8. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +2 -2
  9. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +2 -2
  11. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +2 -2
  13. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +2 -2
  19. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +2 -2
  20. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +2 -2
  21. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +2 -2
  23. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +2 -2
  24. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +2 -2
  25. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +2 -2
  29. data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +2 -2
  34. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +2 -2
  35. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +2 -2
  36. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -1
  37. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -1
  38. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
  40. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  41. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
  43. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
  44. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
  45. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -1
  46. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -1
  47. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  48. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
  49. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  50. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
  51. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
  52. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
  53. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
  54. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -1
  55. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -1
  56. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  57. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
  58. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  59. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
  60. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
  61. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
  62. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
  63. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -1
  64. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -1
  65. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  66. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
  67. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  68. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
  69. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
  70. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
  71. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  72. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +384 -2
  73. data/vendor/assets/javascripts/tinymce/tinymce.d.ts +23 -0
  74. data/vendor/assets/javascripts/tinymce/tinymce.js +383 -2
  75. metadata +6 -6
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 7.3.0 (2024-08-07)
2
+ * TinyMCE version 7.4.1 (TBD)
3
3
  */
4
4
 
5
5
  (function () {
@@ -7290,6 +7290,19 @@
7290
7290
  processor: 'boolean',
7291
7291
  default: false
7292
7292
  });
7293
+ registerOption('allow_mathml_annotation_encodings', {
7294
+ processor: value => {
7295
+ const valid = isArrayOf(value, isString);
7296
+ return valid ? {
7297
+ value,
7298
+ valid
7299
+ } : {
7300
+ valid: false,
7301
+ message: 'Must be an array of strings.'
7302
+ };
7303
+ },
7304
+ default: []
7305
+ });
7293
7306
  registerOption('convert_fonts_to_spans', {
7294
7307
  processor: 'boolean',
7295
7308
  default: true,
@@ -7948,7 +7961,7 @@
7948
7961
  const isContentEditableTrue$1 = isContentEditableTrue$3;
7949
7962
  const isContentEditableFalse$7 = isContentEditableFalse$b;
7950
7963
  const isMedia = isMedia$2;
7951
- const isBlockLike = matchStyleValues('display', 'block table table-cell table-caption list-item');
7964
+ const isBlockLike = matchStyleValues('display', 'block table table-cell table-row table-caption list-item');
7952
7965
  const isCaretContainer = isCaretContainer$2;
7953
7966
  const isCaretContainerBlock = isCaretContainerBlock$1;
7954
7967
  const isElement$2 = isElement$6;
@@ -9635,7 +9648,7 @@
9635
9648
  };
9636
9649
  const isResizable = elm => {
9637
9650
  const selector = getObjectResizing(editor);
9638
- if (!selector) {
9651
+ if (!selector || editor.mode.isReadOnly()) {
9639
9652
  return false;
9640
9653
  }
9641
9654
  if (elm.getAttribute('data-mce-resize') === 'false') {
@@ -13243,6 +13256,9 @@
13243
13256
  if (node && node.attr('id') === 'mce_marker') {
13244
13257
  const marker = node;
13245
13258
  for (node = node.prev; node; node = node.walk(true)) {
13259
+ if (node.name === 'table') {
13260
+ break;
13261
+ }
13246
13262
  if (node.type === 3 || !dom.isBlock(node.name)) {
13247
13263
  if (node.parent && editor.schema.isValidChild(node.parent.name, 'span')) {
13248
13264
  node.parent.insert(marker, node, node.name === 'br');
@@ -15258,14 +15274,24 @@
15258
15274
  }
15259
15275
  };
15260
15276
 
15261
- const {entries, setPrototypeOf, isFrozen, getPrototypeOf, getOwnPropertyDescriptor} = Object;
15262
- let {freeze, seal, create: create$7} = Object;
15263
- let {apply, construct} = typeof Reflect !== 'undefined' && Reflect;
15264
- if (!apply) {
15265
- apply = function apply(fun, thisValue, args) {
15266
- return fun.apply(thisValue, args);
15267
- };
15268
- }
15277
+ /*! @license DOMPurify 3.1.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.1.7/LICENSE */
15278
+
15279
+ const {
15280
+ entries,
15281
+ setPrototypeOf,
15282
+ isFrozen,
15283
+ getPrototypeOf,
15284
+ getOwnPropertyDescriptor
15285
+ } = Object;
15286
+ let {
15287
+ freeze,
15288
+ seal,
15289
+ create: create$7
15290
+ } = Object; // eslint-disable-line import/no-mutable-exports
15291
+ let {
15292
+ apply,
15293
+ construct
15294
+ } = typeof Reflect !== 'undefined' && Reflect;
15269
15295
  if (!freeze) {
15270
15296
  freeze = function freeze(x) {
15271
15297
  return x;
@@ -15276,6 +15302,11 @@
15276
15302
  return x;
15277
15303
  };
15278
15304
  }
15305
+ if (!apply) {
15306
+ apply = function apply(fun, thisValue, args) {
15307
+ return fun.apply(thisValue, args);
15308
+ };
15309
+ }
15279
15310
  if (!construct) {
15280
15311
  construct = function construct(Func, args) {
15281
15312
  return new Func(...args);
@@ -15290,8 +15321,16 @@
15290
15321
  const stringReplace = unapply(String.prototype.replace);
15291
15322
  const stringIndexOf = unapply(String.prototype.indexOf);
15292
15323
  const stringTrim = unapply(String.prototype.trim);
15324
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
15293
15325
  const regExpTest = unapply(RegExp.prototype.test);
15294
15326
  const typeErrorCreate = unconstruct(TypeError);
15327
+
15328
+ /**
15329
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
15330
+ *
15331
+ * @param {Function} func - The function to be wrapped and called.
15332
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
15333
+ */
15295
15334
  function unapply(func) {
15296
15335
  return function (thisArg) {
15297
15336
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -15300,6 +15339,13 @@
15300
15339
  return apply(func, thisArg, args);
15301
15340
  };
15302
15341
  }
15342
+
15343
+ /**
15344
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
15345
+ *
15346
+ * @param {Function} func - The constructor function to be wrapped and called.
15347
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
15348
+ */
15303
15349
  function unconstruct(func) {
15304
15350
  return function () {
15305
15351
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
@@ -15308,10 +15354,21 @@
15308
15354
  return construct(func, args);
15309
15355
  };
15310
15356
  }
15311
- function addToSet(set, array, transformCaseFunc) {
15312
- var _transformCaseFunc;
15313
- transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
15357
+
15358
+ /**
15359
+ * Add properties to a lookup table
15360
+ *
15361
+ * @param {Object} set - The set to which elements will be added.
15362
+ * @param {Array} array - The array containing elements to be added to the set.
15363
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15364
+ * @returns {Object} The modified set with added elements.
15365
+ */
15366
+ function addToSet(set, array) {
15367
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
15314
15368
  if (setPrototypeOf) {
15369
+ // Make 'in' and truthy checks like Boolean(set.constructor)
15370
+ // independent of any properties defined on Object.prototype.
15371
+ // Prevent prototype setters from intercepting set as a this value.
15315
15372
  setPrototypeOf(set, null);
15316
15373
  }
15317
15374
  let l = array.length;
@@ -15320,6 +15377,7 @@
15320
15377
  if (typeof element === 'string') {
15321
15378
  const lcElement = transformCaseFunc(element);
15322
15379
  if (lcElement !== element) {
15380
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
15323
15381
  if (!isFrozen(array)) {
15324
15382
  array[l] = lcElement;
15325
15383
  }
@@ -15330,13 +15388,53 @@
15330
15388
  }
15331
15389
  return set;
15332
15390
  }
15391
+
15392
+ /**
15393
+ * Clean up an array to harden against CSPP
15394
+ *
15395
+ * @param {Array} array - The array to be cleaned.
15396
+ * @returns {Array} The cleaned version of the array
15397
+ */
15398
+ function cleanArray(array) {
15399
+ for (let index = 0; index < array.length; index++) {
15400
+ const isPropertyExist = objectHasOwnProperty(array, index);
15401
+ if (!isPropertyExist) {
15402
+ array[index] = null;
15403
+ }
15404
+ }
15405
+ return array;
15406
+ }
15407
+
15408
+ /**
15409
+ * Shallow clone an object
15410
+ *
15411
+ * @param {Object} object - The object to be cloned.
15412
+ * @returns {Object} A new object that copies the original.
15413
+ */
15333
15414
  function clone(object) {
15334
15415
  const newObject = create$7(null);
15335
15416
  for (const [property, value] of entries(object)) {
15336
- newObject[property] = value;
15417
+ const isPropertyExist = objectHasOwnProperty(object, property);
15418
+ if (isPropertyExist) {
15419
+ if (Array.isArray(value)) {
15420
+ newObject[property] = cleanArray(value);
15421
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
15422
+ newObject[property] = clone(value);
15423
+ } else {
15424
+ newObject[property] = value;
15425
+ }
15426
+ }
15337
15427
  }
15338
15428
  return newObject;
15339
15429
  }
15430
+
15431
+ /**
15432
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
15433
+ *
15434
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
15435
+ * @param {String} prop - The property name for which to find the getter function.
15436
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
15437
+ */
15340
15438
  function lookupGetter(object, prop) {
15341
15439
  while (object !== null) {
15342
15440
  const desc = getOwnPropertyDescriptor(object, prop);
@@ -15350,644 +15448,50 @@
15350
15448
  }
15351
15449
  object = getPrototypeOf(object);
15352
15450
  }
15353
- function fallbackValue(element) {
15354
- console.warn('fallback value for', element);
15451
+ function fallbackValue() {
15355
15452
  return null;
15356
15453
  }
15357
15454
  return fallbackValue;
15358
15455
  }
15359
- const html$1 = freeze([
15360
- 'a',
15361
- 'abbr',
15362
- 'acronym',
15363
- 'address',
15364
- 'area',
15365
- 'article',
15366
- 'aside',
15367
- 'audio',
15368
- 'b',
15369
- 'bdi',
15370
- 'bdo',
15371
- 'big',
15372
- 'blink',
15373
- 'blockquote',
15374
- 'body',
15375
- 'br',
15376
- 'button',
15377
- 'canvas',
15378
- 'caption',
15379
- 'center',
15380
- 'cite',
15381
- 'code',
15382
- 'col',
15383
- 'colgroup',
15384
- 'content',
15385
- 'data',
15386
- 'datalist',
15387
- 'dd',
15388
- 'decorator',
15389
- 'del',
15390
- 'details',
15391
- 'dfn',
15392
- 'dialog',
15393
- 'dir',
15394
- 'div',
15395
- 'dl',
15396
- 'dt',
15397
- 'element',
15398
- 'em',
15399
- 'fieldset',
15400
- 'figcaption',
15401
- 'figure',
15402
- 'font',
15403
- 'footer',
15404
- 'form',
15405
- 'h1',
15406
- 'h2',
15407
- 'h3',
15408
- 'h4',
15409
- 'h5',
15410
- 'h6',
15411
- 'head',
15412
- 'header',
15413
- 'hgroup',
15414
- 'hr',
15415
- 'html',
15416
- 'i',
15417
- 'img',
15418
- 'input',
15419
- 'ins',
15420
- 'kbd',
15421
- 'label',
15422
- 'legend',
15423
- 'li',
15424
- 'main',
15425
- 'map',
15426
- 'mark',
15427
- 'marquee',
15428
- 'menu',
15429
- 'menuitem',
15430
- 'meter',
15431
- 'nav',
15432
- 'nobr',
15433
- 'ol',
15434
- 'optgroup',
15435
- 'option',
15436
- 'output',
15437
- 'p',
15438
- 'picture',
15439
- 'pre',
15440
- 'progress',
15441
- 'q',
15442
- 'rp',
15443
- 'rt',
15444
- 'ruby',
15445
- 's',
15446
- 'samp',
15447
- 'section',
15448
- 'select',
15449
- 'shadow',
15450
- 'small',
15451
- 'source',
15452
- 'spacer',
15453
- 'span',
15454
- 'strike',
15455
- 'strong',
15456
- 'style',
15457
- 'sub',
15458
- 'summary',
15459
- 'sup',
15460
- 'table',
15461
- 'tbody',
15462
- 'td',
15463
- 'template',
15464
- 'textarea',
15465
- 'tfoot',
15466
- 'th',
15467
- 'thead',
15468
- 'time',
15469
- 'tr',
15470
- 'track',
15471
- 'tt',
15472
- 'u',
15473
- 'ul',
15474
- 'var',
15475
- 'video',
15476
- 'wbr'
15477
- ]);
15478
- const svg$1 = freeze([
15479
- 'svg',
15480
- 'a',
15481
- 'altglyph',
15482
- 'altglyphdef',
15483
- 'altglyphitem',
15484
- 'animatecolor',
15485
- 'animatemotion',
15486
- 'animatetransform',
15487
- 'circle',
15488
- 'clippath',
15489
- 'defs',
15490
- 'desc',
15491
- 'ellipse',
15492
- 'filter',
15493
- 'font',
15494
- 'g',
15495
- 'glyph',
15496
- 'glyphref',
15497
- 'hkern',
15498
- 'image',
15499
- 'line',
15500
- 'lineargradient',
15501
- 'marker',
15502
- 'mask',
15503
- 'metadata',
15504
- 'mpath',
15505
- 'path',
15506
- 'pattern',
15507
- 'polygon',
15508
- 'polyline',
15509
- 'radialgradient',
15510
- 'rect',
15511
- 'stop',
15512
- 'style',
15513
- 'switch',
15514
- 'symbol',
15515
- 'text',
15516
- 'textpath',
15517
- 'title',
15518
- 'tref',
15519
- 'tspan',
15520
- 'view',
15521
- 'vkern'
15522
- ]);
15523
- const svgFilters = freeze([
15524
- 'feBlend',
15525
- 'feColorMatrix',
15526
- 'feComponentTransfer',
15527
- 'feComposite',
15528
- 'feConvolveMatrix',
15529
- 'feDiffuseLighting',
15530
- 'feDisplacementMap',
15531
- 'feDistantLight',
15532
- 'feDropShadow',
15533
- 'feFlood',
15534
- 'feFuncA',
15535
- 'feFuncB',
15536
- 'feFuncG',
15537
- 'feFuncR',
15538
- 'feGaussianBlur',
15539
- 'feImage',
15540
- 'feMerge',
15541
- 'feMergeNode',
15542
- 'feMorphology',
15543
- 'feOffset',
15544
- 'fePointLight',
15545
- 'feSpecularLighting',
15546
- 'feSpotLight',
15547
- 'feTile',
15548
- 'feTurbulence'
15549
- ]);
15550
- const svgDisallowed = freeze([
15551
- 'animate',
15552
- 'color-profile',
15553
- 'cursor',
15554
- 'discard',
15555
- 'font-face',
15556
- 'font-face-format',
15557
- 'font-face-name',
15558
- 'font-face-src',
15559
- 'font-face-uri',
15560
- 'foreignobject',
15561
- 'hatch',
15562
- 'hatchpath',
15563
- 'mesh',
15564
- 'meshgradient',
15565
- 'meshpatch',
15566
- 'meshrow',
15567
- 'missing-glyph',
15568
- 'script',
15569
- 'set',
15570
- 'solidcolor',
15571
- 'unknown',
15572
- 'use'
15573
- ]);
15574
- const mathMl$1 = freeze([
15575
- 'math',
15576
- 'menclose',
15577
- 'merror',
15578
- 'mfenced',
15579
- 'mfrac',
15580
- 'mglyph',
15581
- 'mi',
15582
- 'mlabeledtr',
15583
- 'mmultiscripts',
15584
- 'mn',
15585
- 'mo',
15586
- 'mover',
15587
- 'mpadded',
15588
- 'mphantom',
15589
- 'mroot',
15590
- 'mrow',
15591
- 'ms',
15592
- 'mspace',
15593
- 'msqrt',
15594
- 'mstyle',
15595
- 'msub',
15596
- 'msup',
15597
- 'msubsup',
15598
- 'mtable',
15599
- 'mtd',
15600
- 'mtext',
15601
- 'mtr',
15602
- 'munder',
15603
- 'munderover',
15604
- 'mprescripts'
15605
- ]);
15606
- const mathMlDisallowed = freeze([
15607
- 'maction',
15608
- 'maligngroup',
15609
- 'malignmark',
15610
- 'mlongdiv',
15611
- 'mscarries',
15612
- 'mscarry',
15613
- 'msgroup',
15614
- 'mstack',
15615
- 'msline',
15616
- 'msrow',
15617
- 'semantics',
15618
- 'annotation',
15619
- 'annotation-xml',
15620
- 'mprescripts',
15621
- 'none'
15622
- ]);
15456
+
15457
+ const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
15458
+
15459
+ // SVG
15460
+ const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
15461
+ 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']);
15462
+
15463
+ // List of SVG elements that are disallowed by default.
15464
+ // We still need to know them so that we can do namespace
15465
+ // checks properly in case one wants to add them to
15466
+ // allow-list.
15467
+ const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
15468
+ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
15469
+
15470
+ // Similarly to SVG, we want to know all MathML elements,
15471
+ // even those that we disallow by default.
15472
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
15623
15473
  const text = freeze(['#text']);
15624
- const html = freeze([
15625
- 'accept',
15626
- 'action',
15627
- 'align',
15628
- 'alt',
15629
- 'autocapitalize',
15630
- 'autocomplete',
15631
- 'autopictureinpicture',
15632
- 'autoplay',
15633
- 'background',
15634
- 'bgcolor',
15635
- 'border',
15636
- 'capture',
15637
- 'cellpadding',
15638
- 'cellspacing',
15639
- 'checked',
15640
- 'cite',
15641
- 'class',
15642
- 'clear',
15643
- 'color',
15644
- 'cols',
15645
- 'colspan',
15646
- 'controls',
15647
- 'controlslist',
15648
- 'coords',
15649
- 'crossorigin',
15650
- 'datetime',
15651
- 'decoding',
15652
- 'default',
15653
- 'dir',
15654
- 'disabled',
15655
- 'disablepictureinpicture',
15656
- 'disableremoteplayback',
15657
- 'download',
15658
- 'draggable',
15659
- 'enctype',
15660
- 'enterkeyhint',
15661
- 'face',
15662
- 'for',
15663
- 'headers',
15664
- 'height',
15665
- 'hidden',
15666
- 'high',
15667
- 'href',
15668
- 'hreflang',
15669
- 'id',
15670
- 'inputmode',
15671
- 'integrity',
15672
- 'ismap',
15673
- 'kind',
15674
- 'label',
15675
- 'lang',
15676
- 'list',
15677
- 'loading',
15678
- 'loop',
15679
- 'low',
15680
- 'max',
15681
- 'maxlength',
15682
- 'media',
15683
- 'method',
15684
- 'min',
15685
- 'minlength',
15686
- 'multiple',
15687
- 'muted',
15688
- 'name',
15689
- 'nonce',
15690
- 'noshade',
15691
- 'novalidate',
15692
- 'nowrap',
15693
- 'open',
15694
- 'optimum',
15695
- 'pattern',
15696
- 'placeholder',
15697
- 'playsinline',
15698
- 'poster',
15699
- 'preload',
15700
- 'pubdate',
15701
- 'radiogroup',
15702
- 'readonly',
15703
- 'rel',
15704
- 'required',
15705
- 'rev',
15706
- 'reversed',
15707
- 'role',
15708
- 'rows',
15709
- 'rowspan',
15710
- 'spellcheck',
15711
- 'scope',
15712
- 'selected',
15713
- 'shape',
15714
- 'size',
15715
- 'sizes',
15716
- 'span',
15717
- 'srclang',
15718
- 'start',
15719
- 'src',
15720
- 'srcset',
15721
- 'step',
15722
- 'style',
15723
- 'summary',
15724
- 'tabindex',
15725
- 'title',
15726
- 'translate',
15727
- 'type',
15728
- 'usemap',
15729
- 'valign',
15730
- 'value',
15731
- 'width',
15732
- 'xmlns',
15733
- 'slot'
15734
- ]);
15735
- const svg = freeze([
15736
- 'accent-height',
15737
- 'accumulate',
15738
- 'additive',
15739
- 'alignment-baseline',
15740
- 'ascent',
15741
- 'attributename',
15742
- 'attributetype',
15743
- 'azimuth',
15744
- 'basefrequency',
15745
- 'baseline-shift',
15746
- 'begin',
15747
- 'bias',
15748
- 'by',
15749
- 'class',
15750
- 'clip',
15751
- 'clippathunits',
15752
- 'clip-path',
15753
- 'clip-rule',
15754
- 'color',
15755
- 'color-interpolation',
15756
- 'color-interpolation-filters',
15757
- 'color-profile',
15758
- 'color-rendering',
15759
- 'cx',
15760
- 'cy',
15761
- 'd',
15762
- 'dx',
15763
- 'dy',
15764
- 'diffuseconstant',
15765
- 'direction',
15766
- 'display',
15767
- 'divisor',
15768
- 'dur',
15769
- 'edgemode',
15770
- 'elevation',
15771
- 'end',
15772
- 'fill',
15773
- 'fill-opacity',
15774
- 'fill-rule',
15775
- 'filter',
15776
- 'filterunits',
15777
- 'flood-color',
15778
- 'flood-opacity',
15779
- 'font-family',
15780
- 'font-size',
15781
- 'font-size-adjust',
15782
- 'font-stretch',
15783
- 'font-style',
15784
- 'font-variant',
15785
- 'font-weight',
15786
- 'fx',
15787
- 'fy',
15788
- 'g1',
15789
- 'g2',
15790
- 'glyph-name',
15791
- 'glyphref',
15792
- 'gradientunits',
15793
- 'gradienttransform',
15794
- 'height',
15795
- 'href',
15796
- 'id',
15797
- 'image-rendering',
15798
- 'in',
15799
- 'in2',
15800
- 'k',
15801
- 'k1',
15802
- 'k2',
15803
- 'k3',
15804
- 'k4',
15805
- 'kerning',
15806
- 'keypoints',
15807
- 'keysplines',
15808
- 'keytimes',
15809
- 'lang',
15810
- 'lengthadjust',
15811
- 'letter-spacing',
15812
- 'kernelmatrix',
15813
- 'kernelunitlength',
15814
- 'lighting-color',
15815
- 'local',
15816
- 'marker-end',
15817
- 'marker-mid',
15818
- 'marker-start',
15819
- 'markerheight',
15820
- 'markerunits',
15821
- 'markerwidth',
15822
- 'maskcontentunits',
15823
- 'maskunits',
15824
- 'max',
15825
- 'mask',
15826
- 'media',
15827
- 'method',
15828
- 'mode',
15829
- 'min',
15830
- 'name',
15831
- 'numoctaves',
15832
- 'offset',
15833
- 'operator',
15834
- 'opacity',
15835
- 'order',
15836
- 'orient',
15837
- 'orientation',
15838
- 'origin',
15839
- 'overflow',
15840
- 'paint-order',
15841
- 'path',
15842
- 'pathlength',
15843
- 'patterncontentunits',
15844
- 'patterntransform',
15845
- 'patternunits',
15846
- 'points',
15847
- 'preservealpha',
15848
- 'preserveaspectratio',
15849
- 'primitiveunits',
15850
- 'r',
15851
- 'rx',
15852
- 'ry',
15853
- 'radius',
15854
- 'refx',
15855
- 'refy',
15856
- 'repeatcount',
15857
- 'repeatdur',
15858
- 'restart',
15859
- 'result',
15860
- 'rotate',
15861
- 'scale',
15862
- 'seed',
15863
- 'shape-rendering',
15864
- 'specularconstant',
15865
- 'specularexponent',
15866
- 'spreadmethod',
15867
- 'startoffset',
15868
- 'stddeviation',
15869
- 'stitchtiles',
15870
- 'stop-color',
15871
- 'stop-opacity',
15872
- 'stroke-dasharray',
15873
- 'stroke-dashoffset',
15874
- 'stroke-linecap',
15875
- 'stroke-linejoin',
15876
- 'stroke-miterlimit',
15877
- 'stroke-opacity',
15878
- 'stroke',
15879
- 'stroke-width',
15880
- 'style',
15881
- 'surfacescale',
15882
- 'systemlanguage',
15883
- 'tabindex',
15884
- 'targetx',
15885
- 'targety',
15886
- 'transform',
15887
- 'transform-origin',
15888
- 'text-anchor',
15889
- 'text-decoration',
15890
- 'text-rendering',
15891
- 'textlength',
15892
- 'type',
15893
- 'u1',
15894
- 'u2',
15895
- 'unicode',
15896
- 'values',
15897
- 'viewbox',
15898
- 'visibility',
15899
- 'version',
15900
- 'vert-adv-y',
15901
- 'vert-origin-x',
15902
- 'vert-origin-y',
15903
- 'width',
15904
- 'word-spacing',
15905
- 'wrap',
15906
- 'writing-mode',
15907
- 'xchannelselector',
15908
- 'ychannelselector',
15909
- 'x',
15910
- 'x1',
15911
- 'x2',
15912
- 'xmlns',
15913
- 'y',
15914
- 'y1',
15915
- 'y2',
15916
- 'z',
15917
- 'zoomandpan'
15918
- ]);
15919
- const mathMl = freeze([
15920
- 'accent',
15921
- 'accentunder',
15922
- 'align',
15923
- 'bevelled',
15924
- 'close',
15925
- 'columnsalign',
15926
- 'columnlines',
15927
- 'columnspan',
15928
- 'denomalign',
15929
- 'depth',
15930
- 'dir',
15931
- 'display',
15932
- 'displaystyle',
15933
- 'encoding',
15934
- 'fence',
15935
- 'frame',
15936
- 'height',
15937
- 'href',
15938
- 'id',
15939
- 'largeop',
15940
- 'length',
15941
- 'linethickness',
15942
- 'lspace',
15943
- 'lquote',
15944
- 'mathbackground',
15945
- 'mathcolor',
15946
- 'mathsize',
15947
- 'mathvariant',
15948
- 'maxsize',
15949
- 'minsize',
15950
- 'movablelimits',
15951
- 'notation',
15952
- 'numalign',
15953
- 'open',
15954
- 'rowalign',
15955
- 'rowlines',
15956
- 'rowspacing',
15957
- 'rowspan',
15958
- 'rspace',
15959
- 'rquote',
15960
- 'scriptlevel',
15961
- 'scriptminsize',
15962
- 'scriptsizemultiplier',
15963
- 'selection',
15964
- 'separator',
15965
- 'separators',
15966
- 'stretchy',
15967
- 'subscriptshift',
15968
- 'supscriptshift',
15969
- 'symmetric',
15970
- 'voffset',
15971
- 'width',
15972
- 'xmlns'
15973
- ]);
15974
- const xml = freeze([
15975
- 'xlink:href',
15976
- 'xml:id',
15977
- 'xlink:title',
15978
- 'xml:space',
15979
- 'xmlns:xlink'
15980
- ]);
15981
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
15474
+
15475
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
15476
+ 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']);
15477
+ 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']);
15478
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
15479
+
15480
+ // eslint-disable-next-line unicorn/better-regex
15481
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
15982
15482
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
15983
15483
  const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
15984
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/);
15985
- const ARIA_ATTR = seal(/^aria-[\-\w]+$/);
15986
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
15484
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
15485
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
15486
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
15487
+ );
15987
15488
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
15988
- const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
15489
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
15490
+ );
15989
15491
  const DOCTYPE_NAME = seal(/^html$/i);
15990
- var EXPRESSIONS = Object.freeze({
15492
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
15493
+
15494
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
15991
15495
  __proto__: null,
15992
15496
  MUSTACHE_EXPR: MUSTACHE_EXPR,
15993
15497
  ERB_EXPR: ERB_EXPR,
@@ -15997,13 +15501,47 @@
15997
15501
  IS_ALLOWED_URI: IS_ALLOWED_URI,
15998
15502
  IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
15999
15503
  ATTR_WHITESPACE: ATTR_WHITESPACE,
16000
- DOCTYPE_NAME: DOCTYPE_NAME
15504
+ DOCTYPE_NAME: DOCTYPE_NAME,
15505
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT
16001
15506
  });
16002
- const getGlobal = () => typeof window === 'undefined' ? null : window;
15507
+
15508
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
15509
+ const NODE_TYPE = {
15510
+ element: 1,
15511
+ attribute: 2,
15512
+ text: 3,
15513
+ cdataSection: 4,
15514
+ entityReference: 5,
15515
+ // Deprecated
15516
+ entityNode: 6,
15517
+ // Deprecated
15518
+ progressingInstruction: 7,
15519
+ comment: 8,
15520
+ document: 9,
15521
+ documentType: 10,
15522
+ documentFragment: 11,
15523
+ notation: 12 // Deprecated
15524
+ };
15525
+ const getGlobal = function getGlobal() {
15526
+ return typeof window === 'undefined' ? null : window;
15527
+ };
15528
+
15529
+ /**
15530
+ * Creates a no-op policy for internal use only.
15531
+ * Don't export this function outside this module!
15532
+ * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
15533
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15534
+ * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
15535
+ * are not supported or creating the policy failed).
15536
+ */
16003
15537
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
16004
15538
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
16005
15539
  return null;
16006
15540
  }
15541
+
15542
+ // Allow the callers to control the unique policy name
15543
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
15544
+ // Policy creation with duplicate names throws in Trusted Types.
16007
15545
  let suffix = null;
16008
15546
  const ATTR_NAME = 'data-tt-policy-suffix';
16009
15547
  if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
@@ -16020,6 +15558,9 @@
16020
15558
  }
16021
15559
  });
16022
15560
  } catch (_) {
15561
+ // Policy creation failed (most likely another DOMPurify script has
15562
+ // already run). Skip creating the policy, as this will only cause errors
15563
+ // if TT are enforced.
16023
15564
  console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
16024
15565
  return null;
16025
15566
  }
@@ -16027,21 +15568,53 @@
16027
15568
  function createDOMPurify() {
16028
15569
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
16029
15570
  const DOMPurify = root => createDOMPurify(root);
16030
- DOMPurify.version = '3.0.5';
15571
+
15572
+ /**
15573
+ * Version label, exposed for easier checks
15574
+ * if DOMPurify is up to date or not
15575
+ */
15576
+ DOMPurify.version = '3.1.7';
15577
+
15578
+ /**
15579
+ * Array of elements that DOMPurify removed during sanitation.
15580
+ * Empty if nothing was removed.
15581
+ */
16031
15582
  DOMPurify.removed = [];
16032
- if (!window || !window.document || window.document.nodeType !== 9) {
15583
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
15584
+ // Not running in a browser, provide a factory function
15585
+ // so that you can pass your own Window
16033
15586
  DOMPurify.isSupported = false;
16034
15587
  return DOMPurify;
16035
15588
  }
16036
- const originalDocument = window.document;
15589
+ let {
15590
+ document
15591
+ } = window;
15592
+ const originalDocument = document;
16037
15593
  const currentScript = originalDocument.currentScript;
16038
- let {document} = window;
16039
- const {DocumentFragment, HTMLTemplateElement, Node, Element, NodeFilter, NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap, HTMLFormElement, DOMParser, trustedTypes} = window;
15594
+ const {
15595
+ DocumentFragment,
15596
+ HTMLTemplateElement,
15597
+ Node,
15598
+ Element,
15599
+ NodeFilter,
15600
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
15601
+ HTMLFormElement,
15602
+ DOMParser,
15603
+ trustedTypes
15604
+ } = window;
16040
15605
  const ElementPrototype = Element.prototype;
16041
15606
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
15607
+ const remove = lookupGetter(ElementPrototype, 'remove');
16042
15608
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
16043
15609
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
16044
15610
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
15611
+
15612
+ // As per issue #47, the web-components registry is inherited by a
15613
+ // new document created via createHTMLDocument. As per the spec
15614
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
15615
+ // a new empty registry is used when creating a template contents owner
15616
+ // document, so we use that as our parent document to ensure nothing
15617
+ // is inherited.
16045
15618
  if (typeof HTMLTemplateElement === 'function') {
16046
15619
  const template = document.createElement('template');
16047
15620
  if (template.content && template.content.ownerDocument) {
@@ -16050,28 +15623,55 @@
16050
15623
  }
16051
15624
  let trustedTypesPolicy;
16052
15625
  let emptyHTML = '';
16053
- const {implementation, createNodeIterator, createDocumentFragment, getElementsByTagName} = document;
16054
- const {importNode} = originalDocument;
15626
+ const {
15627
+ implementation,
15628
+ createNodeIterator,
15629
+ createDocumentFragment,
15630
+ getElementsByTagName
15631
+ } = document;
15632
+ const {
15633
+ importNode
15634
+ } = originalDocument;
16055
15635
  let hooks = {};
15636
+
15637
+ /**
15638
+ * Expose whether this browser supports running the full DOMPurify.
15639
+ */
16056
15640
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
16057
- const {MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR, DATA_ATTR, ARIA_ATTR, IS_SCRIPT_OR_DATA, ATTR_WHITESPACE} = EXPRESSIONS;
16058
- let {IS_ALLOWED_URI: IS_ALLOWED_URI$1} = EXPRESSIONS;
15641
+ const {
15642
+ MUSTACHE_EXPR,
15643
+ ERB_EXPR,
15644
+ TMPLIT_EXPR,
15645
+ DATA_ATTR,
15646
+ ARIA_ATTR,
15647
+ IS_SCRIPT_OR_DATA,
15648
+ ATTR_WHITESPACE,
15649
+ CUSTOM_ELEMENT
15650
+ } = EXPRESSIONS;
15651
+ let {
15652
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
15653
+ } = EXPRESSIONS;
15654
+
15655
+ /**
15656
+ * We consider the elements and attributes below to be safe. Ideally
15657
+ * don't add any new ones but feel free to remove unwanted ones.
15658
+ */
15659
+
15660
+ /* allowed element names */
16059
15661
  let ALLOWED_TAGS = null;
16060
- const DEFAULT_ALLOWED_TAGS = addToSet({}, [
16061
- ...html$1,
16062
- ...svg$1,
16063
- ...svgFilters,
16064
- ...mathMl$1,
16065
- ...text
16066
- ]);
15662
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
15663
+
15664
+ /* Allowed attribute names */
16067
15665
  let ALLOWED_ATTR = null;
16068
- const DEFAULT_ALLOWED_ATTR = addToSet({}, [
16069
- ...html,
16070
- ...svg,
16071
- ...mathMl,
16072
- ...xml
16073
- ]);
16074
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
15666
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
15667
+
15668
+ /*
15669
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
15670
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
15671
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
15672
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
15673
+ */
15674
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create$7(null, {
16075
15675
  tagNameCheck: {
16076
15676
  writable: true,
16077
15677
  configurable: false,
@@ -16091,135 +15691,193 @@
16091
15691
  value: false
16092
15692
  }
16093
15693
  }));
15694
+
15695
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
16094
15696
  let FORBID_TAGS = null;
15697
+
15698
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
16095
15699
  let FORBID_ATTR = null;
15700
+
15701
+ /* Decide if ARIA attributes are okay */
16096
15702
  let ALLOW_ARIA_ATTR = true;
15703
+
15704
+ /* Decide if custom data attributes are okay */
16097
15705
  let ALLOW_DATA_ATTR = true;
15706
+
15707
+ /* Decide if unknown protocols are okay */
16098
15708
  let ALLOW_UNKNOWN_PROTOCOLS = false;
15709
+
15710
+ /* Decide if self-closing tags in attributes are allowed.
15711
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
16099
15712
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
15713
+
15714
+ /* Output should be safe for common template engines.
15715
+ * This means, DOMPurify removes data attributes, mustaches and ERB
15716
+ */
16100
15717
  let SAFE_FOR_TEMPLATES = false;
15718
+
15719
+ /* Output should be safe even for XML used within HTML and alike.
15720
+ * This means, DOMPurify removes comments when containing risky content.
15721
+ */
15722
+ let SAFE_FOR_XML = true;
15723
+
15724
+ /* Decide if document with <html>... should be returned */
16101
15725
  let WHOLE_DOCUMENT = false;
15726
+
15727
+ /* Track whether config is already set on this instance of DOMPurify. */
16102
15728
  let SET_CONFIG = false;
15729
+
15730
+ /* Decide if all elements (e.g. style, script) must be children of
15731
+ * document.body. By default, browsers might move them to document.head */
16103
15732
  let FORCE_BODY = false;
15733
+
15734
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
15735
+ * string (or a TrustedHTML object if Trusted Types are supported).
15736
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
15737
+ */
16104
15738
  let RETURN_DOM = false;
15739
+
15740
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
15741
+ * string (or a TrustedHTML object if Trusted Types are supported) */
16105
15742
  let RETURN_DOM_FRAGMENT = false;
15743
+
15744
+ /* Try to return a Trusted Type object instead of a string, return a string in
15745
+ * case Trusted Types are not supported */
16106
15746
  let RETURN_TRUSTED_TYPE = false;
15747
+
15748
+ /* Output should be free from DOM clobbering attacks?
15749
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
15750
+ */
16107
15751
  let SANITIZE_DOM = true;
15752
+
15753
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
15754
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
15755
+ *
15756
+ * HTML/DOM spec rules that enable DOM Clobbering:
15757
+ * - Named Access on Window (§7.3.3)
15758
+ * - DOM Tree Accessors (§3.1.5)
15759
+ * - Form Element Parent-Child Relations (§4.10.3)
15760
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
15761
+ * - HTMLCollection (§4.2.10.2)
15762
+ *
15763
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
15764
+ * with a constant string, i.e., `user-content-`
15765
+ */
16108
15766
  let SANITIZE_NAMED_PROPS = false;
16109
15767
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
15768
+
15769
+ /* Keep element content when removing element? */
16110
15770
  let KEEP_CONTENT = true;
15771
+
15772
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
15773
+ * of importing it into a new Document and returning a sanitized copy */
16111
15774
  let IN_PLACE = false;
15775
+
15776
+ /* Allow usage of profiles like html, svg and mathMl */
16112
15777
  let USE_PROFILES = {};
15778
+
15779
+ /* Tags to ignore content of when KEEP_CONTENT is true */
16113
15780
  let FORBID_CONTENTS = null;
16114
- const DEFAULT_FORBID_CONTENTS = addToSet({}, [
16115
- 'annotation-xml',
16116
- 'audio',
16117
- 'colgroup',
16118
- 'desc',
16119
- 'foreignobject',
16120
- 'head',
16121
- 'iframe',
16122
- 'math',
16123
- 'mi',
16124
- 'mn',
16125
- 'mo',
16126
- 'ms',
16127
- 'mtext',
16128
- 'noembed',
16129
- 'noframes',
16130
- 'noscript',
16131
- 'plaintext',
16132
- 'script',
16133
- 'style',
16134
- 'svg',
16135
- 'template',
16136
- 'thead',
16137
- 'title',
16138
- 'video',
16139
- 'xmp'
16140
- ]);
15781
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
15782
+
15783
+ /* Tags that are safe for data: URIs */
16141
15784
  let DATA_URI_TAGS = null;
16142
- const DEFAULT_DATA_URI_TAGS = addToSet({}, [
16143
- 'audio',
16144
- 'video',
16145
- 'img',
16146
- 'source',
16147
- 'image',
16148
- 'track'
16149
- ]);
15785
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
15786
+
15787
+ /* Attributes safe for values like "javascript:" */
16150
15788
  let URI_SAFE_ATTRIBUTES = null;
16151
- const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
16152
- 'alt',
16153
- 'class',
16154
- 'for',
16155
- 'id',
16156
- 'label',
16157
- 'name',
16158
- 'pattern',
16159
- 'placeholder',
16160
- 'role',
16161
- 'summary',
16162
- 'title',
16163
- 'value',
16164
- 'style',
16165
- 'xmlns'
16166
- ]);
15789
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
16167
15790
  const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
16168
15791
  const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
16169
15792
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
15793
+ /* Document namespace */
16170
15794
  let NAMESPACE = HTML_NAMESPACE;
16171
15795
  let IS_EMPTY_INPUT = false;
15796
+
15797
+ /* Allowed XHTML+XML namespaces */
16172
15798
  let ALLOWED_NAMESPACES = null;
16173
- const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [
16174
- MATHML_NAMESPACE,
16175
- SVG_NAMESPACE,
16176
- HTML_NAMESPACE
16177
- ], stringToString);
16178
- let PARSER_MEDIA_TYPE;
16179
- const SUPPORTED_PARSER_MEDIA_TYPES = [
16180
- 'application/xhtml+xml',
16181
- 'text/html'
16182
- ];
15799
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
15800
+
15801
+ /* Parsing of strict XHTML documents */
15802
+ let PARSER_MEDIA_TYPE = null;
15803
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
16183
15804
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
16184
- let transformCaseFunc;
15805
+ let transformCaseFunc = null;
15806
+
15807
+ /* Keep a reference to config to pass to hooks */
16185
15808
  let CONFIG = null;
15809
+
15810
+ /* Ideally, do not touch anything below this line */
15811
+ /* ______________________________________________ */
15812
+
16186
15813
  const formElement = document.createElement('form');
16187
15814
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
16188
15815
  return testValue instanceof RegExp || testValue instanceof Function;
16189
15816
  };
16190
- const _parseConfig = function _parseConfig(cfg) {
15817
+
15818
+ /**
15819
+ * _parseConfig
15820
+ *
15821
+ * @param {Object} cfg optional config literal
15822
+ */
15823
+ // eslint-disable-next-line complexity
15824
+ const _parseConfig = function _parseConfig() {
15825
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16191
15826
  if (CONFIG && CONFIG === cfg) {
16192
15827
  return;
16193
15828
  }
15829
+
15830
+ /* Shield configuration object from tampering */
16194
15831
  if (!cfg || typeof cfg !== 'object') {
16195
15832
  cfg = {};
16196
15833
  }
15834
+
15835
+ /* Shield configuration object from prototype pollution */
16197
15836
  cfg = clone(cfg);
16198
- PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
15837
+ PARSER_MEDIA_TYPE =
15838
+ // eslint-disable-next-line unicorn/prefer-includes
15839
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
15840
+
15841
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
16199
15842
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
16200
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
16201
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
16202
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
16203
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
16204
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
16205
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
16206
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
16207
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
16208
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
16209
- ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
16210
- ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
16211
- ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
16212
- ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
16213
- SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
16214
- WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
16215
- RETURN_DOM = cfg.RETURN_DOM || false;
16216
- RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
16217
- RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
16218
- FORCE_BODY = cfg.FORCE_BODY || false;
16219
- SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
16220
- SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
16221
- KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
16222
- IN_PLACE = cfg.IN_PLACE || false;
15843
+
15844
+ /* Set configuration parameters */
15845
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
15846
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
15847
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
15848
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
15849
+ // eslint-disable-line indent
15850
+ cfg.ADD_URI_SAFE_ATTR,
15851
+ // eslint-disable-line indent
15852
+ transformCaseFunc // eslint-disable-line indent
15853
+ ) // eslint-disable-line indent
15854
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
15855
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
15856
+ // eslint-disable-line indent
15857
+ cfg.ADD_DATA_URI_TAGS,
15858
+ // eslint-disable-line indent
15859
+ transformCaseFunc // eslint-disable-line indent
15860
+ ) // eslint-disable-line indent
15861
+ : DEFAULT_DATA_URI_TAGS;
15862
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
15863
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
15864
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
15865
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
15866
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
15867
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
15868
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
15869
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
15870
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
15871
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
15872
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
15873
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
15874
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
15875
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
15876
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
15877
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
15878
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
15879
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
15880
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
16223
15881
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
16224
15882
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
16225
15883
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
@@ -16238,8 +15896,10 @@
16238
15896
  if (RETURN_DOM_FRAGMENT) {
16239
15897
  RETURN_DOM = true;
16240
15898
  }
15899
+
15900
+ /* Parse profile info */
16241
15901
  if (USE_PROFILES) {
16242
- ALLOWED_TAGS = addToSet({}, [...text]);
15902
+ ALLOWED_TAGS = addToSet({}, text);
16243
15903
  ALLOWED_ATTR = [];
16244
15904
  if (USE_PROFILES.html === true) {
16245
15905
  addToSet(ALLOWED_TAGS, html$1);
@@ -16261,6 +15921,8 @@
16261
15921
  addToSet(ALLOWED_ATTR, xml);
16262
15922
  }
16263
15923
  }
15924
+
15925
+ /* Merge configuration parameters */
16264
15926
  if (cfg.ADD_TAGS) {
16265
15927
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
16266
15928
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
@@ -16282,16 +15944,18 @@
16282
15944
  }
16283
15945
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
16284
15946
  }
15947
+
15948
+ /* Add #text in case KEEP_CONTENT is set to true */
16285
15949
  if (KEEP_CONTENT) {
16286
15950
  ALLOWED_TAGS['#text'] = true;
16287
15951
  }
15952
+
15953
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
16288
15954
  if (WHOLE_DOCUMENT) {
16289
- addToSet(ALLOWED_TAGS, [
16290
- 'html',
16291
- 'head',
16292
- 'body'
16293
- ]);
15955
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
16294
15956
  }
15957
+
15958
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
16295
15959
  if (ALLOWED_TAGS.table) {
16296
15960
  addToSet(ALLOWED_TAGS, ['tbody']);
16297
15961
  delete FORBID_TAGS.tbody;
@@ -16303,48 +15967,57 @@
16303
15967
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
16304
15968
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
16305
15969
  }
15970
+
15971
+ // Overwrite existing TrustedTypes policy.
16306
15972
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
15973
+
15974
+ // Sign local variables required by `sanitize`.
16307
15975
  emptyHTML = trustedTypesPolicy.createHTML('');
16308
15976
  } else {
15977
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
16309
15978
  if (trustedTypesPolicy === undefined) {
16310
15979
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
16311
15980
  }
15981
+
15982
+ // If creating the internal policy succeeded sign internal variables.
16312
15983
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
16313
15984
  emptyHTML = trustedTypesPolicy.createHTML('');
16314
15985
  }
16315
15986
  }
15987
+
15988
+ // Prevent further manipulation of configuration.
15989
+ // Not available in IE8, Safari 5, etc.
16316
15990
  if (freeze) {
16317
15991
  freeze(cfg);
16318
15992
  }
16319
15993
  CONFIG = cfg;
16320
15994
  };
16321
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
16322
- 'mi',
16323
- 'mo',
16324
- 'mn',
16325
- 'ms',
16326
- 'mtext'
16327
- ]);
16328
- const HTML_INTEGRATION_POINTS = addToSet({}, [
16329
- 'foreignobject',
16330
- 'desc',
16331
- 'title',
16332
- 'annotation-xml'
16333
- ]);
16334
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
16335
- 'title',
16336
- 'style',
16337
- 'font',
16338
- 'a',
16339
- 'script'
16340
- ]);
16341
- const ALL_SVG_TAGS = addToSet({}, svg$1);
16342
- addToSet(ALL_SVG_TAGS, svgFilters);
16343
- addToSet(ALL_SVG_TAGS, svgDisallowed);
16344
- const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
16345
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
15995
+ const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
15996
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
15997
+
15998
+ // Certain elements are allowed in both SVG and HTML
15999
+ // namespace. We need to specify them explicitly
16000
+ // so that they don't get erroneously deleted from
16001
+ // HTML namespace.
16002
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
16003
+
16004
+ /* Keep track of all possible SVG and MathML tags
16005
+ * so that we can perform the namespace checks
16006
+ * correctly. */
16007
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
16008
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
16009
+
16010
+ /**
16011
+ * @param {Element} element a DOM element whose namespace is being checked
16012
+ * @returns {boolean} Return false if the element has a
16013
+ * namespace that a spec-compliant parser would never
16014
+ * return. Return true otherwise.
16015
+ */
16346
16016
  const _checkValidNamespace = function _checkValidNamespace(element) {
16347
16017
  let parent = getParentNode(element);
16018
+
16019
+ // In JSDOM, if we're inside shadow DOM, then parentNode
16020
+ // can be null. We just simulate parent in this case.
16348
16021
  if (!parent || !parent.tagName) {
16349
16022
  parent = {
16350
16023
  namespaceURI: NAMESPACE,
@@ -16357,45 +16030,93 @@
16357
16030
  return false;
16358
16031
  }
16359
16032
  if (element.namespaceURI === SVG_NAMESPACE) {
16033
+ // The only way to switch from HTML namespace to SVG
16034
+ // is via <svg>. If it happens via any other tag, then
16035
+ // it should be killed.
16360
16036
  if (parent.namespaceURI === HTML_NAMESPACE) {
16361
16037
  return tagName === 'svg';
16362
16038
  }
16039
+
16040
+ // The only way to switch from MathML to SVG is via`
16041
+ // svg if parent is either <annotation-xml> or MathML
16042
+ // text integration points.
16363
16043
  if (parent.namespaceURI === MATHML_NAMESPACE) {
16364
16044
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
16365
16045
  }
16046
+
16047
+ // We only allow elements that are defined in SVG
16048
+ // spec. All others are disallowed in SVG namespace.
16366
16049
  return Boolean(ALL_SVG_TAGS[tagName]);
16367
16050
  }
16368
16051
  if (element.namespaceURI === MATHML_NAMESPACE) {
16052
+ // The only way to switch from HTML namespace to MathML
16053
+ // is via <math>. If it happens via any other tag, then
16054
+ // it should be killed.
16369
16055
  if (parent.namespaceURI === HTML_NAMESPACE) {
16370
16056
  return tagName === 'math';
16371
16057
  }
16058
+
16059
+ // The only way to switch from SVG to MathML is via
16060
+ // <math> and HTML integration points
16372
16061
  if (parent.namespaceURI === SVG_NAMESPACE) {
16373
16062
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
16374
16063
  }
16064
+
16065
+ // We only allow elements that are defined in MathML
16066
+ // spec. All others are disallowed in MathML namespace.
16375
16067
  return Boolean(ALL_MATHML_TAGS[tagName]);
16376
16068
  }
16377
16069
  if (element.namespaceURI === HTML_NAMESPACE) {
16070
+ // The only way to switch from SVG to HTML is via
16071
+ // HTML integration points, and from MathML to HTML
16072
+ // is via MathML text integration points
16378
16073
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
16379
16074
  return false;
16380
16075
  }
16381
16076
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
16382
16077
  return false;
16383
16078
  }
16079
+
16080
+ // We disallow tags that are specific for MathML
16081
+ // or SVG and should never appear in HTML namespace
16384
16082
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
16385
16083
  }
16084
+
16085
+ // For XHTML and XML documents that support custom namespaces
16386
16086
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
16387
16087
  return true;
16388
16088
  }
16089
+
16090
+ // The code should never reach this place (this means
16091
+ // that the element somehow got namespace that is not
16092
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
16093
+ // Return false just in case.
16389
16094
  return false;
16390
16095
  };
16096
+
16097
+ /**
16098
+ * _forceRemove
16099
+ *
16100
+ * @param {Node} node a DOM node
16101
+ */
16391
16102
  const _forceRemove = function _forceRemove(node) {
16392
- arrayPush(DOMPurify.removed, { element: node });
16103
+ arrayPush(DOMPurify.removed, {
16104
+ element: node
16105
+ });
16393
16106
  try {
16394
- node.parentNode.removeChild(node);
16107
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
16108
+ getParentNode(node).removeChild(node);
16395
16109
  } catch (_) {
16396
- node.remove();
16110
+ remove(node);
16397
16111
  }
16398
16112
  };
16113
+
16114
+ /**
16115
+ * _removeAttribute
16116
+ *
16117
+ * @param {String} name an Attribute name
16118
+ * @param {Node} node a DOM node
16119
+ */
16399
16120
  const _removeAttribute = function _removeAttribute(name, node) {
16400
16121
  try {
16401
16122
  arrayPush(DOMPurify.removed, {
@@ -16409,64 +16130,114 @@
16409
16130
  });
16410
16131
  }
16411
16132
  node.removeAttribute(name);
16133
+
16134
+ // We void attribute values for unremovable "is"" attributes
16412
16135
  if (name === 'is' && !ALLOWED_ATTR[name]) {
16413
16136
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
16414
16137
  try {
16415
16138
  _forceRemove(node);
16416
- } catch (_) {
16417
- }
16139
+ } catch (_) {}
16418
16140
  } else {
16419
16141
  try {
16420
16142
  node.setAttribute(name, '');
16421
- } catch (_) {
16422
- }
16143
+ } catch (_) {}
16423
16144
  }
16424
16145
  }
16425
16146
  };
16147
+
16148
+ /**
16149
+ * _initDocument
16150
+ *
16151
+ * @param {String} dirty a string of dirty markup
16152
+ * @return {Document} a DOM, filled with the dirty markup
16153
+ */
16426
16154
  const _initDocument = function _initDocument(dirty) {
16427
- let doc;
16428
- let leadingWhitespace;
16155
+ /* Create a HTML document */
16156
+ let doc = null;
16157
+ let leadingWhitespace = null;
16429
16158
  if (FORCE_BODY) {
16430
16159
  dirty = '<remove></remove>' + dirty;
16431
16160
  } else {
16161
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
16432
16162
  const matches = stringMatch(dirty, /^[\r\n\t ]+/);
16433
16163
  leadingWhitespace = matches && matches[0];
16434
16164
  }
16435
16165
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
16166
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
16436
16167
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
16437
16168
  }
16438
16169
  const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
16170
+ /*
16171
+ * Use the DOMParser API by default, fallback later if needs be
16172
+ * DOMParser not work for svg when has multiple root element.
16173
+ */
16439
16174
  if (NAMESPACE === HTML_NAMESPACE) {
16440
16175
  try {
16441
16176
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
16442
- } catch (_) {
16443
- }
16177
+ } catch (_) {}
16444
16178
  }
16179
+
16180
+ /* Use createHTMLDocument in case DOMParser is not available */
16445
16181
  if (!doc || !doc.documentElement) {
16446
16182
  doc = implementation.createDocument(NAMESPACE, 'template', null);
16447
16183
  try {
16448
16184
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
16449
16185
  } catch (_) {
16186
+ // Syntax error if dirtyPayload is invalid xml
16450
16187
  }
16451
16188
  }
16452
16189
  const body = doc.body || doc.documentElement;
16453
16190
  if (dirty && leadingWhitespace) {
16454
16191
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
16455
16192
  }
16193
+
16194
+ /* Work on whole document or just its body */
16456
16195
  if (NAMESPACE === HTML_NAMESPACE) {
16457
16196
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
16458
16197
  }
16459
16198
  return WHOLE_DOCUMENT ? doc.documentElement : body;
16460
16199
  };
16461
- const _createIterator = function _createIterator(root) {
16462
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
16463
- };
16200
+
16201
+ /**
16202
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
16203
+ *
16204
+ * @param {Node} root The root element or node to start traversing on.
16205
+ * @return {NodeIterator} The created NodeIterator
16206
+ */
16207
+ const _createNodeIterator = function _createNodeIterator(root) {
16208
+ return createNodeIterator.call(root.ownerDocument || root, root,
16209
+ // eslint-disable-next-line no-bitwise
16210
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
16211
+ };
16212
+
16213
+ /**
16214
+ * _isClobbered
16215
+ *
16216
+ * @param {Node} elm element to check for clobbering attacks
16217
+ * @return {Boolean} true if clobbered, false if safe
16218
+ */
16464
16219
  const _isClobbered = function _isClobbered(elm) {
16465
16220
  return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
16466
16221
  };
16222
+
16223
+ /**
16224
+ * Checks whether the given object is a DOM node.
16225
+ *
16226
+ * @param {Node} object object to check whether it's a DOM node
16227
+ * @return {Boolean} true is object is a DOM node
16228
+ */
16467
16229
  const _isNode = function _isNode(object) {
16468
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
16230
+ return typeof Node === 'function' && object instanceof Node;
16469
16231
  };
16232
+
16233
+ /**
16234
+ * _executeHook
16235
+ * Execute user configurable hooks
16236
+ *
16237
+ * @param {String} entryPoint Name of the hook's entry point
16238
+ * @param {Node} currentNode node to work on with the hook
16239
+ * @param {Object} data additional hook parameters
16240
+ */
16470
16241
  const _executeHook = function _executeHook(entryPoint, currentNode, data) {
16471
16242
  if (!hooks[entryPoint]) {
16472
16243
  return;
@@ -16475,93 +16246,184 @@
16475
16246
  hook.call(DOMPurify, currentNode, data, CONFIG);
16476
16247
  });
16477
16248
  };
16249
+
16250
+ /**
16251
+ * _sanitizeElements
16252
+ *
16253
+ * @protect nodeName
16254
+ * @protect textContent
16255
+ * @protect removeChild
16256
+ *
16257
+ * @param {Node} currentNode to check for permission to exist
16258
+ * @return {Boolean} true if node was killed, false if left alive
16259
+ */
16478
16260
  const _sanitizeElements = function _sanitizeElements(currentNode) {
16479
- let content;
16261
+ let content = null;
16262
+
16263
+ /* Execute a hook if present */
16480
16264
  _executeHook('beforeSanitizeElements', currentNode, null);
16265
+
16266
+ /* Check if element is clobbered or can clobber */
16481
16267
  if (_isClobbered(currentNode)) {
16482
16268
  _forceRemove(currentNode);
16483
16269
  return true;
16484
16270
  }
16271
+
16272
+ /* Now let's check the element's type and name */
16485
16273
  const tagName = transformCaseFunc(currentNode.nodeName);
16274
+
16275
+ /* Execute a hook if present */
16486
16276
  _executeHook('uponSanitizeElement', currentNode, {
16487
16277
  tagName,
16488
16278
  allowedTags: ALLOWED_TAGS
16489
16279
  });
16490
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16280
+
16281
+ /* Detect mXSS attempts abusing namespace confusion */
16282
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16283
+ _forceRemove(currentNode);
16284
+ return true;
16285
+ }
16286
+
16287
+ /* Remove any occurrence of processing instructions */
16288
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
16491
16289
  _forceRemove(currentNode);
16492
16290
  return true;
16493
16291
  }
16292
+
16293
+ /* Remove any kind of possibly harmful comments */
16294
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
16295
+ _forceRemove(currentNode);
16296
+ return true;
16297
+ }
16298
+
16299
+ /* Remove element if anything forbids its presence */
16494
16300
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
16495
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
16496
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))
16301
+ /* Check if we have a custom element to handle */
16302
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
16303
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
16497
16304
  return false;
16498
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName))
16305
+ }
16306
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
16499
16307
  return false;
16308
+ }
16500
16309
  }
16310
+
16311
+ /* Keep content except for bad-listed elements */
16501
16312
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
16502
16313
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
16503
16314
  const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
16504
16315
  if (childNodes && parentNode) {
16505
16316
  const childCount = childNodes.length;
16506
16317
  for (let i = childCount - 1; i >= 0; --i) {
16507
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
16318
+ const childClone = cloneNode(childNodes[i], true);
16319
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
16320
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
16508
16321
  }
16509
16322
  }
16510
16323
  }
16511
16324
  _forceRemove(currentNode);
16512
16325
  return true;
16513
16326
  }
16327
+
16328
+ /* Check whether element has a valid namespace */
16514
16329
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
16515
16330
  _forceRemove(currentNode);
16516
16331
  return true;
16517
16332
  }
16333
+
16334
+ /* Make sure that older browsers don't get fallback-tag mXSS */
16518
16335
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
16519
16336
  _forceRemove(currentNode);
16520
16337
  return true;
16521
16338
  }
16522
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
16339
+
16340
+ /* Sanitize element content to be template-safe */
16341
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
16342
+ /* Get the element's text content */
16523
16343
  content = currentNode.textContent;
16524
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
16525
- content = stringReplace(content, ERB_EXPR, ' ');
16526
- content = stringReplace(content, TMPLIT_EXPR, ' ');
16344
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16345
+ content = stringReplace(content, expr, ' ');
16346
+ });
16527
16347
  if (currentNode.textContent !== content) {
16528
- arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
16348
+ arrayPush(DOMPurify.removed, {
16349
+ element: currentNode.cloneNode()
16350
+ });
16529
16351
  currentNode.textContent = content;
16530
16352
  }
16531
16353
  }
16354
+
16355
+ /* Execute a hook if present */
16532
16356
  _executeHook('afterSanitizeElements', currentNode, null);
16533
16357
  return false;
16534
16358
  };
16359
+
16360
+ /**
16361
+ * _isValidAttribute
16362
+ *
16363
+ * @param {string} lcTag Lowercase tag name of containing element.
16364
+ * @param {string} lcName Lowercase attribute name.
16365
+ * @param {string} value Attribute value.
16366
+ * @return {Boolean} Returns true if `value` is valid, otherwise false.
16367
+ */
16368
+ // eslint-disable-next-line complexity
16535
16369
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
16370
+ /* Make sure attribute cannot clobber */
16536
16371
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
16537
16372
  return false;
16538
16373
  }
16539
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName));
16540
- else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName));
16541
- else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
16542
- if (_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value)));
16543
- else {
16374
+
16375
+ /* Allow valid data-* attributes: At least one character after "-"
16376
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
16377
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
16378
+ We don't need to check the value; it's always URI safe. */
16379
+ 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]) {
16380
+ if (
16381
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
16382
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
16383
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
16384
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
16385
+ // Alternative, second condition checks if it's an `is`-attribute, AND
16386
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
16387
+ lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
16544
16388
  return false;
16545
16389
  }
16546
- } else if (URI_SAFE_ATTRIBUTES[lcName]);
16547
- else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, '')));
16548
- else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);
16549
- else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, '')));
16550
- else if (value) {
16390
+ /* Check value is safe. First, is attr inert? If so, is safe */
16391
+ } 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) {
16551
16392
  return false;
16552
16393
  } else ;
16553
16394
  return true;
16554
16395
  };
16555
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
16556
- return tagName.indexOf('-') > 0;
16557
- };
16396
+
16397
+ /**
16398
+ * _isBasicCustomElement
16399
+ * checks if at least one dash is included in tagName, and it's not the first char
16400
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
16401
+ *
16402
+ * @param {string} tagName name of the tag of the node to sanitize
16403
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16404
+ */
16405
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
16406
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
16407
+ };
16408
+
16409
+ /**
16410
+ * _sanitizeAttributes
16411
+ *
16412
+ * @protect attributes
16413
+ * @protect nodeName
16414
+ * @protect removeAttribute
16415
+ * @protect setAttribute
16416
+ *
16417
+ * @param {Node} currentNode to sanitize
16418
+ */
16558
16419
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
16559
- let attr;
16560
- let value;
16561
- let lcName;
16562
- let l;
16420
+ /* Execute a hook if present */
16563
16421
  _executeHook('beforeSanitizeAttributes', currentNode, null);
16564
- const {attributes} = currentNode;
16422
+ const {
16423
+ attributes
16424
+ } = currentNode;
16425
+
16426
+ /* Check if we have attributes; if not we might have a text node */
16565
16427
  if (!attributes) {
16566
16428
  return;
16567
16429
  }
@@ -16571,99 +16433,174 @@
16571
16433
  keepAttr: true,
16572
16434
  allowedAttributes: ALLOWED_ATTR
16573
16435
  };
16574
- l = attributes.length;
16436
+ let l = attributes.length;
16437
+
16438
+ /* Go backwards over all attributes; safely remove bad ones */
16575
16439
  while (l--) {
16576
- attr = attributes[l];
16577
- const {name, namespaceURI} = attr;
16578
- value = name === 'value' ? attr.value : stringTrim(attr.value);
16440
+ const attr = attributes[l];
16441
+ const {
16442
+ name,
16443
+ namespaceURI,
16444
+ value: attrValue
16445
+ } = attr;
16446
+ const lcName = transformCaseFunc(name);
16447
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
16579
16448
  const initValue = value;
16580
- lcName = transformCaseFunc(name);
16449
+
16450
+ /* Execute a hook if present */
16581
16451
  hookEvent.attrName = lcName;
16582
16452
  hookEvent.attrValue = value;
16583
16453
  hookEvent.keepAttr = true;
16584
- hookEvent.forceKeepAttr = undefined;
16454
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
16585
16455
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
16586
16456
  value = hookEvent.attrValue;
16457
+
16458
+ /* Did the hooks approve of the attribute? */
16587
16459
  if (hookEvent.forceKeepAttr) {
16588
16460
  continue;
16589
16461
  }
16462
+
16463
+ /* Remove attribute */
16464
+
16465
+ /* Did the hooks approve of the attribute? */
16590
16466
  if (!hookEvent.keepAttr) {
16591
16467
  _removeAttribute(name, currentNode);
16592
16468
  continue;
16593
16469
  }
16470
+
16471
+ /* Work around a security issue in jQuery 3.0 */
16594
16472
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
16595
16473
  _removeAttribute(name, currentNode);
16596
16474
  continue;
16597
16475
  }
16476
+
16477
+ /* Sanitize attribute content to be template-safe */
16598
16478
  if (SAFE_FOR_TEMPLATES) {
16599
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
16600
- value = stringReplace(value, ERB_EXPR, ' ');
16601
- value = stringReplace(value, TMPLIT_EXPR, ' ');
16479
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16480
+ value = stringReplace(value, expr, ' ');
16481
+ });
16602
16482
  }
16483
+
16484
+ /* Is `value` valid for this attribute? */
16603
16485
  const lcTag = transformCaseFunc(currentNode.nodeName);
16604
16486
  if (!_isValidAttribute(lcTag, lcName, value)) {
16605
16487
  _removeAttribute(name, currentNode);
16606
16488
  continue;
16607
16489
  }
16490
+
16491
+ /* Full DOM Clobbering protection via namespace isolation,
16492
+ * Prefix id and name attributes with `user-content-`
16493
+ */
16608
16494
  if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16495
+ // Remove the attribute with this value
16609
16496
  _removeAttribute(name, currentNode);
16497
+
16498
+ // Prefix the value and later re-create the attribute with the sanitized value
16610
16499
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
16611
16500
  }
16501
+
16502
+ /* Work around a security issue with comments inside attributes */
16503
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16504
+ _removeAttribute(name, currentNode);
16505
+ continue;
16506
+ }
16507
+
16508
+ /* Handle attributes that require Trusted Types */
16612
16509
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
16613
- if (namespaceURI);
16614
- else {
16510
+ if (namespaceURI) ; else {
16615
16511
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
16616
- case 'TrustedHTML': {
16617
- value = trustedTypesPolicy.createHTML(value);
16618
- break;
16619
- }
16620
- case 'TrustedScriptURL': {
16621
- value = trustedTypesPolicy.createScriptURL(value);
16622
- break;
16623
- }
16512
+ case 'TrustedHTML':
16513
+ {
16514
+ value = trustedTypesPolicy.createHTML(value);
16515
+ break;
16516
+ }
16517
+ case 'TrustedScriptURL':
16518
+ {
16519
+ value = trustedTypesPolicy.createScriptURL(value);
16520
+ break;
16521
+ }
16624
16522
  }
16625
16523
  }
16626
16524
  }
16525
+
16526
+ /* Handle invalid data-* attribute set by try-catching it */
16627
16527
  if (value !== initValue) {
16628
16528
  try {
16629
16529
  if (namespaceURI) {
16630
16530
  currentNode.setAttributeNS(namespaceURI, name, value);
16631
16531
  } else {
16532
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
16632
16533
  currentNode.setAttribute(name, value);
16633
16534
  }
16634
- } catch (_) {
16635
- _removeAttribute(name, currentNode);
16636
- }
16535
+ if (_isClobbered(currentNode)) {
16536
+ _forceRemove(currentNode);
16537
+ } else {
16538
+ arrayPop(DOMPurify.removed);
16539
+ }
16540
+ } catch (_) {}
16637
16541
  }
16638
16542
  }
16543
+
16544
+ /* Execute a hook if present */
16639
16545
  _executeHook('afterSanitizeAttributes', currentNode, null);
16640
16546
  };
16547
+
16548
+ /**
16549
+ * _sanitizeShadowDOM
16550
+ *
16551
+ * @param {DocumentFragment} fragment to iterate over recursively
16552
+ */
16641
16553
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
16642
- let shadowNode;
16643
- const shadowIterator = _createIterator(fragment);
16554
+ let shadowNode = null;
16555
+ const shadowIterator = _createNodeIterator(fragment);
16556
+
16557
+ /* Execute a hook if present */
16644
16558
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
16645
16559
  while (shadowNode = shadowIterator.nextNode()) {
16560
+ /* Execute a hook if present */
16646
16561
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
16562
+
16563
+ /* Sanitize tags and elements */
16647
16564
  if (_sanitizeElements(shadowNode)) {
16648
16565
  continue;
16649
16566
  }
16567
+
16568
+ /* Deep shadow DOM detected */
16650
16569
  if (shadowNode.content instanceof DocumentFragment) {
16651
16570
  _sanitizeShadowDOM(shadowNode.content);
16652
16571
  }
16572
+
16573
+ /* Check attributes, sanitize if necessary */
16653
16574
  _sanitizeAttributes(shadowNode);
16654
16575
  }
16576
+
16577
+ /* Execute a hook if present */
16655
16578
  _executeHook('afterSanitizeShadowDOM', fragment, null);
16656
16579
  };
16580
+
16581
+ /**
16582
+ * Sanitize
16583
+ * Public method providing core sanitation functionality
16584
+ *
16585
+ * @param {String|Node} dirty string or DOM node
16586
+ * @param {Object} cfg object
16587
+ */
16588
+ // eslint-disable-next-line complexity
16657
16589
  DOMPurify.sanitize = function (dirty) {
16658
16590
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16659
- let body;
16660
- let importedNode;
16661
- let currentNode;
16662
- let returnNode;
16591
+ let body = null;
16592
+ let importedNode = null;
16593
+ let currentNode = null;
16594
+ let returnNode = null;
16595
+ /* Make sure we have a string to sanitize.
16596
+ DO NOT return early, as this will return the wrong type if
16597
+ the user has requested a DOM object rather than a string */
16663
16598
  IS_EMPTY_INPUT = !dirty;
16664
16599
  if (IS_EMPTY_INPUT) {
16665
16600
  dirty = '<!-->';
16666
16601
  }
16602
+
16603
+ /* Stringify, in case dirty is an object */
16667
16604
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
16668
16605
  if (typeof dirty.toString === 'function') {
16669
16606
  dirty = dirty.toString();
@@ -16674,17 +16611,26 @@
16674
16611
  throw typeErrorCreate('toString is not a function');
16675
16612
  }
16676
16613
  }
16614
+
16615
+ /* Return dirty HTML if DOMPurify cannot run */
16677
16616
  if (!DOMPurify.isSupported) {
16678
16617
  return dirty;
16679
16618
  }
16619
+
16620
+ /* Assign config vars */
16680
16621
  if (!SET_CONFIG) {
16681
16622
  _parseConfig(cfg);
16682
16623
  }
16624
+
16625
+ /* Clean up removed elements */
16683
16626
  DOMPurify.removed = [];
16627
+
16628
+ /* Check if dirty is correctly typed for IN_PLACE */
16684
16629
  if (typeof dirty === 'string') {
16685
16630
  IN_PLACE = false;
16686
16631
  }
16687
16632
  if (IN_PLACE) {
16633
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
16688
16634
  if (dirty.nodeName) {
16689
16635
  const tagName = transformCaseFunc(dirty.nodeName);
16690
16636
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
@@ -16692,74 +16638,138 @@
16692
16638
  }
16693
16639
  }
16694
16640
  } else if (dirty instanceof Node) {
16641
+ /* If dirty is a DOM element, append to an empty document to avoid
16642
+ elements being stripped by the parser */
16695
16643
  body = _initDocument('<!---->');
16696
16644
  importedNode = body.ownerDocument.importNode(dirty, true);
16697
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
16645
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
16646
+ /* Node is already a body, use as is */
16698
16647
  body = importedNode;
16699
16648
  } else if (importedNode.nodeName === 'HTML') {
16700
16649
  body = importedNode;
16701
16650
  } else {
16651
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
16702
16652
  body.appendChild(importedNode);
16703
16653
  }
16704
16654
  } else {
16705
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
16655
+ /* Exit directly if we have nothing to do */
16656
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
16657
+ // eslint-disable-next-line unicorn/prefer-includes
16658
+ dirty.indexOf('<') === -1) {
16706
16659
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
16707
16660
  }
16661
+
16662
+ /* Initialize the document to work on */
16708
16663
  body = _initDocument(dirty);
16664
+
16665
+ /* Check we have a DOM node from the data */
16709
16666
  if (!body) {
16710
16667
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
16711
16668
  }
16712
16669
  }
16670
+
16671
+ /* Remove first element node (ours) if FORCE_BODY is set */
16713
16672
  if (body && FORCE_BODY) {
16714
16673
  _forceRemove(body.firstChild);
16715
16674
  }
16716
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
16675
+
16676
+ /* Get node iterator */
16677
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
16678
+
16679
+ /* Now start iterating over the created document */
16717
16680
  while (currentNode = nodeIterator.nextNode()) {
16681
+ /* Sanitize tags and elements */
16718
16682
  if (_sanitizeElements(currentNode)) {
16719
16683
  continue;
16720
16684
  }
16685
+
16686
+ /* Shadow DOM detected, sanitize it */
16721
16687
  if (currentNode.content instanceof DocumentFragment) {
16722
16688
  _sanitizeShadowDOM(currentNode.content);
16723
16689
  }
16690
+
16691
+ /* Check attributes, sanitize if necessary */
16724
16692
  _sanitizeAttributes(currentNode);
16725
16693
  }
16694
+
16695
+ /* If we sanitized `dirty` in-place, return it. */
16726
16696
  if (IN_PLACE) {
16727
16697
  return dirty;
16728
16698
  }
16699
+
16700
+ /* Return sanitized string or DOM */
16729
16701
  if (RETURN_DOM) {
16730
16702
  if (RETURN_DOM_FRAGMENT) {
16731
16703
  returnNode = createDocumentFragment.call(body.ownerDocument);
16732
16704
  while (body.firstChild) {
16705
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
16733
16706
  returnNode.appendChild(body.firstChild);
16734
16707
  }
16735
16708
  } else {
16736
16709
  returnNode = body;
16737
16710
  }
16738
16711
  if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
16712
+ /*
16713
+ AdoptNode() is not used because internal state is not reset
16714
+ (e.g. the past names map of a HTMLFormElement), this is safe
16715
+ in theory but we would rather not risk another attack vector.
16716
+ The state that is cloned by importNode() is explicitly defined
16717
+ by the specs.
16718
+ */
16739
16719
  returnNode = importNode.call(originalDocument, returnNode, true);
16740
16720
  }
16741
16721
  return returnNode;
16742
16722
  }
16743
16723
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
16724
+
16725
+ /* Serialize doctype if allowed */
16744
16726
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
16745
16727
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
16746
16728
  }
16729
+
16730
+ /* Sanitize final string template-safe */
16747
16731
  if (SAFE_FOR_TEMPLATES) {
16748
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
16749
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
16750
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
16732
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16733
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
16734
+ });
16751
16735
  }
16752
16736
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
16753
16737
  };
16754
- DOMPurify.setConfig = function (cfg) {
16738
+
16739
+ /**
16740
+ * Public method to set the configuration once
16741
+ * setConfig
16742
+ *
16743
+ * @param {Object} cfg configuration object
16744
+ */
16745
+ DOMPurify.setConfig = function () {
16746
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16755
16747
  _parseConfig(cfg);
16756
16748
  SET_CONFIG = true;
16757
16749
  };
16750
+
16751
+ /**
16752
+ * Public method to remove the configuration
16753
+ * clearConfig
16754
+ *
16755
+ */
16758
16756
  DOMPurify.clearConfig = function () {
16759
16757
  CONFIG = null;
16760
16758
  SET_CONFIG = false;
16761
16759
  };
16760
+
16761
+ /**
16762
+ * Public method to check if an attribute value is valid.
16763
+ * Uses last set config, if any. Otherwise, uses config defaults.
16764
+ * isValidAttribute
16765
+ *
16766
+ * @param {String} tag Tag name of containing element.
16767
+ * @param {String} attr Attribute name.
16768
+ * @param {String} value Attribute value.
16769
+ * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
16770
+ */
16762
16771
  DOMPurify.isValidAttribute = function (tag, attr, value) {
16772
+ /* Initialize shared config vars if necessary. */
16763
16773
  if (!CONFIG) {
16764
16774
  _parseConfig({});
16765
16775
  }
@@ -16767,6 +16777,14 @@
16767
16777
  const lcName = transformCaseFunc(attr);
16768
16778
  return _isValidAttribute(lcTag, lcName, value);
16769
16779
  };
16780
+
16781
+ /**
16782
+ * AddHook
16783
+ * Public method to add DOMPurify hooks
16784
+ *
16785
+ * @param {String} entryPoint entry point for the hook to add
16786
+ * @param {Function} hookFunction function to execute
16787
+ */
16770
16788
  DOMPurify.addHook = function (entryPoint, hookFunction) {
16771
16789
  if (typeof hookFunction !== 'function') {
16772
16790
  return;
@@ -16774,16 +16792,37 @@
16774
16792
  hooks[entryPoint] = hooks[entryPoint] || [];
16775
16793
  arrayPush(hooks[entryPoint], hookFunction);
16776
16794
  };
16795
+
16796
+ /**
16797
+ * RemoveHook
16798
+ * Public method to remove a DOMPurify hook at a given entryPoint
16799
+ * (pops it from the stack of hooks if more are present)
16800
+ *
16801
+ * @param {String} entryPoint entry point for the hook to remove
16802
+ * @return {Function} removed(popped) hook
16803
+ */
16777
16804
  DOMPurify.removeHook = function (entryPoint) {
16778
16805
  if (hooks[entryPoint]) {
16779
16806
  return arrayPop(hooks[entryPoint]);
16780
16807
  }
16781
16808
  };
16809
+
16810
+ /**
16811
+ * RemoveHooks
16812
+ * Public method to remove all DOMPurify hooks at a given entryPoint
16813
+ *
16814
+ * @param {String} entryPoint entry point for the hooks to remove
16815
+ */
16782
16816
  DOMPurify.removeHooks = function (entryPoint) {
16783
16817
  if (hooks[entryPoint]) {
16784
16818
  hooks[entryPoint] = [];
16785
16819
  }
16786
16820
  };
16821
+
16822
+ /**
16823
+ * RemoveAllHooks
16824
+ * Public method to remove all DOMPurify hooks
16825
+ */
16787
16826
  DOMPurify.removeAllHooks = function () {
16788
16827
  hooks = {};
16789
16828
  };
@@ -17219,7 +17258,8 @@
17219
17258
  '#cdata-section',
17220
17259
  'body'
17221
17260
  ],
17222
- ALLOWED_ATTR: []
17261
+ ALLOWED_ATTR: [],
17262
+ SAFE_FOR_XML: false
17223
17263
  };
17224
17264
  const config = { ...basePurifyConfig };
17225
17265
  config.PARSER_MEDIA_TYPE = mimeType;
@@ -17230,37 +17270,55 @@
17230
17270
  }
17231
17271
  return config;
17232
17272
  };
17233
- const sanitizeNamespaceElement = ele => {
17273
+ const sanitizeSvgElement = ele => {
17274
+ const xlinkAttrs = [
17275
+ 'type',
17276
+ 'href',
17277
+ 'role',
17278
+ 'arcrole',
17279
+ 'title',
17280
+ 'show',
17281
+ 'actuate',
17282
+ 'label',
17283
+ 'from',
17284
+ 'to'
17285
+ ].map(name => `xlink:${ name }`);
17286
+ const config = {
17287
+ IN_PLACE: true,
17288
+ USE_PROFILES: {
17289
+ html: true,
17290
+ svg: true,
17291
+ svgFilters: true
17292
+ },
17293
+ ALLOWED_ATTR: xlinkAttrs
17294
+ };
17295
+ purify().sanitize(ele, config);
17296
+ };
17297
+ const sanitizeMathmlElement = (node, settings) => {
17298
+ const config = {
17299
+ IN_PLACE: true,
17300
+ USE_PROFILES: { mathMl: true }
17301
+ };
17302
+ const purify$1 = purify();
17303
+ purify$1.addHook('uponSanitizeElement', (node, evt) => {
17304
+ var _a;
17305
+ const lcTagName = (_a = evt.tagName) !== null && _a !== void 0 ? _a : node.nodeName.toLowerCase();
17306
+ const allowedEncodings = settings.allow_mathml_annotation_encodings;
17307
+ if (lcTagName === 'annotation' && isArray$1(allowedEncodings) && allowedEncodings.length > 0) {
17308
+ const encoding = node.getAttribute('encoding');
17309
+ if (isString(encoding) && contains$2(allowedEncodings, encoding)) {
17310
+ evt.allowedTags[lcTagName] = true;
17311
+ }
17312
+ }
17313
+ });
17314
+ purify$1.sanitize(node, config);
17315
+ };
17316
+ const mkSanitizeNamespaceElement = settings => ele => {
17234
17317
  const namespaceType = toScopeType(ele);
17235
17318
  if (namespaceType === 'svg') {
17236
- const xlinkAttrs = [
17237
- 'type',
17238
- 'href',
17239
- 'role',
17240
- 'arcrole',
17241
- 'title',
17242
- 'show',
17243
- 'actuate',
17244
- 'label',
17245
- 'from',
17246
- 'to'
17247
- ].map(name => `xlink:${ name }`);
17248
- const config = {
17249
- IN_PLACE: true,
17250
- USE_PROFILES: {
17251
- html: true,
17252
- svg: true,
17253
- svgFilters: true
17254
- },
17255
- ALLOWED_ATTR: xlinkAttrs
17256
- };
17257
- purify().sanitize(ele, config);
17319
+ sanitizeSvgElement(ele);
17258
17320
  } else if (namespaceType === 'math') {
17259
- const config = {
17260
- IN_PLACE: true,
17261
- USE_PROFILES: { mathMl: true }
17262
- };
17263
- purify().sanitize(ele, config);
17321
+ sanitizeMathmlElement(ele, settings);
17264
17322
  } else {
17265
17323
  throw new Error('Not a namespace element');
17266
17324
  }
@@ -17276,7 +17334,7 @@
17276
17334
  };
17277
17335
  return {
17278
17336
  sanitizeHtmlElement,
17279
- sanitizeNamespaceElement
17337
+ sanitizeNamespaceElement: mkSanitizeNamespaceElement(settings)
17280
17338
  };
17281
17339
  } else {
17282
17340
  const sanitizeHtmlElement = (body, _mimeType) => {
@@ -18743,6 +18801,9 @@
18743
18801
  return !sel || rng.collapsed;
18744
18802
  };
18745
18803
  const isEditable = () => {
18804
+ if (editor.mode.isReadOnly()) {
18805
+ return false;
18806
+ }
18746
18807
  const rng = getRng$1();
18747
18808
  const fakeSelectedElements = editor.getBody().querySelectorAll('[data-mce-selected="1"]');
18748
18809
  if (fakeSelectedElements.length > 0) {
@@ -22659,6 +22720,9 @@
22659
22720
  const getBlocksToIndent = editor => filter$5(fromDom$1(editor.selection.getSelectedBlocks()), el => !isListComponent(el) && !parentIsListComponent(el) && isEditable(el));
22660
22721
  const handle = (editor, command) => {
22661
22722
  var _a, _b;
22723
+ if (editor.mode.isReadOnly()) {
22724
+ return;
22725
+ }
22662
22726
  const {dom} = editor;
22663
22727
  const indentation = getIndentation(editor);
22664
22728
  const indentUnit = (_b = (_a = /[a-z%]+$/i.exec(indentation)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 'px';
@@ -23398,6 +23462,9 @@
23398
23462
  return getCellFirstCursorPosition(cell);
23399
23463
  });
23400
23464
  }, current => {
23465
+ if (editor.mode.isReadOnly()) {
23466
+ return Optional.none();
23467
+ }
23401
23468
  editor.execCommand('mceTableInsertRowAfter');
23402
23469
  return tabForward(editor, isRoot, current);
23403
23470
  });
@@ -24078,7 +24145,8 @@
24078
24145
  optionalTooltip,
24079
24146
  optionalIcon,
24080
24147
  optionalText,
24081
- onSetup
24148
+ onSetup,
24149
+ defaultedString('context', 'mode:design')
24082
24150
  ];
24083
24151
 
24084
24152
  const baseToolbarToggleButtonFields = [active].concat(baseToolbarButtonFields);
@@ -24941,9 +25009,12 @@
24941
25009
  const isEmptyAnchor = (dom, elm) => {
24942
25010
  return elm && elm.nodeName === 'A' && dom.isEmpty(elm);
24943
25011
  };
24944
- const containerAndSiblingName = (container, nodeName) => {
25012
+ const containerAndPreviousSiblingName = (container, nodeName) => {
24945
25013
  return container.nodeName === nodeName || container.previousSibling && container.previousSibling.nodeName === nodeName;
24946
25014
  };
25015
+ const containerAndNextSiblingName = (container, nodeName) => {
25016
+ return container.nodeName === nodeName || container.nextSibling && container.nextSibling.nodeName === nodeName;
25017
+ };
24947
25018
  const canSplitBlock = (dom, node) => {
24948
25019
  return isNonNullable(node) && dom.isBlock(node) && !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && !/^(fixed|absolute)/i.test(node.style.position) && dom.isEditable(node.parentNode) && dom.getContentEditable(node) !== 'false';
24949
25020
  };
@@ -25085,7 +25156,10 @@
25085
25156
  if (start && isElement$6(container) && container === parentBlock.firstChild) {
25086
25157
  return true;
25087
25158
  }
25088
- if (containerAndSiblingName(container, 'TABLE') || containerAndSiblingName(container, 'HR')) {
25159
+ if (containerAndPreviousSiblingName(container, 'TABLE') || containerAndPreviousSiblingName(container, 'HR')) {
25160
+ if (containerAndNextSiblingName(container, 'BR')) {
25161
+ return !start;
25162
+ }
25089
25163
  return isAfterLastNodeInContainer && !start || !isAfterLastNodeInContainer && start;
25090
25164
  }
25091
25165
  const walker = new DomTreeWalker(container, parentBlock);
@@ -25203,7 +25277,7 @@
25203
25277
  const afterBr = isAfterBr(parentBlockSugar, caretPos, editor.schema);
25204
25278
  const prevBrOpt = afterBr ? findPreviousBr(parentBlockSugar, caretPos, editor.schema).bind(pos => Optional.from(pos.getNode())) : Optional.none();
25205
25279
  newBlock = parentBlockParent.insertBefore(createNewBlock$1(), parentBlock);
25206
- const root = containerAndSiblingName(parentBlock, 'HR') || afterTable ? newBlock : prevBrOpt.getOr(parentBlock);
25280
+ const root = containerAndPreviousSiblingName(parentBlock, 'HR') || afterTable ? newBlock : prevBrOpt.getOr(parentBlock);
25207
25281
  moveToCaretPosition(editor, root);
25208
25282
  } else {
25209
25283
  const tmpRng = includeZwspInRange(rng).cloneRange();
@@ -25463,6 +25537,9 @@
25463
25537
  };
25464
25538
 
25465
25539
  const insertBreak = (breakType, editor, evt) => {
25540
+ if (editor.mode.isReadOnly()) {
25541
+ return;
25542
+ }
25466
25543
  if (!editor.selection.isCollapsed()) {
25467
25544
  execEditorDeleteCommand(editor);
25468
25545
  }
@@ -25478,6 +25555,9 @@
25478
25555
  }
25479
25556
  };
25480
25557
  const insert$1 = (editor, evt) => {
25558
+ if (editor.mode.isReadOnly()) {
25559
+ return;
25560
+ }
25481
25561
  const br = () => insertBreak(linebreak, editor, evt);
25482
25562
  const block = () => insertBreak(blockbreak, editor, evt);
25483
25563
  const logicalAction = getAction(editor, evt);
@@ -25840,16 +25920,17 @@
25840
25920
  });
25841
25921
  }
25842
25922
  nodeChanged(args = {}) {
25843
- const selection = this.editor.selection;
25923
+ const editor = this.editor;
25924
+ const selection = editor.selection;
25844
25925
  let node;
25845
- if (this.editor.initialized && selection && !shouldDisableNodeChange(this.editor) && !this.editor.mode.isReadOnly()) {
25846
- const root = this.editor.getBody();
25926
+ if (editor.initialized && selection && !shouldDisableNodeChange(editor)) {
25927
+ const root = editor.getBody();
25847
25928
  node = selection.getStart(true) || root;
25848
- if (node.ownerDocument !== this.editor.getDoc() || !this.editor.dom.isChildOf(node, root)) {
25929
+ if (node.ownerDocument !== editor.getDoc() || !editor.dom.isChildOf(node, root)) {
25849
25930
  node = root;
25850
25931
  }
25851
25932
  const parents = [];
25852
- this.editor.dom.getParent(node, node => {
25933
+ editor.dom.getParent(node, node => {
25853
25934
  if (node === root) {
25854
25935
  return true;
25855
25936
  } else {
@@ -25857,7 +25938,7 @@
25857
25938
  return false;
25858
25939
  }
25859
25940
  });
25860
- this.editor.dispatch('NodeChange', {
25941
+ editor.dispatch('NodeChange', {
25861
25942
  ...args,
25862
25943
  element: node,
25863
25944
  parents
@@ -28328,7 +28409,7 @@
28328
28409
  }), getTextPatternsLookup(editor));
28329
28410
  const hasDynamicPatterns = () => hasTextPatternsLookup(editor);
28330
28411
  editor.on('keydown', e => {
28331
- if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed()) {
28412
+ if (e.keyCode === 13 && !VK.modifierPressed(e) && editor.selection.isCollapsed() && editor.selection.isEditable()) {
28332
28413
  const patternSet = filterByTrigger(getPatternSet(), 'enter');
28333
28414
  const hasPatterns = patternSet.inlinePatterns.length > 0 || patternSet.blockPatterns.length > 0 || hasDynamicPatterns();
28334
28415
  if (hasPatterns && handleEnter(editor, patternSet)) {
@@ -28337,7 +28418,7 @@
28337
28418
  }
28338
28419
  }, true);
28339
28420
  editor.on('keydown', e => {
28340
- if (e.keyCode === 32 && editor.selection.isCollapsed()) {
28421
+ if (e.keyCode === 32 && editor.selection.isCollapsed() && editor.selection.isEditable()) {
28341
28422
  const patternSet = filterByTrigger(getPatternSet(), 'space');
28342
28423
  const hasPatterns = patternSet.blockPatterns.length > 0 || hasDynamicPatterns();
28343
28424
  if (hasPatterns && handleBlockPatternOnSpace(editor, patternSet)) {
@@ -28346,7 +28427,7 @@
28346
28427
  }
28347
28428
  }, true);
28348
28429
  const handleInlineTrigger = () => {
28349
- if (editor.selection.isCollapsed()) {
28430
+ if (editor.selection.isCollapsed() && editor.selection.isEditable()) {
28350
28431
  const patternSet = filterByTrigger(getPatternSet(), 'space');
28351
28432
  const hasPatterns = patternSet.inlinePatterns.length > 0 || hasDynamicPatterns();
28352
28433
  if (hasPatterns) {
@@ -28794,6 +28875,7 @@
28794
28875
  allow_svg_data_urls: getOption('allow_svg_data_urls'),
28795
28876
  allow_html_in_named_anchor: getOption('allow_html_in_named_anchor'),
28796
28877
  allow_script_urls: getOption('allow_script_urls'),
28878
+ allow_mathml_annotation_encodings: getOption('allow_mathml_annotation_encodings'),
28797
28879
  allow_unsafe_link_target: getOption('allow_unsafe_link_target'),
28798
28880
  convert_unsafe_embeds: getOption('convert_unsafe_embeds'),
28799
28881
  convert_fonts_to_spans: getOption('convert_fonts_to_spans'),
@@ -29076,7 +29158,7 @@
29076
29158
  body.disabled = true;
29077
29159
  editor.readonly = isReadOnly$1(editor);
29078
29160
  editor._editableRoot = hasEditableRoot$1(editor);
29079
- if (!editor.readonly && editor.hasEditableRoot()) {
29161
+ if (editor.hasEditableRoot()) {
29080
29162
  if (editor.inline && DOM$6.getStyle(body, 'position', true) === 'static') {
29081
29163
  body.style.position = 'relative';
29082
29164
  }
@@ -29335,7 +29417,8 @@
29335
29417
  hide: Optional.from(api.hide).getOr(noop),
29336
29418
  isEnabled: Optional.from(api.isEnabled).getOr(always),
29337
29419
  setEnabled: state => {
29338
- if (!editor.mode.isReadOnly()) {
29420
+ const shouldSkip = state && editor.mode.get() === 'readonly';
29421
+ if (!shouldSkip) {
29339
29422
  Optional.from(api.setEnabled).each(f => f(state));
29340
29423
  }
29341
29424
  }
@@ -29548,10 +29631,8 @@
29548
29631
  const setEditableRoot = (editor, state) => {
29549
29632
  if (editor._editableRoot !== state) {
29550
29633
  editor._editableRoot = state;
29551
- if (!editor.readonly) {
29552
- editor.getBody().contentEditable = String(editor.hasEditableRoot());
29553
- editor.nodeChanged();
29554
- }
29634
+ editor.getBody().contentEditable = String(editor.hasEditableRoot());
29635
+ editor.nodeChanged();
29555
29636
  fireEditableRootStateChange(editor, state);
29556
29637
  }
29557
29638
  };
@@ -29992,6 +30073,9 @@
29992
30073
 
29993
30074
  const registerCommands$4 = editor => {
29994
30075
  const applyLinkToSelection = (_command, _ui, value) => {
30076
+ if (editor.mode.isReadOnly()) {
30077
+ return;
30078
+ }
29995
30079
  const linkDetails = isString(value) ? { href: value } : value;
29996
30080
  const anchor = editor.dom.getParent(editor.selection.getNode(), 'a');
29997
30081
  if (isObject(linkDetails) && isString(linkDetails.href)) {
@@ -30029,6 +30113,9 @@
30029
30113
  return Optional.from(topParentBlock).map(SugarElement.fromDom);
30030
30114
  };
30031
30115
  const insert = (editor, before) => {
30116
+ if (editor.mode.isReadOnly()) {
30117
+ return;
30118
+ }
30032
30119
  const dom = editor.dom;
30033
30120
  const rng = editor.selection.getRng();
30034
30121
  const node = before ? editor.selection.getStart() : editor.selection.getEnd();
@@ -30233,7 +30320,6 @@
30233
30320
  }
30234
30321
  }
30235
30322
 
30236
- const internalContentEditableAttr = 'data-mce-contenteditable';
30237
30323
  const toggleClass = (elm, cls, state) => {
30238
30324
  if (has(elm, cls) && !state) {
30239
30325
  remove$6(elm, cls);
@@ -30250,18 +30336,6 @@
30250
30336
  const setContentEditable = (elm, state) => {
30251
30337
  elm.dom.contentEditable = state ? 'true' : 'false';
30252
30338
  };
30253
- const switchOffContentEditableTrue = elm => {
30254
- each$e(descendants(elm, '*[contenteditable="true"]'), elm => {
30255
- set$3(elm, internalContentEditableAttr, 'true');
30256
- setContentEditable(elm, false);
30257
- });
30258
- };
30259
- const switchOnContentEditableTrue = elm => {
30260
- each$e(descendants(elm, `*[${ internalContentEditableAttr }="true"]`), elm => {
30261
- remove$9(elm, internalContentEditableAttr);
30262
- setContentEditable(elm, true);
30263
- });
30264
- };
30265
30339
  const removeFakeSelection = editor => {
30266
30340
  Optional.from(editor.selection.getNode()).each(elm => {
30267
30341
  elm.removeAttribute('data-mce-selected');
@@ -30270,60 +30344,42 @@
30270
30344
  const restoreFakeSelection = editor => {
30271
30345
  editor.selection.setRng(editor.selection.getRng());
30272
30346
  };
30347
+ const setCommonEditorCommands = (editor, state) => {
30348
+ setEditorCommandState(editor, 'StyleWithCSS', state);
30349
+ setEditorCommandState(editor, 'enableInlineTableEditing', state);
30350
+ setEditorCommandState(editor, 'enableObjectResizing', state);
30351
+ };
30352
+ const setEditorReadonly = editor => {
30353
+ editor.readonly = true;
30354
+ editor.selection.controlSelection.hideResizeRect();
30355
+ editor._selectionOverrides.hideFakeCaret();
30356
+ removeFakeSelection(editor);
30357
+ };
30358
+ const unsetEditorReadonly = (editor, body) => {
30359
+ editor.readonly = false;
30360
+ if (editor.hasEditableRoot()) {
30361
+ setContentEditable(body, true);
30362
+ }
30363
+ setCommonEditorCommands(editor, false);
30364
+ if (hasEditorOrUiFocus(editor)) {
30365
+ editor.focus();
30366
+ }
30367
+ restoreFakeSelection(editor);
30368
+ editor.nodeChanged();
30369
+ };
30273
30370
  const toggleReadOnly = (editor, state) => {
30274
30371
  const body = SugarElement.fromDom(editor.getBody());
30275
30372
  toggleClass(body, 'mce-content-readonly', state);
30276
30373
  if (state) {
30277
- editor.selection.controlSelection.hideResizeRect();
30278
- editor._selectionOverrides.hideFakeCaret();
30279
- removeFakeSelection(editor);
30280
- editor.readonly = true;
30281
- setContentEditable(body, false);
30282
- switchOffContentEditableTrue(body);
30283
- } else {
30284
- editor.readonly = false;
30374
+ setEditorReadonly(editor);
30285
30375
  if (editor.hasEditableRoot()) {
30286
30376
  setContentEditable(body, true);
30287
30377
  }
30288
- switchOnContentEditableTrue(body);
30289
- setEditorCommandState(editor, 'StyleWithCSS', false);
30290
- setEditorCommandState(editor, 'enableInlineTableEditing', false);
30291
- setEditorCommandState(editor, 'enableObjectResizing', false);
30292
- if (hasEditorOrUiFocus(editor)) {
30293
- editor.focus();
30294
- }
30295
- restoreFakeSelection(editor);
30296
- editor.nodeChanged();
30297
- }
30298
- };
30299
- const isReadOnly = editor => editor.readonly;
30300
- const registerFilters = editor => {
30301
- editor.parser.addAttributeFilter('contenteditable', nodes => {
30302
- if (isReadOnly(editor)) {
30303
- each$e(nodes, node => {
30304
- node.attr(internalContentEditableAttr, node.attr('contenteditable'));
30305
- node.attr('contenteditable', 'false');
30306
- });
30307
- }
30308
- });
30309
- editor.serializer.addAttributeFilter(internalContentEditableAttr, nodes => {
30310
- if (isReadOnly(editor)) {
30311
- each$e(nodes, node => {
30312
- node.attr('contenteditable', node.attr(internalContentEditableAttr));
30313
- });
30314
- }
30315
- });
30316
- editor.serializer.addTempAttr(internalContentEditableAttr);
30317
- };
30318
- const registerReadOnlyContentFilters = editor => {
30319
- if (editor.serializer) {
30320
- registerFilters(editor);
30321
30378
  } else {
30322
- editor.on('PreInit', () => {
30323
- registerFilters(editor);
30324
- });
30379
+ unsetEditorReadonly(editor, body);
30325
30380
  }
30326
30381
  };
30382
+ const isReadOnly = editor => editor.readonly;
30327
30383
  const isClickEvent = e => e.type === 'click';
30328
30384
  const allowedEvents = ['copy'];
30329
30385
  const isReadOnlyAllowedEvent = e => contains$2(allowedEvents, e.type);
@@ -30350,16 +30406,32 @@
30350
30406
  }
30351
30407
  };
30352
30408
  const registerReadOnlySelectionBlockers = editor => {
30353
- editor.on('ShowCaret', e => {
30409
+ editor.on('beforeinput paste cut dragend dragover draggesture dragdrop drop drag', e => {
30354
30410
  if (isReadOnly(editor)) {
30355
30411
  e.preventDefault();
30356
30412
  }
30357
30413
  });
30358
- editor.on('ObjectSelected', e => {
30359
- if (isReadOnly(editor)) {
30414
+ editor.on('BeforeExecCommand', e => {
30415
+ if ((e.command === 'Undo' || e.command === 'Redo') && isReadOnly(editor)) {
30360
30416
  e.preventDefault();
30361
30417
  }
30362
30418
  });
30419
+ editor.on('input', e => {
30420
+ if (!e.isComposing && isReadOnly(editor)) {
30421
+ const undoLevel = editor.undoManager.add();
30422
+ if (isNonNullable(undoLevel)) {
30423
+ editor.undoManager.undo();
30424
+ }
30425
+ }
30426
+ });
30427
+ editor.on('compositionend', () => {
30428
+ if (isReadOnly(editor)) {
30429
+ const undoLevel = editor.undoManager.add();
30430
+ if (isNonNullable(undoLevel)) {
30431
+ editor.undoManager.undo();
30432
+ }
30433
+ }
30434
+ });
30363
30435
  };
30364
30436
 
30365
30437
  const nativeEvents = Tools.makeMap('focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange ' + 'mouseout mouseenter mouseleave wheel keydown keypress keyup input beforeinput contextmenu dragstart dragend dragover ' + 'draggesture dragdrop drop drag submit ' + 'compositionstart compositionend compositionupdate touchstart touchmove touchend touchcancel', ' ');
@@ -30553,7 +30625,7 @@
30553
30625
  }
30554
30626
  return editor.getBody();
30555
30627
  };
30556
- const isListening = editor => !editor.hidden && !isReadOnly(editor);
30628
+ const isListening = editor => !editor.hidden;
30557
30629
  const fireEvent = (editor, eventName, e) => {
30558
30630
  if (isListening(editor)) {
30559
30631
  editor.dispatch(eventName, e);
@@ -30872,7 +30944,6 @@
30872
30944
  editorReadOnly: true
30873
30945
  }
30874
30946
  });
30875
- registerReadOnlyContentFilters(editor);
30876
30947
  registerReadOnlySelectionBlockers(editor);
30877
30948
  return {
30878
30949
  isReadOnly: () => isReadOnly(editor),
@@ -31048,6 +31119,7 @@
31048
31119
  const icons = {};
31049
31120
  const contextMenus = {};
31050
31121
  const contextToolbars = {};
31122
+ const contexts = {};
31051
31123
  const sidebars = {};
31052
31124
  const views = {};
31053
31125
  const add = (collection, type) => (name, spec) => {
@@ -31057,6 +31129,7 @@
31057
31129
  };
31058
31130
  };
31059
31131
  const addIcon = (name, svgData) => icons[name.toLowerCase()] = svgData;
31132
+ const addContext = (name, pred) => contexts[name.toLowerCase()] = pred;
31060
31133
  return {
31061
31134
  addButton: add(buttons, 'button'),
31062
31135
  addGroupToolbarButton: add(buttons, 'grouptoolbarbutton'),
@@ -31073,6 +31146,7 @@
31073
31146
  addSidebar: add(sidebars, 'sidebar'),
31074
31147
  addView: add(views, 'views'),
31075
31148
  addIcon,
31149
+ addContext,
31076
31150
  getAll: () => ({
31077
31151
  buttons,
31078
31152
  menuItems,
@@ -31081,7 +31155,8 @@
31081
31155
  contextMenus,
31082
31156
  contextToolbars,
31083
31157
  sidebars,
31084
- views
31158
+ views,
31159
+ contexts
31085
31160
  })
31086
31161
  };
31087
31162
  };
@@ -31104,6 +31179,7 @@
31104
31179
  addGroupToolbarButton: bridge.addGroupToolbarButton,
31105
31180
  addToggleMenuItem: bridge.addToggleMenuItem,
31106
31181
  addView: bridge.addView,
31182
+ addContext: bridge.addContext,
31107
31183
  getAll: bridge.getAll
31108
31184
  };
31109
31185
  };
@@ -31540,8 +31616,8 @@
31540
31616
  documentBaseURL: null,
31541
31617
  suffix: null,
31542
31618
  majorVersion: '7',
31543
- minorVersion: '3.0',
31544
- releaseDate: '2024-08-07',
31619
+ minorVersion: '4.1',
31620
+ releaseDate: 'TBD',
31545
31621
  i18n: I18n,
31546
31622
  activeEditor: null,
31547
31623
  focusedEditor: null,