"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Element_js_1 = __importDefault(require("../element/Element.cjs")); const PropertySymbol = __importStar(require("../../PropertySymbol.cjs")); const CSSStyleDeclaration_js_1 = __importDefault(require("../../css/declaration/CSSStyleDeclaration.cjs")); const PointerEvent_js_1 = __importDefault(require("../../event/events/PointerEvent.cjs")); const NodeTypeEnum_js_1 = __importDefault(require("../node/NodeTypeEnum.cjs")); const HTMLElementUtility_js_1 = __importDefault(require("./HTMLElementUtility.cjs")); const DOMStringMap_js_1 = __importDefault(require("../../dom/DOMStringMap.cjs")); const WindowBrowserContext_js_1 = __importDefault(require("../../window/WindowBrowserContext.cjs")); /** * HTML Element. * * Reference: * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement. */ class HTMLElement extends Element_js_1.default { // Events oncopy = null; oncut = null; onpaste = null; oninvalid = null; onanimationcancel = null; onanimationend = null; onanimationiteration = null; onanimationstart = null; onbeforeinput = null; oninput = null; onchange = null; ongotpointercapture = null; onlostpointercapture = null; onpointercancel = null; onpointerdown = null; onpointerenter = null; onpointerleave = null; onpointermove = null; onpointerout = null; onpointerover = null; onpointerup = null; ontransitioncancel = null; ontransitionend = null; ontransitionrun = null; ontransitionstart = null; // Internal properties [PropertySymbol.accessKey] = ''; [PropertySymbol.contentEditable] = 'inherit'; [PropertySymbol.isContentEditable] = false; [PropertySymbol.offsetHeight] = 0; [PropertySymbol.offsetWidth] = 0; [PropertySymbol.offsetLeft] = 0; [PropertySymbol.offsetTop] = 0; [PropertySymbol.clientHeight] = 0; [PropertySymbol.clientWidth] = 0; [PropertySymbol.clientLeft] = 0; [PropertySymbol.clientTop] = 0; [PropertySymbol.style] = null; [PropertySymbol.dataset] = null; // Private properties #customElementDefineCallback = null; /** * Returns access key. * * @returns Access key. */ get accessKey() { return this[PropertySymbol.accessKey]; } /** * Sets access key. * * @param accessKey Access key. */ set accessKey(accessKey) { this[PropertySymbol.accessKey] = accessKey; } /** * Returns content editable. * * @returns Content editable. */ get contentEditable() { return this[PropertySymbol.contentEditable]; } /** * Sets content editable. * * @param contentEditable Content editable. */ set contentEditable(contentEditable) { this[PropertySymbol.contentEditable] = contentEditable; } /** * Returns is content editable. * * @returns Is content editable. */ get isContentEditable() { return this[PropertySymbol.isContentEditable]; } /** * Returns offset height. * * @returns Offset height. */ get offsetHeight() { return this[PropertySymbol.offsetHeight]; } /** * Returns offset width. * * @returns Offset width. */ get offsetWidth() { return this[PropertySymbol.offsetWidth]; } /** * Returns offset left. * * @returns Offset left. */ get offsetLeft() { return this[PropertySymbol.offsetLeft]; } /** * Returns offset top. * * @returns Offset top. */ get offsetTop() { return this[PropertySymbol.offsetTop]; } /** * Returns client height. * * @returns Client height. */ get clientHeight() { return this[PropertySymbol.clientHeight]; } /** * Returns client width. * * @returns Client width. */ get clientWidth() { return this[PropertySymbol.clientWidth]; } /** * Returns client left. * * @returns Client left. */ get clientLeft() { return this[PropertySymbol.clientLeft]; } /** * Returns client top. * * @returns Client top. */ get clientTop() { return this[PropertySymbol.clientTop]; } /** * Returns tab index. * * @returns Tab index. */ get tabIndex() { const tabIndex = this.getAttribute('tabindex'); return tabIndex !== null ? Number(tabIndex) : -1; } /** * Returns tab index. * * @param tabIndex Tab index. */ set tabIndex(tabIndex) { if (tabIndex === -1) { this.removeAttribute('tabindex'); } else { this.setAttribute('tabindex', String(tabIndex)); } } /** * Returns inner text, which is the rendered appearance of text. * * @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute * @returns Inner text. */ get innerText() { if (!this[PropertySymbol.isConnected]) { return this.textContent; } let result = ''; for (const childNode of this[PropertySymbol.nodeArray]) { if (childNode[PropertySymbol.nodeType] === NodeTypeEnum_js_1.default.elementNode) { const childElement = childNode; const computedStyle = this[PropertySymbol.window].getComputedStyle(childElement); if (childElement[PropertySymbol.tagName] !== 'SCRIPT' && childElement[PropertySymbol.tagName] !== 'STYLE') { const display = computedStyle.display; if (display !== 'none') { const textTransform = computedStyle.textTransform; if ((display === 'block' || display === 'flex') && result) { result += '\n'; } let text = childElement.innerText; switch (textTransform) { case 'uppercase': text = text.toUpperCase(); break; case 'lowercase': text = text.toLowerCase(); break; case 'capitalize': text = text.replace(/(^|\s)\S/g, (l) => l.toUpperCase()); break; } result += text; } } } else if (childNode[PropertySymbol.nodeType] === NodeTypeEnum_js_1.default.textNode) { result += childNode.textContent.replace(/[\n\r]/, ''); } } return result; } /** * Sets the inner text, which is the rendered appearance of text. * * @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute * @param innerText Inner text. */ set innerText(text) { const childNodes = this[PropertySymbol.nodeArray]; while (childNodes.length) { this.removeChild(childNodes[0]); } const texts = text.split(/[\n\r]/); const ownerDocument = this[PropertySymbol.ownerDocument]; for (let i = 0, max = texts.length; i < max; i++) { if (i !== 0) { this.appendChild(ownerDocument.createElement('br')); } this.appendChild(ownerDocument.createTextNode(texts[i])); } } /** * Returns outer text. * * @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute * @returns HTML. */ get outerText() { return this.innerText; } /** * Sets outer text. * * @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute * @param text Text. */ set outerText(text) { if (!this[PropertySymbol.parentNode]) { throw new this[PropertySymbol.window].DOMException("Failed to set the 'outerHTML' property on 'Element': This element has no parent node."); } const texts = text.split(/[\n\r]/); for (let i = 0, max = texts.length; i < max; i++) { if (i !== 0) { this[PropertySymbol.parentNode].insertBefore(this[PropertySymbol.ownerDocument].createElement('br'), this); } this[PropertySymbol.parentNode].insertBefore(this[PropertySymbol.ownerDocument].createTextNode(texts[i]), this); } this[PropertySymbol.parentNode].removeChild(this); } /** * Returns style. * * @returns Style. */ get style() { if (!this[PropertySymbol.style]) { this[PropertySymbol.style] = new CSSStyleDeclaration_js_1.default(PropertySymbol.illegalConstructor, this[PropertySymbol.window], { element: this }); } return this[PropertySymbol.style]; } /** * Sets style. * * @param cssText Style as text. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style#setting_styles */ set style(cssText) { this.style.cssText = typeof cssText === 'string' ? cssText : ''; } /** * Returns data set. * * @returns Data set. */ get dataset() { return (this[PropertySymbol.dataset] ??= new DOMStringMap_js_1.default(PropertySymbol.illegalConstructor, this)); } /** * Returns direction. * * @returns Direction. */ get dir() { return this.getAttribute('dir') || ''; } /** * Returns direction. * * @param direction Direction. */ set dir(direction) { this.setAttribute('dir', direction); } /** * Returns hidden. * * @returns Hidden. */ get hidden() { return this.getAttribute('hidden') !== null; } /** * Returns hidden. * * @param hidden Hidden. */ set hidden(hidden) { if (!hidden) { this.removeAttribute('hidden'); } else { this.setAttribute('hidden', ''); } } /** * Returns inert. * * @returns Inert. */ get inert() { return this.getAttribute('inert') !== null; } /** * Returns inert. * * @param inert Inert. */ set inert(inert) { if (!inert) { this.removeAttribute('inert'); } else { this.setAttribute('inert', ''); } } /** * Returns language. * * @returns Language. */ get lang() { return this.getAttribute('lang') || ''; } /** * Returns language. * * @param language Language. */ set lang(lang) { this.setAttribute('lang', lang); } /** * Returns title. * * @returns Title. */ get title() { return this.getAttribute('title') || ''; } /** * Returns title. * * @param title Title. */ set title(title) { this.setAttribute('title', title); } /** * Returns popover. * * @returns Popover. */ get popover() { const value = this.getAttribute('popover'); switch (value) { case null: return null; case '': case 'auto': return 'auto'; case 'manual': return 'manual'; default: return 'manual'; } } /** * Sets popover. * * @param value Value. */ set popover(value) { if (value === null) { this.removeAttribute('popover'); return; } this.setAttribute('popover', value); } /** * Triggers a click event. */ click() { this.dispatchEvent(new PointerEvent_js_1.default('click', { bubbles: true, composed: true, cancelable: true })); } /** * Triggers a blur event. */ blur() { HTMLElementUtility_js_1.default.blur(this); } /** * Triggers a focus event. */ focus() { HTMLElementUtility_js_1.default.focus(this); } /** * @override */ [PropertySymbol.cloneNode](deep = false) { const clone = super[PropertySymbol.cloneNode](deep); clone[PropertySymbol.accessKey] = this[PropertySymbol.accessKey]; clone[PropertySymbol.contentEditable] = this[PropertySymbol.contentEditable]; clone[PropertySymbol.isContentEditable] = this[PropertySymbol.isContentEditable]; return clone; } /** * @override * @see https://html.spec.whatwg.org/multipage/dom.html#htmlelement */ [PropertySymbol.connectedToNode]() { const window = this[PropertySymbol.window]; const localName = this[PropertySymbol.localName]; const allCallbacks = window.customElements[PropertySymbol.callbacks]; // This element can potentially be a custom element that has not been defined yet // Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404) if (this.constructor === window.HTMLElement && localName.includes('-') && allCallbacks) { if (!this.#customElementDefineCallback) { const callback = this.#onCustomElementConnected.bind(this); const callbacks = allCallbacks.get(localName); if (callbacks) { callbacks.push(callback); } else { allCallbacks.set(localName, [callback]); } this.#customElementDefineCallback = callback; } } super[PropertySymbol.connectedToNode](); } /** * @override */ [PropertySymbol.disconnectedFromNode]() { const window = this[PropertySymbol.window]; const localName = this[PropertySymbol.localName]; const allCallbacks = window.customElements[PropertySymbol.callbacks]; // This element can potentially be a custom element that has not been defined yet // Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404) if (this.constructor === window.HTMLElement && localName.includes('-') && allCallbacks) { const callbacks = allCallbacks.get(localName); if (callbacks && this.#customElementDefineCallback) { const index = callbacks.indexOf(this.#customElementDefineCallback); if (index !== -1) { callbacks.splice(index, 1); } if (!callbacks.length) { allCallbacks.delete(localName); } this.#customElementDefineCallback = null; } } super[PropertySymbol.disconnectedFromNode](); } /** * Triggered when a custom element is connected to the DOM. */ #onCustomElementConnected() { if (!this[PropertySymbol.parentNode]) { return; } const localName = this[PropertySymbol.localName]; const newElement = this[PropertySymbol.ownerDocument].createElement(localName); const newCache = newElement[PropertySymbol.cache]; newElement[PropertySymbol.nodeArray] = this[PropertySymbol.nodeArray]; newElement[PropertySymbol.elementArray] = this[PropertySymbol.elementArray]; newElement[PropertySymbol.childNodes] = null; newElement[PropertySymbol.children] = null; newElement[PropertySymbol.isConnected] = this[PropertySymbol.isConnected]; newElement[PropertySymbol.rootNode] = this[PropertySymbol.rootNode]; newElement[PropertySymbol.formNode] = this[PropertySymbol.formNode]; newElement[PropertySymbol.parentNode] = this[PropertySymbol.parentNode]; newElement[PropertySymbol.selectNode] = this[PropertySymbol.selectNode]; newElement[PropertySymbol.textAreaNode] = this[PropertySymbol.textAreaNode]; newElement[PropertySymbol.mutationListeners] = this[PropertySymbol.mutationListeners]; newElement[PropertySymbol.isValue] = this[PropertySymbol.isValue]; newElement[PropertySymbol.cache] = this[PropertySymbol.cache]; newElement[PropertySymbol.affectsCache] = this[PropertySymbol.affectsCache]; newElement[PropertySymbol.attributes][PropertySymbol.namedItems] = this[PropertySymbol.attributes][PropertySymbol.namedItems]; newElement[PropertySymbol.attributes][PropertySymbol.namespaceItems] = this[PropertySymbol.attributes][PropertySymbol.namespaceItems]; for (const attr of newElement[PropertySymbol.attributes][PropertySymbol.namedItems].values()) { attr[PropertySymbol.ownerElement] = newElement; } this[PropertySymbol.nodeArray] = []; this[PropertySymbol.elementArray] = []; this[PropertySymbol.childNodes] = null; this[PropertySymbol.children] = null; this[PropertySymbol.rootNode] = null; this[PropertySymbol.formNode] = null; this[PropertySymbol.selectNode] = null; this[PropertySymbol.textAreaNode] = null; this[PropertySymbol.mutationListeners] = []; this[PropertySymbol.isValue] = null; this[PropertySymbol.cache] = newCache; this[PropertySymbol.affectsCache] = []; this[PropertySymbol.attributes][PropertySymbol.namedItems] = new Map(); this[PropertySymbol.attributes][PropertySymbol.namespaceItems] = new Map(); const parentChildNodes = this[PropertySymbol.parentNode][PropertySymbol.nodeArray]; const parentChildElements = this[PropertySymbol.parentNode][PropertySymbol.elementArray]; parentChildNodes[parentChildNodes.indexOf(this)] = newElement; parentChildElements[parentChildElements.indexOf(this)] = newElement; if (newElement[PropertySymbol.isConnected] && newElement.connectedCallback) { const result = newElement.connectedCallback(); /** * It is common to import dependencies in the connectedCallback() method of web components. * As Happy DOM doesn't have support for dynamic imports yet, this is a temporary solution to wait for imports in connectedCallback(). * * @see https://github.com/capricorn86/happy-dom/issues/1442 */ if (result instanceof Promise) { const asyncTaskManager = new WindowBrowserContext_js_1.default(this[PropertySymbol.window]).getAsyncTaskManager(); if (asyncTaskManager) { const taskID = asyncTaskManager.startTask(); result .then(() => asyncTaskManager.endTask(taskID)) .catch(() => asyncTaskManager.endTask(taskID)); } } } this[PropertySymbol.disconnectedFromDocument](); } } exports.default = HTMLElement; //# sourceMappingURL=HTMLElement.cjs.map