UNPKG

5.45 kBJavaScriptView Raw
1const CustomElementRegistry = require('./CustomElementRegistry');
2const Event = require('./Event');
3const CustomEvent = require('./CustomEvent');
4const Node = require('./Node');
5const DocumentType = require('./DocumentType');
6const Attr = require('./Attr');
7const CSSStyleDeclaration = require('./CSSStyleDeclaration');
8const Comment = require('./Comment');
9const DocumentFragment = require('./DocumentFragment');
10const HTMLElement = require('./HTMLElement');
11const HTMLHtmlElement = require('./HTMLHtmlElement');
12const HTMLStyleElement = require('./HTMLStyleElement');
13const HTMLTemplateElement = require('./HTMLTemplateElement');
14const HTMLTextAreaElement = require('./HTMLTextAreaElement');
15const Range = require('./Range');
16const Text = require('./Text');
17const TreeWalker = require('./TreeWalker');
18
19const headTag = el => el.nodeName === 'head';
20const bodyTag = el => el.nodeName === 'body';
21
22const createElement = (self, name, is) => {
23 const Class = self.customElements.get(is) || HTMLElement;
24 return new Class(self, name);
25};
26
27const getFoundOrNull = result => {
28 if (result) {
29 const el = findById.found;
30 findById.found = null;
31 return el;
32 } else {
33 return null;
34 }
35};
36
37function findById(child) {'use strict';
38 return child.id === this ?
39 !!(findById.found = child) :
40 child.children.some(findById, this);
41}
42
43// interface Document // https://dom.spec.whatwg.org/#document
44module.exports = class Document extends Node {
45
46 constructor(customElements = new CustomElementRegistry()) {
47 super(null);
48 this.nodeType = Node.DOCUMENT_NODE;
49 this.nodeName = '#document';
50 this.appendChild(new DocumentType());
51 this.documentElement = new HTMLHtmlElement(this, 'html');
52 this.appendChild(this.documentElement);
53 this.customElements = customElements;
54 Object.freeze(this.childNodes);
55 }
56
57 createAttribute(name) {
58 const attr = new Attr(
59 {ownerDocument: this},
60 name,
61 name === 'style' ?
62 new CSSStyleDeclaration() :
63 null
64 );
65 attr.ownerElement = null;
66 return attr;
67 }
68
69 createAttributeNS(_, name) {
70 return this.createAttribute(name);
71 }
72
73 createComment(comment) {
74 return new Comment(this, comment);
75 }
76
77 createDocumentFragment() {
78 return new DocumentFragment(this);
79 }
80
81 createElement(name, options) {
82 switch (name) {
83 case 'style':
84 return new HTMLStyleElement(this, name);
85 case 'template':
86 return new HTMLTemplateElement(this, name);
87 case 'textarea':
88 return new HTMLTextAreaElement(this, name);
89 case 'canvas':
90 case 'img':
91 try {
92 const file = name === 'img' ? './HTMLImageElement' : './HTMLCanvasElement';
93 const Constructor = require(file);
94 return new Constructor(this);
95 }
96 catch (o_O) {}
97 default:
98 const extending = 1 < arguments.length && 'is' in options;
99 const el = createElement(this, name, extending ? options.is : name);
100 if (extending)
101 el.setAttribute('is', options.is);
102 return el;
103 }
104 }
105
106 createElementNS(ns, name) {
107 if (ns === 'http://www.w3.org/1999/xhtml') {
108 return this.createElement(name);
109 }
110 return new HTMLElement(this, name + ':' + ns);
111 }
112
113 createEvent(name) {
114 switch (name) {
115 case 'Event':
116 return new Event();
117 case 'CustomEvent':
118 return new CustomEvent();
119 default:
120 throw new Error(name + ' not implemented');
121 }
122 }
123
124 createRange() {
125 return new Range;
126 }
127
128 createTextNode(text) {
129 return new Text(this, text);
130 }
131
132 createTreeWalker(root, whatToShow) {
133 return new TreeWalker(root, whatToShow);
134 }
135
136 getElementsByTagName(name) {
137 const html = this.documentElement;
138 return /html/i.test(name) ?
139 [html] :
140 (name === '*' ? [html] : []).concat(html.getElementsByTagName(name));
141 }
142
143 getElementsByClassName(name) {
144 const html = this.documentElement;
145 return (html.classList.contains(name) ? [html] : [])
146 .concat(html.getElementsByClassName(name));
147 }
148
149 importNode(node) {
150 return node.cloneNode(!!arguments[1]);
151 }
152
153 toString() {
154 return this.childNodes[0] + this.documentElement.outerHTML;
155 }
156
157 get defaultView() {
158 return global;
159 }
160
161 get head() {
162 const html = this.documentElement;
163 return this.documentElement.childNodes.find(headTag) ||
164 html.insertBefore(this.createElement('head'), this.body);
165 }
166
167 get body() {
168 const html = this.documentElement;
169 return html.childNodes.find(bodyTag) ||
170 html.appendChild(this.createElement('body'));
171 }
172
173 // interface NonElementParentNode // https://dom.spec.whatwg.org/#nonelementparentnode
174 getElementById(id) {
175 const html = this.documentElement;
176 return html.id === id ? html : getFoundOrNull(html.children.some(findById, id));
177 }
178
179 // interface ParentNode @ https://dom.spec.whatwg.org/#parentnode
180 get children() {
181 return [this.documentElement];
182 }
183
184 get firstElementChild() {
185 return this.documentElement;
186 }
187
188 get lastElementChild() {
189 return this.documentElement;
190 }
191
192 get childElementCount() {
193 return 1;
194 }
195
196 prepend() { throw new Error('Only one element on document allowed.'); }
197 append() { this.prepend(); }
198
199 querySelector(css) {
200 return this.documentElement.querySelector(css);
201 }
202
203 querySelectorAll(css) {
204 return this.documentElement.querySelectorAll(css);
205 }
206
207};