happy-dom
Version:
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.
992 lines • 33.1 kB
JavaScript
import Element from '../element/Element.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Node from '../node/Node.js';
import NodeIterator from '../../tree-walker/NodeIterator.js';
import TreeWalker from '../../tree-walker/TreeWalker.js';
import Event from '../../event/Event.js';
import DOMImplementation from '../../dom-implementation/DOMImplementation.js';
import NamespaceURI from '../../config/NamespaceURI.js';
import DocumentType from '../document-type/DocumentType.js';
import ParentNodeUtility from '../parent-node/ParentNodeUtility.js';
import QuerySelector from '../../query-selector/QuerySelector.js';
import HTMLCollection from '../element/HTMLCollection.js';
import DocumentReadyStateEnum from './DocumentReadyStateEnum.js';
import Selection from '../../selection/Selection.js';
import VisibilityStateEnum from './VisibilityStateEnum.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import CookieStringUtility from '../../cookie/urilities/CookieStringUtility.js';
import { URL } from 'url';
import HTMLElementConfig from '../../config/HTMLElementConfig.js';
import WindowBrowserContext from '../../window/WindowBrowserContext.js';
import NodeFactory from '../NodeFactory.js';
import SVGElementConfig from '../../config/SVGElementConfig.js';
import StringUtility from '../../utilities/StringUtility.js';
import HTMLParser from '../../html-parser/HTMLParser.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
const PROCESSING_INSTRUCTION_TARGET_REGEXP = /^[a-z][a-z0-9-]+$/;
/**
* Document.
*/
export default class Document extends Node {
// Internal properties
[PropertySymbol.children] = null;
[PropertySymbol.activeElement] = null;
[PropertySymbol.nextActiveElement] = null;
[PropertySymbol.currentScript] = null;
[PropertySymbol.rootNode] = this;
[PropertySymbol.isFirstWrite] = true;
[PropertySymbol.isFirstWriteAfterOpen] = false;
[PropertySymbol.nodeType] = NodeTypeEnum.documentNode;
[PropertySymbol.isConnected] = true;
[PropertySymbol.adoptedStyleSheets] = [];
[PropertySymbol.implementation] = new DOMImplementation(this);
[PropertySymbol.readyState] = DocumentReadyStateEnum.interactive;
[PropertySymbol.referrer] = '';
[PropertySymbol.defaultView] = null;
[PropertySymbol.forms] = null;
[PropertySymbol.affectsComputedStyleCache] = [];
[PropertySymbol.ownerDocument] = null;
[PropertySymbol.elementIdMap] = new Map();
[PropertySymbol.contentType] = 'text/html';
[PropertySymbol.xmlProcessingInstruction] = null;
[PropertySymbol.preloads] = new Map();
// Private properties
#selection = null;
// Events
onreadystatechange = null;
onpointerlockchange = null;
onpointerlockerror = null;
onbeforecopy = null;
onbeforecut = null;
onbeforepaste = null;
onfreeze = null;
onresume = null;
onsearch = null;
onvisibilitychange = null;
onfullscreenchange = null;
onfullscreenerror = null;
onwebkitfullscreenchange = null;
onwebkitfullscreenerror = null;
onbeforexrselect = null;
onabort = null;
onbeforeinput = null;
onblur = null;
oncancel = null;
oncanplay = null;
oncanplaythrough = null;
onchange = null;
onclick = null;
onclose = null;
oncontextlost = null;
oncontextmenu = null;
oncontextrestored = null;
oncuechange = null;
ondblclick = null;
ondrag = null;
ondragend = null;
ondragenter = null;
ondragleave = null;
ondragover = null;
ondragstart = null;
ondrop = null;
ondurationchange = null;
onemptied = null;
onended = null;
onerror = null;
onfocus = null;
onformdata = null;
oninput = null;
oninvalid = null;
onkeydown = null;
onkeypress = null;
onkeyup = null;
onload = null;
onloadeddata = null;
onloadedmetadata = null;
onloadstart = null;
onmousedown = null;
onmouseenter = null;
onmouseleave = null;
onmousemove = null;
onmouseout = null;
onmouseover = null;
onmouseup = null;
onmousewheel = null;
onpause = null;
onplay = null;
onplaying = null;
onprogress = null;
onratechange = null;
onreset = null;
onresize = null;
onscroll = null;
onsecuritypolicyviolation = null;
onseeked = null;
onseeking = null;
onselect = null;
onslotchange = null;
onstalled = null;
onsubmit = null;
onsuspend = null;
ontimeupdate = null;
ontoggle = null;
onvolumechange = null;
onwaiting = null;
onwebkitanimationend = null;
onwebkitanimationiteration = null;
onwebkitanimationstart = null;
onwebkittransitionend = null;
onwheel = null;
onauxclick = null;
ongotpointercapture = null;
onlostpointercapture = null;
onpointerdown = null;
onpointermove = null;
onpointerrawupdate = null;
onpointerup = null;
onpointercancel = null;
onpointerover = null;
onpointerout = null;
onpointerenter = null;
onpointerleave = null;
onselectstart = null;
onselectionchange = null;
onanimationend = null;
onanimationiteration = null;
onanimationstart = null;
ontransitionrun = null;
ontransitionstart = null;
ontransitionend = null;
ontransitioncancel = null;
oncopy = null;
oncut = null;
onpaste = null;
onbeforematch = null;
/**
* Returns adopted style sheets.
*
* @returns Adopted style sheets.
*/
get adoptedStyleSheets() {
return this[PropertySymbol.adoptedStyleSheets];
}
/**
* Sets adopted style sheets.
*
* @param value Adopted style sheets.
*/
set adoptedStyleSheets(value) {
this[PropertySymbol.adoptedStyleSheets] = value;
}
/**
* Returns DOM implementation.
*
* @returns DOM implementation.
*/
get implementation() {
return this[PropertySymbol.implementation];
}
/**
* Returns document ready state.
*
* @returns Document ready state.
*/
get readyState() {
return this[PropertySymbol.readyState];
}
/**
* Returns referrer.
*
* @returns Referrer.
*/
get referrer() {
return this[PropertySymbol.referrer];
}
/**
* Returns default view.
*
* @returns Default view.
*/
get defaultView() {
return this[PropertySymbol.defaultView];
}
/**
* Returns document children.
*/
get children() {
if (!this[PropertySymbol.children]) {
const elements = this[PropertySymbol.elementArray];
this[PropertySymbol.children] = new HTMLCollection(PropertySymbol.illegalConstructor, () => elements);
}
return this[PropertySymbol.children];
}
/**
* Returns character set.
*
* @deprecated
* @returns Character set.
*/
get charset() {
return this.characterSet;
}
/**
* Returns character set.
*
* @returns Character set.
*/
get characterSet() {
const charset = QuerySelector.querySelector(this, 'meta[charset]')?.getAttributeNS(null, 'charset');
return charset ? charset : 'UTF-8';
}
/**
* Returns title.
*
* @returns Title.
*/
get title() {
const element = ParentNodeUtility.getElementByTagName(this, 'title');
if (element) {
return element.text.trim();
}
return '';
}
/**
* Returns set title.
*
*/
set title(title) {
const element = ParentNodeUtility.getElementByTagName(this, 'title');
if (element) {
element.textContent = title;
}
else {
const newElement = this.createElement('title');
newElement.textContent = title;
this.head.appendChild(newElement);
}
}
/**
* Returns a collection of all area elements and a elements in a document with a value for the href attribute.
*/
get links() {
return QuerySelector.querySelectorAll(this, 'a[href],area[href]');
}
/**
* Returns a collection of all form elements in a document.
*/
get forms() {
if (!this[PropertySymbol.forms]) {
this[PropertySymbol.forms] = (ParentNodeUtility.getElementsByTagName(this, 'form'));
}
return this[PropertySymbol.forms];
}
/**
* Last element child.
*
* @returns Element.
*/
get childElementCount() {
return this[PropertySymbol.elementArray].length;
}
/**
* First element child.
*
* @returns Element.
*/
get firstElementChild() {
return this[PropertySymbol.elementArray][0] ?? null;
}
/**
* Last element child.
*
* @returns Element.
*/
get lastElementChild() {
const children = this[PropertySymbol.elementArray];
return children[children.length - 1] ?? null;
}
/**
* Returns cookie string.
*
* @returns Cookie.
*/
get cookie() {
const browserFrame = new WindowBrowserContext(this[PropertySymbol.window]).getBrowserFrame();
if (!browserFrame) {
return '';
}
return CookieStringUtility.cookiesToString(browserFrame.page.context.cookieContainer.getCookies(new URL(this[PropertySymbol.window].location.href), true));
}
/**
* Sets a cookie string.
*
* @param cookie Cookie string.
*/
set cookie(cookie) {
const browserFrame = new WindowBrowserContext(this[PropertySymbol.window]).getBrowserFrame();
if (!browserFrame) {
return;
}
browserFrame.page.context.cookieContainer.addCookies([
CookieStringUtility.stringToCookie(new URL(this[PropertySymbol.window].location.href), cookie)
]);
}
/**
* Node name.
*
* @returns Node name.
*/
get nodeName() {
return '#document';
}
/**
* Returns <html> element.
*
* @returns Element.
*/
get documentElement() {
return this[PropertySymbol.elementArray][0] ?? null;
}
/**
* Returns document type element.
*
* @returns Document type.
*/
get doctype() {
for (const node of this[PropertySymbol.nodeArray]) {
if (node instanceof DocumentType) {
return node;
}
}
return null;
}
/**
* Returns <body> element.
*
* @returns Element.
*/
get body() {
const documentElement = this.documentElement;
return documentElement
? ParentNodeUtility.getElementByTagName(documentElement, 'body')
: null;
}
/**
* Returns <head> element.
*
* @returns Element.
*/
get head() {
const documentElement = this.documentElement;
return documentElement
? ParentNodeUtility.getElementByTagName(documentElement, 'head')
: null;
}
/**
* Returns CSS style sheets.
*
* @returns CSS style sheets.
*/
get styleSheets() {
const styles = (QuerySelector.querySelectorAll(this, 'link[rel="stylesheet"][href],style'));
const styleSheets = [];
for (const style of styles) {
const sheet = style.sheet;
if (sheet) {
styleSheets.push(sheet);
}
}
return styleSheets;
}
/**
* Returns active element.
*
* @returns Active element.
*/
get activeElement() {
if (this[PropertySymbol.activeElement] &&
!this[PropertySymbol.activeElement][PropertySymbol.isConnected]) {
this[PropertySymbol.activeElement] = null;
}
if (this[PropertySymbol.activeElement] &&
this[PropertySymbol.activeElement] instanceof Element) {
let rootNode = (this[PropertySymbol.activeElement].getRootNode());
let activeElement = this[PropertySymbol.activeElement];
while (rootNode !== this) {
activeElement = rootNode.host;
rootNode = activeElement ? activeElement.getRootNode() : this;
}
return activeElement;
}
return this[PropertySymbol.activeElement] || this.body || this.documentElement || null;
}
/**
* Returns scrolling element.
*
* @returns Scrolling element.
*/
get scrollingElement() {
return this.documentElement;
}
/**
* Returns location.
*
* @returns Location.
*/
get location() {
return this[PropertySymbol.window].location;
}
/**
* Returns scripts.
*
* @returns Scripts.
*/
get scripts() {
return this.getElementsByTagName('script');
}
/**
* Returns base URI.
*
* @override
* @returns Base URI.
*/
get baseURI() {
const element = ParentNodeUtility.getElementByTagName(this, 'base');
if (element) {
return element.href;
}
return this[PropertySymbol.window].location.href;
}
/**
* Returns URL.
*
* @returns URL of the current document.
* */
get URL() {
return this[PropertySymbol.window].location.href;
}
/**
* Returns document URI.
*
* @returns URL of the current document.
* */
get documentURI() {
return this.URL;
}
/**
* Returns domain.
*
* @returns Domain.
* */
get domain() {
return this[PropertySymbol.window].location.hostname;
}
/**
* Returns document visibility state.
*
* @returns the visibility state of the current document.
* */
get visibilityState() {
if (this.defaultView) {
return VisibilityStateEnum.visible;
}
return VisibilityStateEnum.hidden;
}
/**
* Returns document hidden state.
*
* @returns the hidden state of the current document.
* */
get hidden() {
if (this.defaultView) {
return false;
}
return true;
}
/**
* Gets the currently executing script element.
*
* @returns the currently executing script element.
*/
get currentScript() {
return this[PropertySymbol.currentScript];
}
/**
* Returns content type.
*
* @returns Content type.
*/
get contentType() {
return this[PropertySymbol.contentType];
}
/**
* Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
append(...nodes) {
ParentNodeUtility.append(this, ...nodes);
}
/**
* Inserts a set of Node objects or DOMString objects before the first child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
*
* @param nodes List of Node or DOMString.
*/
prepend(...nodes) {
ParentNodeUtility.prepend(this, ...nodes);
}
/**
* Replaces the existing children of a node with a specified new set of children.
*
* @param nodes List of Node or DOMString.
*/
replaceChildren(...nodes) {
ParentNodeUtility.replaceChildren(this, ...nodes);
}
/**
* Query CSS selector to find matching elments.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
querySelectorAll(selector) {
return QuerySelector.querySelectorAll(this, selector);
}
/**
* Query CSS Selector to find matching node.
*
* @param selector CSS selector.
* @returns Matching element.
*/
querySelector(selector) {
return QuerySelector.querySelector(this, selector);
}
/**
* Returns true if the command is supported.
* @deprecated
* @param _ Command.
* @returns True if the command is supported, false otherwise.
*/
queryCommandSupported(_) {
if (!arguments.length) {
throw new this[PropertySymbol.window].TypeError("Failed to execute 'queryCommandSupported' on 'Document': 1 argument required, but only 0 present.");
}
return true;
}
/**
* Returns an elements by class name.
*
* @param className Tag name.
* @returns Matching element.
*/
getElementsByClassName(className) {
return ParentNodeUtility.getElementsByClassName(this, className);
}
/**
* Returns an elements by tag name.
*
* @param tagName Tag name.
* @returns Matching element.
*/
getElementsByTagName(tagName) {
return ParentNodeUtility.getElementsByTagName(this, tagName);
}
/**
* Returns an elements by tag name and namespace.
*
* @param namespaceURI Namespace URI.
* @param tagName Tag name.
* @returns Matching element.
*/
getElementsByTagNameNS(namespaceURI, tagName) {
return ParentNodeUtility.getElementsByTagNameNS(this, namespaceURI, tagName);
}
/**
* Returns an element by ID.
*
* @param id ID.
* @returns Matching element.
*/
getElementById(id) {
return ParentNodeUtility.getElementById(this, id);
}
/**
* Returns an element by Name.
*
* @returns Matching element.
* @param name
*/
getElementsByName(name) {
return QuerySelector.querySelectorAll(this, `[name="${name}"]`);
}
/**
* Replaces the document HTML with new HTML.
*
* @param html HTML.
*/
write(html) {
if (this[PropertySymbol.isFirstWrite] || this[PropertySymbol.isFirstWriteAfterOpen]) {
if (this[PropertySymbol.isFirstWrite]) {
if (!this[PropertySymbol.isFirstWriteAfterOpen]) {
this.open();
}
this[PropertySymbol.isFirstWrite] = false;
}
const { documentElement, head, body } = this;
if (!documentElement || !head || !body) {
this.open();
}
this[PropertySymbol.isFirstWrite] = false;
this[PropertySymbol.isFirstWriteAfterOpen] = false;
new HTMLParser(this[PropertySymbol.window], {
evaluateScripts: true
}).parse(html, this);
}
else {
new HTMLParser(this[PropertySymbol.window], {
evaluateScripts: true
}).parse(html, this.body);
}
}
/**
* Opens the document.
*
* @returns Document.
*/
open() {
this[PropertySymbol.isFirstWriteAfterOpen] = true;
for (const eventType of this[PropertySymbol.listeners].bubbling.keys()) {
const listeners = this[PropertySymbol.listeners].bubbling.get(eventType);
if (listeners) {
for (const listener of listeners) {
this.removeEventListener(eventType, listener);
}
}
}
for (const eventType of this[PropertySymbol.listeners].capturing.keys()) {
const listeners = this[PropertySymbol.listeners].capturing.get(eventType);
if (listeners) {
for (const listener of listeners) {
this.removeEventListener(eventType, listener);
}
}
}
const childNodes = this[PropertySymbol.nodeArray];
while (childNodes.length) {
this.removeChild(childNodes[0]);
}
// Default document elements
const doctype = this[PropertySymbol.implementation].createDocumentType('html', '', '');
const documentElement = this.createElement('html');
const bodyElement = this.createElement('body');
const headElement = this.createElement('head');
this.appendChild(doctype);
this.appendChild(documentElement);
documentElement.appendChild(headElement);
documentElement.appendChild(bodyElement);
return this;
}
/**
* Closes the document.
*/
close() { }
/**
* Creates an element.
*
* @param qualifiedName Tag name.
* @param [options] Options.
* @param [options.is] Tag name of a custom element previously defined via customElements.define().
* @returns Element.
*/
createElement(qualifiedName, options) {
return (this.createElementNS(NamespaceURI.html, StringUtility.asciiLowerCase(String(qualifiedName)), options));
}
/**
* Creates an element with the specified namespace URI and qualified name.
*
* @param namespaceURI Namespace URI.
* @param qualifiedName Tag name.
* @param [options] Options.
* @param [options.is] Tag name of a custom element previously defined via customElements.define().
* @returns Element.
*/
createElementNS(namespaceURI, qualifiedName, options) {
const window = this[PropertySymbol.window];
qualifiedName = String(qualifiedName);
if (!qualifiedName) {
throw new window.DOMException("Failed to execute 'createElementNS' on 'Document': The qualified name provided is empty.");
}
const parts = qualifiedName.split(':');
const localName = parts[1] ?? parts[0];
const prefix = parts[1] ? parts[0] : null;
switch (namespaceURI) {
case NamespaceURI.svg:
const config = SVGElementConfig[qualifiedName.toLowerCase()];
const svgElementClass = config && config.localName === qualifiedName
? window[config.className]
: window.SVGElement;
const svgElement = NodeFactory.createNode(this, svgElementClass);
svgElement[PropertySymbol.tagName] = qualifiedName;
svgElement[PropertySymbol.localName] = localName;
svgElement[PropertySymbol.prefix] = prefix;
svgElement[PropertySymbol.namespaceURI] = namespaceURI;
svgElement[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
return svgElement;
case NamespaceURI.html:
// Custom HTML element
// If a polyfill is used, [PropertySymbol.registry] may be undefined
const customElementDefinition = window.customElements[PropertySymbol.registry]?.get(options && options.is ? String(options.is) : qualifiedName);
if (customElementDefinition) {
const element = new customElementDefinition.elementClass();
element[PropertySymbol.tagName] = StringUtility.asciiUpperCase(qualifiedName);
element[PropertySymbol.localName] = localName;
element[PropertySymbol.prefix] = prefix;
element[PropertySymbol.namespaceURI] = namespaceURI;
element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
return element;
}
const elementClass = HTMLElementConfig[qualifiedName]
? window[HTMLElementConfig[qualifiedName].className]
: null;
// Known HTML element
if (elementClass) {
const element = NodeFactory.createNode(this, elementClass);
element[PropertySymbol.tagName] = StringUtility.asciiUpperCase(qualifiedName);
element[PropertySymbol.localName] = localName;
element[PropertySymbol.prefix] = prefix;
element[PropertySymbol.namespaceURI] = namespaceURI;
element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
return element;
}
// Unknown HTML element (if it has an hyphen in the name, it is a custom element that hasn't been defined yet)
const unknownElementClass = qualifiedName.includes('-')
? window.HTMLElement
: window.HTMLUnknownElement;
const unknownElement = NodeFactory.createNode(this, unknownElementClass);
unknownElement[PropertySymbol.tagName] = StringUtility.asciiUpperCase(qualifiedName);
unknownElement[PropertySymbol.localName] = localName;
unknownElement[PropertySymbol.prefix] = prefix;
unknownElement[PropertySymbol.namespaceURI] = namespaceURI;
unknownElement[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
return unknownElement;
default:
const element = NodeFactory.createNode(this, Element);
element[PropertySymbol.tagName] = qualifiedName;
element[PropertySymbol.localName] = localName;
element[PropertySymbol.prefix] = prefix;
element[PropertySymbol.namespaceURI] = namespaceURI;
element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
return element;
}
}
/* eslint-enable jsdoc/valid-types */
/**
* Creates a text node.
*
* @param [data] Text data.
* @returns Text node.
*/
createTextNode(data) {
if (arguments.length < 1) {
throw new this[PropertySymbol.window].TypeError(`Failed to execute 'createTextNode' on 'Document': 1 argument required, but only ${arguments.length} present.`);
}
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
return NodeFactory.createNode(this, this[PropertySymbol.window].Text, String(data));
}
/**
* Creates a comment node.
*
* @param [data] Text data.
* @returns Text node.
*/
createComment(data) {
if (arguments.length < 1) {
throw new this[PropertySymbol.window].TypeError(`Failed to execute 'createComment' on 'Document': 1 argument required, but only ${arguments.length} present.`);
}
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
return NodeFactory.createNode(this, this[PropertySymbol.window].Comment, String(data));
}
/**
* Creates a document fragment.
*
* @returns Document fragment.
*/
createDocumentFragment() {
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
return NodeFactory.createNode(this, this[PropertySymbol.window].DocumentFragment);
}
/**
* Creates a node iterator.
*
* @param root Root.
* @param [whatToShow] What to show.
* @param [filter] Filter.
*/
createNodeIterator(root, whatToShow = -1, filter = null) {
return new NodeIterator(root, whatToShow, filter);
}
/**
* Creates a Tree Walker.
*
* @param root Root.
* @param [whatToShow] What to show.
* @param [filter] Filter.
*/
createTreeWalker(root, whatToShow = -1, filter = null) {
return new TreeWalker(root, whatToShow, filter);
}
/**
* Creates an event.
*
* @deprecated
* @param type Type.
* @returns Event.
*/
createEvent(type) {
if (typeof this[PropertySymbol.window][type] === 'function') {
return new this[PropertySymbol.window][type]('init');
}
return new Event('init');
}
/**
* Creates an Attr node.
*
* @param qualifiedName Name.
* @returns Attribute.
*/
createAttribute(qualifiedName) {
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
const attribute = NodeFactory.createNode(this, this[PropertySymbol.window].Attr);
const name = StringUtility.asciiLowerCase(qualifiedName);
const parts = name.split(':');
attribute[PropertySymbol.name] = name;
attribute[PropertySymbol.localName] = parts[1] ?? name;
attribute[PropertySymbol.prefix] = parts[1] ? parts[0] : null;
return attribute;
}
/**
* Creates a namespaced Attr node.
*
* @param namespaceURI Namespace URI.
* @param qualifiedName Qualified name.
* @returns Element.
*/
createAttributeNS(namespaceURI, qualifiedName) {
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
const attribute = NodeFactory.createNode(this, this[PropertySymbol.window].Attr);
const parts = qualifiedName.split(':');
attribute[PropertySymbol.namespaceURI] = namespaceURI;
attribute[PropertySymbol.name] = qualifiedName;
attribute[PropertySymbol.localName] = parts[1] ?? qualifiedName;
attribute[PropertySymbol.prefix] = parts[1] ? parts[0] : null;
if (!namespaceURI && attribute[PropertySymbol.prefix]) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'createAttributeNS' on 'Document': The namespace URI provided ('${namespaceURI || ''}') is not valid for the qualified name provided ('${qualifiedName}').`, DOMExceptionNameEnum.namespaceError);
}
return attribute;
}
/**
* Imports a node.
*
* @param node Node to import.
* @param [deep=false] Set to "true" if the clone should be deep.
*/
importNode(node, deep = false) {
if (!(node instanceof Node)) {
throw new this[PropertySymbol.window].DOMException('Parameter 1 was not of type Node.');
}
const clone = node.cloneNode(deep);
this.#importNode(clone);
return clone;
}
/**
* Creates a range.
*
* @returns Range.
*/
createRange() {
return new this[PropertySymbol.window].Range();
}
/**
* Adopts a node.
*
* @param node Node to adopt.
* @returns Adopted node.
*/
adoptNode(node) {
if (!(node instanceof Node)) {
throw new this[PropertySymbol.window].DOMException('Parameter 1 was not of type Node.');
}
const adopted = node[PropertySymbol.parentNode]
? node[PropertySymbol.parentNode].removeChild(node)
: node;
const document = this;
Object.defineProperty(adopted, 'ownerDocument', { value: document });
return adopted;
}
/**
* Returns selection.
*
* @returns Selection.
*/
getSelection() {
if (!this.#selection) {
this.#selection = new Selection(this);
}
return this.#selection;
}
/**
* Returns a boolean value indicating whether the document or any element inside the document has focus.
*
* @returns "true" if the document has focus.
*/
hasFocus() {
return !!this.activeElement;
}
/**
* Creates a Processing Instruction node.
*
* @param target Target.
* @param data Data.
* @returns ProcessingInstruction.
*/
createProcessingInstruction(target, data) {
if (arguments.length < 2) {
throw new this[PropertySymbol.window].TypeError(`Failed to execute 'createProcessingInstruction' on 'Document': 2 arguments required, but only ${arguments.length} present.`);
}
target = String(target);
data = String(data);
if (!target || !PROCESSING_INSTRUCTION_TARGET_REGEXP.test(target)) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'createProcessingInstruction' on 'Document': The target provided ('${target}') is not a valid name.`);
}
if (data.includes('?>')) {
throw new this[PropertySymbol.window].DOMException(`Failed to execute 'createProcessingInstruction' on 'Document': The data provided ('?>') contains '?>'`);
}
// We should use the NodeFactory and not the class constructor, so that owner document will be this document
const element = NodeFactory.createNode(this, this[PropertySymbol.window].ProcessingInstruction);
element[PropertySymbol.data] = data;
element[PropertySymbol.target] = target;
return element;
}
/**
* Get element at a given point.
*
* @param _x horizontal coordinate
* @param _y vertical coordinate
* @returns Always returns null since Happy DOM does not render elements.
*/
elementFromPoint(_x, _y) {
return null;
}
/**
* Imports a node.
*
* @param node Node.
*/
#importNode(node) {
node[PropertySymbol.ownerDocument] = this;
for (const child of node[PropertySymbol.nodeArray]) {
this.#importNode(child);
}
}
}
//# sourceMappingURL=Document.js.map