You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
2.8 KiB
JavaScript

import {ELEMENT_NODE} from '../shared/constants.js';
import {CUSTOM_ELEMENTS, END, NEXT} from '../shared/symbols.js';
import {htmlClasses} from '../shared/register-html-class.js';
import {Document} from '../interface/document.js';
import {NodeList} from '../interface/node-list.js';
import {customElements} from '../interface/custom-element-registry.js';
import {HTMLElement} from './element.js';
const createHTMLElement = (ownerDocument, builtin, localName, options) => {
if (!builtin && htmlClasses.has(localName)) {
const Class = htmlClasses.get(localName);
return new Class(ownerDocument, localName);
}
const {[CUSTOM_ELEMENTS]: {active, registry}} = ownerDocument;
if (active) {
const ce = builtin ? options.is : localName;
if (registry.has(ce)) {
const {Class} = registry.get(ce);
const element = new Class(ownerDocument, localName);
customElements.set(element, {connected: false});
return element;
}
}
return new HTMLElement(ownerDocument, localName);
};
/**
* @implements globalThis.HTMLDocument
*/
export class HTMLDocument extends Document {
constructor() { super('text/html'); }
get all() {
const nodeList = new NodeList;
let {[NEXT]: next, [END]: end} = this;
while (next !== end) {
switch (next.nodeType) {
case ELEMENT_NODE:
nodeList.push(next);
break;
}
next = next[NEXT];
}
return nodeList;
}
/**
* @type HTMLHeadElement
*/
get head() {
const {documentElement} = this;
let {firstElementChild} = documentElement;
if (!firstElementChild || firstElementChild.tagName !== 'HEAD') {
firstElementChild = this.createElement('head');
documentElement.prepend(firstElementChild);
}
return firstElementChild;
}
/**
* @type HTMLBodyElement
*/
get body() {
const {head} = this;
let {nextElementSibling} = head;
if (!nextElementSibling || nextElementSibling.tagName !== 'BODY') {
nextElementSibling = this.createElement('body');
head.after(nextElementSibling);
}
return nextElementSibling;
}
/**
* @type HTMLTitleElement
*/
get title() {
const {head} = this;
return head.getElementsByTagName('title').at(0)?.textContent || '';
}
set title(textContent) {
const {head} = this;
let title = head.getElementsByTagName('title').at(0);
if (title)
title.textContent = textContent;
else {
head.insertBefore(
this.createElement('title'),
head.firstChild
).textContent = textContent;
}
}
createElement(localName, options) {
const builtin = !!(options && options.is);
const element = createHTMLElement(this, builtin, localName, options);
if (builtin)
element.setAttribute('is', options.is);
return element;
}
}