tinymce-rails 7.7.0 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +201 -378
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/icons/default/icons.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/models/dom/model.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +2 -2
  19. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +2 -2
  24. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
  29. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +1 -1
  34. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +1 -1
  35. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -3
  36. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -3
  37. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  38. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -3
  39. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  40. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -3
  41. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
  43. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
  44. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -3
  45. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -3
  46. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  47. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -3
  48. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  49. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -3
  50. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
  51. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
  52. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
  53. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -3
  54. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -3
  55. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  56. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -3
  57. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  58. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -3
  59. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
  60. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
  61. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
  62. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -3
  63. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -3
  64. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  65. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -3
  66. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  67. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -3
  68. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
  69. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
  70. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  71. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +136 -190
  72. data/vendor/assets/javascripts/tinymce/tinymce.d.ts +40 -3
  73. data/vendor/assets/javascripts/tinymce/tinymce.js +122 -176
  74. metadata +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 7.7.0 (TBD)
2
+ * TinyMCE version 7.8.0 (TBD)
3
3
  */
4
4
 
5
5
  (function () {
@@ -7210,6 +7210,8 @@
7210
7210
  },
7211
7211
  default: []
7212
7212
  });
7213
+ registerOption('extended_mathml_attributes', { processor: 'string[]' });
7214
+ registerOption('extended_mathml_elements', { processor: 'string[]' });
7213
7215
  registerOption('inline_boundaries', {
7214
7216
  processor: 'boolean',
7215
7217
  default: true
@@ -7248,6 +7250,11 @@
7248
7250
  });
7249
7251
  registerOption('event_root', { processor: 'string' });
7250
7252
  registerOption('service_message', { processor: 'string' });
7253
+ registerOption('onboarding', {
7254
+ processor: 'boolean',
7255
+ default: true
7256
+ });
7257
+ registerOption('tiny_cloud_entry_url', { processor: 'string' });
7251
7258
  registerOption('theme', {
7252
7259
  processor: value => value === false || isString(value) || isFunction(value),
7253
7260
  default: 'silver'
@@ -8035,7 +8042,7 @@
8035
8042
  }
8036
8043
  };
8037
8044
  const startBlink = () => {
8038
- cursorInterval = setInterval(() => {
8045
+ cursorInterval = window.setInterval(() => {
8039
8046
  lastVisualCaret.on(caretState => {
8040
8047
  if (hasFocus()) {
8041
8048
  dom.toggleClass(caretState.caret, 'mce-visual-caret-hidden');
@@ -9387,10 +9394,11 @@
9387
9394
  'div[data-ephox-embed-iri]',
9388
9395
  'div.tiny-pageembed',
9389
9396
  'div.mce-toc',
9390
- 'div[data-mce-toc]'
9397
+ 'div[data-mce-toc]',
9398
+ 'div.mce-footnotes'
9391
9399
  ];
9392
9400
  const isZeroWidth = elem => isText$c(elem) && get$3(elem) === ZWSP$1;
9393
- const context = (editor, elem, wrapName, nodeName) => parent(elem).fold(() => 'skipping', parent => {
9401
+ const context = (editor, elem, wrapName, nodeName) => parentElement(elem).fold(() => 'skipping', parent => {
9394
9402
  if (nodeName === 'br' || isZeroWidth(elem)) {
9395
9403
  return 'valid';
9396
9404
  } else if (isAnnotation(elem)) {
@@ -9719,13 +9727,13 @@
9719
9727
  if (!isNumber(time)) {
9720
9728
  time = 0;
9721
9729
  }
9722
- return setTimeout(callback, time);
9730
+ return window.setTimeout(callback, time);
9723
9731
  };
9724
9732
  const wrappedSetInterval = (callback, time) => {
9725
9733
  if (!isNumber(time)) {
9726
9734
  time = 0;
9727
9735
  }
9728
- return setInterval(callback, time);
9736
+ return window.setInterval(callback, time);
9729
9737
  };
9730
9738
  const Delay = {
9731
9739
  setEditorTimeout: (editor, callback, time) => {
@@ -9740,7 +9748,7 @@
9740
9748
  if (!editor.removed) {
9741
9749
  callback();
9742
9750
  } else {
9743
- clearInterval(timer);
9751
+ window.clearInterval(timer);
9744
9752
  }
9745
9753
  }, time);
9746
9754
  return timer;
@@ -11005,10 +11013,11 @@
11005
11013
  };
11006
11014
  const scrollToMarker = (editor, marker, viewHeight, alignToTop, doc) => {
11007
11015
  const pos = marker.pos;
11016
+ const scrollMargin = 30;
11008
11017
  if (alignToTop) {
11009
- to(pos.left, pos.top, doc);
11018
+ to(pos.left, Math.max(0, pos.top - scrollMargin), doc);
11010
11019
  } else {
11011
- const y = pos.top - viewHeight + marker.height;
11020
+ const y = pos.top - viewHeight + marker.height + scrollMargin;
11012
11021
  to(-editor.getBody().getBoundingClientRect().left, y, doc);
11013
11022
  }
11014
11023
  };
@@ -15314,7 +15323,7 @@
15314
15323
  }
15315
15324
  };
15316
15325
 
15317
- /*! @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 */
15326
+ /*! @license DOMPurify 3.2.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.4/LICENSE */
15318
15327
 
15319
15328
  const {
15320
15329
  entries,
@@ -15353,8 +15362,10 @@
15353
15362
  };
15354
15363
  }
15355
15364
  const arrayForEach = unapply(Array.prototype.forEach);
15365
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
15356
15366
  const arrayPop = unapply(Array.prototype.pop);
15357
15367
  const arrayPush = unapply(Array.prototype.push);
15368
+ const arraySplice = unapply(Array.prototype.splice);
15358
15369
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
15359
15370
  const stringToString = unapply(String.prototype.toString);
15360
15371
  const stringMatch = unapply(String.prototype.match);
@@ -15364,12 +15375,11 @@
15364
15375
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
15365
15376
  const regExpTest = unapply(RegExp.prototype.test);
15366
15377
  const typeErrorCreate = unconstruct(TypeError);
15367
-
15368
15378
  /**
15369
15379
  * Creates a new function that calls the given function with a specified thisArg and arguments.
15370
15380
  *
15371
- * @param {Function} func - The function to be wrapped and called.
15372
- * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
15381
+ * @param func - The function to be wrapped and called.
15382
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
15373
15383
  */
15374
15384
  function unapply(func) {
15375
15385
  return function (thisArg) {
@@ -15379,12 +15389,11 @@
15379
15389
  return apply(func, thisArg, args);
15380
15390
  };
15381
15391
  }
15382
-
15383
15392
  /**
15384
15393
  * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
15385
15394
  *
15386
- * @param {Function} func - The constructor function to be wrapped and called.
15387
- * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
15395
+ * @param func - The constructor function to be wrapped and called.
15396
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
15388
15397
  */
15389
15398
  function unconstruct(func) {
15390
15399
  return function () {
@@ -15394,14 +15403,13 @@
15394
15403
  return construct(func, args);
15395
15404
  };
15396
15405
  }
15397
-
15398
15406
  /**
15399
15407
  * Add properties to a lookup table
15400
15408
  *
15401
- * @param {Object} set - The set to which elements will be added.
15402
- * @param {Array} array - The array containing elements to be added to the set.
15403
- * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15404
- * @returns {Object} The modified set with added elements.
15409
+ * @param set - The set to which elements will be added.
15410
+ * @param array - The array containing elements to be added to the set.
15411
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15412
+ * @returns The modified set with added elements.
15405
15413
  */
15406
15414
  function addToSet(set, array) {
15407
15415
  let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
@@ -15428,12 +15436,11 @@
15428
15436
  }
15429
15437
  return set;
15430
15438
  }
15431
-
15432
15439
  /**
15433
15440
  * Clean up an array to harden against CSPP
15434
15441
  *
15435
- * @param {Array} array - The array to be cleaned.
15436
- * @returns {Array} The cleaned version of the array
15442
+ * @param array - The array to be cleaned.
15443
+ * @returns The cleaned version of the array
15437
15444
  */
15438
15445
  function cleanArray(array) {
15439
15446
  for (let index = 0; index < array.length; index++) {
@@ -15444,12 +15451,11 @@
15444
15451
  }
15445
15452
  return array;
15446
15453
  }
15447
-
15448
15454
  /**
15449
15455
  * Shallow clone an object
15450
15456
  *
15451
- * @param {Object} object - The object to be cloned.
15452
- * @returns {Object} A new object that copies the original.
15457
+ * @param object - The object to be cloned.
15458
+ * @returns A new object that copies the original.
15453
15459
  */
15454
15460
  function clone(object) {
15455
15461
  const newObject = create$7(null);
@@ -15467,13 +15473,12 @@
15467
15473
  }
15468
15474
  return newObject;
15469
15475
  }
15470
-
15471
15476
  /**
15472
15477
  * This method automatically checks if the prop is function or getter and behaves accordingly.
15473
15478
  *
15474
- * @param {Object} object - The object to look up the getter function in its prototype chain.
15475
- * @param {String} prop - The property name for which to find the getter function.
15476
- * @returns {Function} The getter function found in the prototype chain or a fallback function.
15479
+ * @param object - The object to look up the getter function in its prototype chain.
15480
+ * @param prop - The property name for which to find the getter function.
15481
+ * @returns The getter function found in the prototype chain or a fallback function.
15477
15482
  */
15478
15483
  function lookupGetter(object, prop) {
15479
15484
  while (object !== null) {
@@ -15495,18 +15500,14 @@
15495
15500
  }
15496
15501
 
15497
15502
  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']);
15498
-
15499
- // SVG
15500
15503
  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']);
15501
15504
  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']);
15502
-
15503
15505
  // List of SVG elements that are disallowed by default.
15504
15506
  // We still need to know them so that we can do namespace
15505
15507
  // checks properly in case one wants to add them to
15506
15508
  // allow-list.
15507
15509
  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']);
15508
15510
  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']);
15509
-
15510
15511
  // Similarly to SVG, we want to know all MathML elements,
15511
15512
  // even those that we disallow by default.
15512
15513
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
@@ -15520,8 +15521,8 @@
15520
15521
  // eslint-disable-next-line unicorn/better-regex
15521
15522
  const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
15522
15523
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
15523
- const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
15524
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
15524
+ const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
15525
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
15525
15526
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
15526
15527
  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
15527
15528
  );
@@ -15533,18 +15534,19 @@
15533
15534
 
15534
15535
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
15535
15536
  __proto__: null,
15536
- MUSTACHE_EXPR: MUSTACHE_EXPR,
15537
- ERB_EXPR: ERB_EXPR,
15538
- TMPLIT_EXPR: TMPLIT_EXPR,
15539
- DATA_ATTR: DATA_ATTR,
15540
15537
  ARIA_ATTR: ARIA_ATTR,
15541
- IS_ALLOWED_URI: IS_ALLOWED_URI,
15542
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
15543
15538
  ATTR_WHITESPACE: ATTR_WHITESPACE,
15539
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
15540
+ DATA_ATTR: DATA_ATTR,
15544
15541
  DOCTYPE_NAME: DOCTYPE_NAME,
15545
- CUSTOM_ELEMENT: CUSTOM_ELEMENT
15542
+ ERB_EXPR: ERB_EXPR,
15543
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
15544
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
15545
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
15546
+ TMPLIT_EXPR: TMPLIT_EXPR
15546
15547
  });
15547
15548
 
15549
+ /* eslint-disable @typescript-eslint/indent */
15548
15550
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
15549
15551
  const NODE_TYPE = {
15550
15552
  element: 1,
@@ -15565,20 +15567,18 @@
15565
15567
  const getGlobal = function getGlobal() {
15566
15568
  return typeof window === 'undefined' ? null : window;
15567
15569
  };
15568
-
15569
15570
  /**
15570
15571
  * Creates a no-op policy for internal use only.
15571
15572
  * Don't export this function outside this module!
15572
- * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
15573
- * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15574
- * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
15573
+ * @param trustedTypes The policy factory.
15574
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15575
+ * @return The policy created (or null, if Trusted Types
15575
15576
  * are not supported or creating the policy failed).
15576
15577
  */
15577
15578
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
15578
15579
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
15579
15580
  return null;
15580
15581
  }
15581
-
15582
15582
  // Allow the callers to control the unique policy name
15583
15583
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
15584
15584
  // Policy creation with duplicate names throws in Trusted Types.
@@ -15605,22 +15605,25 @@
15605
15605
  return null;
15606
15606
  }
15607
15607
  };
15608
+ const _createHooksMap = function _createHooksMap() {
15609
+ return {
15610
+ afterSanitizeAttributes: [],
15611
+ afterSanitizeElements: [],
15612
+ afterSanitizeShadowDOM: [],
15613
+ beforeSanitizeAttributes: [],
15614
+ beforeSanitizeElements: [],
15615
+ beforeSanitizeShadowDOM: [],
15616
+ uponSanitizeAttribute: [],
15617
+ uponSanitizeElement: [],
15618
+ uponSanitizeShadowNode: []
15619
+ };
15620
+ };
15608
15621
  function createDOMPurify() {
15609
15622
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
15610
15623
  const DOMPurify = root => createDOMPurify(root);
15611
-
15612
- /**
15613
- * Version label, exposed for easier checks
15614
- * if DOMPurify is up to date or not
15615
- */
15616
- DOMPurify.version = '3.1.7';
15617
-
15618
- /**
15619
- * Array of elements that DOMPurify removed during sanitation.
15620
- * Empty if nothing was removed.
15621
- */
15624
+ DOMPurify.version = '3.2.4';
15622
15625
  DOMPurify.removed = [];
15623
- if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
15626
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
15624
15627
  // Not running in a browser, provide a factory function
15625
15628
  // so that you can pass your own Window
15626
15629
  DOMPurify.isSupported = false;
@@ -15648,7 +15651,6 @@
15648
15651
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
15649
15652
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
15650
15653
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
15651
-
15652
15654
  // As per issue #47, the web-components registry is inherited by a
15653
15655
  // new document created via createHTMLDocument. As per the spec
15654
15656
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -15672,8 +15674,7 @@
15672
15674
  const {
15673
15675
  importNode
15674
15676
  } = originalDocument;
15675
- let hooks = {};
15676
-
15677
+ let hooks = _createHooksMap();
15677
15678
  /**
15678
15679
  * Expose whether this browser supports running the full DOMPurify.
15679
15680
  */
@@ -15691,22 +15692,18 @@
15691
15692
  let {
15692
15693
  IS_ALLOWED_URI: IS_ALLOWED_URI$1
15693
15694
  } = EXPRESSIONS;
15694
-
15695
15695
  /**
15696
15696
  * We consider the elements and attributes below to be safe. Ideally
15697
15697
  * don't add any new ones but feel free to remove unwanted ones.
15698
15698
  */
15699
-
15700
15699
  /* allowed element names */
15701
15700
  let ALLOWED_TAGS = null;
15702
15701
  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
15703
-
15704
15702
  /* Allowed attribute names */
15705
15703
  let ALLOWED_ATTR = null;
15706
15704
  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
15707
-
15708
15705
  /*
15709
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
15706
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
15710
15707
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
15711
15708
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
15712
15709
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
@@ -15731,65 +15728,49 @@
15731
15728
  value: false
15732
15729
  }
15733
15730
  }));
15734
-
15735
15731
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
15736
15732
  let FORBID_TAGS = null;
15737
-
15738
15733
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
15739
15734
  let FORBID_ATTR = null;
15740
-
15741
15735
  /* Decide if ARIA attributes are okay */
15742
15736
  let ALLOW_ARIA_ATTR = true;
15743
-
15744
15737
  /* Decide if custom data attributes are okay */
15745
15738
  let ALLOW_DATA_ATTR = true;
15746
-
15747
15739
  /* Decide if unknown protocols are okay */
15748
15740
  let ALLOW_UNKNOWN_PROTOCOLS = false;
15749
-
15750
15741
  /* Decide if self-closing tags in attributes are allowed.
15751
15742
  * Usually removed due to a mXSS issue in jQuery 3.0 */
15752
15743
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
15753
-
15754
15744
  /* Output should be safe for common template engines.
15755
15745
  * This means, DOMPurify removes data attributes, mustaches and ERB
15756
15746
  */
15757
15747
  let SAFE_FOR_TEMPLATES = false;
15758
-
15759
15748
  /* Output should be safe even for XML used within HTML and alike.
15760
15749
  * This means, DOMPurify removes comments when containing risky content.
15761
15750
  */
15762
15751
  let SAFE_FOR_XML = true;
15763
-
15764
15752
  /* Decide if document with <html>... should be returned */
15765
15753
  let WHOLE_DOCUMENT = false;
15766
-
15767
15754
  /* Track whether config is already set on this instance of DOMPurify. */
15768
15755
  let SET_CONFIG = false;
15769
-
15770
15756
  /* Decide if all elements (e.g. style, script) must be children of
15771
15757
  * document.body. By default, browsers might move them to document.head */
15772
15758
  let FORCE_BODY = false;
15773
-
15774
15759
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
15775
15760
  * string (or a TrustedHTML object if Trusted Types are supported).
15776
15761
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
15777
15762
  */
15778
15763
  let RETURN_DOM = false;
15779
-
15780
15764
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
15781
15765
  * string (or a TrustedHTML object if Trusted Types are supported) */
15782
15766
  let RETURN_DOM_FRAGMENT = false;
15783
-
15784
15767
  /* Try to return a Trusted Type object instead of a string, return a string in
15785
15768
  * case Trusted Types are not supported */
15786
15769
  let RETURN_TRUSTED_TYPE = false;
15787
-
15788
15770
  /* Output should be free from DOM clobbering attacks?
15789
15771
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
15790
15772
  */
15791
15773
  let SANITIZE_DOM = true;
15792
-
15793
15774
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
15794
15775
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
15795
15776
  *
@@ -15805,25 +15786,19 @@
15805
15786
  */
15806
15787
  let SANITIZE_NAMED_PROPS = false;
15807
15788
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
15808
-
15809
15789
  /* Keep element content when removing element? */
15810
15790
  let KEEP_CONTENT = true;
15811
-
15812
15791
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
15813
15792
  * of importing it into a new Document and returning a sanitized copy */
15814
15793
  let IN_PLACE = false;
15815
-
15816
15794
  /* Allow usage of profiles like html, svg and mathMl */
15817
15795
  let USE_PROFILES = {};
15818
-
15819
15796
  /* Tags to ignore content of when KEEP_CONTENT is true */
15820
15797
  let FORBID_CONTENTS = null;
15821
15798
  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']);
15822
-
15823
15799
  /* Tags that are safe for data: URIs */
15824
15800
  let DATA_URI_TAGS = null;
15825
15801
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
15826
-
15827
15802
  /* Attributes safe for values like "javascript:" */
15828
15803
  let URI_SAFE_ATTRIBUTES = null;
15829
15804
  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
@@ -15833,32 +15808,33 @@
15833
15808
  /* Document namespace */
15834
15809
  let NAMESPACE = HTML_NAMESPACE;
15835
15810
  let IS_EMPTY_INPUT = false;
15836
-
15837
15811
  /* Allowed XHTML+XML namespaces */
15838
15812
  let ALLOWED_NAMESPACES = null;
15839
15813
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
15840
-
15814
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
15815
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
15816
+ // Certain elements are allowed in both SVG and HTML
15817
+ // namespace. We need to specify them explicitly
15818
+ // so that they don't get erroneously deleted from
15819
+ // HTML namespace.
15820
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
15841
15821
  /* Parsing of strict XHTML documents */
15842
15822
  let PARSER_MEDIA_TYPE = null;
15843
15823
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
15844
15824
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
15845
15825
  let transformCaseFunc = null;
15846
-
15847
15826
  /* Keep a reference to config to pass to hooks */
15848
15827
  let CONFIG = null;
15849
-
15850
15828
  /* Ideally, do not touch anything below this line */
15851
15829
  /* ______________________________________________ */
15852
-
15853
15830
  const formElement = document.createElement('form');
15854
15831
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
15855
15832
  return testValue instanceof RegExp || testValue instanceof Function;
15856
15833
  };
15857
-
15858
15834
  /**
15859
15835
  * _parseConfig
15860
15836
  *
15861
- * @param {Object} cfg optional config literal
15837
+ * @param cfg optional config literal
15862
15838
  */
15863
15839
  // eslint-disable-next-line complexity
15864
15840
  const _parseConfig = function _parseConfig() {
@@ -15866,39 +15842,23 @@
15866
15842
  if (CONFIG && CONFIG === cfg) {
15867
15843
  return;
15868
15844
  }
15869
-
15870
15845
  /* Shield configuration object from tampering */
15871
15846
  if (!cfg || typeof cfg !== 'object') {
15872
15847
  cfg = {};
15873
15848
  }
15874
-
15875
15849
  /* Shield configuration object from prototype pollution */
15876
15850
  cfg = clone(cfg);
15877
15851
  PARSER_MEDIA_TYPE =
15878
15852
  // eslint-disable-next-line unicorn/prefer-includes
15879
15853
  SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
15880
-
15881
15854
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
15882
15855
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
15883
-
15884
15856
  /* Set configuration parameters */
15885
15857
  ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
15886
15858
  ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
15887
15859
  ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
15888
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
15889
- // eslint-disable-line indent
15890
- cfg.ADD_URI_SAFE_ATTR,
15891
- // eslint-disable-line indent
15892
- transformCaseFunc // eslint-disable-line indent
15893
- ) // eslint-disable-line indent
15894
- : DEFAULT_URI_SAFE_ATTRIBUTES;
15895
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
15896
- // eslint-disable-line indent
15897
- cfg.ADD_DATA_URI_TAGS,
15898
- // eslint-disable-line indent
15899
- transformCaseFunc // eslint-disable-line indent
15900
- ) // eslint-disable-line indent
15901
- : DEFAULT_DATA_URI_TAGS;
15860
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
15861
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
15902
15862
  FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
15903
15863
  FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
15904
15864
  FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
@@ -15920,6 +15880,8 @@
15920
15880
  IN_PLACE = cfg.IN_PLACE || false; // Default false
15921
15881
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
15922
15882
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
15883
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
15884
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
15923
15885
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
15924
15886
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
15925
15887
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
@@ -15936,7 +15898,6 @@
15936
15898
  if (RETURN_DOM_FRAGMENT) {
15937
15899
  RETURN_DOM = true;
15938
15900
  }
15939
-
15940
15901
  /* Parse profile info */
15941
15902
  if (USE_PROFILES) {
15942
15903
  ALLOWED_TAGS = addToSet({}, text);
@@ -15961,7 +15922,6 @@
15961
15922
  addToSet(ALLOWED_ATTR, xml);
15962
15923
  }
15963
15924
  }
15964
-
15965
15925
  /* Merge configuration parameters */
15966
15926
  if (cfg.ADD_TAGS) {
15967
15927
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
@@ -15984,17 +15944,14 @@
15984
15944
  }
15985
15945
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
15986
15946
  }
15987
-
15988
15947
  /* Add #text in case KEEP_CONTENT is set to true */
15989
15948
  if (KEEP_CONTENT) {
15990
15949
  ALLOWED_TAGS['#text'] = true;
15991
15950
  }
15992
-
15993
15951
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
15994
15952
  if (WHOLE_DOCUMENT) {
15995
15953
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
15996
15954
  }
15997
-
15998
15955
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
15999
15956
  if (ALLOWED_TAGS.table) {
16000
15957
  addToSet(ALLOWED_TAGS, ['tbody']);
@@ -16007,10 +15964,8 @@
16007
15964
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
16008
15965
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
16009
15966
  }
16010
-
16011
15967
  // Overwrite existing TrustedTypes policy.
16012
15968
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
16013
-
16014
15969
  // Sign local variables required by `sanitize`.
16015
15970
  emptyHTML = trustedTypesPolicy.createHTML('');
16016
15971
  } else {
@@ -16018,13 +15973,11 @@
16018
15973
  if (trustedTypesPolicy === undefined) {
16019
15974
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
16020
15975
  }
16021
-
16022
15976
  // If creating the internal policy succeeded sign internal variables.
16023
15977
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
16024
15978
  emptyHTML = trustedTypesPolicy.createHTML('');
16025
15979
  }
16026
15980
  }
16027
-
16028
15981
  // Prevent further manipulation of configuration.
16029
15982
  // Not available in IE8, Safari 5, etc.
16030
15983
  if (freeze) {
@@ -16032,30 +15985,19 @@
16032
15985
  }
16033
15986
  CONFIG = cfg;
16034
15987
  };
16035
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
16036
- const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
16037
-
16038
- // Certain elements are allowed in both SVG and HTML
16039
- // namespace. We need to specify them explicitly
16040
- // so that they don't get erroneously deleted from
16041
- // HTML namespace.
16042
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
16043
-
16044
15988
  /* Keep track of all possible SVG and MathML tags
16045
15989
  * so that we can perform the namespace checks
16046
15990
  * correctly. */
16047
15991
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
16048
15992
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
16049
-
16050
15993
  /**
16051
- * @param {Element} element a DOM element whose namespace is being checked
16052
- * @returns {boolean} Return false if the element has a
15994
+ * @param element a DOM element whose namespace is being checked
15995
+ * @returns Return false if the element has a
16053
15996
  * namespace that a spec-compliant parser would never
16054
15997
  * return. Return true otherwise.
16055
15998
  */
16056
15999
  const _checkValidNamespace = function _checkValidNamespace(element) {
16057
16000
  let parent = getParentNode(element);
16058
-
16059
16001
  // In JSDOM, if we're inside shadow DOM, then parentNode
16060
16002
  // can be null. We just simulate parent in this case.
16061
16003
  if (!parent || !parent.tagName) {
@@ -16076,14 +16018,12 @@
16076
16018
  if (parent.namespaceURI === HTML_NAMESPACE) {
16077
16019
  return tagName === 'svg';
16078
16020
  }
16079
-
16080
16021
  // The only way to switch from MathML to SVG is via`
16081
16022
  // svg if parent is either <annotation-xml> or MathML
16082
16023
  // text integration points.
16083
16024
  if (parent.namespaceURI === MATHML_NAMESPACE) {
16084
16025
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
16085
16026
  }
16086
-
16087
16027
  // We only allow elements that are defined in SVG
16088
16028
  // spec. All others are disallowed in SVG namespace.
16089
16029
  return Boolean(ALL_SVG_TAGS[tagName]);
@@ -16095,13 +16035,11 @@
16095
16035
  if (parent.namespaceURI === HTML_NAMESPACE) {
16096
16036
  return tagName === 'math';
16097
16037
  }
16098
-
16099
16038
  // The only way to switch from SVG to MathML is via
16100
16039
  // <math> and HTML integration points
16101
16040
  if (parent.namespaceURI === SVG_NAMESPACE) {
16102
16041
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
16103
16042
  }
16104
-
16105
16043
  // We only allow elements that are defined in MathML
16106
16044
  // spec. All others are disallowed in MathML namespace.
16107
16045
  return Boolean(ALL_MATHML_TAGS[tagName]);
@@ -16116,28 +16054,24 @@
16116
16054
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
16117
16055
  return false;
16118
16056
  }
16119
-
16120
16057
  // We disallow tags that are specific for MathML
16121
16058
  // or SVG and should never appear in HTML namespace
16122
16059
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
16123
16060
  }
16124
-
16125
16061
  // For XHTML and XML documents that support custom namespaces
16126
16062
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
16127
16063
  return true;
16128
16064
  }
16129
-
16130
16065
  // The code should never reach this place (this means
16131
16066
  // that the element somehow got namespace that is not
16132
16067
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
16133
16068
  // Return false just in case.
16134
16069
  return false;
16135
16070
  };
16136
-
16137
16071
  /**
16138
16072
  * _forceRemove
16139
16073
  *
16140
- * @param {Node} node a DOM node
16074
+ * @param node a DOM node
16141
16075
  */
16142
16076
  const _forceRemove = function _forceRemove(node) {
16143
16077
  arrayPush(DOMPurify.removed, {
@@ -16150,46 +16084,43 @@
16150
16084
  remove(node);
16151
16085
  }
16152
16086
  };
16153
-
16154
16087
  /**
16155
16088
  * _removeAttribute
16156
16089
  *
16157
- * @param {String} name an Attribute name
16158
- * @param {Node} node a DOM node
16090
+ * @param name an Attribute name
16091
+ * @param element a DOM node
16159
16092
  */
16160
- const _removeAttribute = function _removeAttribute(name, node) {
16093
+ const _removeAttribute = function _removeAttribute(name, element) {
16161
16094
  try {
16162
16095
  arrayPush(DOMPurify.removed, {
16163
- attribute: node.getAttributeNode(name),
16164
- from: node
16096
+ attribute: element.getAttributeNode(name),
16097
+ from: element
16165
16098
  });
16166
16099
  } catch (_) {
16167
16100
  arrayPush(DOMPurify.removed, {
16168
16101
  attribute: null,
16169
- from: node
16102
+ from: element
16170
16103
  });
16171
16104
  }
16172
- node.removeAttribute(name);
16173
-
16174
- // We void attribute values for unremovable "is"" attributes
16175
- if (name === 'is' && !ALLOWED_ATTR[name]) {
16105
+ element.removeAttribute(name);
16106
+ // We void attribute values for unremovable "is" attributes
16107
+ if (name === 'is') {
16176
16108
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
16177
16109
  try {
16178
- _forceRemove(node);
16110
+ _forceRemove(element);
16179
16111
  } catch (_) {}
16180
16112
  } else {
16181
16113
  try {
16182
- node.setAttribute(name, '');
16114
+ element.setAttribute(name, '');
16183
16115
  } catch (_) {}
16184
16116
  }
16185
16117
  }
16186
16118
  };
16187
-
16188
16119
  /**
16189
16120
  * _initDocument
16190
16121
  *
16191
- * @param {String} dirty a string of dirty markup
16192
- * @return {Document} a DOM, filled with the dirty markup
16122
+ * @param dirty - a string of dirty markup
16123
+ * @return a DOM, filled with the dirty markup
16193
16124
  */
16194
16125
  const _initDocument = function _initDocument(dirty) {
16195
16126
  /* Create a HTML document */
@@ -16216,7 +16147,6 @@
16216
16147
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
16217
16148
  } catch (_) {}
16218
16149
  }
16219
-
16220
16150
  /* Use createHTMLDocument in case DOMParser is not available */
16221
16151
  if (!doc || !doc.documentElement) {
16222
16152
  doc = implementation.createDocument(NAMESPACE, 'template', null);
@@ -16230,112 +16160,86 @@
16230
16160
  if (dirty && leadingWhitespace) {
16231
16161
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
16232
16162
  }
16233
-
16234
16163
  /* Work on whole document or just its body */
16235
16164
  if (NAMESPACE === HTML_NAMESPACE) {
16236
16165
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
16237
16166
  }
16238
16167
  return WHOLE_DOCUMENT ? doc.documentElement : body;
16239
16168
  };
16240
-
16241
16169
  /**
16242
16170
  * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
16243
16171
  *
16244
- * @param {Node} root The root element or node to start traversing on.
16245
- * @return {NodeIterator} The created NodeIterator
16172
+ * @param root The root element or node to start traversing on.
16173
+ * @return The created NodeIterator
16246
16174
  */
16247
16175
  const _createNodeIterator = function _createNodeIterator(root) {
16248
16176
  return createNodeIterator.call(root.ownerDocument || root, root,
16249
16177
  // eslint-disable-next-line no-bitwise
16250
16178
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
16251
16179
  };
16252
-
16253
16180
  /**
16254
16181
  * _isClobbered
16255
16182
  *
16256
- * @param {Node} elm element to check for clobbering attacks
16257
- * @return {Boolean} true if clobbered, false if safe
16183
+ * @param element element to check for clobbering attacks
16184
+ * @return true if clobbered, false if safe
16258
16185
  */
16259
- const _isClobbered = function _isClobbered(elm) {
16260
- 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');
16186
+ const _isClobbered = function _isClobbered(element) {
16187
+ return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
16261
16188
  };
16262
-
16263
16189
  /**
16264
16190
  * Checks whether the given object is a DOM node.
16265
16191
  *
16266
- * @param {Node} object object to check whether it's a DOM node
16267
- * @return {Boolean} true is object is a DOM node
16192
+ * @param value object to check whether it's a DOM node
16193
+ * @return true is object is a DOM node
16268
16194
  */
16269
- const _isNode = function _isNode(object) {
16270
- return typeof Node === 'function' && object instanceof Node;
16195
+ const _isNode = function _isNode(value) {
16196
+ return typeof Node === 'function' && value instanceof Node;
16271
16197
  };
16272
-
16273
- /**
16274
- * _executeHook
16275
- * Execute user configurable hooks
16276
- *
16277
- * @param {String} entryPoint Name of the hook's entry point
16278
- * @param {Node} currentNode node to work on with the hook
16279
- * @param {Object} data additional hook parameters
16280
- */
16281
- const _executeHook = function _executeHook(entryPoint, currentNode, data) {
16282
- if (!hooks[entryPoint]) {
16283
- return;
16284
- }
16285
- arrayForEach(hooks[entryPoint], hook => {
16198
+ function _executeHooks(hooks, currentNode, data) {
16199
+ arrayForEach(hooks, hook => {
16286
16200
  hook.call(DOMPurify, currentNode, data, CONFIG);
16287
16201
  });
16288
- };
16289
-
16202
+ }
16290
16203
  /**
16291
16204
  * _sanitizeElements
16292
16205
  *
16293
16206
  * @protect nodeName
16294
16207
  * @protect textContent
16295
16208
  * @protect removeChild
16296
- *
16297
- * @param {Node} currentNode to check for permission to exist
16298
- * @return {Boolean} true if node was killed, false if left alive
16209
+ * @param currentNode to check for permission to exist
16210
+ * @return true if node was killed, false if left alive
16299
16211
  */
16300
16212
  const _sanitizeElements = function _sanitizeElements(currentNode) {
16301
16213
  let content = null;
16302
-
16303
16214
  /* Execute a hook if present */
16304
- _executeHook('beforeSanitizeElements', currentNode, null);
16305
-
16215
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
16306
16216
  /* Check if element is clobbered or can clobber */
16307
16217
  if (_isClobbered(currentNode)) {
16308
16218
  _forceRemove(currentNode);
16309
16219
  return true;
16310
16220
  }
16311
-
16312
16221
  /* Now let's check the element's type and name */
16313
16222
  const tagName = transformCaseFunc(currentNode.nodeName);
16314
-
16315
16223
  /* Execute a hook if present */
16316
- _executeHook('uponSanitizeElement', currentNode, {
16224
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
16317
16225
  tagName,
16318
16226
  allowedTags: ALLOWED_TAGS
16319
16227
  });
16320
-
16321
16228
  /* Detect mXSS attempts abusing namespace confusion */
16322
16229
  if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16323
16230
  _forceRemove(currentNode);
16324
16231
  return true;
16325
16232
  }
16326
-
16327
16233
  /* Remove any occurrence of processing instructions */
16328
16234
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
16329
16235
  _forceRemove(currentNode);
16330
16236
  return true;
16331
16237
  }
16332
-
16333
16238
  /* Remove any kind of possibly harmful comments */
16334
16239
  if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
16335
16240
  _forceRemove(currentNode);
16336
16241
  return true;
16337
16242
  }
16338
-
16339
16243
  /* Remove element if anything forbids its presence */
16340
16244
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
16341
16245
  /* Check if we have a custom element to handle */
@@ -16347,7 +16251,6 @@
16347
16251
  return false;
16348
16252
  }
16349
16253
  }
16350
-
16351
16254
  /* Keep content except for bad-listed elements */
16352
16255
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
16353
16256
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
@@ -16364,19 +16267,16 @@
16364
16267
  _forceRemove(currentNode);
16365
16268
  return true;
16366
16269
  }
16367
-
16368
16270
  /* Check whether element has a valid namespace */
16369
16271
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
16370
16272
  _forceRemove(currentNode);
16371
16273
  return true;
16372
16274
  }
16373
-
16374
16275
  /* Make sure that older browsers don't get fallback-tag mXSS */
16375
16276
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
16376
16277
  _forceRemove(currentNode);
16377
16278
  return true;
16378
16279
  }
16379
-
16380
16280
  /* Sanitize element content to be template-safe */
16381
16281
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
16382
16282
  /* Get the element's text content */
@@ -16391,19 +16291,17 @@
16391
16291
  currentNode.textContent = content;
16392
16292
  }
16393
16293
  }
16394
-
16395
16294
  /* Execute a hook if present */
16396
- _executeHook('afterSanitizeElements', currentNode, null);
16295
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
16397
16296
  return false;
16398
16297
  };
16399
-
16400
16298
  /**
16401
16299
  * _isValidAttribute
16402
16300
  *
16403
- * @param {string} lcTag Lowercase tag name of containing element.
16404
- * @param {string} lcName Lowercase attribute name.
16405
- * @param {string} value Attribute value.
16406
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
16301
+ * @param lcTag Lowercase tag name of containing element.
16302
+ * @param lcName Lowercase attribute name.
16303
+ * @param value Attribute value.
16304
+ * @return Returns true if `value` is valid, otherwise false.
16407
16305
  */
16408
16306
  // eslint-disable-next-line complexity
16409
16307
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
@@ -16411,7 +16309,6 @@
16411
16309
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
16412
16310
  return false;
16413
16311
  }
16414
-
16415
16312
  /* Allow valid data-* attributes: At least one character after "-"
16416
16313
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
16417
16314
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
@@ -16433,19 +16330,17 @@
16433
16330
  } else ;
16434
16331
  return true;
16435
16332
  };
16436
-
16437
16333
  /**
16438
16334
  * _isBasicCustomElement
16439
16335
  * checks if at least one dash is included in tagName, and it's not the first char
16440
16336
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
16441
16337
  *
16442
- * @param {string} tagName name of the tag of the node to sanitize
16443
- * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16338
+ * @param tagName name of the tag of the node to sanitize
16339
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16444
16340
  */
16445
16341
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
16446
16342
  return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
16447
16343
  };
16448
-
16449
16344
  /**
16450
16345
  * _sanitizeAttributes
16451
16346
  *
@@ -16454,27 +16349,26 @@
16454
16349
  * @protect removeAttribute
16455
16350
  * @protect setAttribute
16456
16351
  *
16457
- * @param {Node} currentNode to sanitize
16352
+ * @param currentNode to sanitize
16458
16353
  */
16459
16354
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
16460
16355
  /* Execute a hook if present */
16461
- _executeHook('beforeSanitizeAttributes', currentNode, null);
16356
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
16462
16357
  const {
16463
16358
  attributes
16464
16359
  } = currentNode;
16465
-
16466
16360
  /* Check if we have attributes; if not we might have a text node */
16467
- if (!attributes) {
16361
+ if (!attributes || _isClobbered(currentNode)) {
16468
16362
  return;
16469
16363
  }
16470
16364
  const hookEvent = {
16471
16365
  attrName: '',
16472
16366
  attrValue: '',
16473
16367
  keepAttr: true,
16474
- allowedAttributes: ALLOWED_ATTR
16368
+ allowedAttributes: ALLOWED_ATTR,
16369
+ forceKeepAttr: undefined
16475
16370
  };
16476
16371
  let l = attributes.length;
16477
-
16478
16372
  /* Go backwards over all attributes; safely remove bad ones */
16479
16373
  while (l--) {
16480
16374
  const attr = attributes[l];
@@ -16486,65 +16380,54 @@
16486
16380
  const lcName = transformCaseFunc(name);
16487
16381
  let value = name === 'value' ? attrValue : stringTrim(attrValue);
16488
16382
  const initValue = value;
16489
-
16490
16383
  /* Execute a hook if present */
16491
16384
  hookEvent.attrName = lcName;
16492
16385
  hookEvent.attrValue = value;
16493
16386
  hookEvent.keepAttr = true;
16494
16387
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
16495
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
16388
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
16496
16389
  value = hookEvent.attrValue;
16497
-
16390
+ /* Full DOM Clobbering protection via namespace isolation,
16391
+ * Prefix id and name attributes with `user-content-`
16392
+ */
16393
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16394
+ // Remove the attribute with this value
16395
+ _removeAttribute(name, currentNode);
16396
+ // Prefix the value and later re-create the attribute with the sanitized value
16397
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
16398
+ }
16399
+ /* Work around a security issue with comments inside attributes */
16400
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16401
+ _removeAttribute(name, currentNode);
16402
+ continue;
16403
+ }
16498
16404
  /* Did the hooks approve of the attribute? */
16499
16405
  if (hookEvent.forceKeepAttr) {
16500
16406
  continue;
16501
16407
  }
16502
-
16503
16408
  /* Remove attribute */
16504
-
16505
16409
  /* Did the hooks approve of the attribute? */
16506
16410
  if (!hookEvent.keepAttr) {
16507
16411
  _removeAttribute(name, currentNode);
16508
16412
  continue;
16509
16413
  }
16510
-
16511
16414
  /* Work around a security issue in jQuery 3.0 */
16512
16415
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
16513
16416
  _removeAttribute(name, currentNode);
16514
16417
  continue;
16515
16418
  }
16516
-
16517
16419
  /* Sanitize attribute content to be template-safe */
16518
16420
  if (SAFE_FOR_TEMPLATES) {
16519
16421
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16520
16422
  value = stringReplace(value, expr, ' ');
16521
16423
  });
16522
16424
  }
16523
-
16524
16425
  /* Is `value` valid for this attribute? */
16525
16426
  const lcTag = transformCaseFunc(currentNode.nodeName);
16526
16427
  if (!_isValidAttribute(lcTag, lcName, value)) {
16527
16428
  _removeAttribute(name, currentNode);
16528
16429
  continue;
16529
16430
  }
16530
-
16531
- /* Full DOM Clobbering protection via namespace isolation,
16532
- * Prefix id and name attributes with `user-content-`
16533
- */
16534
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16535
- // Remove the attribute with this value
16536
- _removeAttribute(name, currentNode);
16537
-
16538
- // Prefix the value and later re-create the attribute with the sanitized value
16539
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
16540
- }
16541
-
16542
- /* Work around a security issue with comments inside attributes */
16543
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16544
- _removeAttribute(name, currentNode);
16545
- continue;
16546
- }
16547
-
16548
16431
  /* Handle attributes that require Trusted Types */
16549
16432
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
16550
16433
  if (namespaceURI) ; else {
@@ -16562,7 +16445,6 @@
16562
16445
  }
16563
16446
  }
16564
16447
  }
16565
-
16566
16448
  /* Handle invalid data-* attribute set by try-catching it */
16567
16449
  if (value !== initValue) {
16568
16450
  try {
@@ -16580,51 +16462,34 @@
16580
16462
  } catch (_) {}
16581
16463
  }
16582
16464
  }
16583
-
16584
16465
  /* Execute a hook if present */
16585
- _executeHook('afterSanitizeAttributes', currentNode, null);
16466
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
16586
16467
  };
16587
-
16588
16468
  /**
16589
16469
  * _sanitizeShadowDOM
16590
16470
  *
16591
- * @param {DocumentFragment} fragment to iterate over recursively
16471
+ * @param fragment to iterate over recursively
16592
16472
  */
16593
16473
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
16594
16474
  let shadowNode = null;
16595
16475
  const shadowIterator = _createNodeIterator(fragment);
16596
-
16597
16476
  /* Execute a hook if present */
16598
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
16477
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
16599
16478
  while (shadowNode = shadowIterator.nextNode()) {
16600
16479
  /* Execute a hook if present */
16601
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
16602
-
16480
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
16603
16481
  /* Sanitize tags and elements */
16604
- if (_sanitizeElements(shadowNode)) {
16605
- continue;
16606
- }
16607
-
16482
+ _sanitizeElements(shadowNode);
16483
+ /* Check attributes next */
16484
+ _sanitizeAttributes(shadowNode);
16608
16485
  /* Deep shadow DOM detected */
16609
16486
  if (shadowNode.content instanceof DocumentFragment) {
16610
16487
  _sanitizeShadowDOM(shadowNode.content);
16611
16488
  }
16612
-
16613
- /* Check attributes, sanitize if necessary */
16614
- _sanitizeAttributes(shadowNode);
16615
16489
  }
16616
-
16617
16490
  /* Execute a hook if present */
16618
- _executeHook('afterSanitizeShadowDOM', fragment, null);
16491
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
16619
16492
  };
16620
-
16621
- /**
16622
- * Sanitize
16623
- * Public method providing core sanitation functionality
16624
- *
16625
- * @param {String|Node} dirty string or DOM node
16626
- * @param {Object} cfg object
16627
- */
16628
16493
  // eslint-disable-next-line complexity
16629
16494
  DOMPurify.sanitize = function (dirty) {
16630
16495
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -16639,7 +16504,6 @@
16639
16504
  if (IS_EMPTY_INPUT) {
16640
16505
  dirty = '<!-->';
16641
16506
  }
16642
-
16643
16507
  /* Stringify, in case dirty is an object */
16644
16508
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
16645
16509
  if (typeof dirty.toString === 'function') {
@@ -16651,20 +16515,16 @@
16651
16515
  throw typeErrorCreate('toString is not a function');
16652
16516
  }
16653
16517
  }
16654
-
16655
16518
  /* Return dirty HTML if DOMPurify cannot run */
16656
16519
  if (!DOMPurify.isSupported) {
16657
16520
  return dirty;
16658
16521
  }
16659
-
16660
16522
  /* Assign config vars */
16661
16523
  if (!SET_CONFIG) {
16662
16524
  _parseConfig(cfg);
16663
16525
  }
16664
-
16665
16526
  /* Clean up removed elements */
16666
16527
  DOMPurify.removed = [];
16667
-
16668
16528
  /* Check if dirty is correctly typed for IN_PLACE */
16669
16529
  if (typeof dirty === 'string') {
16670
16530
  IN_PLACE = false;
@@ -16698,45 +16558,34 @@
16698
16558
  dirty.indexOf('<') === -1) {
16699
16559
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
16700
16560
  }
16701
-
16702
16561
  /* Initialize the document to work on */
16703
16562
  body = _initDocument(dirty);
16704
-
16705
16563
  /* Check we have a DOM node from the data */
16706
16564
  if (!body) {
16707
16565
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
16708
16566
  }
16709
16567
  }
16710
-
16711
16568
  /* Remove first element node (ours) if FORCE_BODY is set */
16712
16569
  if (body && FORCE_BODY) {
16713
16570
  _forceRemove(body.firstChild);
16714
16571
  }
16715
-
16716
16572
  /* Get node iterator */
16717
16573
  const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
16718
-
16719
16574
  /* Now start iterating over the created document */
16720
16575
  while (currentNode = nodeIterator.nextNode()) {
16721
16576
  /* Sanitize tags and elements */
16722
- if (_sanitizeElements(currentNode)) {
16723
- continue;
16724
- }
16725
-
16577
+ _sanitizeElements(currentNode);
16578
+ /* Check attributes next */
16579
+ _sanitizeAttributes(currentNode);
16726
16580
  /* Shadow DOM detected, sanitize it */
16727
16581
  if (currentNode.content instanceof DocumentFragment) {
16728
16582
  _sanitizeShadowDOM(currentNode.content);
16729
16583
  }
16730
-
16731
- /* Check attributes, sanitize if necessary */
16732
- _sanitizeAttributes(currentNode);
16733
16584
  }
16734
-
16735
16585
  /* If we sanitized `dirty` in-place, return it. */
16736
16586
  if (IN_PLACE) {
16737
16587
  return dirty;
16738
16588
  }
16739
-
16740
16589
  /* Return sanitized string or DOM */
16741
16590
  if (RETURN_DOM) {
16742
16591
  if (RETURN_DOM_FRAGMENT) {
@@ -16761,12 +16610,10 @@
16761
16610
  return returnNode;
16762
16611
  }
16763
16612
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
16764
-
16765
16613
  /* Serialize doctype if allowed */
16766
16614
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
16767
16615
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
16768
16616
  }
16769
-
16770
16617
  /* Sanitize final string template-safe */
16771
16618
  if (SAFE_FOR_TEMPLATES) {
16772
16619
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -16775,39 +16622,15 @@
16775
16622
  }
16776
16623
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
16777
16624
  };
16778
-
16779
- /**
16780
- * Public method to set the configuration once
16781
- * setConfig
16782
- *
16783
- * @param {Object} cfg configuration object
16784
- */
16785
16625
  DOMPurify.setConfig = function () {
16786
16626
  let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16787
16627
  _parseConfig(cfg);
16788
16628
  SET_CONFIG = true;
16789
16629
  };
16790
-
16791
- /**
16792
- * Public method to remove the configuration
16793
- * clearConfig
16794
- *
16795
- */
16796
16630
  DOMPurify.clearConfig = function () {
16797
16631
  CONFIG = null;
16798
16632
  SET_CONFIG = false;
16799
16633
  };
16800
-
16801
- /**
16802
- * Public method to check if an attribute value is valid.
16803
- * Uses last set config, if any. Otherwise, uses config defaults.
16804
- * isValidAttribute
16805
- *
16806
- * @param {String} tag Tag name of containing element.
16807
- * @param {String} attr Attribute name.
16808
- * @param {String} value Attribute value.
16809
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
16810
- */
16811
16634
  DOMPurify.isValidAttribute = function (tag, attr, value) {
16812
16635
  /* Initialize shared config vars if necessary. */
16813
16636
  if (!CONFIG) {
@@ -16817,54 +16640,24 @@
16817
16640
  const lcName = transformCaseFunc(attr);
16818
16641
  return _isValidAttribute(lcTag, lcName, value);
16819
16642
  };
16820
-
16821
- /**
16822
- * AddHook
16823
- * Public method to add DOMPurify hooks
16824
- *
16825
- * @param {String} entryPoint entry point for the hook to add
16826
- * @param {Function} hookFunction function to execute
16827
- */
16828
16643
  DOMPurify.addHook = function (entryPoint, hookFunction) {
16829
16644
  if (typeof hookFunction !== 'function') {
16830
16645
  return;
16831
16646
  }
16832
- hooks[entryPoint] = hooks[entryPoint] || [];
16833
16647
  arrayPush(hooks[entryPoint], hookFunction);
16834
16648
  };
16835
-
16836
- /**
16837
- * RemoveHook
16838
- * Public method to remove a DOMPurify hook at a given entryPoint
16839
- * (pops it from the stack of hooks if more are present)
16840
- *
16841
- * @param {String} entryPoint entry point for the hook to remove
16842
- * @return {Function} removed(popped) hook
16843
- */
16844
- DOMPurify.removeHook = function (entryPoint) {
16845
- if (hooks[entryPoint]) {
16846
- return arrayPop(hooks[entryPoint]);
16649
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
16650
+ if (hookFunction !== undefined) {
16651
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
16652
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
16847
16653
  }
16654
+ return arrayPop(hooks[entryPoint]);
16848
16655
  };
16849
-
16850
- /**
16851
- * RemoveHooks
16852
- * Public method to remove all DOMPurify hooks at a given entryPoint
16853
- *
16854
- * @param {String} entryPoint entry point for the hooks to remove
16855
- */
16856
16656
  DOMPurify.removeHooks = function (entryPoint) {
16857
- if (hooks[entryPoint]) {
16858
- hooks[entryPoint] = [];
16859
- }
16657
+ hooks[entryPoint] = [];
16860
16658
  };
16861
-
16862
- /**
16863
- * RemoveAllHooks
16864
- * Public method to remove all DOMPurify hooks
16865
- */
16866
16659
  DOMPurify.removeAllHooks = function () {
16867
- hooks = {};
16660
+ hooks = _createHooksMap();
16868
16661
  };
16869
16662
  return DOMPurify;
16870
16663
  }
@@ -17346,17 +17139,39 @@
17346
17139
  const encoding = el.getAttribute('encoding');
17347
17140
  return hasAllowedEncodings && isString(encoding) && contains$2(allowedEncodings, encoding);
17348
17141
  };
17142
+ const isValidElementOpt = (node, lcTagName) => {
17143
+ if (hasAllowedEncodings && lcTagName === 'semantics') {
17144
+ return Optional.some(true);
17145
+ } else if (lcTagName === 'annotation') {
17146
+ return Optional.some(isElement$6(node) && hasValidEncoding(node));
17147
+ } else if (isArray$1(settings.extended_mathml_elements)) {
17148
+ if (settings.extended_mathml_elements.includes(lcTagName)) {
17149
+ return Optional.from(true);
17150
+ } else {
17151
+ return Optional.none();
17152
+ }
17153
+ } else {
17154
+ return Optional.none();
17155
+ }
17156
+ };
17349
17157
  purify$1.addHook('uponSanitizeElement', (node, evt) => {
17350
17158
  var _a;
17351
17159
  const lcTagName = (_a = evt.tagName) !== null && _a !== void 0 ? _a : node.nodeName.toLowerCase();
17352
- if (hasAllowedEncodings && lcTagName === 'semantics') {
17353
- evt.allowedTags[lcTagName] = true;
17354
- }
17355
- if (lcTagName === 'annotation') {
17356
- const keepElement = hasValidEncoding(node);
17160
+ const keepElementOpt = isValidElementOpt(node, lcTagName);
17161
+ keepElementOpt.each(keepElement => {
17357
17162
  evt.allowedTags[lcTagName] = keepElement;
17358
- if (!keepElement) {
17359
- node.remove();
17163
+ if (!keepElement && settings.sanitize) {
17164
+ if (isElement$6(node)) {
17165
+ node.remove();
17166
+ }
17167
+ }
17168
+ });
17169
+ });
17170
+ purify$1.addHook('uponSanitizeAttribute', (_node, event) => {
17171
+ if (isArray$1(settings.extended_mathml_attributes)) {
17172
+ const keepAttribute = settings.extended_mathml_attributes.includes(event.attrName);
17173
+ if (keepAttribute) {
17174
+ event.forceKeepAttr = true;
17360
17175
  }
17361
17176
  }
17362
17177
  });
@@ -23679,15 +23494,18 @@
23679
23494
  return getCellFirstCursorPosition(cell);
23680
23495
  });
23681
23496
  }, current => {
23682
- if (editor.mode.isReadOnly()) {
23497
+ if (editor.mode.isReadOnly() || !isCellInEditableTable(current)) {
23683
23498
  return Optional.none();
23684
23499
  }
23685
23500
  editor.execCommand('mceTableInsertRowAfter');
23686
23501
  return tabForward(editor, isRoot, current);
23687
23502
  });
23688
23503
  };
23689
- const tabForward = (editor, isRoot, cell) => tabGo(editor, isRoot, next(cell, isEditable$2));
23690
- const tabBackward = (editor, isRoot, cell) => tabGo(editor, isRoot, prev(cell, isEditable$2));
23504
+ const isCellInEditableTable = cell => closest$4(cell, isTag('table')).exists(isEditable$2);
23505
+ const tabForward = (editor, isRoot, cell) => tabGo(editor, isRoot, next(cell, isCellEditable));
23506
+ const tabBackward = (editor, isRoot, cell) => tabGo(editor, isRoot, prev(cell, isCellEditable));
23507
+ const isCellEditable = cell => isEditable$2(cell) || descendant(cell, isEditableHTMLElement);
23508
+ const isEditableHTMLElement = node => isHTMLElement$1(node) && isEditable$2(node);
23691
23509
  const handleTab = (editor, forward) => {
23692
23510
  const rootElements = [
23693
23511
  'table',
@@ -24345,6 +24163,7 @@
24345
24163
  const optionOf = (key, schema) => field(key, key, asOption(), schema);
24346
24164
  const optionString = key => optionOf(key, string);
24347
24165
  const optionFunction = key => optionOf(key, functionProcessor);
24166
+ const optionObjOf = (key, objSchema) => optionOf(key, objOf(objSchema));
24348
24167
  const defaulted = (key, fallback) => field(key, key, defaulted$1(fallback), anyValue());
24349
24168
  const defaultedOf = (key, fallback, schema) => field(key, key, defaulted$1(fallback), schema);
24350
24169
  const defaultedNumber = (key, fallback) => defaultedOf(key, fallback, number);
@@ -24419,7 +24238,7 @@
24419
24238
  onAction,
24420
24239
  customField('original', identity)
24421
24240
  ]);
24422
- const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);
24241
+ const launchButtonFields$1 = baseToolbarButtonFields.concat([defaultedType('contextformbutton')]);
24423
24242
  const launchToggleButtonFields = baseToolbarToggleButtonFields.concat([defaultedType('contextformtogglebutton')]);
24424
24243
  const toggleOrNormal = choose('type', {
24425
24244
  contextformbutton: contextButtonFields,
@@ -24429,7 +24248,7 @@
24429
24248
  optionalLabel,
24430
24249
  requiredArrayOf('commands', toggleOrNormal),
24431
24250
  optionOf('launch', choose('type', {
24432
- contextformbutton: launchButtonFields,
24251
+ contextformbutton: launchButtonFields$1,
24433
24252
  contextformtogglebutton: launchToggleButtonFields
24434
24253
  })),
24435
24254
  defaultedFunction('onInput', noop),
@@ -24465,8 +24284,10 @@
24465
24284
  contextsizeinputform: contextSizeInputFormFields
24466
24285
  });
24467
24286
 
24287
+ const launchButtonFields = baseToolbarButtonFields.concat([defaultedType('contexttoolbarbutton')]);
24468
24288
  objOf([
24469
24289
  defaultedType('contexttoolbar'),
24290
+ optionObjOf('launch', launchButtonFields),
24470
24291
  requiredOf('items', oneOf([
24471
24292
  string,
24472
24293
  arrOfObj([
@@ -29172,6 +28993,8 @@
29172
28993
  allow_unsafe_link_target: getOption('allow_unsafe_link_target'),
29173
28994
  convert_unsafe_embeds: getOption('convert_unsafe_embeds'),
29174
28995
  convert_fonts_to_spans: getOption('convert_fonts_to_spans'),
28996
+ extended_mathml_attributes: getOption('extended_mathml_attributes'),
28997
+ extended_mathml_elements: getOption('extended_mathml_elements'),
29175
28998
  fix_list_elements: getOption('fix_list_elements'),
29176
28999
  font_size_legacy_values: getOption('font_size_legacy_values'),
29177
29000
  forced_root_block: getOption('forced_root_block'),
@@ -31853,7 +31676,7 @@
31853
31676
  documentBaseURL: null,
31854
31677
  suffix: null,
31855
31678
  majorVersion: '7',
31856
- minorVersion: '7.0',
31679
+ minorVersion: '8.0',
31857
31680
  releaseDate: 'TBD',
31858
31681
  i18n: I18n,
31859
31682
  activeEditor: null,
@@ -32273,7 +32096,7 @@
32273
32096
  if (!done) {
32274
32097
  done = true;
32275
32098
  if (timer !== null) {
32276
- clearTimeout(timer);
32099
+ window.clearTimeout(timer);
32277
32100
  timer = null;
32278
32101
  }
32279
32102
  completer.apply(null, args);
@@ -32283,7 +32106,7 @@
32283
32106
  const reject = complete(rejectCb);
32284
32107
  const start = (...args) => {
32285
32108
  if (!done && timer === null) {
32286
- timer = setTimeout(() => reject.apply(null, args), timeout);
32109
+ timer = window.setTimeout(() => reject.apply(null, args), timeout);
32287
32110
  }
32288
32111
  };
32289
32112
  return {