/*
Trix 2.1.10
Copyright © 2024 37signals, LLC
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Trix = factory());
})(this, (function () { 'use strict';
var name = "trix";
var version = "2.1.10";
var description = "A rich text editor for everyday writing";
var main = "dist/trix.umd.min.js";
var module = "dist/trix.esm.min.js";
var style = "dist/trix.css";
var files = [
"dist/*.css",
"dist/*.js",
"dist/*.map",
"src/{inspector,trix}/*.js"
];
var repository = {
type: "git",
url: "git+https://github.com/basecamp/trix.git"
};
var keywords = [
"rich text",
"wysiwyg",
"editor"
];
var author = "37signals, LLC";
var license = "MIT";
var bugs = {
url: "https://github.com/basecamp/trix/issues"
};
var homepage = "https://trix-editor.org/";
var devDependencies = {
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.4",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@web/dev-server": "^0.1.34",
"babel-eslint": "^10.1.0",
concurrently: "^7.4.0",
eslint: "^7.32.0",
esm: "^3.2.25",
karma: "6.4.1",
"karma-chrome-launcher": "3.2.0",
"karma-qunit": "^4.1.2",
"karma-sauce-launcher": "^4.3.6",
"node-sass": "^7.0.1",
qunit: "2.19.1",
rangy: "^1.3.0",
rollup: "^2.56.3",
"rollup-plugin-includepaths": "^0.2.4",
"rollup-plugin-terser": "^7.0.2",
svgo: "^2.8.0",
webdriverio: "^7.19.5"
};
var resolutions = {
webdriverio: "^7.19.5"
};
var scripts = {
"build-css": "node-sass --functions=./assets/trix/stylesheets/functions assets/trix.scss dist/trix.css",
"build-js": "rollup -c",
"build-assets": "cp -f assets/*.html dist/",
build: "yarn run build-js && yarn run build-css && yarn run build-assets",
watch: "rollup -c -w",
lint: "eslint .",
pretest: "yarn run lint && yarn run build",
test: "karma start",
prerelease: "yarn version && yarn test",
release: "npm adduser && npm publish",
postrelease: "git push && git push --tags",
dev: "web-dev-server --app-index index.html --root-dir dist --node-resolve --open",
start: "yarn build-assets && concurrently --kill-others --names js,css,dev-server 'yarn watch' 'yarn build-css --watch' 'yarn dev'"
};
var dependencies = {
dompurify: "^3.2.3"
};
var _package = {
name: name,
version: version,
description: description,
main: main,
module: module,
style: style,
files: files,
repository: repository,
keywords: keywords,
author: author,
license: license,
bugs: bugs,
homepage: homepage,
devDependencies: devDependencies,
resolutions: resolutions,
scripts: scripts,
dependencies: dependencies
};
const attachmentSelector = "[data-trix-attachment]";
const attachments = {
preview: {
presentation: "gallery",
caption: {
name: true,
size: true
}
},
file: {
caption: {
size: true
}
}
};
const attributes = {
default: {
tagName: "div",
parse: false
},
quote: {
tagName: "blockquote",
nestable: true
},
heading1: {
tagName: "h1",
terminal: true,
breakOnReturn: true,
group: false
},
code: {
tagName: "pre",
terminal: true,
htmlAttributes: ["language"],
text: {
plaintext: true
}
},
bulletList: {
tagName: "ul",
parse: false
},
bullet: {
tagName: "li",
listAttribute: "bulletList",
group: false,
nestable: true,
test(element) {
return tagName$1(element.parentNode) === attributes[this.listAttribute].tagName;
}
},
numberList: {
tagName: "ol",
parse: false
},
number: {
tagName: "li",
listAttribute: "numberList",
group: false,
nestable: true,
test(element) {
return tagName$1(element.parentNode) === attributes[this.listAttribute].tagName;
}
},
attachmentGallery: {
tagName: "div",
exclusive: true,
terminal: true,
parse: false,
group: false
}
};
const tagName$1 = element => {
var _element$tagName;
return element === null || element === void 0 || (_element$tagName = element.tagName) === null || _element$tagName === void 0 ? void 0 : _element$tagName.toLowerCase();
};
const androidVersionMatch = navigator.userAgent.match(/android\s([0-9]+.*Chrome)/i);
const androidVersion = androidVersionMatch && parseInt(androidVersionMatch[1]);
var browser$1 = {
// Android emits composition events when moving the cursor through existing text
// Introduced in Chrome 65: https://bugs.chromium.org/p/chromium/issues/detail?id=764439#c9
composesExistingText: /Android.*Chrome/.test(navigator.userAgent),
// Android 13, especially on Samsung keyboards, emits extra compositionend and beforeinput events
// that can make the input handler lose the current selection or enter an infinite input -> render -> input
// loop.
recentAndroid: androidVersion && androidVersion > 12,
samsungAndroid: androidVersion && navigator.userAgent.match(/Android.*SM-/),
// IE 11 activates resizing handles on editable elements that have "layout"
forcesObjectResizing: /Trident.*rv:11/.test(navigator.userAgent),
// https://www.w3.org/TR/input-events-1/ + https://www.w3.org/TR/input-events-2/
supportsInputEvents: typeof InputEvent !== "undefined" && ["data", "getTargetRanges", "inputType"].every(prop => prop in InputEvent.prototype)
};
var css$3 = {
attachment: "attachment",
attachmentCaption: "attachment__caption",
attachmentCaptionEditor: "attachment__caption-editor",
attachmentMetadata: "attachment__metadata",
attachmentMetadataContainer: "attachment__metadata-container",
attachmentName: "attachment__name",
attachmentProgress: "attachment__progress",
attachmentSize: "attachment__size",
attachmentToolbar: "attachment__toolbar",
attachmentGallery: "attachment-gallery"
};
var lang$1 = {
attachFiles: "Attach Files",
bold: "Bold",
bullets: "Bullets",
byte: "Byte",
bytes: "Bytes",
captionPlaceholder: "Add a caption…",
code: "Code",
heading1: "Heading",
indent: "Increase Level",
italic: "Italic",
link: "Link",
numbers: "Numbers",
outdent: "Decrease Level",
quote: "Quote",
redo: "Redo",
remove: "Remove",
strike: "Strikethrough",
undo: "Undo",
unlink: "Unlink",
url: "URL",
urlPlaceholder: "Enter a URL…",
GB: "GB",
KB: "KB",
MB: "MB",
PB: "PB",
TB: "TB"
};
/* eslint-disable
no-case-declarations,
*/
const sizes = [lang$1.bytes, lang$1.KB, lang$1.MB, lang$1.GB, lang$1.TB, lang$1.PB];
var file_size_formatting = {
prefix: "IEC",
precision: 2,
formatter(number) {
switch (number) {
case 0:
return "0 ".concat(lang$1.bytes);
case 1:
return "1 ".concat(lang$1.byte);
default:
let base;
if (this.prefix === "SI") {
base = 1000;
} else if (this.prefix === "IEC") {
base = 1024;
}
const exp = Math.floor(Math.log(number) / Math.log(base));
const humanSize = number / Math.pow(base, exp);
const string = humanSize.toFixed(this.precision);
const withoutInsignificantZeros = string.replace(/0*$/, "").replace(/\.$/, "");
return "".concat(withoutInsignificantZeros, " ").concat(sizes[exp]);
}
}
};
const ZERO_WIDTH_SPACE = "\uFEFF";
const NON_BREAKING_SPACE = "\u00A0";
const OBJECT_REPLACEMENT_CHARACTER = "\uFFFC";
const extend = function (properties) {
for (const key in properties) {
const value = properties[key];
this[key] = value;
}
return this;
};
const html$2 = document.documentElement;
const match = html$2.matches;
const handleEvent = function (eventName) {
let {
onElement,
matchingSelector,
withCallback,
inPhase,
preventDefault,
times
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const element = onElement ? onElement : html$2;
const selector = matchingSelector;
const useCapture = inPhase === "capturing";
const handler = function (event) {
if (times != null && --times === 0) {
handler.destroy();
}
const target = findClosestElementFromNode(event.target, {
matchingSelector: selector
});
if (target != null) {
withCallback === null || withCallback === void 0 || withCallback.call(target, event, target);
if (preventDefault) {
event.preventDefault();
}
}
};
handler.destroy = () => element.removeEventListener(eventName, handler, useCapture);
element.addEventListener(eventName, handler, useCapture);
return handler;
};
const handleEventOnce = function (eventName) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
options.times = 1;
return handleEvent(eventName, options);
};
const triggerEvent = function (eventName) {
let {
onElement,
bubbles,
cancelable,
attributes
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const element = onElement != null ? onElement : html$2;
bubbles = bubbles !== false;
cancelable = cancelable !== false;
const event = document.createEvent("Events");
event.initEvent(eventName, bubbles, cancelable);
if (attributes != null) {
extend.call(event, attributes);
}
return element.dispatchEvent(event);
};
const elementMatchesSelector = function (element, selector) {
if ((element === null || element === void 0 ? void 0 : element.nodeType) === 1) {
return match.call(element, selector);
}
};
const findClosestElementFromNode = function (node) {
let {
matchingSelector,
untilNode
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
while (node && node.nodeType !== Node.ELEMENT_NODE) {
node = node.parentNode;
}
if (node == null) {
return;
}
if (matchingSelector != null) {
if (node.closest && untilNode == null) {
return node.closest(matchingSelector);
} else {
while (node && node !== untilNode) {
if (elementMatchesSelector(node, matchingSelector)) {
return node;
}
node = node.parentNode;
}
}
} else {
return node;
}
};
const findInnerElement = function (element) {
while ((_element = element) !== null && _element !== void 0 && _element.firstElementChild) {
var _element;
element = element.firstElementChild;
}
return element;
};
const innerElementIsActive = element => document.activeElement !== element && elementContainsNode(element, document.activeElement);
const elementContainsNode = function (element, node) {
if (!element || !node) {
return;
}
while (node) {
if (node === element) {
return true;
}
node = node.parentNode;
}
};
const findNodeFromContainerAndOffset = function (container, offset) {
if (!container) {
return;
}
if (container.nodeType === Node.TEXT_NODE) {
return container;
} else if (offset === 0) {
return container.firstChild != null ? container.firstChild : container;
} else {
return container.childNodes.item(offset - 1);
}
};
const findElementFromContainerAndOffset = function (container, offset) {
const node = findNodeFromContainerAndOffset(container, offset);
return findClosestElementFromNode(node);
};
const findChildIndexOfNode = function (node) {
var _node;
if (!((_node = node) !== null && _node !== void 0 && _node.parentNode)) {
return;
}
let childIndex = 0;
node = node.previousSibling;
while (node) {
childIndex++;
node = node.previousSibling;
}
return childIndex;
};
const removeNode = node => {
var _node$parentNode;
return node === null || node === void 0 || (_node$parentNode = node.parentNode) === null || _node$parentNode === void 0 ? void 0 : _node$parentNode.removeChild(node);
};
const walkTree = function (tree) {
let {
onlyNodesOfType,
usingFilter,
expandEntityReferences
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const whatToShow = (() => {
switch (onlyNodesOfType) {
case "element":
return NodeFilter.SHOW_ELEMENT;
case "text":
return NodeFilter.SHOW_TEXT;
case "comment":
return NodeFilter.SHOW_COMMENT;
default:
return NodeFilter.SHOW_ALL;
}
})();
return document.createTreeWalker(tree, whatToShow, usingFilter != null ? usingFilter : null, expandEntityReferences === true);
};
const tagName = element => {
var _element$tagName;
return element === null || element === void 0 || (_element$tagName = element.tagName) === null || _element$tagName === void 0 ? void 0 : _element$tagName.toLowerCase();
};
const makeElement = function (tag) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let key, value;
if (typeof tag === "object") {
options = tag;
tag = options.tagName;
} else {
options = {
attributes: options
};
}
const element = document.createElement(tag);
if (options.editable != null) {
if (options.attributes == null) {
options.attributes = {};
}
options.attributes.contenteditable = options.editable;
}
if (options.attributes) {
for (key in options.attributes) {
value = options.attributes[key];
element.setAttribute(key, value);
}
}
if (options.style) {
for (key in options.style) {
value = options.style[key];
element.style[key] = value;
}
}
if (options.data) {
for (key in options.data) {
value = options.data[key];
element.dataset[key] = value;
}
}
if (options.className) {
options.className.split(" ").forEach(className => {
element.classList.add(className);
});
}
if (options.textContent) {
element.textContent = options.textContent;
}
if (options.childNodes) {
[].concat(options.childNodes).forEach(childNode => {
element.appendChild(childNode);
});
}
return element;
};
let blockTagNames = undefined;
const getBlockTagNames = function () {
if (blockTagNames != null) {
return blockTagNames;
}
blockTagNames = [];
for (const key in attributes) {
const attributes$1 = attributes[key];
if (attributes$1.tagName) {
blockTagNames.push(attributes$1.tagName);
}
}
return blockTagNames;
};
const nodeIsBlockContainer = node => nodeIsBlockStartComment(node === null || node === void 0 ? void 0 : node.firstChild);
const nodeProbablyIsBlockContainer = function (node) {
return getBlockTagNames().includes(tagName(node)) && !getBlockTagNames().includes(tagName(node.firstChild));
};
const nodeIsBlockStart = function (node) {
let {
strict
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
strict: true
};
if (strict) {
return nodeIsBlockStartComment(node);
} else {
return nodeIsBlockStartComment(node) || !nodeIsBlockStartComment(node.firstChild) && nodeProbablyIsBlockContainer(node);
}
};
const nodeIsBlockStartComment = node => nodeIsCommentNode(node) && (node === null || node === void 0 ? void 0 : node.data) === "block";
const nodeIsCommentNode = node => (node === null || node === void 0 ? void 0 : node.nodeType) === Node.COMMENT_NODE;
const nodeIsCursorTarget = function (node) {
let {
name
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (!node) {
return;
}
if (nodeIsTextNode(node)) {
if (node.data === ZERO_WIDTH_SPACE) {
if (name) {
return node.parentNode.dataset.trixCursorTarget === name;
} else {
return true;
}
}
} else {
return nodeIsCursorTarget(node.firstChild);
}
};
const nodeIsAttachmentElement = node => elementMatchesSelector(node, attachmentSelector);
const nodeIsEmptyTextNode = node => nodeIsTextNode(node) && (node === null || node === void 0 ? void 0 : node.data) === "";
const nodeIsTextNode = node => (node === null || node === void 0 ? void 0 : node.nodeType) === Node.TEXT_NODE;
const input = {
level2Enabled: true,
getLevel() {
if (this.level2Enabled && browser$1.supportsInputEvents) {
return 2;
} else {
return 0;
}
},
pickFiles(callback) {
const input = makeElement("input", {
type: "file",
multiple: true,
hidden: true,
id: this.fileInputId
});
input.addEventListener("change", () => {
callback(input.files);
removeNode(input);
});
removeNode(document.getElementById(this.fileInputId));
document.body.appendChild(input);
input.click();
}
};
var key_names = {
8: "backspace",
9: "tab",
13: "return",
27: "escape",
37: "left",
39: "right",
46: "delete",
68: "d",
72: "h",
79: "o"
};
var parser = {
removeBlankTableCells: false,
tableCellSeparator: " | ",
tableRowSeparator: "\n"
};
var text_attributes = {
bold: {
tagName: "strong",
inheritable: true,
parser(element) {
const style = window.getComputedStyle(element);
return style.fontWeight === "bold" || style.fontWeight >= 600;
}
},
italic: {
tagName: "em",
inheritable: true,
parser(element) {
const style = window.getComputedStyle(element);
return style.fontStyle === "italic";
}
},
href: {
groupTagName: "a",
parser(element) {
const matchingSelector = "a:not(".concat(attachmentSelector, ")");
const link = element.closest(matchingSelector);
if (link) {
return link.getAttribute("href");
}
}
},
strike: {
tagName: "del",
inheritable: true
},
frozen: {
style: {
backgroundColor: "highlight"
}
}
};
var toolbar = {
getDefaultHTML() {
return "
\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n\n \n \n \n\n \n\n \n \n \n \n
\n\n ");
}
};
const undo = {
interval: 5000
};
var config = /*#__PURE__*/Object.freeze({
__proto__: null,
attachments: attachments,
blockAttributes: attributes,
browser: browser$1,
css: css$3,
fileSize: file_size_formatting,
input: input,
keyNames: key_names,
lang: lang$1,
parser: parser,
textAttributes: text_attributes,
toolbar: toolbar,
undo: undo
});
class BasicObject {
static proxyMethod(expression) {
const {
name,
toMethod,
toProperty,
optional
} = parseProxyMethodExpression(expression);
this.prototype[name] = function () {
let subject;
let object;
if (toMethod) {
if (optional) {
var _this$toMethod;
object = (_this$toMethod = this[toMethod]) === null || _this$toMethod === void 0 ? void 0 : _this$toMethod.call(this);
} else {
object = this[toMethod]();
}
} else if (toProperty) {
object = this[toProperty];
}
if (optional) {
var _object;
subject = (_object = object) === null || _object === void 0 ? void 0 : _object[name];
if (subject) {
return apply$1.call(subject, object, arguments);
}
} else {
subject = object[name];
return apply$1.call(subject, object, arguments);
}
};
}
}
const parseProxyMethodExpression = function (expression) {
const match = expression.match(proxyMethodExpressionPattern);
if (!match) {
throw new Error("can't parse @proxyMethod expression: ".concat(expression));
}
const args = {
name: match[4]
};
if (match[2] != null) {
args.toMethod = match[1];
} else {
args.toProperty = match[1];
}
if (match[3] != null) {
args.optional = true;
}
return args;
};
const {
apply: apply$1
} = Function.prototype;
const proxyMethodExpressionPattern = new RegExp("\
^\
(.+?)\
(\\(\\))?\
(\\?)?\
\\.\
(.+?)\
$\
");
var _Array$from, _$codePointAt$1, _$1, _String$fromCodePoint;
class UTF16String extends BasicObject {
static box() {
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
if (value instanceof this) {
return value;
} else {
return this.fromUCS2String(value === null || value === void 0 ? void 0 : value.toString());
}
}
static fromUCS2String(ucs2String) {
return new this(ucs2String, ucs2decode(ucs2String));
}
static fromCodepoints(codepoints) {
return new this(ucs2encode(codepoints), codepoints);
}
constructor(ucs2String, codepoints) {
super(...arguments);
this.ucs2String = ucs2String;
this.codepoints = codepoints;
this.length = this.codepoints.length;
this.ucs2Length = this.ucs2String.length;
}
offsetToUCS2Offset(offset) {
return ucs2encode(this.codepoints.slice(0, Math.max(0, offset))).length;
}
offsetFromUCS2Offset(ucs2Offset) {
return ucs2decode(this.ucs2String.slice(0, Math.max(0, ucs2Offset))).length;
}
slice() {
return this.constructor.fromCodepoints(this.codepoints.slice(...arguments));
}
charAt(offset) {
return this.slice(offset, offset + 1);
}
isEqualTo(value) {
return this.constructor.box(value).ucs2String === this.ucs2String;
}
toJSON() {
return this.ucs2String;
}
getCacheKey() {
return this.ucs2String;
}
toString() {
return this.ucs2String;
}
}
const hasArrayFrom = ((_Array$from = Array.from) === null || _Array$from === void 0 ? void 0 : _Array$from.call(Array, "\ud83d\udc7c").length) === 1;
const hasStringCodePointAt$1 = ((_$codePointAt$1 = (_$1 = " ").codePointAt) === null || _$codePointAt$1 === void 0 ? void 0 : _$codePointAt$1.call(_$1, 0)) != null;
const hasStringFromCodePoint = ((_String$fromCodePoint = String.fromCodePoint) === null || _String$fromCodePoint === void 0 ? void 0 : _String$fromCodePoint.call(String, 32, 128124)) === " \ud83d\udc7c";
// UCS-2 conversion helpers ported from Mathias Bynens' Punycode.js:
// https://github.com/bestiejs/punycode.js#punycodeucs2
let ucs2decode, ucs2encode;
// Creates an array containing the numeric code points of each Unicode
// character in the string. While JavaScript uses UCS-2 internally,
// this function will convert a pair of surrogate halves (each of which
// UCS-2 exposes as separate characters) into a single code point,
// matching UTF-16.
if (hasArrayFrom && hasStringCodePointAt$1) {
ucs2decode = string => Array.from(string).map(char => char.codePointAt(0));
} else {
ucs2decode = function (string) {
const output = [];
let counter = 0;
const {
length
} = string;
while (counter < length) {
let value = string.charCodeAt(counter++);
if (0xd800 <= value && value <= 0xdbff && counter < length) {
// high surrogate, and there is a next character
const extra = string.charCodeAt(counter++);
if ((extra & 0xfc00) === 0xdc00) {
// low surrogate
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
} else {
// unmatched surrogate; only append this code unit, in case the
// next code unit is the high surrogate of a surrogate pair
counter--;
}
}
output.push(value);
}
return output;
};
}
// Creates a string based on an array of numeric code points.
if (hasStringFromCodePoint) {
ucs2encode = array => String.fromCodePoint(...Array.from(array || []));
} else {
ucs2encode = function (array) {
const characters = (() => {
const result = [];
Array.from(array).forEach(value => {
let output = "";
if (value > 0xffff) {
value -= 0x10000;
output += String.fromCharCode(value >>> 10 & 0x3ff | 0xd800);
value = 0xdc00 | value & 0x3ff;
}
result.push(output + String.fromCharCode(value));
});
return result;
})();
return characters.join("");
};
}
let id$2 = 0;
class TrixObject extends BasicObject {
static fromJSONString(jsonString) {
return this.fromJSON(JSON.parse(jsonString));
}
constructor() {
super(...arguments);
this.id = ++id$2;
}
hasSameConstructorAs(object) {
return this.constructor === (object === null || object === void 0 ? void 0 : object.constructor);
}
isEqualTo(object) {
return this === object;
}
inspect() {
const parts = [];
const contents = this.contentsForInspection() || {};
for (const key in contents) {
const value = contents[key];
parts.push("".concat(key, "=").concat(value));
}
return "#<".concat(this.constructor.name, ":").concat(this.id).concat(parts.length ? " ".concat(parts.join(", ")) : "", ">");
}
contentsForInspection() {}
toJSONString() {
return JSON.stringify(this);
}
toUTF16String() {
return UTF16String.box(this);
}
getCacheKey() {
return this.id.toString();
}
}
/* eslint-disable
id-length,
*/
const arraysAreEqual = function () {
let a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
if (a.length !== b.length) {
return false;
}
for (let index = 0; index < a.length; index++) {
const value = a[index];
if (value !== b[index]) {
return false;
}
}
return true;
};
const arrayStartsWith = function () {
let a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
return arraysAreEqual(a.slice(0, b.length), b);
};
const spliceArray = function (array) {
const result = array.slice(0);
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
result.splice(...args);
return result;
};
const summarizeArrayChange = function () {
let oldArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let newArray = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
const added = [];
const removed = [];
const existingValues = new Set();
oldArray.forEach(value => {
existingValues.add(value);
});
const currentValues = new Set();
newArray.forEach(value => {
currentValues.add(value);
if (!existingValues.has(value)) {
added.push(value);
}
});
oldArray.forEach(value => {
if (!currentValues.has(value)) {
removed.push(value);
}
});
return {
added,
removed
};
};
// https://github.com/mathiasbynens/unicode-2.1.8/blob/master/Bidi_Class/Right_To_Left/regex.js
const RTL_PATTERN = /[\u05BE\u05C0\u05C3\u05D0-\u05EA\u05F0-\u05F4\u061B\u061F\u0621-\u063A\u0640-\u064A\u066D\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D5\u06E5\u06E6\u200F\u202B\u202E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE72\uFE74\uFE76-\uFEFC]/;
const getDirection = function () {
const input = makeElement("input", {
dir: "auto",
name: "x",
dirName: "x.dir"
});
const textArea = makeElement("textarea", {
dir: "auto",
name: "y",
dirName: "y.dir"
});
const form = makeElement("form");
form.appendChild(input);
form.appendChild(textArea);
const supportsDirName = function () {
try {
return new FormData(form).has(textArea.dirName);
} catch (error) {
return false;
}
}();
const supportsDirSelector = function () {
try {
return input.matches(":dir(ltr),:dir(rtl)");
} catch (error) {
return false;
}
}();
if (supportsDirName) {
return function (string) {
textArea.value = string;
return new FormData(form).get(textArea.dirName);
};
} else if (supportsDirSelector) {
return function (string) {
input.value = string;
if (input.matches(":dir(rtl)")) {
return "rtl";
} else {
return "ltr";
}
};
} else {
return function (string) {
const char = string.trim().charAt(0);
if (RTL_PATTERN.test(char)) {
return "rtl";
} else {
return "ltr";
}
};
}
}();
let allAttributeNames = null;
let blockAttributeNames = null;
let textAttributeNames = null;
let listAttributeNames = null;
const getAllAttributeNames = () => {
if (!allAttributeNames) {
allAttributeNames = getTextAttributeNames().concat(getBlockAttributeNames());
}
return allAttributeNames;
};
const getBlockConfig = attributeName => attributes[attributeName];
const getBlockAttributeNames = () => {
if (!blockAttributeNames) {
blockAttributeNames = Object.keys(attributes);
}
return blockAttributeNames;
};
const getTextConfig = attributeName => text_attributes[attributeName];
const getTextAttributeNames = () => {
if (!textAttributeNames) {
textAttributeNames = Object.keys(text_attributes);
}
return textAttributeNames;
};
const getListAttributeNames = () => {
if (!listAttributeNames) {
listAttributeNames = [];
for (const key in attributes) {
const {
listAttribute
} = attributes[key];
if (listAttribute != null) {
listAttributeNames.push(listAttribute);
}
}
}
return listAttributeNames;
};
/* eslint-disable
*/
const installDefaultCSSForTagName = function (tagName, defaultCSS) {
const styleElement = insertStyleElementForTagName(tagName);
styleElement.textContent = defaultCSS.replace(/%t/g, tagName);
};
const insertStyleElementForTagName = function (tagName) {
const element = document.createElement("style");
element.setAttribute("type", "text/css");
element.setAttribute("data-tag-name", tagName.toLowerCase());
const nonce = getCSPNonce();
if (nonce) {
element.setAttribute("nonce", nonce);
}
document.head.insertBefore(element, document.head.firstChild);
return element;
};
const getCSPNonce = function () {
const element = getMetaElement("trix-csp-nonce") || getMetaElement("csp-nonce");
if (element) {
const {
nonce,
content
} = element;
return nonce == "" ? content : nonce;
}
};
const getMetaElement = name => document.head.querySelector("meta[name=".concat(name, "]"));
const testTransferData = {
"application/x-trix-feature-detection": "test"
};
const dataTransferIsPlainText = function (dataTransfer) {
const text = dataTransfer.getData("text/plain");
const html = dataTransfer.getData("text/html");
if (text && html) {
const {
body
} = new DOMParser().parseFromString(html, "text/html");
if (body.textContent === text) {
return !body.querySelector("*");
}
} else {
return text === null || text === void 0 ? void 0 : text.length;
}
};
const dataTransferIsMsOfficePaste = _ref => {
let {
dataTransfer
} = _ref;
return dataTransfer.types.includes("Files") && dataTransfer.types.includes("text/html") && dataTransfer.getData("text/html").includes("urn:schemas-microsoft-com:office:office");
};
const dataTransferIsWritable = function (dataTransfer) {
if (!(dataTransfer !== null && dataTransfer !== void 0 && dataTransfer.setData)) return false;
for (const key in testTransferData) {
const value = testTransferData[key];
try {
dataTransfer.setData(key, value);
if (!dataTransfer.getData(key) === value) return false;
} catch (error) {
return false;
}
}
return true;
};
const keyEventIsKeyboardCommand = function () {
if (/Mac|^iP/.test(navigator.platform)) {
return event => event.metaKey;
} else {
return event => event.ctrlKey;
}
}();
function shouldRenderInmmediatelyToDealWithIOSDictation(inputEvent) {
if (/iPhone|iPad/.test(navigator.userAgent)) {
// Handle garbled content and duplicated newlines when using dictation on iOS 18+. Upon dictation completion, iOS sends
// the list of insertText / insertParagraph events in a quick sequence. If we don't render
// the editor synchronously, the internal range fails to update and results in garbled content or duplicated newlines.
//
// This workaround is necessary because iOS doesn't send composing events as expected while dictating:
// https://bugs.webkit.org/show_bug.cgi?id=261764
return !inputEvent.inputType || inputEvent.inputType === "insertParagraph";
} else {
return false;
}
}
const defer = fn => setTimeout(fn, 1);
/* eslint-disable
id-length,
*/
const copyObject = function () {
let object = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const result = {};
for (const key in object) {
const value = object[key];
result[key] = value;
}
return result;
};
const objectsAreEqual = function () {
let a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (const key in a) {
const value = a[key];
if (value !== b[key]) {
return false;
}
}
return true;
};
const normalizeRange = function (range) {
if (range == null) return;
if (!Array.isArray(range)) {
range = [range, range];
}
return [copyValue(range[0]), copyValue(range[1] != null ? range[1] : range[0])];
};
const rangeIsCollapsed = function (range) {
if (range == null) return;
const [start, end] = normalizeRange(range);
return rangeValuesAreEqual(start, end);
};
const rangesAreEqual = function (leftRange, rightRange) {
if (leftRange == null || rightRange == null) return;
const [leftStart, leftEnd] = normalizeRange(leftRange);
const [rightStart, rightEnd] = normalizeRange(rightRange);
return rangeValuesAreEqual(leftStart, rightStart) && rangeValuesAreEqual(leftEnd, rightEnd);
};
const copyValue = function (value) {
if (typeof value === "number") {
return value;
} else {
return copyObject(value);
}
};
const rangeValuesAreEqual = function (left, right) {
if (typeof left === "number") {
return left === right;
} else {
return objectsAreEqual(left, right);
}
};
class SelectionChangeObserver extends BasicObject {
constructor() {
super(...arguments);
this.update = this.update.bind(this);
this.selectionManagers = [];
}
start() {
if (!this.started) {
this.started = true;
document.addEventListener("selectionchange", this.update, true);
}
}
stop() {
if (this.started) {
this.started = false;
return document.removeEventListener("selectionchange", this.update, true);
}
}
registerSelectionManager(selectionManager) {
if (!this.selectionManagers.includes(selectionManager)) {
this.selectionManagers.push(selectionManager);
return this.start();
}
}
unregisterSelectionManager(selectionManager) {
this.selectionManagers = this.selectionManagers.filter(sm => sm !== selectionManager);
if (this.selectionManagers.length === 0) {
return this.stop();
}
}
notifySelectionManagersOfSelectionChange() {
return this.selectionManagers.map(selectionManager => selectionManager.selectionDidChange());
}
update() {
this.notifySelectionManagersOfSelectionChange();
}
reset() {
this.update();
}
}
const selectionChangeObserver = new SelectionChangeObserver();
const getDOMSelection = function () {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
return selection;
}
};
const getDOMRange = function () {
var _getDOMSelection;
const domRange = (_getDOMSelection = getDOMSelection()) === null || _getDOMSelection === void 0 ? void 0 : _getDOMSelection.getRangeAt(0);
if (domRange) {
if (!domRangeIsPrivate(domRange)) {
return domRange;
}
}
};
const setDOMRange = function (domRange) {
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(domRange);
return selectionChangeObserver.update();
};
// In Firefox, clicking certain elements changes the selection to a
// private element used to draw its UI. Attempting to access properties of those
// elements throws an error.
// https://bugzilla.mozilla.org/show_bug.cgi?id=208427
const domRangeIsPrivate = domRange => nodeIsPrivate(domRange.startContainer) || nodeIsPrivate(domRange.endContainer);
const nodeIsPrivate = node => !Object.getPrototypeOf(node);
/* eslint-disable
id-length,
no-useless-escape,
*/
const normalizeSpaces = string => string.replace(new RegExp("".concat(ZERO_WIDTH_SPACE), "g"), "").replace(new RegExp("".concat(NON_BREAKING_SPACE), "g"), " ");
const normalizeNewlines = string => string.replace(/\r\n?/g, "\n");
const breakableWhitespacePattern = new RegExp("[^\\S".concat(NON_BREAKING_SPACE, "]"));
const squishBreakableWhitespace = string => string
// Replace all breakable whitespace characters with a space
.replace(new RegExp("".concat(breakableWhitespacePattern.source), "g"), " ")
// Replace two or more spaces with a single space
.replace(/\ {2,}/g, " ");
const summarizeStringChange = function (oldString, newString) {
let added, removed;
oldString = UTF16String.box(oldString);
newString = UTF16String.box(newString);
if (newString.length < oldString.length) {
[removed, added] = utf16StringDifferences(oldString, newString);
} else {
[added, removed] = utf16StringDifferences(newString, oldString);
}
return {
added,
removed
};
};
const utf16StringDifferences = function (a, b) {
if (a.isEqualTo(b)) {
return ["", ""];
}
const diffA = utf16StringDifference(a, b);
const {
length
} = diffA.utf16String;
let diffB;
if (length) {
const {
offset
} = diffA;
const codepoints = a.codepoints.slice(0, offset).concat(a.codepoints.slice(offset + length));
diffB = utf16StringDifference(b, UTF16String.fromCodepoints(codepoints));
} else {
diffB = utf16StringDifference(b, a);
}
return [diffA.utf16String.toString(), diffB.utf16String.toString()];
};
const utf16StringDifference = function (a, b) {
let leftIndex = 0;
let rightIndexA = a.length;
let rightIndexB = b.length;
while (leftIndex < rightIndexA && a.charAt(leftIndex).isEqualTo(b.charAt(leftIndex))) {
leftIndex++;
}
while (rightIndexA > leftIndex + 1 && a.charAt(rightIndexA - 1).isEqualTo(b.charAt(rightIndexB - 1))) {
rightIndexA--;
rightIndexB--;
}
return {
utf16String: a.slice(leftIndex, rightIndexA),
offset: leftIndex
};
};
class Hash extends TrixObject {
static fromCommonAttributesOfObjects() {
let objects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
if (!objects.length) {
return new this();
}
let hash = box(objects[0]);
let keys = hash.getKeys();
objects.slice(1).forEach(object => {
keys = hash.getKeysCommonToHash(box(object));
hash = hash.slice(keys);
});
return hash;
}
static box(values) {
return box(values);
}
constructor() {
let values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
super(...arguments);
this.values = copy(values);
}
add(key, value) {
return this.merge(object(key, value));
}
remove(key) {
return new Hash(copy(this.values, key));
}
get(key) {
return this.values[key];
}
has(key) {
return key in this.values;
}
merge(values) {
return new Hash(merge(this.values, unbox(values)));
}
slice(keys) {
const values = {};
Array.from(keys).forEach(key => {
if (this.has(key)) {
values[key] = this.values[key];
}
});
return new Hash(values);
}
getKeys() {
return Object.keys(this.values);
}
getKeysCommonToHash(hash) {
hash = box(hash);
return this.getKeys().filter(key => this.values[key] === hash.values[key]);
}
isEqualTo(values) {
return arraysAreEqual(this.toArray(), box(values).toArray());
}
isEmpty() {
return this.getKeys().length === 0;
}
toArray() {
if (!this.array) {
const result = [];
for (const key in this.values) {
const value = this.values[key];
result.push(result.push(key, value));
}
this.array = result.slice(0);
}
return this.array;
}
toObject() {
return copy(this.values);
}
toJSON() {
return this.toObject();
}
contentsForInspection() {
return {
values: JSON.stringify(this.values)
};
}
}
const object = function (key, value) {
const result = {};
result[key] = value;
return result;
};
const merge = function (object, values) {
const result = copy(object);
for (const key in values) {
const value = values[key];
result[key] = value;
}
return result;
};
const copy = function (object, keyToRemove) {
const result = {};
const sortedKeys = Object.keys(object).sort();
sortedKeys.forEach(key => {
if (key !== keyToRemove) {
result[key] = object[key];
}
});
return result;
};
const box = function (object) {
if (object instanceof Hash) {
return object;
} else {
return new Hash(object);
}
};
const unbox = function (object) {
if (object instanceof Hash) {
return object.values;
} else {
return object;
}
};
class ObjectGroup {
static groupObjects() {
let ungroupedObjects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let {
depth,
asTree
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let group;
if (asTree) {
if (depth == null) {
depth = 0;
}
}
const objects = [];
Array.from(ungroupedObjects).forEach(object => {
var _object$canBeGrouped2;
if (group) {
var _object$canBeGrouped, _group$canBeGroupedWi, _group;
if ((_object$canBeGrouped = object.canBeGrouped) !== null && _object$canBeGrouped !== void 0 && _object$canBeGrouped.call(object, depth) && (_group$canBeGroupedWi = (_group = group[group.length - 1]).canBeGroupedWith) !== null && _group$canBeGroupedWi !== void 0 && _group$canBeGroupedWi.call(_group, object, depth)) {
group.push(object);
return;
} else {
objects.push(new this(group, {
depth,
asTree
}));
group = null;
}
}
if ((_object$canBeGrouped2 = object.canBeGrouped) !== null && _object$canBeGrouped2 !== void 0 && _object$canBeGrouped2.call(object, depth)) {
group = [object];
} else {
objects.push(object);
}
});
if (group) {
objects.push(new this(group, {
depth,
asTree
}));
}
return objects;
}
constructor() {
let objects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let {
depth,
asTree
} = arguments.length > 1 ? arguments[1] : undefined;
this.objects = objects;
if (asTree) {
this.depth = depth;
this.objects = this.constructor.groupObjects(this.objects, {
asTree,
depth: this.depth + 1
});
}
}
getObjects() {
return this.objects;
}
getDepth() {
return this.depth;
}
getCacheKey() {
const keys = ["objectGroup"];
Array.from(this.getObjects()).forEach(object => {
keys.push(object.getCacheKey());
});
return keys.join("/");
}
}
class ObjectMap extends BasicObject {
constructor() {
let objects = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
super(...arguments);
this.objects = {};
Array.from(objects).forEach(object => {
const hash = JSON.stringify(object);
if (this.objects[hash] == null) {
this.objects[hash] = object;
}
});
}
find(object) {
const hash = JSON.stringify(object);
return this.objects[hash];
}
}
class ElementStore {
constructor(elements) {
this.reset(elements);
}
add(element) {
const key = getKey(element);
this.elements[key] = element;
}
remove(element) {
const key = getKey(element);
const value = this.elements[key];
if (value) {
delete this.elements[key];
return value;
}
}
reset() {
let elements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
this.elements = {};
Array.from(elements).forEach(element => {
this.add(element);
});
return elements;
}
}
const getKey = element => element.dataset.trixStoreKey;
class Operation extends BasicObject {
isPerforming() {
return this.performing === true;
}
hasPerformed() {
return this.performed === true;
}
hasSucceeded() {
return this.performed && this.succeeded;
}
hasFailed() {
return this.performed && !this.succeeded;
}
getPromise() {
if (!this.promise) {
this.promise = new Promise((resolve, reject) => {
this.performing = true;
return this.perform((succeeded, result) => {
this.succeeded = succeeded;
this.performing = false;
this.performed = true;
if (this.succeeded) {
resolve(result);
} else {
reject(result);
}
});
});
}
return this.promise;
}
perform(callback) {
return callback(false);
}
release() {
var _this$promise, _this$promise$cancel;
(_this$promise = this.promise) === null || _this$promise === void 0 || (_this$promise$cancel = _this$promise.cancel) === null || _this$promise$cancel === void 0 || _this$promise$cancel.call(_this$promise);
this.promise = null;
this.performing = null;
this.performed = null;
this.succeeded = null;
}
}
Operation.proxyMethod("getPromise().then");
Operation.proxyMethod("getPromise().catch");
class ObjectView extends BasicObject {
constructor(object) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
super(...arguments);
this.object = object;
this.options = options;
this.childViews = [];
this.rootView = this;
}
getNodes() {
if (!this.nodes) {
this.nodes = this.createNodes();
}
return this.nodes.map(node => node.cloneNode(true));
}
invalidate() {
var _this$parentView;
this.nodes = null;
this.childViews = [];
return (_this$parentView = this.parentView) === null || _this$parentView === void 0 ? void 0 : _this$parentView.invalidate();
}
invalidateViewForObject(object) {
var _this$findViewForObje;
return (_this$findViewForObje = this.findViewForObject(object)) === null || _this$findViewForObje === void 0 ? void 0 : _this$findViewForObje.invalidate();
}
findOrCreateCachedChildView(viewClass, object, options) {
let view = this.getCachedViewForObject(object);
if (view) {
this.recordChildView(view);
} else {
view = this.createChildView(...arguments);
this.cacheViewForObject(view, object);
}
return view;
}
createChildView(viewClass, object) {
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (object instanceof ObjectGroup) {
options.viewClass = viewClass;
viewClass = ObjectGroupView;
}
const view = new viewClass(object, options);
return this.recordChildView(view);
}
recordChildView(view) {
view.parentView = this;
view.rootView = this.rootView;
this.childViews.push(view);
return view;
}
getAllChildViews() {
let views = [];
this.childViews.forEach(childView => {
views.push(childView);
views = views.concat(childView.getAllChildViews());
});
return views;
}
findElement() {
return this.findElementForObject(this.object);
}
findElementForObject(object) {
const id = object === null || object === void 0 ? void 0 : object.id;
if (id) {
return this.rootView.element.querySelector("[data-trix-id='".concat(id, "']"));
}
}
findViewForObject(object) {
for (const view of this.getAllChildViews()) {
if (view.object === object) {
return view;
}
}
}
getViewCache() {
if (this.rootView === this) {
if (this.isViewCachingEnabled()) {
if (!this.viewCache) {
this.viewCache = {};
}
return this.viewCache;
}
} else {
return this.rootView.getViewCache();
}
}
isViewCachingEnabled() {
return this.shouldCacheViews !== false;
}
enableViewCaching() {
this.shouldCacheViews = true;
}
disableViewCaching() {
this.shouldCacheViews = false;
}
getCachedViewForObject(object) {
var _this$getViewCache;
return (_this$getViewCache = this.getViewCache()) === null || _this$getViewCache === void 0 ? void 0 : _this$getViewCache[object.getCacheKey()];
}
cacheViewForObject(view, object) {
const cache = this.getViewCache();
if (cache) {
cache[object.getCacheKey()] = view;
}
}
garbageCollectCachedViews() {
const cache = this.getViewCache();
if (cache) {
const views = this.getAllChildViews().concat(this);
const objectKeys = views.map(view => view.object.getCacheKey());
for (const key in cache) {
if (!objectKeys.includes(key)) {
delete cache[key];
}
}
}
}
}
class ObjectGroupView extends ObjectView {
constructor() {
super(...arguments);
this.objectGroup = this.object;
this.viewClass = this.options.viewClass;
delete this.options.viewClass;
}
getChildViews() {
if (!this.childViews.length) {
Array.from(this.objectGroup.getObjects()).forEach(object => {
this.findOrCreateCachedChildView(this.viewClass, object, this.options);
});
}
return this.childViews;
}
createNodes() {
const element = this.createContainerElement();
this.getChildViews().forEach(view => {
Array.from(view.getNodes()).forEach(node => {
element.appendChild(node);
});
});
return [element];
}
createContainerElement() {
let depth = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.objectGroup.getDepth();
return this.getChildViews()[0].createContainerElement(depth);
}
}
/*! @license DOMPurify 3.2.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.3/LICENSE */
const {
entries,
setPrototypeOf,
isFrozen,
getPrototypeOf,
getOwnPropertyDescriptor
} = Object;
let {
freeze,
seal,
create
} = Object; // eslint-disable-line import/no-mutable-exports
let {
apply,
construct
} = typeof Reflect !== 'undefined' && Reflect;
if (!freeze) {
freeze = function freeze(x) {
return x;
};
}
if (!seal) {
seal = function seal(x) {
return x;
};
}
if (!apply) {
apply = function apply(fun, thisValue, args) {
return fun.apply(thisValue, args);
};
}
if (!construct) {
construct = function construct(Func, args) {
return new Func(...args);
};
}
const arrayForEach = unapply(Array.prototype.forEach);
const arrayPop = unapply(Array.prototype.pop);
const arrayPush = unapply(Array.prototype.push);
const stringToLowerCase = unapply(String.prototype.toLowerCase);
const stringToString = unapply(String.prototype.toString);
const stringMatch = unapply(String.prototype.match);
const stringReplace = unapply(String.prototype.replace);
const stringIndexOf = unapply(String.prototype.indexOf);
const stringTrim = unapply(String.prototype.trim);
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
const regExpTest = unapply(RegExp.prototype.test);
const typeErrorCreate = unconstruct(TypeError);
/**
* Creates a new function that calls the given function with a specified thisArg and arguments.
*
* @param func - The function to be wrapped and called.
* @returns A new function that calls the given function with a specified thisArg and arguments.
*/
function unapply(func) {
return function (thisArg) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return apply(func, thisArg, args);
};
}
/**
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
*
* @param func - The constructor function to be wrapped and called.
* @returns A new function that constructs an instance of the given constructor function with the provided arguments.
*/
function unconstruct(func) {
return function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return construct(func, args);
};
}
/**
* Add properties to a lookup table
*
* @param set - The set to which elements will be added.
* @param array - The array containing elements to be added to the set.
* @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
* @returns The modified set with added elements.
*/
function addToSet(set, array) {
let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
if (setPrototypeOf) {
// Make 'in' and truthy checks like Boolean(set.constructor)
// independent of any properties defined on Object.prototype.
// Prevent prototype setters from intercepting set as a this value.
setPrototypeOf(set, null);
}
let l = array.length;
while (l--) {
let element = array[l];
if (typeof element === 'string') {
const lcElement = transformCaseFunc(element);
if (lcElement !== element) {
// Config presets (e.g. tags.js, attrs.js) are immutable.
if (!isFrozen(array)) {
array[l] = lcElement;
}
element = lcElement;
}
}
set[element] = true;
}
return set;
}
/**
* Clean up an array to harden against CSPP
*
* @param array - The array to be cleaned.
* @returns The cleaned version of the array
*/
function cleanArray(array) {
for (let index = 0; index < array.length; index++) {
const isPropertyExist = objectHasOwnProperty(array, index);
if (!isPropertyExist) {
array[index] = null;
}
}
return array;
}
/**
* Shallow clone an object
*
* @param object - The object to be cloned.
* @returns A new object that copies the original.
*/
function clone(object) {
const newObject = create(null);
for (const [property, value] of entries(object)) {
const isPropertyExist = objectHasOwnProperty(object, property);
if (isPropertyExist) {
if (Array.isArray(value)) {
newObject[property] = cleanArray(value);
} else if (value && typeof value === 'object' && value.constructor === Object) {
newObject[property] = clone(value);
} else {
newObject[property] = value;
}
}
}
return newObject;
}
/**
* This method automatically checks if the prop is function or getter and behaves accordingly.
*
* @param object - The object to look up the getter function in its prototype chain.
* @param prop - The property name for which to find the getter function.
* @returns The getter function found in the prototype chain or a fallback function.
*/
function lookupGetter(object, prop) {
while (object !== null) {
const desc = getOwnPropertyDescriptor(object, prop);
if (desc) {
if (desc.get) {
return unapply(desc.get);
}
if (typeof desc.value === 'function') {
return unapply(desc.value);
}
}
object = getPrototypeOf(object);
}
function fallbackValue() {
return null;
}
return fallbackValue;
}
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']);
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']);
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']);
// List of SVG elements that are disallowed by default.
// We still need to know them so that we can do namespace
// checks properly in case one wants to add them to
// allow-list.
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']);
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']);
// Similarly to SVG, we want to know all MathML elements,
// even those that we disallow by default.
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
const text = freeze(['#text']);
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']);
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']);
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']);
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
// eslint-disable-next-line unicorn/better-regex
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
const TMPLIT_EXPR = seal(/\$\{[\w\W]*}/gm); // eslint-disable-line unicorn/better-regex
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
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
);
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
);
const DOCTYPE_NAME = seal(/^html$/i);
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
__proto__: null,
ARIA_ATTR: ARIA_ATTR,
ATTR_WHITESPACE: ATTR_WHITESPACE,
CUSTOM_ELEMENT: CUSTOM_ELEMENT,
DATA_ATTR: DATA_ATTR,
DOCTYPE_NAME: DOCTYPE_NAME,
ERB_EXPR: ERB_EXPR,
IS_ALLOWED_URI: IS_ALLOWED_URI,
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
MUSTACHE_EXPR: MUSTACHE_EXPR,
TMPLIT_EXPR: TMPLIT_EXPR
});
/* eslint-disable @typescript-eslint/indent */
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
const NODE_TYPE = {
element: 1,
attribute: 2,
text: 3,
cdataSection: 4,
entityReference: 5,
// Deprecated
entityNode: 6,
// Deprecated
progressingInstruction: 7,
comment: 8,
document: 9,
documentType: 10,
documentFragment: 11,
notation: 12 // Deprecated
};
const getGlobal = function getGlobal() {
return typeof window === 'undefined' ? null : window;
};
/**
* Creates a no-op policy for internal use only.
* Don't export this function outside this module!
* @param trustedTypes The policy factory.
* @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
* @return The policy created (or null, if Trusted Types
* are not supported or creating the policy failed).
*/
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
return null;
}
// Allow the callers to control the unique policy name
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
// Policy creation with duplicate names throws in Trusted Types.
let suffix = null;
const ATTR_NAME = 'data-tt-policy-suffix';
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
suffix = purifyHostElement.getAttribute(ATTR_NAME);
}
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
try {
return trustedTypes.createPolicy(policyName, {
createHTML(html) {
return html;
},
createScriptURL(scriptUrl) {
return scriptUrl;
}
});
} catch (_) {
// Policy creation failed (most likely another DOMPurify script has
// already run). Skip creating the policy, as this will only cause errors
// if TT are enforced.
console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
return null;
}
};
const _createHooksMap = function _createHooksMap() {
return {
afterSanitizeAttributes: [],
afterSanitizeElements: [],
afterSanitizeShadowDOM: [],
beforeSanitizeAttributes: [],
beforeSanitizeElements: [],
beforeSanitizeShadowDOM: [],
uponSanitizeAttribute: [],
uponSanitizeElement: [],
uponSanitizeShadowNode: []
};
};
function createDOMPurify() {
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
const DOMPurify = root => createDOMPurify(root);
DOMPurify.version = '3.2.3';
DOMPurify.removed = [];
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
// Not running in a browser, provide a factory function
// so that you can pass your own Window
DOMPurify.isSupported = false;
return DOMPurify;
}
let {
document
} = window;
const originalDocument = document;
const currentScript = originalDocument.currentScript;
const {
DocumentFragment,
HTMLTemplateElement,
Node,
Element,
NodeFilter,
NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
HTMLFormElement,
DOMParser,
trustedTypes
} = window;
const ElementPrototype = Element.prototype;
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
const remove = lookupGetter(ElementPrototype, 'remove');
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
// As per issue #47, the web-components registry is inherited by a
// new document created via createHTMLDocument. As per the spec
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
// a new empty registry is used when creating a template contents owner
// document, so we use that as our parent document to ensure nothing
// is inherited.
if (typeof HTMLTemplateElement === 'function') {
const template = document.createElement('template');
if (template.content && template.content.ownerDocument) {
document = template.content.ownerDocument;
}
}
let trustedTypesPolicy;
let emptyHTML = '';
const {
implementation,
createNodeIterator,
createDocumentFragment,
getElementsByTagName
} = document;
const {
importNode
} = originalDocument;
let hooks = _createHooksMap();
/**
* Expose whether this browser supports running the full DOMPurify.
*/
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
const {
MUSTACHE_EXPR,
ERB_EXPR,
TMPLIT_EXPR,
DATA_ATTR,
ARIA_ATTR,
IS_SCRIPT_OR_DATA,
ATTR_WHITESPACE,
CUSTOM_ELEMENT
} = EXPRESSIONS;
let {
IS_ALLOWED_URI: IS_ALLOWED_URI$1
} = EXPRESSIONS;
/**
* We consider the elements and attributes below to be safe. Ideally
* don't add any new ones but feel free to remove unwanted ones.
*/
/* allowed element names */
let ALLOWED_TAGS = null;
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
/* Allowed attribute names */
let ALLOWED_ATTR = null;
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
/*
* Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
*/
let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
tagNameCheck: {
writable: true,
configurable: false,
enumerable: true,
value: null
},
attributeNameCheck: {
writable: true,
configurable: false,
enumerable: true,
value: null
},
allowCustomizedBuiltInElements: {
writable: true,
configurable: false,
enumerable: true,
value: false
}
}));
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
let FORBID_TAGS = null;
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
let FORBID_ATTR = null;
/* Decide if ARIA attributes are okay */
let ALLOW_ARIA_ATTR = true;
/* Decide if custom data attributes are okay */
let ALLOW_DATA_ATTR = true;
/* Decide if unknown protocols are okay */
let ALLOW_UNKNOWN_PROTOCOLS = false;
/* Decide if self-closing tags in attributes are allowed.
* Usually removed due to a mXSS issue in jQuery 3.0 */
let ALLOW_SELF_CLOSE_IN_ATTR = true;
/* Output should be safe for common template engines.
* This means, DOMPurify removes data attributes, mustaches and ERB
*/
let SAFE_FOR_TEMPLATES = false;
/* Output should be safe even for XML used within HTML and alike.
* This means, DOMPurify removes comments when containing risky content.
*/
let SAFE_FOR_XML = true;
/* Decide if document with ... should be returned */
let WHOLE_DOCUMENT = false;
/* Track whether config is already set on this instance of DOMPurify. */
let SET_CONFIG = false;
/* Decide if all elements (e.g. style, script) must be children of
* document.body. By default, browsers might move them to document.head */
let FORCE_BODY = false;
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
* string (or a TrustedHTML object if Trusted Types are supported).
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
*/
let RETURN_DOM = false;
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
* string (or a TrustedHTML object if Trusted Types are supported) */
let RETURN_DOM_FRAGMENT = false;
/* Try to return a Trusted Type object instead of a string, return a string in
* case Trusted Types are not supported */
let RETURN_TRUSTED_TYPE = false;
/* Output should be free from DOM clobbering attacks?
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
*/
let SANITIZE_DOM = true;
/* Achieve full DOM Clobbering protection by isolating the namespace of named
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
*
* HTML/DOM spec rules that enable DOM Clobbering:
* - Named Access on Window (§7.3.3)
* - DOM Tree Accessors (§3.1.5)
* - Form Element Parent-Child Relations (§4.10.3)
* - Iframe srcdoc / Nested WindowProxies (§4.8.5)
* - HTMLCollection (§4.2.10.2)
*
* Namespace isolation is implemented by prefixing `id` and `name` attributes
* with a constant string, i.e., `user-content-`
*/
let SANITIZE_NAMED_PROPS = false;
const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
/* Keep element content when removing element? */
let KEEP_CONTENT = true;
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
* of importing it into a new Document and returning a sanitized copy */
let IN_PLACE = false;
/* Allow usage of profiles like html, svg and mathMl */
let USE_PROFILES = {};
/* Tags to ignore content of when KEEP_CONTENT is true */
let FORBID_CONTENTS = null;
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']);
/* Tags that are safe for data: URIs */
let DATA_URI_TAGS = null;
const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
/* Attributes safe for values like "javascript:" */
let URI_SAFE_ATTRIBUTES = null;
const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
/* Document namespace */
let NAMESPACE = HTML_NAMESPACE;
let IS_EMPTY_INPUT = false;
/* Allowed XHTML+XML namespaces */
let ALLOWED_NAMESPACES = null;
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
// Certain elements are allowed in both SVG and HTML
// namespace. We need to specify them explicitly
// so that they don't get erroneously deleted from
// HTML namespace.
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
/* Parsing of strict XHTML documents */
let PARSER_MEDIA_TYPE = null;
const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
let transformCaseFunc = null;
/* Keep a reference to config to pass to hooks */
let CONFIG = null;
/* Ideally, do not touch anything below this line */
/* ______________________________________________ */
const formElement = document.createElement('form');
const isRegexOrFunction = function isRegexOrFunction(testValue) {
return testValue instanceof RegExp || testValue instanceof Function;
};
/**
* _parseConfig
*
* @param cfg optional config literal
*/
// eslint-disable-next-line complexity
const _parseConfig = function _parseConfig() {
let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (CONFIG && CONFIG === cfg) {
return;
}
/* Shield configuration object from tampering */
if (!cfg || typeof cfg !== 'object') {
cfg = {};
}
/* Shield configuration object from prototype pollution */
cfg = clone(cfg);
PARSER_MEDIA_TYPE =
// eslint-disable-next-line unicorn/prefer-includes
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
/* Set configuration parameters */
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
IN_PLACE = cfg.IN_PLACE || false; // Default false
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
}
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
}
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
}
if (SAFE_FOR_TEMPLATES) {
ALLOW_DATA_ATTR = false;
}
if (RETURN_DOM_FRAGMENT) {
RETURN_DOM = true;
}
/* Parse profile info */
if (USE_PROFILES) {
ALLOWED_TAGS = addToSet({}, text);
ALLOWED_ATTR = [];
if (USE_PROFILES.html === true) {
addToSet(ALLOWED_TAGS, html$1);
addToSet(ALLOWED_ATTR, html);
}
if (USE_PROFILES.svg === true) {
addToSet(ALLOWED_TAGS, svg$1);
addToSet(ALLOWED_ATTR, svg);
addToSet(ALLOWED_ATTR, xml);
}
if (USE_PROFILES.svgFilters === true) {
addToSet(ALLOWED_TAGS, svgFilters);
addToSet(ALLOWED_ATTR, svg);
addToSet(ALLOWED_ATTR, xml);
}
if (USE_PROFILES.mathMl === true) {
addToSet(ALLOWED_TAGS, mathMl$1);
addToSet(ALLOWED_ATTR, mathMl);
addToSet(ALLOWED_ATTR, xml);
}
}
/* Merge configuration parameters */
if (cfg.ADD_TAGS) {
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
ALLOWED_TAGS = clone(ALLOWED_TAGS);
}
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
}
if (cfg.ADD_ATTR) {
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
ALLOWED_ATTR = clone(ALLOWED_ATTR);
}
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
}
if (cfg.ADD_URI_SAFE_ATTR) {
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
}
if (cfg.FORBID_CONTENTS) {
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
FORBID_CONTENTS = clone(FORBID_CONTENTS);
}
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
}
/* Add #text in case KEEP_CONTENT is set to true */
if (KEEP_CONTENT) {
ALLOWED_TAGS['#text'] = true;
}
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
if (WHOLE_DOCUMENT) {
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
}
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
if (ALLOWED_TAGS.table) {
addToSet(ALLOWED_TAGS, ['tbody']);
delete FORBID_TAGS.tbody;
}
if (cfg.TRUSTED_TYPES_POLICY) {
if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
}
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
}
// Overwrite existing TrustedTypes policy.
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
// Sign local variables required by `sanitize`.
emptyHTML = trustedTypesPolicy.createHTML('');
} else {
// Uninitialized policy, attempt to initialize the internal dompurify policy.
if (trustedTypesPolicy === undefined) {
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
}
// If creating the internal policy succeeded sign internal variables.
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
emptyHTML = trustedTypesPolicy.createHTML('');
}
}
// Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
if (freeze) {
freeze(cfg);
}
CONFIG = cfg;
};
/* Keep track of all possible SVG and MathML tags
* so that we can perform the namespace checks
* correctly. */
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
/**
* @param element a DOM element whose namespace is being checked
* @returns Return false if the element has a
* namespace that a spec-compliant parser would never
* return. Return true otherwise.
*/
const _checkValidNamespace = function _checkValidNamespace(element) {
let parent = getParentNode(element);
// In JSDOM, if we're inside shadow DOM, then parentNode
// can be null. We just simulate parent in this case.
if (!parent || !parent.tagName) {
parent = {
namespaceURI: NAMESPACE,
tagName: 'template'
};
}
const tagName = stringToLowerCase(element.tagName);
const parentTagName = stringToLowerCase(parent.tagName);
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
return false;
}
if (element.namespaceURI === SVG_NAMESPACE) {
// The only way to switch from HTML namespace to SVG
// is via