uhtml
Version:
A micro HTML/SVG render
134 lines (111 loc) • 3.43 kB
JavaScript
'use strict';
const { DOCUMENT_NODE } = require('domconstants/constants');
const { setParentNode } = require('./utils.js');
const { childNodes, documentElement, nodeName, ownerDocument } = require('./symbols.js');
const Attribute = (require('./attribute.js'));
const Comment = (require('./comment.js'));
const DocumentFragment = (require('./document-fragment.js'));
const DocumentType = (require('./document-type.js'));
const Element = (require('./element.js'));
const Event = (require('./event.js'));
const SVGElement = (require('./svg-element.js'));
const Parent = (require('./parent.js'));
const Range = (require('./range.js'));
const Text = (require('./text.js'));
const TreeWalker = (require('./tree-walker.js'));
const doctype = Symbol('doctype');
const head = Symbol('head');
const body = Symbol('body');
const defaultView = Object.create(globalThis, {
Event: { value: Event },
});
/** @typedef {import("./attribute.js").Attribute} Attribute */
module.exports = class Document extends Parent {
constructor(type = 'html') {
super(DOCUMENT_NODE, null)[nodeName] = '#document';
this[documentElement] = null;
this[doctype] = null;
this[head] = null;
this[body] = null;
if (type === 'html') {
const html = (this[documentElement] = new Element(type, this));
this[childNodes] = [
(this[doctype] = new DocumentType(type, this)),
html
].map(setParentNode, this)
html[childNodes] = [
(this[head] = new Element('head', this)),
(this[body] = new Element('body', this)),
].map(setParentNode, html);
}
}
/** @type {globalThis} */
get defaultView() {
return defaultView;
}
/** @type {import("./document-type.js").default?} */
get doctype() {
return this[doctype];
}
/** @type {import("./element.js").default?} */
get documentElement() {
return this[documentElement];
}
/** @type {import("./element.js").default?} */
get head() {
return this[head];
}
/** @type {import("./element.js").default?} */
get body() {
return this[body];
}
/** @type {Attribute} */
createAttribute(name) {
const attribute = new Attribute(name);
attribute[ownerDocument] = this;
return attribute;
}
/** @type {import("./comment.js").default} */
createComment(data) {
return new Comment(data, this);
}
/** @type {import("./document-fragment.js").default} */
createDocumentFragment() {
return new DocumentFragment(this);
}
/** @type {import("./element.js").default} */
createElement(name, options = null) {
const element = new Element(name, this);
if (options?.is) element.setAttribute('is', options.is);
return element;
}
/** @type {import("./svg-element.js").default} */
createElementNS(_, name) {
return new SVGElement(name, this);
}
/** @type {globalThis.Range} */
createRange() {
return new Range;
}
/** @type {import("./text.js").default} */
createTextNode(data) {
return new Text(data, this);
}
/** @type {globalThis.TreeWalker} */
createTreeWalker(parent, accept) {
return new TreeWalker(parent, accept);
}
/**
* Same as `document.importNode`
* @template T
* @param {T} externalNode
* @param {boolean} deep
* @returns {T}
*/
importNode(externalNode, deep = false) {
return externalNode.cloneNode(deep);
}
toString() {
return this[childNodes].join('');
}
}