UNPKG

@lynx-js/offscreen-document

Version:

Offscreen Document allows developers to use particular DOM in WebWorker

190 lines 6.95 kB
// Copyright 2023 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import { enableEvent, operations, } from './OffscreenDocument.js'; import { OffscreenCSSStyleDeclaration } from './OffscreenCSSStyleDeclaration.js'; import { OperationType } from '../types/ElementOperation.js'; export const ancestorDocument = Symbol('ancestorDocument'); export const _attributes = Symbol('_attributes'); export const _children = Symbol('_children'); export const textContent = Symbol('textContent'); export const _cssRuleContents = Symbol('_cssRuleContents'); export const uniqueId = Symbol('uniqueId'); const _style = Symbol('_style'); export class OffscreenElement extends EventTarget { [textContent] = ''; [_style]; [_attributes] = new Map(); _parentElement = null; [_children] = []; [_cssRuleContents]; #sheet; /** * @private */ [uniqueId]; /** * @private */ [ancestorDocument]; localName; constructor(localName, elementUniqueId) { super(); this.localName = localName; this[uniqueId] = elementUniqueId; } get sheet() { if (!this.#sheet) { const uid = this[uniqueId]; const ancestor = this[ancestorDocument]; const cssRules = []; this.#sheet = { cssRules, insertRule: (rule, index) => { cssRules.splice(index, 0, { style: { set cssText(text) { ancestor[operations].push(OperationType.sheetRuleUpdateCssText, uid, index, text); }, }, }); if (!this[_cssRuleContents]) { this[_cssRuleContents] = []; } this[_cssRuleContents].splice(index, 0, rule); this[ancestorDocument][operations].push(OperationType.sheetInsertRule, uid, index, rule); return index; }, }; } return this.#sheet; } get tagName() { return this.localName.toUpperCase(); } get style() { if (!this[_style]) { this[_style] = new OffscreenCSSStyleDeclaration(this); } return this[_style]; } get children() { return this[_children].slice(); } get parentElement() { return this._parentElement; } get parentNode() { return this._parentElement; } get firstElementChild() { return this[_children][0] ?? null; } get lastElementChild() { return this[_children][this[_children].length - 1] ?? null; } get nextElementSibling() { const parent = this._parentElement; if (parent) { const nextElementSiblingIndex = parent[_children].indexOf(this); if (nextElementSiblingIndex >= 0) { return parent[_children][nextElementSiblingIndex + 1] || null; } } return null; } _remove() { if (this._parentElement) { const currentIdx = this._parentElement[_children].indexOf(this); this._parentElement[_children].splice(currentIdx, 1); this._parentElement = null; } } setAttribute(qualifiedName, value) { this[_attributes].set(qualifiedName, value); this[ancestorDocument][operations].push(OperationType.SetAttribute, this[uniqueId], qualifiedName, value); } getAttribute(qualifiedName) { return this[_attributes].get(qualifiedName) ?? null; } removeAttribute(qualifiedName) { this[_attributes].delete(qualifiedName); this[ancestorDocument][operations].push(OperationType.RemoveAttribute, this[uniqueId], qualifiedName); } append(...nodes) { this[ancestorDocument][operations].push(OperationType.Append, this[uniqueId], nodes.length, ...nodes.map(node => node[uniqueId])); for (const node of nodes) { node._remove(); node._parentElement = this; } this[_children].push(...nodes); } appendChild(node) { this[ancestorDocument][operations].push(OperationType.Append, this[uniqueId], 1, node[uniqueId]); node._remove(); node._parentElement = this; this[_children].push(node); return node; } replaceWith(...nodes) { this[ancestorDocument][operations].push(OperationType.ReplaceWith, this[uniqueId], nodes.length, ...nodes.map(node => node[uniqueId])); if (this._parentElement) { const parent = this._parentElement; this._parentElement = null; const currentIdx = parent[_children].indexOf(this); parent[_children].splice(currentIdx, 1, ...nodes); for (const node of nodes) { node._parentElement = parent; } } } getAttributeNames() { return [...this[_attributes].keys()]; } remove() { this[ancestorDocument][operations].push(OperationType.Remove, this[uniqueId]); this._remove(); } insertBefore(newNode, refNode) { newNode._remove(); if (refNode) { const refNodeIndex = this[_children].indexOf(refNode); if (refNodeIndex >= 0) { newNode._parentElement = this; this[_children].splice(refNodeIndex, 0, newNode); } } else { newNode._parentElement = this; this[_children].push(newNode); } this[ancestorDocument][operations].push(OperationType.InsertBefore, this[uniqueId], newNode[uniqueId], refNode?.[uniqueId] ?? 0); return newNode; } removeChild(child) { if (!child) { throw new DOMException('The node to be removed is not a child of this node.', 'NotFoundError'); } if (child._parentElement !== this) { throw new DOMException('The node to be removed is not a child of this node.', 'NotFoundError'); } this[ancestorDocument][operations].push(OperationType.RemoveChild, this[uniqueId], child[uniqueId]); child._remove(); return child; } addEventListener(type, callback, options) { this[ancestorDocument][enableEvent](type, this[uniqueId]); super.addEventListener(type, callback, options); } set textContent(text) { this[ancestorDocument][operations].push(OperationType.SetTextContent, this[uniqueId], text); for (const child of this.children) { child.remove(); } this[textContent] = text; if (this[_cssRuleContents]) { this[_cssRuleContents] = []; } } } //# sourceMappingURL=OffscreenElement.js.map