tinymce-rails 7.3.0 → 7.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/app/assets/source/tinymce/tinymce.js +1056 -980
- data/lib/tinymce/rails/version.rb +2 -2
- data/vendor/assets/javascripts/tinymce/icons/default/icons.js +1 -1
- data/vendor/assets/javascripts/tinymce/models/dom/model.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
- data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +2 -2
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
- data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
- data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +384 -2
- data/vendor/assets/javascripts/tinymce/tinymce.d.ts +23 -0
- data/vendor/assets/javascripts/tinymce/tinymce.js +383 -2
- metadata +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TinyMCE version 7.
|
|
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
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
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
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
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
|
-
|
|
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(
|
|
15354
|
-
console.warn('fallback value for', element);
|
|
15451
|
+
function fallbackValue() {
|
|
15355
15452
|
return null;
|
|
15356
15453
|
}
|
|
15357
15454
|
return fallbackValue;
|
|
15358
15455
|
}
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
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
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 !==
|
|
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
|
-
|
|
15589
|
+
let {
|
|
15590
|
+
document
|
|
15591
|
+
} = window;
|
|
15592
|
+
const originalDocument = document;
|
|
16037
15593
|
const currentScript = originalDocument.currentScript;
|
|
16038
|
-
|
|
16039
|
-
|
|
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 {
|
|
16054
|
-
|
|
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 {
|
|
16058
|
-
|
|
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
|
-
|
|
16062
|
-
|
|
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
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
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
|
-
|
|
16116
|
-
|
|
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
|
-
|
|
16144
|
-
|
|
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
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16208
|
-
|
|
16209
|
-
|
|
16210
|
-
|
|
16211
|
-
|
|
16212
|
-
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
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({},
|
|
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
|
-
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
const
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
]);
|
|
16334
|
-
const
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
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, {
|
|
16103
|
+
arrayPush(DOMPurify.removed, {
|
|
16104
|
+
element: node
|
|
16105
|
+
});
|
|
16393
16106
|
try {
|
|
16394
|
-
node
|
|
16107
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
16108
|
+
getParentNode(node).removeChild(node);
|
|
16395
16109
|
} catch (_) {
|
|
16396
|
-
|
|
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
|
-
|
|
16428
|
-
let
|
|
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
|
-
|
|
16462
|
-
|
|
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 === '
|
|
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
|
-
|
|
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
|
|
16496
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16525
|
-
|
|
16526
|
-
|
|
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, {
|
|
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
|
-
|
|
16540
|
-
|
|
16541
|
-
|
|
16542
|
-
|
|
16543
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16556
|
-
|
|
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
|
-
|
|
16560
|
-
let value;
|
|
16561
|
-
let lcName;
|
|
16562
|
-
let l;
|
|
16420
|
+
/* Execute a hook if present */
|
|
16563
16421
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
16564
|
-
const {
|
|
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 {
|
|
16578
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
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
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
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
|
-
|
|
16635
|
-
|
|
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 =
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16749
|
-
|
|
16750
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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 =
|
|
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
|
|
25923
|
+
const editor = this.editor;
|
|
25924
|
+
const selection = editor.selection;
|
|
25844
25925
|
let node;
|
|
25845
|
-
if (
|
|
25846
|
-
const root =
|
|
25926
|
+
if (editor.initialized && selection && !shouldDisableNodeChange(editor)) {
|
|
25927
|
+
const root = editor.getBody();
|
|
25847
25928
|
node = selection.getStart(true) || root;
|
|
25848
|
-
if (node.ownerDocument !==
|
|
25929
|
+
if (node.ownerDocument !== editor.getDoc() || !editor.dom.isChildOf(node, root)) {
|
|
25849
25930
|
node = root;
|
|
25850
25931
|
}
|
|
25851
25932
|
const parents = [];
|
|
25852
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
29552
|
-
|
|
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
|
|
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
|
|
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('
|
|
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('
|
|
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
|
|
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: '
|
|
31544
|
-
releaseDate: '
|
|
31619
|
+
minorVersion: '4.1',
|
|
31620
|
+
releaseDate: 'TBD',
|
|
31545
31621
|
i18n: I18n,
|
|
31546
31622
|
activeEditor: null,
|
|
31547
31623
|
focusedEditor: null,
|