@lynx-js/offscreen-document
Version:
Offscreen Document allows developers to use particular DOM in WebWorker
190 lines • 6.95 kB
JavaScript
// 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