source: trip-planner-front/node_modules/critters/src/dom.js@ 76712b2

Last change on this file since 76712b2 was e29cc2e, checked in by Ema <ema_spirova@…>, 3 years ago

primeNG components

  • Property mode set to 100644
File size: 5.9 KB
RevLine 
[6a3a178]1/**
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17import parse5 from 'parse5';
[e29cc2e]18import { selectAll, selectOne } from 'css-select';
19import treeAdapter from 'parse5-htmlparser2-tree-adapter';
[6a3a178]20
21// htmlparser2 has a relatively DOM-like tree format, which we'll massage into a DOM elsewhere
22const PARSE5_OPTS = {
23 treeAdapter
24};
25
26/**
27 * Parse HTML into a mutable, serializable DOM Document.
28 * The DOM implementation is an htmlparser2 DOM enhanced with basic DOM mutation methods.
29 * @param {String} html HTML to parse into a Document instance
30 */
31export function createDocument(html) {
[e29cc2e]32 const document = /** @type {HTMLDocument} */ (
33 parse5.parse(html, PARSE5_OPTS)
34 );
[6a3a178]35
36 defineProperties(document, DocumentExtensions);
37
38 // Extend Element.prototype with DOM manipulation methods.
39 const scratch = document.createElement('div');
40 // Get a reference to the base Node class - used by createTextNode()
41 document.$$Node = scratch.constructor;
42 const elementProto = Object.getPrototypeOf(scratch);
43 defineProperties(elementProto, ElementExtensions);
44 elementProto.ownerDocument = document;
45
46 return document;
47}
48
49/**
50 * Serialize a Document to an HTML String
[e29cc2e]51 * @param {HTMLDocument} document A Document, such as one created via `createDocument()`
[6a3a178]52 */
53export function serializeDocument(document) {
54 return parse5.serialize(document, PARSE5_OPTS);
55}
56
[e29cc2e]57/** @typedef {treeAdapter.Document & typeof ElementExtensions} HTMLDocument */
58
[6a3a178]59/**
60 * Methods and descriptors to mix into Element.prototype
[e29cc2e]61 * @private
[6a3a178]62 */
63const ElementExtensions = {
[e29cc2e]64 /** @extends treeAdapter.Element.prototype */
[6a3a178]65
66 nodeName: {
67 get() {
68 return this.tagName.toUpperCase();
69 }
70 },
71
72 id: reflectedProperty('id'),
73
74 className: reflectedProperty('class'),
75
76 insertBefore(child, referenceNode) {
77 if (!referenceNode) return this.appendChild(child);
78 treeAdapter.insertBefore(this, child, referenceNode);
79 return child;
80 },
81
82 appendChild(child) {
83 treeAdapter.appendChild(this, child);
84 return child;
85 },
86
87 removeChild(child) {
88 treeAdapter.detachNode(child);
89 },
90
91 remove() {
92 treeAdapter.detachNode(this);
93 },
94
95 textContent: {
96 get() {
97 return getText(this);
98 },
99
100 set(text) {
101 this.children = [];
102 treeAdapter.insertText(this, text);
103 }
104 },
105
106 setAttribute(name, value) {
107 if (this.attribs == null) this.attribs = {};
108 if (value == null) value = '';
109 this.attribs[name] = value;
110 },
111
112 removeAttribute(name) {
113 if (this.attribs != null) {
114 delete this.attribs[name];
115 }
116 },
117
118 getAttribute(name) {
119 return this.attribs != null && this.attribs[name];
120 },
121
122 hasAttribute(name) {
123 return this.attribs != null && this.attribs[name] != null;
124 },
125
126 getAttributeNode(name) {
127 const value = this.getAttribute(name);
128 if (value != null) return { specified: true, value };
129 }
130};
131
132/**
133 * Methods and descriptors to mix into the global document instance
134 * @private
135 */
136const DocumentExtensions = {
[e29cc2e]137 /** @extends treeAdapter.Document.prototype */
[6a3a178]138
139 // document is just an Element in htmlparser2, giving it a nodeType of ELEMENT_NODE.
140 // TODO: verify if these are needed for css-select
141 nodeType: {
142 get() {
143 return 9;
144 }
145 },
146
147 contentType: {
148 get() {
149 return 'text/html';
150 }
151 },
152
153 nodeName: {
154 get() {
155 return '#document';
156 }
157 },
158
159 documentElement: {
160 get() {
161 // Find the first <html> element within the document
162 return this.childNodes.filter(
163 (child) => String(child.tagName).toLowerCase() === 'html'
164 );
165 }
166 },
167
168 compatMode: {
169 get() {
170 const compatMode = {
171 'no-quirks': 'CSS1Compat',
172 quirks: 'BackCompat',
173 'limited-quirks': 'CSS1Compat'
174 };
175 return compatMode[treeAdapter.getDocumentMode(this)];
176 }
177 },
178
179 body: {
180 get() {
181 return this.querySelector('body');
182 }
183 },
184
185 createElement(name) {
186 return treeAdapter.createElement(name, null, []);
187 },
188
189 createTextNode(text) {
190 // there is no dedicated createTextNode equivalent exposed in htmlparser2's DOM
191 const Node = this.$$Node;
192 return new Node({
193 type: 'text',
194 data: text,
195 parent: null,
196 prev: null,
197 next: null
198 });
199 },
200
201 querySelector(sel) {
[e29cc2e]202 return selectOne(sel, this.documentElement);
[6a3a178]203 },
204
205 querySelectorAll(sel) {
206 if (sel === ':root') {
207 return this;
208 }
[e29cc2e]209 return selectAll(sel, this.documentElement);
[6a3a178]210 }
211};
212
213/**
214 * Essentially `Object.defineProperties()`, except function values are assigned as value descriptors for convenience.
215 * @private
216 */
217function defineProperties(obj, properties) {
218 for (const i in properties) {
219 const value = properties[i];
220 Object.defineProperty(
221 obj,
222 i,
223 typeof value === 'function' ? { value } : value
224 );
225 }
226}
227
228/**
229 * Create a property descriptor defining a getter/setter pair alias for a named attribute.
230 * @private
231 */
232function reflectedProperty(attributeName) {
233 return {
234 get() {
235 return this.getAttribute(attributeName);
236 },
237 set(value) {
238 this.setAttribute(attributeName, value);
239 }
240 };
241}
242
243/**
244 * Helper to get the text content of a node
245 * https://github.com/fb55/domutils/blob/master/src/stringify.ts#L21
246 * @private
247 */
248function getText(node) {
249 if (Array.isArray(node)) return node.map(getText).join('');
250 if (treeAdapter.isElementNode(node))
251 return node.name === 'br' ? '\n' : getText(node.children);
252 if (treeAdapter.isTextNode(node)) return node.data;
253 return '';
254}
Note: See TracBrowser for help on using the repository browser.