makestatic-parse-html
Version:
Parses HTML files to an abstract syntax tree
180 lines (161 loc) • 4.26 kB
JavaScript
/**
* Adapter for css-select so it ccan query the parse5 DOM tree.
*/
class DomAdapter {
static isTag (elem) {
return elem.tagName !== undefined
}
static getParent (elem) {
return elem.parentNode
}
static getName (elem) {
return elem.tagName
}
static getChildren (elem) {
/* istanbul ignore next: guard against no child nodes */
return elem.childNodes || []
}
static getText (elem, text = '') {
const childs = DomAdapter.getChildren(elem)
childs.forEach((node) => {
if (DomAdapter.isTag(node)) {
text += DomAdapter.getText(node, text)
} else if (node.nodeName === '#text') {
text += node.value
}
})
return text
}
static getSiblings (elem) {
const parent = DomAdapter.getParent(elem)
return parent && DomAdapter.getChildren(parent)
}
static existsOne (test, nodes) {
let node
for (let i = 0; i < nodes.length; i++) {
node = nodes[i]
if (DomAdapter.isTag(node) &&
(test(node) ||
DomAdapter.existsOne(test, DomAdapter.getChildren(node)))) {
return true
}
}
return false
}
static getAttributeValue (elem, name) {
let attr
/* istanbul ignore next: guard against no attributes */
let attrs = elem.attrs || []
for (let i = 0; i < attrs.length; i++) {
attr = attrs[i]
if (attr.name === name) {
return attr.value
}
}
}
static hasAttrib (elem, name) {
return DomAdapter.getAttributeValue(elem, name) !== undefined
}
static findOne (test, nodes) {
let childs, elem
for (let i = 0; i < nodes.length; i++) {
if (!DomAdapter.isTag(nodes[i])) {
continue
}
if (test(nodes[i])) {
return nodes[i]
}
childs = DomAdapter.getChildren(nodes[i])
elem = DomAdapter.findAll(test, childs)
if (elem.length) {
return elem[0]
}
}
}
static findAll (test, nodes) {
let result = []
let childs
for (let i = 0; i < nodes.length; i++) {
if (!DomAdapter.isTag(nodes[i])) {
continue
}
if (test(nodes[i])) {
result.push(nodes[i])
}
childs = DomAdapter.getChildren(nodes[i])
result = result.concat(DomAdapter.findAll(test, childs))
}
return result
}
/**
* Utility to get an attribute from a node.
*
* Iterates the attribute list and gets the first attribute that has
* the given name and optionally a specific value.
*
* Note this is an extension to the interface required by `css-select`.
*
* @static {function} getAttribute
* @param {Object} el the node.
* @param {String} name the attribute name.
* @param {String} [value] the attribute value.
*
* @returns the attribute or undefined.
*/
static getAttribute (el, name, value) {
const attrs = el.attrs
if (!attrs) {
return
}
let attr
for (let i = 0; i < attrs.length; i++) {
attr = attrs[i]
if (attr.name === name) {
if (value && attr.value === value) {
return attr
} else if (!value) {
return attr
}
}
}
}
static setAttributeValue (elem, name, value) {
/* istanbul ignore next: guard against no attributes */
let attrs = elem.attrs || []
let attr = attrs.find((att) => {
return att.name === name
})
if (attr) {
attr.value = value
} else {
attrs.push({name: name, value: value})
}
}
static createElement (tagName, attributes) {
let attrs = []
for (let k in attributes) {
attrs.push({name: k, value: attributes[k]})
}
return {
nodeName: tagName,
tagName: tagName,
attrs: attrs,
namespaceURI: 'http://www.w3.org/1999/xhtml',
childNodes: []
}
}
static createTextNode (data) {
return {nodeName: '#text', value: data}
}
static prependChild (parent, elem) {
// TODO: remove from existing element if parentNode
elem.parentNode = parent
parent.childNodes.unshift(elem)
}
static appendChild (parent, elem) {
// TODO: remove from existing element if parentNode
elem.parentNode = parent
parent.childNodes.push(elem)
}
}
module.exports = DomAdapter