xmldom-sre
Version:
A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.
376 lines (353 loc) • 12.2 kB
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.
*
* @template {unknown} T
* @param {Array<T> | ({length:number, [number]: T})} list
* @param {function (item: T, index: number, list:Array<T> | ({length:number, [number]: T})):boolean} predicate
* @param {Partial<Pick<ArrayConstructor['prototype'], 'find'>>?} ac `Array.prototype` by default,
* allows injecting a custom implementation in tests
* @returns {T | undefined}
*
* @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 (Object.prototype.hasOwnProperty.call(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.
*
* @template T
* @param {T} object the object to freeze
* @param {Pick<ObjectConstructor, 'freeze'>} [oc=Object] `Object` by default,
* allows to inject custom object constructor for tests
* @returns {Readonly<T>}
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
*/
function freeze(object, oc) {
if (oc === undefined) {
oc = Object;
}
return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object;
}
/**
* Since we can not rely on `Object.assign` we provide a simplified version
* that is sufficient for our needs.
*
* @param {Object} target
* @param {Object | null | undefined} source
*
* @returns {Object} target
* @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 (Object.prototype.hasOwnProperty.call(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
* @return {boolean}
* @see HTML_BOOLEAN_ATTRIBUTES
* @see https://html.spec.whatwg.org/#boolean-attributes
* @see https://html.spec.whatwg.org/#attributes-3
*/
function isHTMLBooleanAttribute(name) {
return HTML_BOOLEAN_ATTRIBUTES.hasOwnProperty(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
* @return {boolean}
* @see HTML_VOID_ELEMENTS
* @see https://html.spec.whatwg.org/#void-elements
*/
function isHTMLVoidElement(tagName) {
return HTML_VOID_ELEMENTS.hasOwnProperty(tagName.toLowerCase());
}
/**
* Tag names that are raw text elements according to HTML spec.
* The value denotes whether they are escapable or not.
*
* @see isHTMLEscapableRawTextElement
* @see 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
* @return {boolean}
* @see isHTMLEscapableRawTextElement
* @see 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 HTML_RAW_TEXT_ELEMENTS.hasOwnProperty(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
* @return {boolean}
* @see isHTMLRawTextElement
* @see 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 HTML_RAW_TEXT_ELEMENTS.hasOwnProperty(key) && HTML_RAW_TEXT_ELEMENTS[key];
}
/**
* 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 DOMParser.prototype.parseFromString
*/
var MIME_TYPE = freeze({
/**
* `text/html`, the only mime type that triggers treating an XML document as HTML.
*
* @see DOMParser.SupportedType.isHTML
* @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',
/**
* Helper method to check a mime type if it indicates an HTML document
*
* @param {string} [value]
* @returns {boolean}
*
* @see [IANA MimeType registration](https://www.iana.org/assignments/media-types/text/html)
* @see [Wikipedia](https://en.wikipedia.org/wiki/HTML)
* @see [`DOMParser.parseFromString` @ MDN](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString)
* @see [`DOMParser.parseFromString` @ HTML Specification](https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring)
*/
isHTML: function (value) {
return value === 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 in some cases.
*
* @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
*/
hasDefaultHTMLNamespace: function (mimeType) {
return MIME_TYPE.isHTML(mimeType) || mimeType === MIME_TYPE.XML_XHTML_APPLICATION;
},
/**
* `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/html`, 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',
});
/**
* 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',
/**
* Checks if `uri` equals `NAMESPACE.HTML`.
*
* @param {string} [uri]
*
* @see NAMESPACE.HTML
*/
isHTML: function (uri) {
return uri === NAMESPACE.HTML;
},
/**
* 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.isHTMLBooleanAttribute = isHTMLBooleanAttribute;
exports.isHTMLRawTextElement = isHTMLRawTextElement;
exports.isHTMLEscapableRawTextElement = isHTMLEscapableRawTextElement;
exports.isHTMLVoidElement = isHTMLVoidElement;
exports.MIME_TYPE = MIME_TYPE;
exports.NAMESPACE = NAMESPACE;