@adobe/htlengine
Version:
Javascript Based HTL (Sightly) parser
159 lines (143 loc) • 4.51 kB
JavaScript
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-disable class-methods-use-this */
const DOMFactory = require('./DOMFactory');
module.exports = class VDOMFactory extends DOMFactory {
/**
* Create a new DOM factory.
* @param {DOMImplementation} domImplementation
*/
constructor(domImplementation) {
super();
this._impl = domImplementation;
}
/**
* If set to {@code true}, calling {@link #end()} will keep the document fragment if it exists.
* Otherwise a `<body>` element is returned.
* @param {boolean} value flag that dictates the return behaviour.
* @returns {VDOMFactory} this.
*/
withKeepFragment(value) {
this._keepFragment = value;
return this;
}
start() {
this._doc = this._impl.createHTMLDocument();
this._isFragment = true;
this._root = this._doc.createDocumentFragment();
this._usedNodes = [];
return this._root;
}
end() {
if (this._isFragment) {
if (this._keepFragment) {
return this._root;
}
this._doc.body.appendChild(this._root);
return this._doc.body;
}
return this._doc;
}
doctype(node, name) {
if (name !== 'html') {
this._doc.doctype.name = name;
}
this._isFragment = false;
this._root = this._doc.documentElement;
return this._root;
}
create(tagName) {
const tag = tagName.toLowerCase();
if (tag === 'html') {
this._isFragment = false;
return this._doc.documentElement;
}
if (tag === 'head') {
this._isFragment = false;
return this._doc.head;
}
if (tag === 'body') {
this._isFragment = false;
return this._doc.body;
}
return this._doc.createElement(tagName);
}
append(node, value) {
if (typeof value === 'object' && value.cloneNode) {
if (value.nodeName === 'BODY') {
// we do not clone the body, as it will not be reused in the document again
// if this is not desired, the calling application can clone the document first.
if (node.nodeName === 'HTML') {
// case: <html>${resource.document.body}</html>
node.removeChild(this._doc.body);
node.appendChild(value);
return;
}
// case <html><body>${resource.document.body}</body></html>
// or <div>${resource.document.body}</div>
while (value.firstChild) {
node.appendChild(value.firstChild);
}
return;
}
if (this._usedNodes.includes(value)) {
// eslint-disable-next-line no-param-reassign
value = value.cloneNode(true);
} else {
this._usedNodes.push(value);
}
node.appendChild(value);
return;
}
// node list
if (typeof value === 'object' && value.forEach) {
Array.from(value).forEach((child) => {
if (typeof child !== 'object' || !child.cloneNode) {
const template = this._doc.createElement('template');
template.innerHTML = String(child);
// eslint-disable-next-line no-param-reassign
child = template.content;
} else {
// eslint-disable-next-line no-param-reassign
child = child.cloneNode(true);
}
node.appendChild(child);
});
return;
}
const template = this._doc.createElement('template');
template.innerHTML = String(value);
node.appendChild(template.content);
}
text(node, text) {
// ignore text in document node
if (node.nodeName === '#document') {
return;
}
node.appendChild(this._doc.createTextNode(text));
}
rem(node, text) {
node.appendChild(this._doc.createComment(text));
}
attr(node, name, value) {
node.setAttribute(name, value != null ? value : '');
}
push(parent, node) {
if (node.nodeName === 'HTML') {
return node;
}
return parent.appendChild(node);
}
pop(node) {
return node.parentNode;
}
};