430 lines
13 KiB
JavaScript
430 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Ponyfill for `Array.prototype.find` which is only available in ES6 runtimes.
|
|
*
|
|
* Works with anything that has a `length` property and index access properties,
|
|
* including NodeList.
|
|
*
|
|
* @param {T[] | { length: number; [number]: T }} list
|
|
* @param {function (item: T, index: number, list:T[]):boolean} predicate
|
|
* @param {Partial<Pick<ArrayConstructor['prototype'], 'find'>>?} ac
|
|
* Allows injecting a custom implementation in tests (`Array.prototype` by default).
|
|
* @returns {T | undefined}
|
|
* @template {unknown} T
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
|
* @see https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.find
|
|
*/
|
|
function find(list, predicate, ac) {
|
|
if (ac === undefined) {
|
|
ac = Array.prototype;
|
|
}
|
|
if (list && typeof ac.find === 'function') {
|
|
return ac.find.call(list, predicate);
|
|
}
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (hasOwn(list, i)) {
|
|
var item = list[i];
|
|
if (predicate.call(undefined, item, i, list)) {
|
|
return item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* "Shallow freezes" an object to render it immutable.
|
|
* Uses `Object.freeze` if available,
|
|
* otherwise the immutability is only in the type.
|
|
*
|
|
* Is used to create "enum like" objects.
|
|
*
|
|
* If `Object.getOwnPropertyDescriptors` is available,
|
|
* a new object with all properties of object but without any prototype is created and returned
|
|
* after freezing it.
|
|
*
|
|
* @param {T} object
|
|
* The object to freeze.
|
|
* @param {Pick<ObjectConstructor, 'create' | 'freeze' | 'getOwnPropertyDescriptors'>} [oc=Object]
|
|
* `Object` by default,
|
|
* allows to inject custom object constructor for tests.
|
|
* @returns {Readonly<T>}
|
|
* @template {Object} T
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
|
|
* @prettierignore
|
|
*/
|
|
function freeze(object, oc) {
|
|
if (oc === undefined) {
|
|
oc = Object;
|
|
}
|
|
if (oc && typeof oc.getOwnPropertyDescriptors === 'function') {
|
|
object = oc.create(null, oc.getOwnPropertyDescriptors(object));
|
|
}
|
|
return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object;
|
|
}
|
|
|
|
/**
|
|
* Implementation for `Object.hasOwn` but ES5 compatible.
|
|
*
|
|
* @param {any} object
|
|
* @param {string | number} key
|
|
* @returns {boolean}
|
|
*/
|
|
function hasOwn(object, key) {
|
|
return Object.prototype.hasOwnProperty.call(object, key);
|
|
}
|
|
|
|
/**
|
|
* Since xmldom can not rely on `Object.assign`,
|
|
* it uses/provides a simplified version that is sufficient for its needs.
|
|
*
|
|
* @param {Object} target
|
|
* @param {Object | null | undefined} source
|
|
* @returns {Object}
|
|
* The target with the merged/overridden properties.
|
|
* @throws {TypeError}
|
|
* If target is not an object.
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
|
* @see https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign
|
|
*/
|
|
function assign(target, source) {
|
|
if (target === null || typeof target !== 'object') {
|
|
throw new TypeError('target is not an object');
|
|
}
|
|
for (var key in source) {
|
|
if (hasOwn(source, key)) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
|
|
/**
|
|
* A number of attributes are boolean attributes.
|
|
* The presence of a boolean attribute on an element represents the `true` value,
|
|
* and the absence of the attribute represents the `false` value.
|
|
*
|
|
* If the attribute is present, its value must either be the empty string, or a value that is
|
|
* an ASCII case-insensitive match for the attribute's canonical name,
|
|
* with no leading or trailing whitespace.
|
|
*
|
|
* Note: The values `"true"` and `"false"` are not allowed on boolean attributes.
|
|
* To represent a `false` value, the attribute has to be omitted altogether.
|
|
*
|
|
* @see https://html.spec.whatwg.org/#boolean-attributes
|
|
* @see https://html.spec.whatwg.org/#attributes-3
|
|
*/
|
|
var HTML_BOOLEAN_ATTRIBUTES = freeze({
|
|
allowfullscreen: true,
|
|
async: true,
|
|
autofocus: true,
|
|
autoplay: true,
|
|
checked: true,
|
|
controls: true,
|
|
default: true,
|
|
defer: true,
|
|
disabled: true,
|
|
formnovalidate: true,
|
|
hidden: true,
|
|
ismap: true,
|
|
itemscope: true,
|
|
loop: true,
|
|
multiple: true,
|
|
muted: true,
|
|
nomodule: true,
|
|
novalidate: true,
|
|
open: true,
|
|
playsinline: true,
|
|
readonly: true,
|
|
required: true,
|
|
reversed: true,
|
|
selected: true,
|
|
});
|
|
|
|
/**
|
|
* Check if `name` is matching one of the HTML boolean attribute names.
|
|
* This method doesn't check if such attributes are allowed in the context of the current
|
|
* document/parsing.
|
|
*
|
|
* @param {string} name
|
|
* @returns {boolean}
|
|
* @see {@link HTML_BOOLEAN_ATTRIBUTES}
|
|
* @see https://html.spec.whatwg.org/#boolean-attributes
|
|
* @see https://html.spec.whatwg.org/#attributes-3
|
|
*/
|
|
function isHTMLBooleanAttribute(name) {
|
|
return hasOwn(HTML_BOOLEAN_ATTRIBUTES, name.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* Void elements only have a start tag; end tags must not be specified for void elements.
|
|
* These elements should be written as self-closing like this: `<area />`.
|
|
* This should not be confused with optional tags that HTML allows to omit the end tag for
|
|
* (like `li`, `tr` and others), which can have content after them,
|
|
* so they can not be written as self-closing.
|
|
* xmldom does not have any logic for optional end tags cases,
|
|
* and will report them as a warning.
|
|
* Content that would go into the unopened element,
|
|
* will instead be added as a sibling text node.
|
|
*
|
|
* @type {Readonly<{
|
|
* area: boolean;
|
|
* col: boolean;
|
|
* img: boolean;
|
|
* wbr: boolean;
|
|
* link: boolean;
|
|
* hr: boolean;
|
|
* source: boolean;
|
|
* br: boolean;
|
|
* input: boolean;
|
|
* param: boolean;
|
|
* meta: boolean;
|
|
* embed: boolean;
|
|
* track: boolean;
|
|
* base: boolean;
|
|
* }>}
|
|
* @see https://html.spec.whatwg.org/#void-elements
|
|
* @see https://html.spec.whatwg.org/#optional-tags
|
|
*/
|
|
var HTML_VOID_ELEMENTS = freeze({
|
|
area: true,
|
|
base: true,
|
|
br: true,
|
|
col: true,
|
|
embed: true,
|
|
hr: true,
|
|
img: true,
|
|
input: true,
|
|
link: true,
|
|
meta: true,
|
|
param: true,
|
|
source: true,
|
|
track: true,
|
|
wbr: true,
|
|
});
|
|
|
|
/**
|
|
* Check if `tagName` is matching one of the HTML void element names.
|
|
* This method doesn't check if such tags are allowed in the context of the current
|
|
* document/parsing.
|
|
*
|
|
* @param {string} tagName
|
|
* @returns {boolean}
|
|
* @see {@link HTML_VOID_ELEMENTS}
|
|
* @see https://html.spec.whatwg.org/#void-elements
|
|
*/
|
|
function isHTMLVoidElement(tagName) {
|
|
return hasOwn(HTML_VOID_ELEMENTS, tagName.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* Tag names that are raw text elements according to HTML spec.
|
|
* The value denotes whether they are escapable or not.
|
|
*
|
|
* @see {@link isHTMLEscapableRawTextElement}
|
|
* @see {@link isHTMLRawTextElement}
|
|
* @see https://html.spec.whatwg.org/#raw-text-elements
|
|
* @see https://html.spec.whatwg.org/#escapable-raw-text-elements
|
|
*/
|
|
var HTML_RAW_TEXT_ELEMENTS = freeze({
|
|
script: false,
|
|
style: false,
|
|
textarea: true,
|
|
title: true,
|
|
});
|
|
|
|
/**
|
|
* Check if `tagName` is matching one of the HTML raw text element names.
|
|
* This method doesn't check if such tags are allowed in the context of the current
|
|
* document/parsing.
|
|
*
|
|
* @param {string} tagName
|
|
* @returns {boolean}
|
|
* @see {@link isHTMLEscapableRawTextElement}
|
|
* @see {@link HTML_RAW_TEXT_ELEMENTS}
|
|
* @see https://html.spec.whatwg.org/#raw-text-elements
|
|
* @see https://html.spec.whatwg.org/#escapable-raw-text-elements
|
|
*/
|
|
function isHTMLRawTextElement(tagName) {
|
|
var key = tagName.toLowerCase();
|
|
return hasOwn(HTML_RAW_TEXT_ELEMENTS, key) && !HTML_RAW_TEXT_ELEMENTS[key];
|
|
}
|
|
/**
|
|
* Check if `tagName` is matching one of the HTML escapable raw text element names.
|
|
* This method doesn't check if such tags are allowed in the context of the current
|
|
* document/parsing.
|
|
*
|
|
* @param {string} tagName
|
|
* @returns {boolean}
|
|
* @see {@link isHTMLRawTextElement}
|
|
* @see {@link HTML_RAW_TEXT_ELEMENTS}
|
|
* @see https://html.spec.whatwg.org/#raw-text-elements
|
|
* @see https://html.spec.whatwg.org/#escapable-raw-text-elements
|
|
*/
|
|
function isHTMLEscapableRawTextElement(tagName) {
|
|
var key = tagName.toLowerCase();
|
|
return hasOwn(HTML_RAW_TEXT_ELEMENTS, key) && HTML_RAW_TEXT_ELEMENTS[key];
|
|
}
|
|
/**
|
|
* Only returns true if `value` matches MIME_TYPE.HTML, which indicates an HTML document.
|
|
*
|
|
* @param {string} mimeType
|
|
* @returns {mimeType is 'text/html'}
|
|
* @see https://www.iana.org/assignments/media-types/text/html
|
|
* @see https://en.wikipedia.org/wiki/HTML
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString
|
|
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring
|
|
*/
|
|
function isHTMLMimeType(mimeType) {
|
|
return mimeType === MIME_TYPE.HTML;
|
|
}
|
|
/**
|
|
* For both the `text/html` and the `application/xhtml+xml` namespace the spec defines that the
|
|
* HTML namespace is provided as the default.
|
|
*
|
|
* @param {string} mimeType
|
|
* @returns {boolean}
|
|
* @see https://dom.spec.whatwg.org/#dom-document-createelement
|
|
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
|
|
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
|
|
*/
|
|
function hasDefaultHTMLNamespace(mimeType) {
|
|
return isHTMLMimeType(mimeType) || mimeType === MIME_TYPE.XML_XHTML_APPLICATION;
|
|
}
|
|
|
|
/**
|
|
* All mime types that are allowed as input to `DOMParser.parseFromString`
|
|
*
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#Argument02
|
|
* MDN
|
|
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#domparsersupportedtype
|
|
* WHATWG HTML Spec
|
|
* @see {@link DOMParser.prototype.parseFromString}
|
|
*/
|
|
var MIME_TYPE = freeze({
|
|
/**
|
|
* `text/html`, the only mime type that triggers treating an XML document as HTML.
|
|
*
|
|
* @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration
|
|
* @see https://en.wikipedia.org/wiki/HTML Wikipedia
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN
|
|
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring
|
|
* WHATWG HTML Spec
|
|
*/
|
|
HTML: 'text/html',
|
|
|
|
/**
|
|
* `application/xml`, the standard mime type for XML documents.
|
|
*
|
|
* @see https://www.iana.org/assignments/media-types/application/xml IANA MimeType
|
|
* registration
|
|
* @see https://tools.ietf.org/html/rfc7303#section-9.1 RFC 7303
|
|
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia
|
|
*/
|
|
XML_APPLICATION: 'application/xml',
|
|
|
|
/**
|
|
* `text/xml`, an alias for `application/xml`.
|
|
*
|
|
* @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303
|
|
* @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration
|
|
* @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia
|
|
*/
|
|
XML_TEXT: 'text/xml',
|
|
|
|
/**
|
|
* `application/xhtml+xml`, indicates an XML document that has the default HTML namespace,
|
|
* but is parsed as an XML document.
|
|
*
|
|
* @see https://www.iana.org/assignments/media-types/application/xhtml+xml IANA MimeType
|
|
* registration
|
|
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument WHATWG DOM Spec
|
|
* @see https://en.wikipedia.org/wiki/XHTML Wikipedia
|
|
*/
|
|
XML_XHTML_APPLICATION: 'application/xhtml+xml',
|
|
|
|
/**
|
|
* `image/svg+xml`,
|
|
*
|
|
* @see https://www.iana.org/assignments/media-types/image/svg+xml IANA MimeType registration
|
|
* @see https://www.w3.org/TR/SVG11/ W3C SVG 1.1
|
|
* @see https://en.wikipedia.org/wiki/Scalable_Vector_Graphics Wikipedia
|
|
*/
|
|
XML_SVG_IMAGE: 'image/svg+xml',
|
|
});
|
|
/**
|
|
* @typedef {'application/xhtml+xml' | 'application/xml' | 'image/svg+xml' | 'text/html' | 'text/xml'}
|
|
* MimeType
|
|
*/
|
|
/**
|
|
* @type {MimeType[]}
|
|
* @private
|
|
* Basically `Object.values`, which is not available in ES5.
|
|
*/
|
|
var _MIME_TYPES = Object.keys(MIME_TYPE).map(function (key) {
|
|
return MIME_TYPE[key];
|
|
});
|
|
|
|
/**
|
|
* Only returns true if `mimeType` is one of the allowed values for
|
|
* `DOMParser.parseFromString`.
|
|
*
|
|
* @param {string} mimeType
|
|
* @returns {mimeType is 'application/xhtml+xml' | 'application/xml' | 'image/svg+xml' | 'text/html' | 'text/xml'}
|
|
*
|
|
*/
|
|
function isValidMimeType(mimeType) {
|
|
return _MIME_TYPES.indexOf(mimeType) > -1;
|
|
}
|
|
/**
|
|
* Namespaces that are used in this code base.
|
|
*
|
|
* @see http://www.w3.org/TR/REC-xml-names
|
|
*/
|
|
var NAMESPACE = freeze({
|
|
/**
|
|
* The XHTML namespace.
|
|
*
|
|
* @see http://www.w3.org/1999/xhtml
|
|
*/
|
|
HTML: 'http://www.w3.org/1999/xhtml',
|
|
|
|
/**
|
|
* The SVG namespace.
|
|
*
|
|
* @see http://www.w3.org/2000/svg
|
|
*/
|
|
SVG: 'http://www.w3.org/2000/svg',
|
|
|
|
/**
|
|
* The `xml:` namespace.
|
|
*
|
|
* @see http://www.w3.org/XML/1998/namespace
|
|
*/
|
|
XML: 'http://www.w3.org/XML/1998/namespace',
|
|
|
|
/**
|
|
* The `xmlns:` namespace.
|
|
*
|
|
* @see https://www.w3.org/2000/xmlns/
|
|
*/
|
|
XMLNS: 'http://www.w3.org/2000/xmlns/',
|
|
});
|
|
|
|
exports.assign = assign;
|
|
exports.find = find;
|
|
exports.freeze = freeze;
|
|
exports.HTML_BOOLEAN_ATTRIBUTES = HTML_BOOLEAN_ATTRIBUTES;
|
|
exports.HTML_RAW_TEXT_ELEMENTS = HTML_RAW_TEXT_ELEMENTS;
|
|
exports.HTML_VOID_ELEMENTS = HTML_VOID_ELEMENTS;
|
|
exports.hasDefaultHTMLNamespace = hasDefaultHTMLNamespace;
|
|
exports.hasOwn = hasOwn;
|
|
exports.isHTMLBooleanAttribute = isHTMLBooleanAttribute;
|
|
exports.isHTMLRawTextElement = isHTMLRawTextElement;
|
|
exports.isHTMLEscapableRawTextElement = isHTMLEscapableRawTextElement;
|
|
exports.isHTMLMimeType = isHTMLMimeType;
|
|
exports.isHTMLVoidElement = isHTMLVoidElement;
|
|
exports.isValidMimeType = isValidMimeType;
|
|
exports.MIME_TYPE = MIME_TYPE;
|
|
exports.NAMESPACE = NAMESPACE;
|