NeahNew/node_modules/@xmldom/xmldom/lib/dom.js
2025-05-03 14:17:46 +02:00

3136 lines
101 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
var conventions = require('./conventions');
var find = conventions.find;
var hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace;
var hasOwn = conventions.hasOwn;
var isHTMLMimeType = conventions.isHTMLMimeType;
var isHTMLRawTextElement = conventions.isHTMLRawTextElement;
var isHTMLVoidElement = conventions.isHTMLVoidElement;
var MIME_TYPE = conventions.MIME_TYPE;
var NAMESPACE = conventions.NAMESPACE;
/**
* Private DOM Constructor symbol
*
* Internal symbol used for construction of all classes whose constructors should be private.
* Currently used for checks in `Node`, `Document`, `Element`, `Attr`, `CharacterData`, `Text`, `Comment`,
* `CDATASection`, `DocumentType`, `Notation`, `Entity`, `EntityReference`, `DocumentFragment`, `ProcessingInstruction`
* so the constructor can't be used from outside the module.
*/
var PDC = Symbol();
var errors = require('./errors');
var DOMException = errors.DOMException;
var DOMExceptionName = errors.DOMExceptionName;
var g = require('./grammar');
/**
* Checks if the given symbol equals the Private DOM Constructor symbol (PDC)
* and throws an Illegal constructor exception when the symbols don't match.
* This ensures that the constructor remains private and can't be used outside this module.
*/
function checkSymbol(symbol) {
if (symbol !== PDC) {
throw new TypeError('Illegal constructor');
}
}
/**
* A prerequisite for `[].filter`, to drop elements that are empty.
*
* @param {string} input
* The string to be checked.
* @returns {boolean}
* Returns `true` if the input string is not empty, `false` otherwise.
*/
function notEmptyString(input) {
return input !== '';
}
/**
* Splits a string on ASCII whitespace characters (U+0009 TAB, U+000A LF, U+000C FF, U+000D CR,
* U+0020 SPACE).
* It follows the definition from the infra specification from WHATWG.
*
* @param {string} input
* The string to be split.
* @returns {string[]}
* An array of the split strings. The array can be empty if the input string is empty or only
* contains whitespace characters.
* @see {@link https://infra.spec.whatwg.org/#split-on-ascii-whitespace}
* @see {@link https://infra.spec.whatwg.org/#ascii-whitespace}
*/
function splitOnASCIIWhitespace(input) {
// U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE
return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : [];
}
/**
* Adds element as a key to current if it is not already present.
*
* @param {Record<string, boolean | undefined>} current
* The current record object to which the element will be added as a key.
* The object's keys are string types and values are either boolean or undefined.
* @param {string} element
* The string to be added as a key to the current record.
* @returns {Record<string, boolean | undefined>}
* The updated record object after the addition of the new element.
*/
function orderedSetReducer(current, element) {
if (!hasOwn(current, element)) {
current[element] = true;
}
return current;
}
/**
* Converts a string into an ordered set by splitting the input on ASCII whitespace and
* ensuring uniqueness of elements.
* This follows the definition of an ordered set from the infra specification by WHATWG.
*
* @param {string} input
* The input string to be transformed into an ordered set.
* @returns {string[]}
* An array of unique strings obtained from the input, preserving the original order.
* The array can be empty if the input string is empty or only contains whitespace characters.
* @see {@link https://infra.spec.whatwg.org/#ordered-set}
*/
function toOrderedSet(input) {
if (!input) return [];
var list = splitOnASCIIWhitespace(input);
return Object.keys(list.reduce(orderedSetReducer, {}));
}
/**
* Uses `list.indexOf` to implement a function that behaves like `Array.prototype.includes`.
* This function is used in environments where `Array.prototype.includes` may not be available.
*
* @param {any[]} list
* The array in which to search for the element.
* @returns {function(any): boolean}
* A function that accepts an element and returns a boolean indicating whether the element is
* included in the provided list.
*/
function arrayIncludes(list) {
return function (element) {
return list && list.indexOf(element) !== -1;
};
}
/**
* Validates a qualified name based on the criteria provided in the DOM specification by
* WHATWG.
*
* @param {string} qualifiedName
* The qualified name to be validated.
* @throws {DOMException}
* With code {@link DOMException.INVALID_CHARACTER_ERR} if the qualified name contains an
* invalid character.
* @see {@link https://dom.spec.whatwg.org/#validate}
*/
function validateQualifiedName(qualifiedName) {
if (!g.QName_exact.test(qualifiedName)) {
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in qualified name "' + qualifiedName + '"');
}
}
/**
* Validates a qualified name and the namespace associated with it,
* based on the criteria provided in the DOM specification by WHATWG.
*
* @param {string | null} namespace
* The namespace to be validated. It can be a string or null.
* @param {string} qualifiedName
* The qualified name to be validated.
* @returns {[namespace: string | null, prefix: string | null, localName: string]}
* Returns a tuple with the namespace,
* prefix and local name of the qualified name.
* @throws {DOMException}
* Throws a DOMException if the qualified name or the namespace is not valid.
* @see {@link https://dom.spec.whatwg.org/#validate-and-extract}
*/
function validateAndExtract(namespace, qualifiedName) {
validateQualifiedName(qualifiedName);
namespace = namespace || null;
/**
* @type {string | null}
*/
var prefix = null;
var localName = qualifiedName;
if (qualifiedName.indexOf(':') >= 0) {
var splitResult = qualifiedName.split(':');
prefix = splitResult[0];
localName = splitResult[1];
}
if (prefix !== null && namespace === null) {
throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is non-null and namespace is null');
}
if (prefix === 'xml' && namespace !== conventions.NAMESPACE.XML) {
throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is "xml" and namespace is not the XML namespace');
}
if ((prefix === 'xmlns' || qualifiedName === 'xmlns') && namespace !== conventions.NAMESPACE.XMLNS) {
throw new DOMException(
DOMException.NAMESPACE_ERR,
'either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace'
);
}
if (namespace === conventions.NAMESPACE.XMLNS && prefix !== 'xmlns' && qualifiedName !== 'xmlns') {
throw new DOMException(
DOMException.NAMESPACE_ERR,
'namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns"'
);
}
return [namespace, prefix, localName];
}
/**
* Copies properties from one object to another.
* It only copies the object's own (not inherited) properties.
*
* @param {Object} src
* The source object from which properties are copied.
* @param {Object} dest
* The destination object to which properties are copied.
*/
function copy(src, dest) {
for (var p in src) {
if (hasOwn(src, p)) {
dest[p] = src[p];
}
}
}
/**
* Extends a class with the properties and methods of a super class.
* It uses a form of prototypal inheritance, and establishes the `constructor` property
* correctly(?).
*
* It is not clear to the current maintainers if this implementation is making sense,
* since it creates an intermediate prototype function,
* which all properties of `Super` are copied onto using `_copy`.
*
* @param {Object} Class
* The class that is to be extended.
* @param {Object} Super
* The super class from which properties and methods are inherited.
* @private
*/
function _extends(Class, Super) {
var pt = Class.prototype;
if (!(pt instanceof Super)) {
function t() {}
t.prototype = Super.prototype;
t = new t();
copy(pt, t);
Class.prototype = pt = t;
}
if (pt.constructor != Class) {
if (typeof Class != 'function') {
console.error('unknown Class:' + Class);
}
pt.constructor = Class;
}
}
var NodeType = {};
var ELEMENT_NODE = (NodeType.ELEMENT_NODE = 1);
var ATTRIBUTE_NODE = (NodeType.ATTRIBUTE_NODE = 2);
var TEXT_NODE = (NodeType.TEXT_NODE = 3);
var CDATA_SECTION_NODE = (NodeType.CDATA_SECTION_NODE = 4);
var ENTITY_REFERENCE_NODE = (NodeType.ENTITY_REFERENCE_NODE = 5);
var ENTITY_NODE = (NodeType.ENTITY_NODE = 6);
var PROCESSING_INSTRUCTION_NODE = (NodeType.PROCESSING_INSTRUCTION_NODE = 7);
var COMMENT_NODE = (NodeType.COMMENT_NODE = 8);
var DOCUMENT_NODE = (NodeType.DOCUMENT_NODE = 9);
var DOCUMENT_TYPE_NODE = (NodeType.DOCUMENT_TYPE_NODE = 10);
var DOCUMENT_FRAGMENT_NODE = (NodeType.DOCUMENT_FRAGMENT_NODE = 11);
var NOTATION_NODE = (NodeType.NOTATION_NODE = 12);
var DocumentPosition = conventions.freeze({
DOCUMENT_POSITION_DISCONNECTED: 1,
DOCUMENT_POSITION_PRECEDING: 2,
DOCUMENT_POSITION_FOLLOWING: 4,
DOCUMENT_POSITION_CONTAINS: 8,
DOCUMENT_POSITION_CONTAINED_BY: 16,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32,
});
//helper functions for compareDocumentPosition
/**
* Finds the common ancestor in two parent chains.
*
* @param {Node[]} a
* The first parent chain.
* @param {Node[]} b
* The second parent chain.
* @returns {Node}
* The common ancestor node if it exists. If there is no common ancestor, the function will
* return `null`.
*/
function commonAncestor(a, b) {
if (b.length < a.length) return commonAncestor(b, a);
var c = null;
for (var n in a) {
if (a[n] !== b[n]) return c;
c = a[n];
}
return c;
}
/**
* Assigns a unique identifier to a document to ensure consistency while comparing unrelated
* nodes.
*
* @param {Document} doc
* The document to which a unique identifier is to be assigned.
* @returns {string}
* The unique identifier of the document. If the document already had a unique identifier, the
* function will return the existing one.
*/
function docGUID(doc) {
if (!doc.guid) doc.guid = Math.random();
return doc.guid;
}
//-- end of helper functions
/**
* The NodeList interface provides the abstraction of an ordered collection of nodes,
* without defining or constraining how this collection is implemented.
* NodeList objects in the DOM are live.
* The items in the NodeList are accessible via an integral index, starting from 0.
* You can also access the items of the NodeList with a `for...of` loop.
*
* @class NodeList
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
* @constructs NodeList
*/
function NodeList() {}
NodeList.prototype = {
/**
* The number of nodes in the list. The range of valid child node indices is 0 to length-1
* inclusive.
*
* @type {number}
*/
length: 0,
/**
* Returns the item at `index`. If index is greater than or equal to the number of nodes in
* the list, this returns null.
*
* @param index
* Unsigned long Index into the collection.
* @returns {Node | null}
* The node at position `index` in the NodeList,
* or null if that is not a valid index.
*/
item: function (index) {
return index >= 0 && index < this.length ? this[index] : null;
},
/**
* Returns a string representation of the NodeList.
*
* @param {unknown} nodeFilter
* __A filter function? Not implemented according to the spec?__.
* @returns {string}
* A string representation of the NodeList.
*/
toString: function (nodeFilter) {
for (var buf = [], i = 0; i < this.length; i++) {
serializeToString(this[i], buf, nodeFilter);
}
return buf.join('');
},
/**
* Filters the NodeList based on a predicate.
*
* @param {function(Node): boolean} predicate
* - A predicate function to filter the NodeList.
* @returns {Node[]}
* An array of nodes that satisfy the predicate.
* @private
*/
filter: function (predicate) {
return Array.prototype.filter.call(this, predicate);
},
/**
* Returns the first index at which a given node can be found in the NodeList, or -1 if it is
* not present.
*
* @param {Node} item
* - The Node item to locate in the NodeList.
* @returns {number}
* The first index of the node in the NodeList; -1 if not found.
* @private
*/
indexOf: function (item) {
return Array.prototype.indexOf.call(this, item);
},
};
NodeList.prototype[Symbol.iterator] = function () {
var me = this;
var index = 0;
return {
next: function () {
if (index < me.length) {
return {
value: me[index++],
done: false,
};
} else {
return {
done: true,
};
}
},
return: function () {
return {
done: true,
};
},
};
};
/**
* Represents a live collection of nodes that is automatically updated when its associated
* document changes.
*
* @class LiveNodeList
* @param {Node} node
* The associated node.
* @param {function} refresh
* The function to refresh the live node list.
* @augments NodeList
* @constructs LiveNodeList
*/
function LiveNodeList(node, refresh) {
this._node = node;
this._refresh = refresh;
_updateLiveList(this);
}
/**
* Updates the live node list.
*
* @param {LiveNodeList} list
* The live node list to update.
* @private
*/
function _updateLiveList(list) {
var inc = list._node._inc || list._node.ownerDocument._inc;
if (list._inc !== inc) {
var ls = list._refresh(list._node);
__set__(list, 'length', ls.length);
if (!list.$$length || ls.length < list.$$length) {
for (var i = ls.length; i in list; i++) {
if (hasOwn(list, i)) {
delete list[i];
}
}
}
copy(ls, list);
list._inc = inc;
}
}
/**
* Returns the node at position `index` in the LiveNodeList, or null if that is not a valid
* index.
*
* @param {number} i
* Index into the collection.
* @returns {Node | null}
* The node at position `index` in the LiveNodeList, or null if that is not a valid index.
*/
LiveNodeList.prototype.item = function (i) {
_updateLiveList(this);
return this[i] || null;
};
_extends(LiveNodeList, NodeList);
/**
* Objects implementing the NamedNodeMap interface are used to represent collections of nodes
* that can be accessed by name.
* Note that NamedNodeMap does not inherit from NodeList;
* NamedNodeMaps are not maintained in any particular order.
* Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal
* index,
* but this is simply to allow convenient enumeration of the contents of a NamedNodeMap,
* and does not imply that the DOM specifies an order to these Nodes.
* NamedNodeMap objects in the DOM are live.
* used for attributes or DocumentType entities
*
* This implementation only supports property indices, but does not support named properties,
* as specified in the living standard.
*
* @class NamedNodeMap
* @see https://dom.spec.whatwg.org/#interface-namednodemap
* @see https://webidl.spec.whatwg.org/#dfn-supported-property-names
* @constructs NamedNodeMap
*/
function NamedNodeMap() {}
/**
* Returns the index of a node within the list.
*
* @param {Array} list
* The list of nodes.
* @param {Node} node
* The node to find.
* @returns {number}
* The index of the node within the list, or -1 if not found.
* @private
*/
function _findNodeIndex(list, node) {
var i = 0;
while (i < list.length) {
if (list[i] === node) {
return i;
}
i++;
}
}
/**
* Adds a new attribute to the list and updates the owner element of the attribute.
*
* @param {Element} el
* The element which will become the owner of the new attribute.
* @param {NamedNodeMap} list
* The list to which the new attribute will be added.
* @param {Attr} newAttr
* The new attribute to be added.
* @param {Attr} oldAttr
* The old attribute to be replaced, or null if no attribute is to be replaced.
* @returns {void}
* @private
*/
function _addNamedNode(el, list, newAttr, oldAttr) {
if (oldAttr) {
list[_findNodeIndex(list, oldAttr)] = newAttr;
} else {
list[list.length] = newAttr;
list.length++;
}
if (el) {
newAttr.ownerElement = el;
var doc = el.ownerDocument;
if (doc) {
oldAttr && _onRemoveAttribute(doc, el, oldAttr);
_onAddAttribute(doc, el, newAttr);
}
}
}
/**
* Removes an attribute from the list and updates the owner element of the attribute.
*
* @param {Element} el
* The element which is the current owner of the attribute.
* @param {NamedNodeMap} list
* The list from which the attribute will be removed.
* @param {Attr} attr
* The attribute to be removed.
* @returns {void}
* @private
*/
function _removeNamedNode(el, list, attr) {
//console.log('remove attr:'+attr)
var i = _findNodeIndex(list, attr);
if (i >= 0) {
var lastIndex = list.length - 1;
while (i <= lastIndex) {
list[i] = list[++i];
}
list.length = lastIndex;
if (el) {
var doc = el.ownerDocument;
if (doc) {
_onRemoveAttribute(doc, el, attr);
}
attr.ownerElement = null;
}
}
}
NamedNodeMap.prototype = {
length: 0,
item: NodeList.prototype.item,
/**
* Get an attribute by name. Note: Name is in lower case in case of HTML namespace and
* document.
*
* @param {string} localName
* The local name of the attribute.
* @returns {Attr | null}
* The attribute with the given local name, or null if no such attribute exists.
* @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
*/
getNamedItem: function (localName) {
if (this._ownerElement && this._ownerElement._isInHTMLDocumentAndNamespace()) {
localName = localName.toLowerCase();
}
var i = 0;
while (i < this.length) {
var attr = this[i];
if (attr.nodeName === localName) {
return attr;
}
i++;
}
return null;
},
/**
* Set an attribute.
*
* @param {Attr} attr
* The attribute to set.
* @returns {Attr | null}
* The old attribute with the same local name and namespace URI as the new one, or null if no
* such attribute exists.
* @throws {DOMException}
* With code:
* - {@link INUSE_ATTRIBUTE_ERR} - If the attribute is already an attribute of another
* element.
* @see https://dom.spec.whatwg.org/#concept-element-attributes-set
*/
setNamedItem: function (attr) {
var el = attr.ownerElement;
if (el && el !== this._ownerElement) {
throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR);
}
var oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
if (oldAttr === attr) {
return attr;
}
_addNamedNode(this._ownerElement, this, attr, oldAttr);
return oldAttr;
},
/**
* Set an attribute, replacing an existing attribute with the same local name and namespace
* URI if one exists.
*
* @param {Attr} attr
* The attribute to set.
* @returns {Attr | null}
* The old attribute with the same local name and namespace URI as the new one, or null if no
* such attribute exists.
* @throws {DOMException}
* Throws a DOMException with the name "InUseAttributeError" if the attribute is already an
* attribute of another element.
* @see https://dom.spec.whatwg.org/#concept-element-attributes-set
*/
setNamedItemNS: function (attr) {
return this.setNamedItem(attr);
},
/**
* Removes an attribute specified by the local name.
*
* @param {string} localName
* The local name of the attribute to be removed.
* @returns {Attr}
* The attribute node that was removed.
* @throws {DOMException}
* With code:
* - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given name is found.
* @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
* @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
*/
removeNamedItem: function (localName) {
var attr = this.getNamedItem(localName);
if (!attr) {
throw new DOMException(DOMException.NOT_FOUND_ERR, localName);
}
_removeNamedNode(this._ownerElement, this, attr);
return attr;
},
/**
* Removes an attribute specified by the namespace and local name.
*
* @param {string | null} namespaceURI
* The namespace URI of the attribute to be removed.
* @param {string} localName
* The local name of the attribute to be removed.
* @returns {Attr}
* The attribute node that was removed.
* @throws {DOMException}
* With code:
* - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given namespace URI and local
* name is found.
* @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditemns
* @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-namespace
*/
removeNamedItemNS: function (namespaceURI, localName) {
var attr = this.getNamedItemNS(namespaceURI, localName);
if (!attr) {
throw new DOMException(DOMException.NOT_FOUND_ERR, namespaceURI ? namespaceURI + ' : ' + localName : localName);
}
_removeNamedNode(this._ownerElement, this, attr);
return attr;
},
/**
* Get an attribute by namespace and local name.
*
* @param {string | null} namespaceURI
* The namespace URI of the attribute.
* @param {string} localName
* The local name of the attribute.
* @returns {Attr | null}
* The attribute with the given namespace URI and local name, or null if no such attribute
* exists.
* @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
*/
getNamedItemNS: function (namespaceURI, localName) {
if (!namespaceURI) {
namespaceURI = null;
}
var i = 0;
while (i < this.length) {
var node = this[i];
if (node.localName === localName && node.namespaceURI === namespaceURI) {
return node;
}
i++;
}
return null;
},
};
NamedNodeMap.prototype[Symbol.iterator] = function () {
var me = this;
var index = 0;
return {
next: function () {
if (index < me.length) {
return {
value: me[index++],
done: false,
};
} else {
return {
done: true,
};
}
},
return: function () {
return {
done: true,
};
},
};
};
/**
* The DOMImplementation interface provides a number of methods for performing operations that
* are independent of any particular instance of the document object model.
*
* The DOMImplementation interface represents an object providing methods which are not
* dependent on any particular document.
* Such an object is returned by the `Document.implementation` property.
*
* **The individual methods describe the differences compared to the specs**.
*
* @class DOMImplementation
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core
* (Initial)
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core
* @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard
* @constructs DOMImplementation
*/
function DOMImplementation() {}
DOMImplementation.prototype = {
/**
* Test if the DOM implementation implements a specific feature and version, as specified in
* {@link https://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMFeatures DOM Features}.
*
* The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given
* feature is supported. The different implementations fairly diverged in what kind of
* features were reported. The latest version of the spec settled to force this method to
* always return true, where the functionality was accurate and in use.
*
* @deprecated
* It is deprecated and modern browsers return true in all cases.
* @function DOMImplementation#hasFeature
* @param {string} feature
* The name of the feature to test.
* @param {string} [version]
* This is the version number of the feature to test.
* @returns {boolean}
* Always returns true.
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN
* @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core
* @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-5CED94D7 DOM Level 3 Core
*/
hasFeature: function (feature, version) {
return true;
},
/**
* Creates a DOM Document object of the specified type with its document element. Note that
* based on the {@link DocumentType}
* given to create the document, the implementation may instantiate specialized
* {@link Document} objects that support additional features than the "Core", such as "HTML"
* {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML}.
* On the other hand, setting the {@link DocumentType} after the document was created makes
* this very unlikely to happen. Alternatively, specialized {@link Document} creation methods,
* such as createHTMLDocument
* {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML},
* can be used to obtain specific types of {@link Document} objects.
*
* __It behaves slightly different from the description in the living standard__:
* - There is no interface/class `XMLDocument`, it returns a `Document`
* instance (with it's `type` set to `'xml'`).
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
*
* @function DOMImplementation.createDocument
* @param {string | null} namespaceURI
* The
* {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-namespaceURI namespace URI}
* of the document element to create or null.
* @param {string | null} qualifiedName
* The
* {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified name}
* of the document element to be created or null.
* @param {DocumentType | null} [doctype=null]
* The type of document to be created or null. When doctype is not null, its
* {@link Node#ownerDocument} attribute is set to the document being created. Default is
* `null`
* @returns {Document}
* A new {@link Document} object with its document element. If the NamespaceURI,
* qualifiedName, and doctype are null, the returned {@link Document} is empty with no
* document element.
* @throws {DOMException}
* With code:
*
* - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
* according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
* - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed, if the qualifiedName has a
* prefix and the namespaceURI is null, or if the qualifiedName is null and the namespaceURI
* is different from null, or if the qualifiedName has a prefix that is "xml" and the
* namespaceURI is different from "{@link http://www.w3.org/XML/1998/namespace}"
* {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#Namespaces XML Namespaces},
* or if the DOM implementation does not support the "XML" feature but a non-null namespace
* URI was provided, since namespaces were defined by XML.
* - `WRONG_DOCUMENT_ERR`: Raised if doctype has already been used with a different document
* or was created from a different implementation.
* - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
* "XML" and the language exposed through the Document does not support XML Namespaces (such
* as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
* @since DOM Level 2.
* @see {@link #createHTMLDocument}
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Living Standard
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocument DOM
* Level 3 Core
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM
* Level 2 Core (initial)
*/
createDocument: function (namespaceURI, qualifiedName, doctype) {
var contentType = MIME_TYPE.XML_APPLICATION;
if (namespaceURI === NAMESPACE.HTML) {
contentType = MIME_TYPE.XML_XHTML_APPLICATION;
} else if (namespaceURI === NAMESPACE.SVG) {
contentType = MIME_TYPE.XML_SVG_IMAGE;
}
var doc = new Document(PDC, { contentType: contentType });
doc.implementation = this;
doc.childNodes = new NodeList();
doc.doctype = doctype || null;
if (doctype) {
doc.appendChild(doctype);
}
if (qualifiedName) {
var root = doc.createElementNS(namespaceURI, qualifiedName);
doc.appendChild(root);
}
return doc;
},
/**
* Creates an empty DocumentType node. Entity declarations and notations are not made
* available. Entity reference expansions and default attribute additions do not occur.
*
* **This behavior is slightly different from the one in the specs**:
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
* - `publicId` and `systemId` contain the raw data including any possible quotes,
* so they can always be serialized back to the original value
* - `internalSubset` contains the raw string between `[` and `]` if present,
* but is not parsed or validated in any form.
*
* @function DOMImplementation#createDocumentType
* @param {string} qualifiedName
* The {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified
* name} of the document type to be created.
* @param {string} [publicId]
* The external subset public identifier.
* @param {string} [systemId]
* The external subset system identifier.
* @param {string} [internalSubset]
* the internal subset or an empty string if it is not present
* @returns {DocumentType}
* A new {@link DocumentType} node with {@link Node#ownerDocument} set to null.
* @throws {DOMException}
* With code:
*
* - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
* according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
* - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed.
* - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
* "XML" and the language exposed through the Document does not support XML Namespaces (such
* as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
* @since DOM Level 2.
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType
* MDN
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living
* Standard
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-3-Core-DOM-createDocType DOM
* Level 3 Core
* @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM
* Level 2 Core
* @see https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md#050
* @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-Core-DocType-internalSubset
* @prettierignore
*/
createDocumentType: function (qualifiedName, publicId, systemId, internalSubset) {
validateQualifiedName(qualifiedName);
var node = new DocumentType(PDC);
node.name = qualifiedName;
node.nodeName = qualifiedName;
node.publicId = publicId || '';
node.systemId = systemId || '';
node.internalSubset = internalSubset || '';
node.childNodes = new NodeList();
return node;
},
/**
* Returns an HTML document, that might already have a basic DOM structure.
*
* __It behaves slightly different from the description in the living standard__:
* - If the first argument is `false` no initial nodes are added (steps 3-7 in the specs are
* omitted)
* - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
*
* @param {string | false} [title]
* A string containing the title to give the new HTML document.
* @returns {Document}
* The HTML document.
* @since WHATWG Living Standard.
* @see {@link #createDocument}
* @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
* @see https://dom.spec.whatwg.org/#html-document
*/
createHTMLDocument: function (title) {
var doc = new Document(PDC, { contentType: MIME_TYPE.HTML });
doc.implementation = this;
doc.childNodes = new NodeList();
if (title !== false) {
doc.doctype = this.createDocumentType('html');
doc.doctype.ownerDocument = doc;
doc.appendChild(doc.doctype);
var htmlNode = doc.createElement('html');
doc.appendChild(htmlNode);
var headNode = doc.createElement('head');
htmlNode.appendChild(headNode);
if (typeof title === 'string') {
var titleNode = doc.createElement('title');
titleNode.appendChild(doc.createTextNode(title));
headNode.appendChild(titleNode);
}
htmlNode.appendChild(doc.createElement('body'));
}
return doc;
},
};
/**
* The DOM Node interface is an abstract base class upon which many other DOM API objects are
* based, thus letting those object types to be used similarly and often interchangeably. As an
* abstract class, there is no such thing as a plain Node object. All objects that implement
* Node functionality are based on one of its subclasses. Most notable are Document, Element,
* and DocumentFragment.
*
* In addition, every kind of DOM node is represented by an interface based on Node. These
* include Attr, CharacterData (which Text, Comment, CDATASection and ProcessingInstruction are
* all based on), and DocumentType.
*
* In some cases, a particular feature of the base Node interface may not apply to one of its
* child interfaces; in that case, the inheriting node may return null or throw an exception,
* depending on circumstances. For example, attempting to add children to a node type that
* cannot have children will throw an exception.
*
* **This behavior is slightly different from the in the specs**:
* - unimplemented interfaces: `EventTarget`
*
* @class
* @abstract
* @param {Symbol} symbol
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
* @see https://dom.spec.whatwg.org/#node
* @prettierignore
*/
function Node(symbol) {
checkSymbol(symbol);
}
Node.prototype = {
/**
* The first child of this node.
*
* @type {Node | null}
*/
firstChild: null,
/**
* The last child of this node.
*
* @type {Node | null}
*/
lastChild: null,
/**
* The previous sibling of this node.
*
* @type {Node | null}
*/
previousSibling: null,
/**
* The next sibling of this node.
*
* @type {Node | null}
*/
nextSibling: null,
/**
* The parent node of this node.
*
* @type {Node | null}
*/
parentNode: null,
/**
* The parent element of this node.
*
* @type {Element | null}
*/
get parentElement() {
return this.parentNode && this.parentNode.nodeType === this.ELEMENT_NODE ? this.parentNode : null;
},
/**
* The child nodes of this node.
*
* @type {NodeList}
*/
childNodes: null,
/**
* The document object associated with this node.
*
* @type {Document | null}
*/
ownerDocument: null,
/**
* The value of this node.
*
* @type {string | null}
*/
nodeValue: null,
/**
* The namespace URI of this node.
*
* @type {string | null}
*/
namespaceURI: null,
/**
* The prefix of the namespace for this node.
*
* @type {string | null}
*/
prefix: null,
/**
* The local part of the qualified name of this node.
*
* @type {string | null}
*/
localName: null,
/**
* The baseURI is currently always `about:blank`,
* since that's what happens when you create a document from scratch.
*
* @type {'about:blank'}
*/
baseURI: 'about:blank',
/**
* Is true if this node is part of a document.
*
* @type {boolean}
*/
get isConnected() {
var rootNode = this.getRootNode();
return rootNode && rootNode.nodeType === rootNode.DOCUMENT_NODE;
},
/**
* Checks whether `other` is an inclusive descendant of this node.
*
* @param {Node | null | undefined} other
* The node to check.
* @returns {boolean}
* True if `other` is an inclusive descendant of this node; false otherwise.
* @see https://dom.spec.whatwg.org/#dom-node-contains
*/
contains: function (other) {
if (!other) return false;
var parent = other;
do {
if (this === parent) return true;
parent = other.parentNode;
} while (parent);
return false;
},
/**
* @typedef GetRootNodeOptions
* @property {boolean} [composed=false]
*/
/**
* Searches for the root node of this node.
*
* **This behavior is slightly different from the in the specs**:
* - ignores `options.composed`, since `ShadowRoot`s are unsupported, always returns root.
*
* @param {GetRootNodeOptions} [options]
* @returns {Node}
* Root node.
* @see https://dom.spec.whatwg.org/#dom-node-getrootnode
* @see https://dom.spec.whatwg.org/#concept-shadow-including-root
*/
getRootNode: function (options) {
var parent = this;
do {
if (!parent.parentNode) {
return parent;
}
parent = parent.parentNode;
} while (parent);
},
/**
* Checks whether the given node is equal to this node.
*
* @param {Node} [otherNode]
* @see https://dom.spec.whatwg.org/#concept-node-equals
*/
isEqualNode: function (otherNode) {
if (!otherNode) return false;
if (this.nodeType !== otherNode.nodeType) return false;
switch (this.nodeType) {
case this.DOCUMENT_TYPE_NODE:
if (this.name !== otherNode.name) return false;
if (this.publicId !== otherNode.publicId) return false;
if (this.systemId !== otherNode.systemId) return false;
break;
case this.ELEMENT_NODE:
if (this.namespaceURI !== otherNode.namespaceURI) return false;
if (this.prefix !== otherNode.prefix) return false;
if (this.localName !== otherNode.localName) return false;
if (this.attributes.length !== otherNode.attributes.length) return false;
for (var i = 0; i < this.attributes.length; i++) {
var attr = this.attributes.item(i);
if (!attr.isEqualNode(otherNode.getAttributeNodeNS(attr.namespaceURI, attr.localName))) {
return false;
}
}
break;
case this.ATTRIBUTE_NODE:
if (this.namespaceURI !== otherNode.namespaceURI) return false;
if (this.localName !== otherNode.localName) return false;
if (this.value !== otherNode.value) return false;
break;
case this.PROCESSING_INSTRUCTION_NODE:
if (this.target !== otherNode.target || this.data !== otherNode.data) {
return false;
}
break;
case this.TEXT_NODE:
case this.COMMENT_NODE:
if (this.data !== otherNode.data) return false;
break;
}
if (this.childNodes.length !== otherNode.childNodes.length) {
return false;
}
for (var i = 0; i < this.childNodes.length; i++) {
if (!this.childNodes[i].isEqualNode(otherNode.childNodes[i])) {
return false;
}
}
return true;
},
/**
* Checks whether or not the given node is this node.
*
* @param {Node} [otherNode]
*/
isSameNode: function (otherNode) {
return this === otherNode;
},
/**
* Inserts a node before a reference node as a child of this node.
*
* @param {Node} newChild
* The new child node to be inserted.
* @param {Node | null} refChild
* The reference node before which newChild will be inserted.
* @returns {Node}
* The new child node successfully inserted.
* @throws {DOMException}
* Throws a DOMException if inserting the node would result in a DOM tree that is not
* well-formed, or if `child` is provided but is not a child of `parent`.
* See {@link _insertBefore} for more details.
* @since Modified in DOM L2
*/
insertBefore: function (newChild, refChild) {
return _insertBefore(this, newChild, refChild);
},
/**
* Replaces an old child node with a new child node within this node.
*
* @param {Node} newChild
* The new node that is to replace the old node.
* If it already exists in the DOM, it is removed from its original position.
* @param {Node} oldChild
* The existing child node to be replaced.
* @returns {Node}
* Returns the replaced child node.
* @throws {DOMException}
* Throws a DOMException if replacing the node would result in a DOM tree that is not
* well-formed, or if `oldChild` is not a child of `this`.
* This can also occur if the pre-replacement validity assertion fails.
* See {@link _insertBefore}, {@link Node.removeChild}, and
* {@link assertPreReplacementValidityInDocument} for more details.
* @see https://dom.spec.whatwg.org/#concept-node-replace
*/
replaceChild: function (newChild, oldChild) {
_insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
if (oldChild) {
this.removeChild(oldChild);
}
},
/**
* Removes an existing child node from this node.
*
* @param {Node} oldChild
* The child node to be removed.
* @returns {Node}
* Returns the removed child node.
* @throws {DOMException}
* Throws a DOMException if `oldChild` is not a child of `this`.
* See {@link _removeChild} for more details.
*/
removeChild: function (oldChild) {
return _removeChild(this, oldChild);
},
/**
* Appends a child node to this node.
*
* @param {Node} newChild
* The child node to be appended to this node.
* If it already exists in the DOM, it is removed from its original position.
* @returns {Node}
* Returns the appended child node.
* @throws {DOMException}
* Throws a DOMException if appending the node would result in a DOM tree that is not
* well-formed, or if `newChild` is not a valid Node.
* See {@link insertBefore} for more details.
*/
appendChild: function (newChild) {
return this.insertBefore(newChild, null);
},
/**
* Determines whether this node has any child nodes.
*
* @returns {boolean}
* Returns true if this node has any child nodes, and false otherwise.
*/
hasChildNodes: function () {
return this.firstChild != null;
},
/**
* Creates a copy of the calling node.
*
* @param {boolean} deep
* If true, the contents of the node are recursively copied.
* If false, only the node itself (and its attributes, if it is an element) are copied.
* @returns {Node}
* Returns the newly created copy of the node.
* @throws {DOMException}
* May throw a DOMException if operations within {@link Element#setAttributeNode} or
* {@link Node#appendChild} (which are potentially invoked in this method) do not meet their
* specific constraints.
* @see {@link cloneNode}
*/
cloneNode: function (deep) {
return cloneNode(this.ownerDocument || this, this, deep);
},
/**
* Puts the specified node and all of its subtree into a "normalized" form. In a normalized
* subtree, no text nodes in the subtree are empty and there are no adjacent text nodes.
*
* Specifically, this method merges any adjacent text nodes (i.e., nodes for which `nodeType`
* is `TEXT_NODE`) into a single node with the combined data. It also removes any empty text
* nodes.
*
* This method operates recursively, so it also normalizes any and all descendent nodes within
* the subtree.
*
* @throws {DOMException}
* May throw a DOMException if operations within removeChild or appendData (which are
* potentially invoked in this method) do not meet their specific constraints.
* @since Modified in DOM Level 2
* @see {@link Node.removeChild}
* @see {@link CharacterData.appendData}
*/
normalize: function () {
var child = this.firstChild;
while (child) {
var next = child.nextSibling;
if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
this.removeChild(next);
child.appendData(next.data);
} else {
child.normalize();
child = next;
}
}
},
/**
* Checks whether the DOM implementation implements a specific feature and its version.
*
* @deprecated
* Since `DOMImplementation.hasFeature` is deprecated and always returns true.
* @param {string} feature
* The package name of the feature to test. This is the same name that can be passed to the
* method `hasFeature` on `DOMImplementation`.
* @param {string} version
* This is the version number of the package name to test.
* @returns {boolean}
* Returns true in all cases in the current implementation.
* @since Introduced in DOM Level 2
* @see {@link DOMImplementation.hasFeature}
*/
isSupported: function (feature, version) {
return this.ownerDocument.implementation.hasFeature(feature, version);
},
/**
* Look up the prefix associated to the given namespace URI, starting from this node.
* **The default namespace declarations are ignored by this method.**
* See Namespace Prefix Lookup for details on the algorithm used by this method.
*
* **This behavior is different from the in the specs**:
* - no node type specific handling
* - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
*
* @param {string | null} namespaceURI
* The namespace URI for which to find the associated prefix.
* @returns {string | null}
* The associated prefix, if found; otherwise, null.
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix
* @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo
* @see https://dom.spec.whatwg.org/#dom-node-lookupprefix
* @see https://github.com/xmldom/xmldom/issues/322
* @prettierignore
*/
lookupPrefix: function (namespaceURI) {
var el = this;
while (el) {
var map = el._nsMap;
//console.dir(map)
if (map) {
for (var n in map) {
if (hasOwn(map, n) && map[n] === namespaceURI) {
return n;
}
}
}
el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
}
return null;
},
/**
* This function is used to look up the namespace URI associated with the given prefix,
* starting from this node.
*
* **This behavior is different from the in the specs**:
* - no node type specific handling
* - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
*
* @param {string | null} prefix
* The prefix for which to find the associated namespace URI.
* @returns {string | null}
* The associated namespace URI, if found; otherwise, null.
* @since DOM Level 3
* @see https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI
* @prettierignore
*/
lookupNamespaceURI: function (prefix) {
var el = this;
while (el) {
var map = el._nsMap;
//console.dir(map)
if (map) {
if (hasOwn(map, prefix)) {
return map[prefix];
}
}
el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
}
return null;
},
/**
* Determines whether the given namespace URI is the default namespace.
*
* The function works by looking up the prefix associated with the given namespace URI. If no
* prefix is found (i.e., the namespace URI is not registered in the namespace map of this
* node or any of its ancestors), it returns `true`, implying the namespace URI is considered
* the default.
*
* **This behavior is different from the in the specs**:
* - no node type specific handling
* - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
*
* @param {string | null} namespaceURI
* The namespace URI to be checked.
* @returns {boolean}
* Returns true if the given namespace URI is the default namespace, false otherwise.
* @since DOM Level 3
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace
* @see https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
* @prettierignore
*/
isDefaultNamespace: function (namespaceURI) {
var prefix = this.lookupPrefix(namespaceURI);
return prefix == null;
},
/**
* Compares the reference node with a node with regard to their position in the document and
* according to the document order.
*
* @param {Node} other
* The node to compare the reference node to.
* @returns {number}
* Returns how the node is positioned relatively to the reference node according to the
* bitmask. 0 if reference node and given node are the same.
* @since DOM Level 3
* @see https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-compare
* @see https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
*/
compareDocumentPosition: function (other) {
if (this === other) return 0;
var node1 = other;
var node2 = this;
var attr1 = null;
var attr2 = null;
if (node1 instanceof Attr) {
attr1 = node1;
node1 = attr1.ownerElement;
}
if (node2 instanceof Attr) {
attr2 = node2;
node2 = attr2.ownerElement;
if (attr1 && node1 && node2 === node1) {
for (var i = 0, attr; (attr = node2.attributes[i]); i++) {
if (attr === attr1)
return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
if (attr === attr2)
return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
}
}
}
if (!node1 || !node2 || node2.ownerDocument !== node1.ownerDocument) {
return (
DocumentPosition.DOCUMENT_POSITION_DISCONNECTED +
DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
(docGUID(node2.ownerDocument) > docGUID(node1.ownerDocument)
? DocumentPosition.DOCUMENT_POSITION_FOLLOWING
: DocumentPosition.DOCUMENT_POSITION_PRECEDING)
);
}
if (attr2 && node1 === node2) {
return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
}
if (attr1 && node1 === node2) {
return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
}
var chain1 = [];
var ancestor1 = node1.parentNode;
while (ancestor1) {
if (!attr2 && ancestor1 === node2) {
return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
}
chain1.push(ancestor1);
ancestor1 = ancestor1.parentNode;
}
chain1.reverse();
var chain2 = [];
var ancestor2 = node2.parentNode;
while (ancestor2) {
if (!attr1 && ancestor2 === node1) {
return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
}
chain2.push(ancestor2);
ancestor2 = ancestor2.parentNode;
}
chain2.reverse();
var ca = commonAncestor(chain1, chain2);
for (var n in ca.childNodes) {
var child = ca.childNodes[n];
if (child === node2) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
if (child === node1) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
if (chain2.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
if (chain1.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
}
return 0;
},
};
/**
* Encodes special XML characters to their corresponding entities.
*
* @param {string} c
* The character to be encoded.
* @returns {string}
* The encoded character.
* @private
*/
function _xmlEncoder(c) {
return (
(c == '<' && '&lt;') || (c == '>' && '&gt;') || (c == '&' && '&amp;') || (c == '"' && '&quot;') || '&#' + c.charCodeAt() + ';'
);
}
copy(NodeType, Node);
copy(NodeType, Node.prototype);
copy(DocumentPosition, Node);
copy(DocumentPosition, Node.prototype);
/**
* @param callback
* Return true for continue,false for break.
* @returns
* boolean true: break visit;
*/
function _visitNode(node, callback) {
if (callback(node)) {
return true;
}
if ((node = node.firstChild)) {
do {
if (_visitNode(node, callback)) {
return true;
}
} while ((node = node.nextSibling));
}
}
/**
* @typedef DocumentOptions
* @property {string} [contentType=MIME_TYPE.XML_APPLICATION]
*/
/**
* The Document interface describes the common properties and methods for any kind of document.
*
* It should usually be created using `new DOMImplementation().createDocument(...)`
* or `new DOMImplementation().createHTMLDocument(...)`.
*
* The constructor is considered a private API and offers to initially set the `contentType`
* property via it's options parameter.
*
* @class
* @param {Symbol} symbol
* @param {DocumentOptions} [options]
* @augments Node
* @private
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document
* @see https://dom.spec.whatwg.org/#interface-document
*/
function Document(symbol, options) {
checkSymbol(symbol);
var opt = options || {};
this.ownerDocument = this;
/**
* The mime type of the document is determined at creation time and can not be modified.
*
* @type {string}
* @see https://dom.spec.whatwg.org/#concept-document-content-type
* @see {@link DOMImplementation}
* @see {@link MIME_TYPE}
* @readonly
*/
this.contentType = opt.contentType || MIME_TYPE.XML_APPLICATION;
/**
* @type {'html' | 'xml'}
* @see https://dom.spec.whatwg.org/#concept-document-type
* @see {@link DOMImplementation}
* @readonly
*/
this.type = isHTMLMimeType(this.contentType) ? 'html' : 'xml';
}
/**
* Updates the namespace mapping of an element when a new attribute is added.
*
* @param {Document} doc
* The document that the element belongs to.
* @param {Element} el
* The element to which the attribute is being added.
* @param {Attr} newAttr
* The new attribute being added.
* @private
*/
function _onAddAttribute(doc, el, newAttr) {
doc && doc._inc++;
var ns = newAttr.namespaceURI;
if (ns === NAMESPACE.XMLNS) {
//update namespace
el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
}
}
/**
* Updates the namespace mapping of an element when an attribute is removed.
*
* @param {Document} doc
* The document that the element belongs to.
* @param {Element} el
* The element from which the attribute is being removed.
* @param {Attr} newAttr
* The attribute being removed.
* @param {boolean} remove
* Indicates whether the attribute is to be removed.
* @private
*/
function _onRemoveAttribute(doc, el, newAttr, remove) {
doc && doc._inc++;
var ns = newAttr.namespaceURI;
if (ns === NAMESPACE.XMLNS) {
//update namespace
delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
}
}
/**
* Updates `parent.childNodes`, adjusting the indexed items and its `length`.
* If `newChild` is provided and has no nextSibling, it will be appended.
* Otherwise, it's assumed that an item has been removed or inserted,
* and `parent.firstNode` and its `.nextSibling` to re-indexing all child nodes of `parent`.
*
* @param {Document} doc
* The parent document of `el`.
* @param {Node} parent
* The parent node whose childNodes list needs to be updated.
* @param {Node} [newChild]
* The new child node to be appended. If not provided, the function assumes a node has been
* removed.
* @private
*/
function _onUpdateChild(doc, parent, newChild) {
if (doc && doc._inc) {
doc._inc++;
var childNodes = parent.childNodes;
// assumes nextSibling and previousSibling were already configured upfront
if (newChild && !newChild.nextSibling) {
// if an item has been appended, we only need to update the last index and the length
childNodes[childNodes.length++] = newChild;
} else {
// otherwise we need to reindex all items,
// which can take a while when processing nodes with a lot of children
var child = parent.firstChild;
var i = 0;
while (child) {
childNodes[i++] = child;
child = child.nextSibling;
}
childNodes.length = i;
delete childNodes[childNodes.length];
}
}
}
/**
* Removes the connections between `parentNode` and `child`
* and any existing `child.previousSibling` or `child.nextSibling`.
*
* @param {Node} parentNode
* The parent node from which the child node is to be removed.
* @param {Node} child
* The child node to be removed from the parentNode.
* @returns {Node}
* Returns the child node that was removed.
* @throws {DOMException}
* With code:
* - {@link DOMException.NOT_FOUND_ERR} If the parentNode is not the parent of the child node.
* @private
* @see https://github.com/xmldom/xmldom/issues/135
* @see https://github.com/xmldom/xmldom/issues/145
*/
function _removeChild(parentNode, child) {
if (parentNode !== child.parentNode) {
throw new DOMException(DOMException.NOT_FOUND_ERR, "child's parent is not parent");
}
var oldPreviousSibling = child.previousSibling;
var oldNextSibling = child.nextSibling;
if (oldPreviousSibling) {
oldPreviousSibling.nextSibling = oldNextSibling;
} else {
parentNode.firstChild = oldNextSibling;
}
if (oldNextSibling) {
oldNextSibling.previousSibling = oldPreviousSibling;
} else {
parentNode.lastChild = oldPreviousSibling;
}
_onUpdateChild(parentNode.ownerDocument, parentNode);
child.parentNode = null;
child.previousSibling = null;
child.nextSibling = null;
return child;
}
/**
* Returns `true` if `node` can be a parent for insertion.
*
* @param {Node} node
* @returns {boolean}
*/
function hasValidParentNodeType(node) {
return (
node &&
(node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.ELEMENT_NODE)
);
}
/**
* Returns `true` if `node` can be inserted according to it's `nodeType`.
*
* @param {Node} node
* @returns {boolean}
*/
function hasInsertableNodeType(node) {
return (
node &&
(node.nodeType === Node.CDATA_SECTION_NODE ||
node.nodeType === Node.COMMENT_NODE ||
node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ||
node.nodeType === Node.DOCUMENT_TYPE_NODE ||
node.nodeType === Node.ELEMENT_NODE ||
node.nodeType === Node.PROCESSING_INSTRUCTION_NODE ||
node.nodeType === Node.TEXT_NODE)
);
}
/**
* Returns true if `node` is a DOCTYPE node.
*
* @param {Node} node
* @returns {boolean}
*/
function isDocTypeNode(node) {
return node && node.nodeType === Node.DOCUMENT_TYPE_NODE;
}
/**
* Returns true if the node is an element.
*
* @param {Node} node
* @returns {boolean}
*/
function isElementNode(node) {
return node && node.nodeType === Node.ELEMENT_NODE;
}
/**
* Returns true if `node` is a text node.
*
* @param {Node} node
* @returns {boolean}
*/
function isTextNode(node) {
return node && node.nodeType === Node.TEXT_NODE;
}
/**
* Check if en element node can be inserted before `child`, or at the end if child is falsy,
* according to the presence and position of a doctype node on the same level.
*
* @param {Document} doc
* The document node.
* @param {Node} child
* The node that would become the nextSibling if the element would be inserted.
* @returns {boolean}
* `true` if an element can be inserted before child.
* @private
*/
function isElementInsertionPossible(doc, child) {
var parentChildNodes = doc.childNodes || [];
if (find(parentChildNodes, isElementNode) || isDocTypeNode(child)) {
return false;
}
var docTypeNode = find(parentChildNodes, isDocTypeNode);
return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
}
/**
* Check if en element node can be inserted before `child`, or at the end if child is falsy,
* according to the presence and position of a doctype node on the same level.
*
* @param {Node} doc
* The document node.
* @param {Node} child
* The node that would become the nextSibling if the element would be inserted.
* @returns {boolean}
* `true` if an element can be inserted before child.
* @private
*/
function isElementReplacementPossible(doc, child) {
var parentChildNodes = doc.childNodes || [];
function hasElementChildThatIsNotChild(node) {
return isElementNode(node) && node !== child;
}
if (find(parentChildNodes, hasElementChildThatIsNotChild)) {
return false;
}
var docTypeNode = find(parentChildNodes, isDocTypeNode);
return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
}
/**
* Asserts pre-insertion validity of a node into a parent before a child.
* Throws errors for invalid node combinations that would result in an ill-formed DOM.
*
* @param {Node} parent
* The parent node to insert `node` into.
* @param {Node} node
* The node to insert.
* @param {Node | null} child
* The node that should become the `nextSibling` of `node`. If null, no sibling is considered.
* @throws {DOMException}
* With code:
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `parent` is not a Document,
* DocumentFragment, or Element node.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a host-including inclusive
* ancestor of `parent`. (Currently not implemented)
* - {@link DOMException.NOT_FOUND_ERR} If `child` is non-null and its `parent` is not
* `parent`.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is not a DocumentFragment,
* DocumentType, Element, or CharacterData node.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If either `node` is a Text node and `parent` is
* a document, or if `node` is a doctype and `parent` is not a document.
* @private
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
* @see https://dom.spec.whatwg.org/#concept-node-replace
*/
function assertPreInsertionValidity1to5(parent, node, child) {
// 1. If `parent` is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
if (!hasValidParentNodeType(parent)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Unexpected parent node type ' + parent.nodeType);
}
// 2. If `node` is a host-including inclusive ancestor of `parent`, then throw a "HierarchyRequestError" DOMException.
// not implemented!
// 3. If `child` is non-null and its parent is not `parent`, then throw a "NotFoundError" DOMException.
if (child && child.parentNode !== parent) {
throw new DOMException(DOMException.NOT_FOUND_ERR, 'child not in parent');
}
if (
// 4. If `node` is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
!hasInsertableNodeType(node) ||
// 5. If either `node` is a Text node and `parent` is a document,
// the sax parser currently adds top level text nodes, this will be fixed in 0.9.0
// || (node.nodeType === Node.TEXT_NODE && parent.nodeType === Node.DOCUMENT_NODE)
// or `node` is a doctype and `parent` is not a document, then throw a "HierarchyRequestError" DOMException.
(isDocTypeNode(node) && parent.nodeType !== Node.DOCUMENT_NODE)
) {
throw new DOMException(
DOMException.HIERARCHY_REQUEST_ERR,
'Unexpected node type ' + node.nodeType + ' for parent node type ' + parent.nodeType
);
}
}
/**
* Asserts pre-insertion validity of a node into a document before a child.
* Throws errors for invalid node combinations that would result in an ill-formed DOM.
*
* @param {Document} parent
* The parent node to insert `node` into.
* @param {Node} node
* The node to insert.
* @param {Node | undefined} child
* The node that should become the `nextSibling` of `node`. If undefined, no sibling is
* considered.
* @returns {Node}
* @throws {DOMException}
* With code:
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with more than
* one element child or has a Text node child.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with one
* element child and either `parent` has an element child, `child` is a doctype, or `child` is
* non-null and a doctype is following `child`.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is an Element and `parent` has an
* element child, `child` is a doctype, or `child` is non-null and a doctype is following
* `child`.
* - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentType and `parent` has a
* doctype child, `child` is non-null and an element is preceding `child`, or `child` is null
* and `parent` has an element child.
* @private
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
* @see https://dom.spec.whatwg.org/#concept-node-replace
*/
function assertPreInsertionValidityInDocument(parent, node, child) {
var parentChildNodes = parent.childNodes || [];
var nodeChildNodes = node.childNodes || [];
// DocumentFragment
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
var nodeChildElements = nodeChildNodes.filter(isElementNode);
// If node has more than one element child or has a Text node child.
if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
}
// Otherwise, if `node` has one element child and either `parent` has an element child,
// `child` is a doctype, or `child` is non-null and a doctype is following `child`.
if (nodeChildElements.length === 1 && !isElementInsertionPossible(parent, child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
}
}
// Element
if (isElementNode(node)) {
// `parent` has an element child, `child` is a doctype,
// or `child` is non-null and a doctype is following `child`.
if (!isElementInsertionPossible(parent, child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
}
}
// DocumentType
if (isDocTypeNode(node)) {
// `parent` has a doctype child,
if (find(parentChildNodes, isDocTypeNode)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
}
var parentElementChild = find(parentChildNodes, isElementNode);
// `child` is non-null and an element is preceding `child`,
if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
}
// or `child` is null and `parent` has an element child.
if (!child && parentElementChild) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can not be appended since element is present');
}
}
}
/**
* @param {Document} parent
* The parent node to insert `node` into.
* @param {Node} node
* The node to insert.
* @param {Node | undefined} child
* the node that should become the `nextSibling` of `node`
* @returns {Node}
* @throws {DOMException}
* For several node combinations that would create a DOM that is not well-formed.
* @throws {DOMException}
* If `child` is provided but is not a child of `parent`.
* @private
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
* @see https://dom.spec.whatwg.org/#concept-node-replace
*/
function assertPreReplacementValidityInDocument(parent, node, child) {
var parentChildNodes = parent.childNodes || [];
var nodeChildNodes = node.childNodes || [];
// DocumentFragment
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
var nodeChildElements = nodeChildNodes.filter(isElementNode);
// If `node` has more than one element child or has a Text node child.
if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
}
// Otherwise, if `node` has one element child and either `parent` has an element child that is not `child` or a doctype is following `child`.
if (nodeChildElements.length === 1 && !isElementReplacementPossible(parent, child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
}
}
// Element
if (isElementNode(node)) {
// `parent` has an element child that is not `child` or a doctype is following `child`.
if (!isElementReplacementPossible(parent, child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
}
}
// DocumentType
if (isDocTypeNode(node)) {
function hasDoctypeChildThatIsNotChild(node) {
return isDocTypeNode(node) && node !== child;
}
// `parent` has a doctype child that is not `child`,
if (find(parentChildNodes, hasDoctypeChildThatIsNotChild)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
}
var parentElementChild = find(parentChildNodes, isElementNode);
// or an element is preceding `child`.
if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
}
}
}
/**
* Inserts a node into a parent node before a child node.
*
* @param {Node} parent
* The parent node to insert the node into.
* @param {Node} node
* The node to insert into the parent.
* @param {Node | null} child
* The node that should become the next sibling of the node.
* If null, the function inserts the node at the end of the children of the parent node.
* @param {Function} [_inDocumentAssertion]
* An optional function to check pre-insertion validity if parent is a document node.
* Defaults to {@link assertPreInsertionValidityInDocument}
* @returns {Node}
* Returns the inserted node.
* @throws {DOMException}
* Throws a DOMException if inserting the node would result in a DOM tree that is not
* well-formed. See {@link assertPreInsertionValidity1to5},
* {@link assertPreInsertionValidityInDocument}.
* @throws {DOMException}
* Throws a DOMException if child is provided but is not a child of the parent. See
* {@link Node.removeChild}
* @private
* @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
*/
function _insertBefore(parent, node, child, _inDocumentAssertion) {
// To ensure pre-insertion validity of a node into a parent before a child, run these steps:
assertPreInsertionValidity1to5(parent, node, child);
// If parent is a document, and any of the statements below, switched on the interface node implements,
// are true, then throw a "HierarchyRequestError" DOMException.
if (parent.nodeType === Node.DOCUMENT_NODE) {
(_inDocumentAssertion || assertPreInsertionValidityInDocument)(parent, node, child);
}
var cp = node.parentNode;
if (cp) {
cp.removeChild(node); //remove and update
}
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var newFirst = node.firstChild;
if (newFirst == null) {
return node;
}
var newLast = node.lastChild;
} else {
newFirst = newLast = node;
}
var pre = child ? child.previousSibling : parent.lastChild;
newFirst.previousSibling = pre;
newLast.nextSibling = child;
if (pre) {
pre.nextSibling = newFirst;
} else {
parent.firstChild = newFirst;
}
if (child == null) {
parent.lastChild = newLast;
} else {
child.previousSibling = newLast;
}
do {
newFirst.parentNode = parent;
} while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
_onUpdateChild(parent.ownerDocument || parent, parent, node);
if (node.nodeType == DOCUMENT_FRAGMENT_NODE) {
node.firstChild = node.lastChild = null;
}
return node;
}
Document.prototype = {
/**
* The implementation that created this document.
*
* @type DOMImplementation
* @readonly
*/
implementation: null,
nodeName: '#document',
nodeType: DOCUMENT_NODE,
/**
* The DocumentType node of the document.
*
* @type DocumentType
* @readonly
*/
doctype: null,
documentElement: null,
_inc: 1,
insertBefore: function (newChild, refChild) {
//raises
if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
var child = newChild.firstChild;
while (child) {
var next = child.nextSibling;
this.insertBefore(child, refChild);
child = next;
}
return newChild;
}
_insertBefore(this, newChild, refChild);
newChild.ownerDocument = this;
if (this.documentElement === null && newChild.nodeType === ELEMENT_NODE) {
this.documentElement = newChild;
}
return newChild;
},
removeChild: function (oldChild) {
var removed = _removeChild(this, oldChild);
if (removed === this.documentElement) {
this.documentElement = null;
}
return removed;
},
replaceChild: function (newChild, oldChild) {
//raises
_insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
newChild.ownerDocument = this;
if (oldChild) {
this.removeChild(oldChild);
}
if (isElementNode(newChild)) {
this.documentElement = newChild;
}
},
// Introduced in DOM Level 2:
importNode: function (importedNode, deep) {
return importNode(this, importedNode, deep);
},
// Introduced in DOM Level 2:
getElementById: function (id) {
var rtv = null;
_visitNode(this.documentElement, function (node) {
if (node.nodeType == ELEMENT_NODE) {
if (node.getAttribute('id') == id) {
rtv = node;
return true;
}
}
});
return rtv;
},
/**
* Creates a new `Element` that is owned by this `Document`.
* In HTML Documents `localName` is the lower cased `tagName`,
* otherwise no transformation is being applied.
* When `contentType` implies the HTML namespace, it will be set as `namespaceURI`.
*
* __This implementation differs from the specification:__ - The provided name is not checked
* against the `Name` production,
* so no related error will be thrown.
* - There is no interface `HTMLElement`, it is always an `Element`.
* - There is no support for a second argument to indicate using custom elements.
*
* @param {string} tagName
* @returns {Element}
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
* @see https://dom.spec.whatwg.org/#dom-document-createelement
* @see https://dom.spec.whatwg.org/#concept-create-element
*/
createElement: function (tagName) {
var node = new Element(PDC);
node.ownerDocument = this;
if (this.type === 'html') {
tagName = tagName.toLowerCase();
}
if (hasDefaultHTMLNamespace(this.contentType)) {
node.namespaceURI = NAMESPACE.HTML;
}
node.nodeName = tagName;
node.tagName = tagName;
node.localName = tagName;
node.childNodes = new NodeList();
var attrs = (node.attributes = new NamedNodeMap());
attrs._ownerElement = node;
return node;
},
/**
* @returns {DocumentFragment}
*/
createDocumentFragment: function () {
var node = new DocumentFragment(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
return node;
},
/**
* @param {string} data
* @returns {Text}
*/
createTextNode: function (data) {
var node = new Text(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.appendData(data);
return node;
},
/**
* @param {string} data
* @returns {Comment}
*/
createComment: function (data) {
var node = new Comment(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.appendData(data);
return node;
},
/**
* @param {string} data
* @returns {CDATASection}
*/
createCDATASection: function (data) {
var node = new CDATASection(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.appendData(data);
return node;
},
/**
* @param {string} target
* @param {string} data
* @returns {ProcessingInstruction}
*/
createProcessingInstruction: function (target, data) {
var node = new ProcessingInstruction(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.nodeName = node.target = target;
node.nodeValue = node.data = data;
return node;
},
/**
* Creates an `Attr` node that is owned by this document.
* In HTML Documents `localName` is the lower cased `name`,
* otherwise no transformation is being applied.
*
* __This implementation differs from the specification:__ - The provided name is not checked
* against the `Name` production,
* so no related error will be thrown.
*
* @param {string} name
* @returns {Attr}
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createAttribute
* @see https://dom.spec.whatwg.org/#dom-document-createattribute
*/
createAttribute: function (name) {
if (!g.QName_exact.test(name)) {
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in name "' + name + '"');
}
if (this.type === 'html') {
name = name.toLowerCase();
}
return this._createAttribute(name);
},
_createAttribute: function (name) {
var node = new Attr(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.name = name;
node.nodeName = name;
node.localName = name;
node.specified = true;
return node;
},
/**
* Creates an EntityReference object.
* The current implementation does not fill the `childNodes` with those of the corresponding
* `Entity`
*
* @deprecated
* In DOM Level 4.
* @param {string} name
* The name of the entity to reference. No namespace well-formedness checks are performed.
* @returns {EntityReference}
* @throws {DOMException}
* With code `INVALID_CHARACTER_ERR` when `name` is not valid.
* @throws {DOMException}
* with code `NOT_SUPPORTED_ERR` when the document is of type `html`
* @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-392B75AE
*/
createEntityReference: function (name) {
if (!g.Name.test(name)) {
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'not a valid xml name "' + name + '"');
}
if (this.type === 'html') {
throw new DOMException('document is an html document', DOMExceptionName.NotSupportedError);
}
var node = new EntityReference(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.nodeName = name;
return node;
},
// Introduced in DOM Level 2:
/**
* @param {string} namespaceURI
* @param {string} qualifiedName
* @returns {Element}
*/
createElementNS: function (namespaceURI, qualifiedName) {
var validated = validateAndExtract(namespaceURI, qualifiedName);
var node = new Element(PDC);
var attrs = (node.attributes = new NamedNodeMap());
node.childNodes = new NodeList();
node.ownerDocument = this;
node.nodeName = qualifiedName;
node.tagName = qualifiedName;
node.namespaceURI = validated[0];
node.prefix = validated[1];
node.localName = validated[2];
attrs._ownerElement = node;
return node;
},
// Introduced in DOM Level 2:
/**
* @param {string} namespaceURI
* @param {string} qualifiedName
* @returns {Attr}
*/
createAttributeNS: function (namespaceURI, qualifiedName) {
var validated = validateAndExtract(namespaceURI, qualifiedName);
var node = new Attr(PDC);
node.ownerDocument = this;
node.childNodes = new NodeList();
node.nodeName = qualifiedName;
node.name = qualifiedName;
node.specified = true;
node.namespaceURI = validated[0];
node.prefix = validated[1];
node.localName = validated[2];
return node;
},
};
_extends(Document, Node);
function Element(symbol) {
checkSymbol(symbol);
this._nsMap = Object.create(null);
}
Element.prototype = {
nodeType: ELEMENT_NODE,
/**
* The attributes of this element.
*
* @type {NamedNodeMap | null}
*/
attributes: null,
getQualifiedName: function () {
return this.prefix ? this.prefix + ':' + this.localName : this.localName;
},
_isInHTMLDocumentAndNamespace: function () {
return this.ownerDocument.type === 'html' && this.namespaceURI === NAMESPACE.HTML;
},
/**
* Implementaton of Level2 Core function hasAttributes.
*
* @returns {boolean}
* True if attribute list is not empty.
* @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-NodeHasAttrs
*/
hasAttributes: function () {
return !!(this.attributes && this.attributes.length);
},
hasAttribute: function (name) {
return !!this.getAttributeNode(name);
},
/**
* Returns elements first attribute whose qualified name is `name`, and `null`
* if there is no such attribute.
*
* @param {string} name
* @returns {string | null}
*/
getAttribute: function (name) {
var attr = this.getAttributeNode(name);
return attr ? attr.value : null;
},
getAttributeNode: function (name) {
if (this._isInHTMLDocumentAndNamespace()) {
name = name.toLowerCase();
}
return this.attributes.getNamedItem(name);
},
/**
* Sets the value of elements first attribute whose qualified name is qualifiedName to value.
*
* @param {string} name
* @param {string} value
*/
setAttribute: function (name, value) {
if (this._isInHTMLDocumentAndNamespace()) {
name = name.toLowerCase();
}
var attr = this.getAttributeNode(name);
if (attr) {
attr.value = attr.nodeValue = '' + value;
} else {
attr = this.ownerDocument._createAttribute(name);
attr.value = attr.nodeValue = '' + value;
this.setAttributeNode(attr);
}
},
removeAttribute: function (name) {
var attr = this.getAttributeNode(name);
attr && this.removeAttributeNode(attr);
},
setAttributeNode: function (newAttr) {
return this.attributes.setNamedItem(newAttr);
},
setAttributeNodeNS: function (newAttr) {
return this.attributes.setNamedItemNS(newAttr);
},
removeAttributeNode: function (oldAttr) {
//console.log(this == oldAttr.ownerElement)
return this.attributes.removeNamedItem(oldAttr.nodeName);
},
//get real attribute name,and remove it by removeAttributeNode
removeAttributeNS: function (namespaceURI, localName) {
var old = this.getAttributeNodeNS(namespaceURI, localName);
old && this.removeAttributeNode(old);
},
hasAttributeNS: function (namespaceURI, localName) {
return this.getAttributeNodeNS(namespaceURI, localName) != null;
},
/**
* Returns elements attribute whose namespace is `namespaceURI` and local name is
* `localName`,
* or `null` if there is no such attribute.
*
* @param {string} namespaceURI
* @param {string} localName
* @returns {string | null}
*/
getAttributeNS: function (namespaceURI, localName) {
var attr = this.getAttributeNodeNS(namespaceURI, localName);
return attr ? attr.value : null;
},
/**
* Sets the value of elements attribute whose namespace is `namespaceURI` and local name is
* `localName` to value.
*
* @param {string} namespaceURI
* @param {string} qualifiedName
* @param {string} value
* @see https://dom.spec.whatwg.org/#dom-element-setattributens
*/
setAttributeNS: function (namespaceURI, qualifiedName, value) {
var validated = validateAndExtract(namespaceURI, qualifiedName);
var localName = validated[2];
var attr = this.getAttributeNodeNS(namespaceURI, localName);
if (attr) {
attr.value = attr.nodeValue = '' + value;
} else {
attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
attr.value = attr.nodeValue = '' + value;
this.setAttributeNode(attr);
}
},
getAttributeNodeNS: function (namespaceURI, localName) {
return this.attributes.getNamedItemNS(namespaceURI, localName);
},
/**
* Returns a LiveNodeList of all child elements which have **all** of the given class name(s).
*
* Returns an empty list if `classNames` is an empty string or only contains HTML white space
* characters.
*
* Warning: This returns a live LiveNodeList.
* Changes in the DOM will reflect in the array as the changes occur.
* If an element selected by this array no longer qualifies for the selector,
* it will automatically be removed. Be aware of this for iteration purposes.
*
* @param {string} classNames
* Is a string representing the class name(s) to match; multiple class names are separated by
* (ASCII-)whitespace.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
* @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname
*/
getElementsByClassName: function (classNames) {
var classNamesSet = toOrderedSet(classNames);
return new LiveNodeList(this, function (base) {
var ls = [];
if (classNamesSet.length > 0) {
_visitNode(base, function (node) {
if (node !== base && node.nodeType === ELEMENT_NODE) {
var nodeClassNames = node.getAttribute('class');
// can be null if the attribute does not exist
if (nodeClassNames) {
// before splitting and iterating just compare them for the most common case
var matches = classNames === nodeClassNames;
if (!matches) {
var nodeClassNamesSet = toOrderedSet(nodeClassNames);
matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet));
}
if (matches) {
ls.push(node);
}
}
}
});
}
return ls;
});
},
/**
* Returns a LiveNodeList of elements with the given qualifiedName.
* Searching for all descendants can be done by passing `*` as `qualifiedName`.
*
* All descendants of the specified element are searched, but not the element itself.
* The returned list is live, which means it updates itself with the DOM tree automatically.
* Therefore, there is no need to call `Element.getElementsByTagName()`
* with the same element and arguments repeatedly if the DOM changes in between calls.
*
* When called on an HTML element in an HTML document,
* `getElementsByTagName` lower-cases the argument before searching for it.
* This is undesirable when trying to match camel-cased SVG elements (such as
* `<linearGradient>`) in an HTML document.
* Instead, use `Element.getElementsByTagNameNS()`,
* which preserves the capitalization of the tag name.
*
* `Element.getElementsByTagName` is similar to `Document.getElementsByTagName()`,
* except that it only searches for elements that are descendants of the specified element.
*
* @param {string} qualifiedName
* @returns {LiveNodeList}
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
* @see https://dom.spec.whatwg.org/#concept-getelementsbytagname
*/
getElementsByTagName: function (qualifiedName) {
var isHTMLDocument = (this.nodeType === DOCUMENT_NODE ? this : this.ownerDocument).type === 'html';
var lowerQualifiedName = qualifiedName.toLowerCase();
return new LiveNodeList(this, function (base) {
var ls = [];
_visitNode(base, function (node) {
if (node === base || node.nodeType !== ELEMENT_NODE) {
return;
}
if (qualifiedName === '*') {
ls.push(node);
} else {
var nodeQualifiedName = node.getQualifiedName();
var matchingQName = isHTMLDocument && node.namespaceURI === NAMESPACE.HTML ? lowerQualifiedName : qualifiedName;
if (nodeQualifiedName === matchingQName) {
ls.push(node);
}
}
});
return ls;
});
},
getElementsByTagNameNS: function (namespaceURI, localName) {
return new LiveNodeList(this, function (base) {
var ls = [];
_visitNode(base, function (node) {
if (
node !== base &&
node.nodeType === ELEMENT_NODE &&
(namespaceURI === '*' || node.namespaceURI === namespaceURI) &&
(localName === '*' || node.localName == localName)
) {
ls.push(node);
}
});
return ls;
});
},
};
Document.prototype.getElementsByClassName = Element.prototype.getElementsByClassName;
Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
_extends(Element, Node);
function Attr(symbol) {
checkSymbol(symbol);
this.namespaceURI = null;
this.prefix = null;
this.ownerElement = null;
}
Attr.prototype.nodeType = ATTRIBUTE_NODE;
_extends(Attr, Node);
function CharacterData(symbol) {
checkSymbol(symbol);
}
CharacterData.prototype = {
data: '',
substringData: function (offset, count) {
return this.data.substring(offset, offset + count);
},
appendData: function (text) {
text = this.data + text;
this.nodeValue = this.data = text;
this.length = text.length;
},
insertData: function (offset, text) {
this.replaceData(offset, 0, text);
},
deleteData: function (offset, count) {
this.replaceData(offset, count, '');
},
replaceData: function (offset, count, text) {
var start = this.data.substring(0, offset);
var end = this.data.substring(offset + count);
text = start + text + end;
this.nodeValue = this.data = text;
this.length = text.length;
},
};
_extends(CharacterData, Node);
function Text(symbol) {
checkSymbol(symbol);
}
Text.prototype = {
nodeName: '#text',
nodeType: TEXT_NODE,
splitText: function (offset) {
var text = this.data;
var newText = text.substring(offset);
text = text.substring(0, offset);
this.data = this.nodeValue = text;
this.length = text.length;
var newNode = this.ownerDocument.createTextNode(newText);
if (this.parentNode) {
this.parentNode.insertBefore(newNode, this.nextSibling);
}
return newNode;
},
};
_extends(Text, CharacterData);
function Comment(symbol) {
checkSymbol(symbol);
}
Comment.prototype = {
nodeName: '#comment',
nodeType: COMMENT_NODE,
};
_extends(Comment, CharacterData);
function CDATASection(symbol) {
checkSymbol(symbol);
}
CDATASection.prototype = {
nodeName: '#cdata-section',
nodeType: CDATA_SECTION_NODE,
};
_extends(CDATASection, Text);
function DocumentType(symbol) {
checkSymbol(symbol);
}
DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
_extends(DocumentType, Node);
function Notation(symbol) {
checkSymbol(symbol);
}
Notation.prototype.nodeType = NOTATION_NODE;
_extends(Notation, Node);
function Entity(symbol) {
checkSymbol(symbol);
}
Entity.prototype.nodeType = ENTITY_NODE;
_extends(Entity, Node);
function EntityReference(symbol) {
checkSymbol(symbol);
}
EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
_extends(EntityReference, Node);
function DocumentFragment(symbol) {
checkSymbol(symbol);
}
DocumentFragment.prototype.nodeName = '#document-fragment';
DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
_extends(DocumentFragment, Node);
function ProcessingInstruction(symbol) {
checkSymbol(symbol);
}
ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
_extends(ProcessingInstruction, CharacterData);
function XMLSerializer() {}
XMLSerializer.prototype.serializeToString = function (node, nodeFilter) {
return nodeSerializeToString.call(node, nodeFilter);
};
Node.prototype.toString = nodeSerializeToString;
function nodeSerializeToString(nodeFilter) {
var buf = [];
var refNode = (this.nodeType === DOCUMENT_NODE && this.documentElement) || this;
var prefix = refNode.prefix;
var uri = refNode.namespaceURI;
if (uri && prefix == null) {
var prefix = refNode.lookupPrefix(uri);
if (prefix == null) {
var visibleNamespaces = [
{ namespace: uri, prefix: null },
//{namespace:uri,prefix:''}
];
}
}
serializeToString(this, buf, nodeFilter, visibleNamespaces);
return buf.join('');
}
function needNamespaceDefine(node, isHTML, visibleNamespaces) {
var prefix = node.prefix || '';
var uri = node.namespaceURI;
// According to [Namespaces in XML 1.0](https://www.w3.org/TR/REC-xml-names/#ns-using) ,
// and more specifically https://www.w3.org/TR/REC-xml-names/#nsc-NoPrefixUndecl :
// > In a namespace declaration for a prefix [...], the attribute value MUST NOT be empty.
// in a similar manner [Namespaces in XML 1.1](https://www.w3.org/TR/xml-names11/#ns-using)
// and more specifically https://www.w3.org/TR/xml-names11/#nsc-NSDeclared :
// > [...] Furthermore, the attribute value [...] must not be an empty string.
// so serializing empty namespace value like xmlns:ds="" would produce an invalid XML document.
if (!uri) {
return false;
}
if ((prefix === 'xml' && uri === NAMESPACE.XML) || uri === NAMESPACE.XMLNS) {
return false;
}
var i = visibleNamespaces.length;
while (i--) {
var ns = visibleNamespaces[i];
// get namespace prefix
if (ns.prefix === prefix) {
return ns.namespace !== uri;
}
}
return true;
}
/**
* Literal whitespace other than space that appear in attribute values are serialized as
* their entity references, so they will be preserved.
* (In contrast to whitespace literals in the input which are normalized to spaces).
*
* Well-formed constraint: No < in Attribute Values:
* > The replacement text of any entity referred to directly or indirectly
* > in an attribute value must not contain a <.
*
* @see https://www.w3.org/TR/xml11/#CleanAttrVals
* @see https://www.w3.org/TR/xml11/#NT-AttValue
* @see https://www.w3.org/TR/xml11/#AVNormalize
* @see https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes
* @prettierignore
*/
function addSerializedAttribute(buf, qualifiedName, value) {
buf.push(' ', qualifiedName, '="', value.replace(/[<>&"\t\n\r]/g, _xmlEncoder), '"');
}
function serializeToString(node, buf, nodeFilter, visibleNamespaces) {
if (!visibleNamespaces) {
visibleNamespaces = [];
}
var doc = node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
var isHTML = doc.type === 'html';
if (nodeFilter) {
node = nodeFilter(node);
if (node) {
if (typeof node == 'string') {
buf.push(node);
return;
}
} else {
return;
}
//buf.sort.apply(attrs, attributeSorter);
}
switch (node.nodeType) {
case ELEMENT_NODE:
var attrs = node.attributes;
var len = attrs.length;
var child = node.firstChild;
var nodeName = node.tagName;
var prefixedNodeName = nodeName;
if (!isHTML && !node.prefix && node.namespaceURI) {
var defaultNS;
// lookup current default ns from `xmlns` attribute
for (var ai = 0; ai < attrs.length; ai++) {
if (attrs.item(ai).name === 'xmlns') {
defaultNS = attrs.item(ai).value;
break;
}
}
if (!defaultNS) {
// lookup current default ns in visibleNamespaces
for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) {
var namespace = visibleNamespaces[nsi];
if (namespace.prefix === '' && namespace.namespace === node.namespaceURI) {
defaultNS = namespace.namespace;
break;
}
}
}
if (defaultNS !== node.namespaceURI) {
for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) {
var namespace = visibleNamespaces[nsi];
if (namespace.namespace === node.namespaceURI) {
if (namespace.prefix) {
prefixedNodeName = namespace.prefix + ':' + nodeName;
}
break;
}
}
}
}
buf.push('<', prefixedNodeName);
for (var i = 0; i < len; i++) {
// add namespaces for attributes
var attr = attrs.item(i);
if (attr.prefix == 'xmlns') {
visibleNamespaces.push({
prefix: attr.localName,
namespace: attr.value,
});
} else if (attr.nodeName == 'xmlns') {
visibleNamespaces.push({ prefix: '', namespace: attr.value });
}
}
for (var i = 0; i < len; i++) {
var attr = attrs.item(i);
if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
var prefix = attr.prefix || '';
var uri = attr.namespaceURI;
addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : 'xmlns', uri);
visibleNamespaces.push({ prefix: prefix, namespace: uri });
}
serializeToString(attr, buf, nodeFilter, visibleNamespaces);
}
// add namespace for current node
if (nodeName === prefixedNodeName && needNamespaceDefine(node, isHTML, visibleNamespaces)) {
var prefix = node.prefix || '';
var uri = node.namespaceURI;
addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : 'xmlns', uri);
visibleNamespaces.push({ prefix: prefix, namespace: uri });
}
// in XML elements can be closed when they have no children
var canCloseTag = !child;
if (canCloseTag && (isHTML || node.namespaceURI === NAMESPACE.HTML)) {
// in HTML (doc or ns) only void elements can be closed right away
canCloseTag = isHTMLVoidElement(nodeName);
}
if (canCloseTag) {
buf.push('/>');
} else {
buf.push('>');
//if is cdata child node
if (isHTML && isHTMLRawTextElement(nodeName)) {
while (child) {
if (child.data) {
buf.push(child.data);
} else {
serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
}
child = child.nextSibling;
}
} else {
while (child) {
serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
child = child.nextSibling;
}
}
buf.push('</', prefixedNodeName, '>');
}
// remove added visible namespaces
//visibleNamespaces.length = startVisibleNamespaces;
return;
case DOCUMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
var child = node.firstChild;
while (child) {
serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
child = child.nextSibling;
}
return;
case ATTRIBUTE_NODE:
return addSerializedAttribute(buf, node.name, node.value);
case TEXT_NODE:
/*
* The ampersand character (&) and the left angle bracket (<) must not appear in their literal form,
* except when used as markup delimiters, or within a comment, a processing instruction,
* or a CDATA section.
* If they are needed elsewhere, they must be escaped using either numeric character
* references or the strings `&amp;` and `&lt;` respectively.
* The right angle bracket (>) may be represented using the string " &gt; ",
* and must, for compatibility, be escaped using either `&gt;`,
* or a character reference when it appears in the string `]]>` in content,
* when that string is not marking the end of a CDATA section.
*
* In the content of elements, character data is any string of characters which does not
* contain the start-delimiter of any markup and does not include the CDATA-section-close
* delimiter, `]]>`.
*
* @see https://www.w3.org/TR/xml/#NT-CharData
* @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
*/
return buf.push(node.data.replace(/[<&>]/g, _xmlEncoder));
case CDATA_SECTION_NODE:
return buf.push(g.CDATA_START, node.data, g.CDATA_END);
case COMMENT_NODE:
return buf.push(g.COMMENT_START, node.data, g.COMMENT_END);
case DOCUMENT_TYPE_NODE:
var pubid = node.publicId;
var sysid = node.systemId;
buf.push(g.DOCTYPE_DECL_START, ' ', node.name);
if (pubid) {
buf.push(' ', g.PUBLIC, ' ', pubid);
if (sysid && sysid !== '.') {
buf.push(' ', sysid);
}
} else if (sysid && sysid !== '.') {
buf.push(' ', g.SYSTEM, ' ', sysid);
}
if (node.internalSubset) {
buf.push(' [', node.internalSubset, ']');
}
buf.push('>');
return;
case PROCESSING_INSTRUCTION_NODE:
return buf.push('<?', node.target, ' ', node.data, '?>');
case ENTITY_REFERENCE_NODE:
return buf.push('&', node.nodeName, ';');
//case ENTITY_NODE:
//case NOTATION_NODE:
default:
buf.push('??', node.nodeName);
}
}
function importNode(doc, node, deep) {
var node2;
switch (node.nodeType) {
case ELEMENT_NODE:
node2 = node.cloneNode(false);
node2.ownerDocument = doc;
//var attrs = node2.attributes;
//var len = attrs.length;
//for(var i=0;i<len;i++){
//node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
//}
case DOCUMENT_FRAGMENT_NODE:
break;
case ATTRIBUTE_NODE:
deep = true;
break;
//case ENTITY_REFERENCE_NODE:
//case PROCESSING_INSTRUCTION_NODE:
////case TEXT_NODE:
//case CDATA_SECTION_NODE:
//case COMMENT_NODE:
// deep = false;
// break;
//case DOCUMENT_NODE:
//case DOCUMENT_TYPE_NODE:
//cannot be imported.
//case ENTITY_NODE:
//case NOTATION_NODE
//can not hit in level3
//default:throw e;
}
if (!node2) {
node2 = node.cloneNode(false); //false
}
node2.ownerDocument = doc;
node2.parentNode = null;
if (deep) {
var child = node.firstChild;
while (child) {
node2.appendChild(importNode(doc, child, deep));
child = child.nextSibling;
}
}
return node2;
}
/**
* Creates a copy of a node from an existing one.
*
* @param {Document} doc
* The Document object representing the document that the new node will belong to.
* @param {Node} node
* The node to clone.
* @param {boolean} deep
* If true, the contents of the node are recursively copied.
* If false, only the node itself (and its attributes, if it is an element) are copied.
* @returns {Node}
* Returns the newly created copy of the node.
* @throws {DOMException}
* May throw a DOMException if operations within setAttributeNode or appendChild (which are
* potentially invoked in this function) do not meet their specific constraints.
*/
function cloneNode(doc, node, deep) {
var node2 = new node.constructor(PDC);
for (var n in node) {
if (hasOwn(node, n)) {
var v = node[n];
if (typeof v != 'object') {
if (v != node2[n]) {
node2[n] = v;
}
}
}
}
if (node.childNodes) {
node2.childNodes = new NodeList();
}
node2.ownerDocument = doc;
switch (node2.nodeType) {
case ELEMENT_NODE:
var attrs = node.attributes;
var attrs2 = (node2.attributes = new NamedNodeMap());
var len = attrs.length;
attrs2._ownerElement = node2;
for (var i = 0; i < len; i++) {
node2.setAttributeNode(cloneNode(doc, attrs.item(i), true));
}
break;
case ATTRIBUTE_NODE:
deep = true;
}
if (deep) {
var child = node.firstChild;
while (child) {
node2.appendChild(cloneNode(doc, child, deep));
child = child.nextSibling;
}
}
return node2;
}
function __set__(object, key, value) {
object[key] = value;
}
//do dynamic
try {
if (Object.defineProperty) {
Object.defineProperty(LiveNodeList.prototype, 'length', {
get: function () {
_updateLiveList(this);
return this.$$length;
},
});
Object.defineProperty(Node.prototype, 'textContent', {
get: function () {
return getTextContent(this);
},
set: function (data) {
switch (this.nodeType) {
case ELEMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
while (this.firstChild) {
this.removeChild(this.firstChild);
}
if (data || String(data)) {
this.appendChild(this.ownerDocument.createTextNode(data));
}
break;
default:
this.data = data;
this.value = data;
this.nodeValue = data;
}
},
});
function getTextContent(node) {
switch (node.nodeType) {
case ELEMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
var buf = [];
node = node.firstChild;
while (node) {
if (node.nodeType !== 7 && node.nodeType !== 8) {
buf.push(getTextContent(node));
}
node = node.nextSibling;
}
return buf.join('');
default:
return node.nodeValue;
}
}
__set__ = function (object, key, value) {
//console.log(value)
object['$$' + key] = value;
};
}
} catch (e) {
//ie8
}
exports._updateLiveList = _updateLiveList;
exports.Attr = Attr;
exports.CDATASection = CDATASection;
exports.CharacterData = CharacterData;
exports.Comment = Comment;
exports.Document = Document;
exports.DocumentFragment = DocumentFragment;
exports.DocumentType = DocumentType;
exports.DOMImplementation = DOMImplementation;
exports.Element = Element;
exports.Entity = Entity;
exports.EntityReference = EntityReference;
exports.LiveNodeList = LiveNodeList;
exports.NamedNodeMap = NamedNodeMap;
exports.Node = Node;
exports.NodeList = NodeList;
exports.Notation = Notation;
exports.Text = Text;
exports.ProcessingInstruction = ProcessingInstruction;
exports.XMLSerializer = XMLSerializer;