"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Element_1 = __importDefault(require("../element/Element")); const CSSStyleDeclaration_1 = __importDefault(require("../../css/declaration/CSSStyleDeclaration")); const FocusEvent_1 = __importDefault(require("../../event/events/FocusEvent")); const PointerEvent_1 = __importDefault(require("../../event/events/PointerEvent")); const DatasetUtility_1 = __importDefault(require("./DatasetUtility")); const NodeTypeEnum_1 = __importDefault(require("../node/NodeTypeEnum")); const DOMException_1 = __importDefault(require("../../exception/DOMException")); /** * HTML Element. * * Reference: * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement. */ class HTMLElement extends Element_1.default { constructor() { super(...arguments); this.accessKey = ''; this.accessKeyLabel = ''; this.contentEditable = 'inherit'; this.isContentEditable = false; this.offsetHeight = 0; this.offsetWidth = 0; this.offsetLeft = 0; this.offsetTop = 0; this.clientHeight = 0; this.clientWidth = 0; this._style = null; this._dataset = null; // Events this.oncopy = null; this.oncut = null; this.onpaste = null; this.oninvalid = null; this.onanimationcancel = null; this.onanimationend = null; this.onanimationiteration = null; this.onanimationstart = null; this.onbeforeinput = null; this.oninput = null; this.onchange = null; this.ongotpointercapture = null; this.onlostpointercapture = null; this.onpointercancel = null; this.onpointerdown = null; this.onpointerenter = null; this.onpointerleave = null; this.onpointermove = null; this.onpointerout = null; this.onpointerover = null; this.onpointerup = null; this.ontransitioncancel = null; this.ontransitionend = null; this.ontransitionrun = null; this.ontransitionstart = null; } /** * Returns tab index. * * @returns Tab index. */ get tabIndex() { const tabIndex = this.getAttributeNS(null, 'tabindex'); return tabIndex !== null ? Number(tabIndex) : -1; } /** * Returns tab index. * * @param tabIndex Tab index. */ set tabIndex(tabIndex) { if (tabIndex === -1) { this.removeAttributeNS(null, 'tabindex'); } else { this.setAttributeNS(null, '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.isConnected) { return this.textContent; } let result = ''; for (const childNode of this.childNodes) { if (childNode.nodeType === NodeTypeEnum_1.default.elementNode) { const childElement = childNode; const computedStyle = this.ownerDocument.defaultView.getComputedStyle(childElement); if (childElement.tagName !== 'SCRIPT' && childElement.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.nodeType === NodeTypeEnum_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) { for (const child of this.childNodes.slice()) { this.removeChild(child); } const texts = text.split(/[\n\r]/); for (let i = 0, max = texts.length; i < max; i++) { if (i !== 0) { this.appendChild(this.ownerDocument.createElement('br')); } this.appendChild(this.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.parentNode) { throw new DOMException_1.default("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.parentNode.insertBefore(this.ownerDocument.createElement('br'), this); } this.parentNode.insertBefore(this.ownerDocument.createTextNode(texts[i]), this); } this.parentNode.removeChild(this); } /** * Returns style. * * @returns Style. */ get style() { if (!this._style) { this._style = new CSSStyleDeclaration_1.default(this); } return this._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() { if (this._dataset) { return this._dataset; } const dataset = {}; const attributes = this._attributes; for (const name of Object.keys(attributes)) { if (name.startsWith('data-')) { const key = DatasetUtility_1.default.kebabToCamelCase(name.replace('data-', '')); dataset[key] = attributes[name].value; } } // Documentation for Proxy: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy this._dataset = new Proxy(dataset, { get: (dataset, key) => { const name = 'data-' + DatasetUtility_1.default.camelCaseToKebab(key); if (this._attributes[name]) { dataset[key] = this._attributes[name].value; return this._attributes[name].value; } if (dataset[key] !== undefined) { delete dataset[key]; } return undefined; }, set: (dataset, key, value) => { this.setAttribute('data-' + DatasetUtility_1.default.camelCaseToKebab(key), value); dataset[key] = value; return true; }, deleteProperty: (dataset, key) => { const name = 'data-' + DatasetUtility_1.default.camelCaseToKebab(key); const result1 = delete attributes[name]; const result2 = delete dataset[key]; return result1 && result2; }, ownKeys: (dataset) => { // According to Mozilla we have to update the dataset object (target) to contain the same keys as what we return: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/ownKeys // "The result List must contain the keys of all non-configurable own properties of the target object." const keys = []; const deleteKeys = []; for (const name of Object.keys(attributes)) { if (name.startsWith('data-')) { const key = DatasetUtility_1.default.kebabToCamelCase(name.replace('data-', '')); keys.push(key); dataset[key] = attributes[name].value; if (!dataset[key]) { deleteKeys.push(key); } } } for (const key of deleteKeys) { delete dataset[key]; } return keys; }, has: (_dataset, key) => { return !!attributes['data-' + DatasetUtility_1.default.camelCaseToKebab(key)]; } }); return this._dataset; } /** * Returns direction. * * @returns Direction. */ get dir() { return this.getAttributeNS(null, 'dir') || ''; } /** * Returns direction. * * @param direction Direction. */ set dir(direction) { this.setAttributeNS(null, 'dir', direction); } /** * Returns hidden. * * @returns Hidden. */ get hidden() { return this.getAttributeNS(null, 'hidden') !== null; } /** * Returns hidden. * * @param hidden Hidden. */ set hidden(hidden) { if (!hidden) { this.removeAttributeNS(null, 'hidden'); } else { this.setAttributeNS(null, 'hidden', ''); } } /** * Returns language. * * @returns Language. */ get lang() { return this.getAttributeNS(null, 'lang') || ''; } /** * Returns language. * * @param language Language. */ set lang(lang) { this.setAttributeNS(null, 'lang', lang); } /** * Returns title. * * @returns Title. */ get title() { return this.getAttributeNS(null, 'title') || ''; } /** * Returns title. * * @param title Title. */ set title(title) { this.setAttributeNS(null, 'title', title); } /** * Triggers a click event. */ click() { const event = new PointerEvent_1.default('click', { bubbles: true, composed: true }); event._target = this; event._currentTarget = this; this.dispatchEvent(event); } /** * Triggers a blur event. */ blur() { if (this.ownerDocument['_activeElement'] !== this || !this.isConnected) { return; } this.ownerDocument['_activeElement'] = null; this.dispatchEvent(new FocusEvent_1.default('blur', { bubbles: false, composed: true })); this.dispatchEvent(new FocusEvent_1.default('focusout', { bubbles: true, composed: true })); } /** * Triggers a focus event. */ focus() { if (this.ownerDocument['_activeElement'] === this || !this.isConnected) { return; } if (this.ownerDocument['_activeElement'] !== null) { this.ownerDocument['_activeElement'].blur(); } this.ownerDocument['_activeElement'] = this; this.dispatchEvent(new FocusEvent_1.default('focus', { bubbles: false, composed: true })); this.dispatchEvent(new FocusEvent_1.default('focusin', { bubbles: true, composed: true })); } /** * @override */ setAttributeNode(attribute) { const replacedAttribute = super.setAttributeNode(attribute); if (attribute.name === 'style' && this._style) { this._style.cssText = attribute.value; } return replacedAttribute; } /** * @override */ removeAttributeNode(attribute) { super.removeAttributeNode(attribute); if (attribute.name === 'style' && this._style) { this._style.cssText = ''; } return attribute; } /** * @override */ cloneNode(deep = false) { const clone = super.cloneNode(deep); clone.accessKey = this.accessKey; clone.accessKeyLabel = this.accessKeyLabel; clone.contentEditable = this.contentEditable; clone.isContentEditable = this.isContentEditable; if (this._style) { clone.style.cssText = this._style.cssText; } return clone; } } exports.default = HTMLElement; //# sourceMappingURL=HTMLElement.js.map