mirror of https://github.com/jkjoy/sunpeiwen.git
91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
import { isTag } from 'domhandler';
|
|
import { Picker } from 'selderee';
|
|
|
|
function hp2Builder(nodes) {
|
|
return new Picker(handleArray(nodes));
|
|
}
|
|
function handleArray(nodes) {
|
|
const matchers = nodes.map(handleNode);
|
|
return (el, ...tail) => matchers.flatMap(m => m(el, ...tail));
|
|
}
|
|
function handleNode(node) {
|
|
switch (node.type) {
|
|
case 'terminal': {
|
|
const result = [node.valueContainer];
|
|
return (el, ...tail) => result;
|
|
}
|
|
case 'tagName':
|
|
return handleTagName(node);
|
|
case 'attrValue':
|
|
return handleAttrValueName(node);
|
|
case 'attrPresence':
|
|
return handleAttrPresenceName(node);
|
|
case 'pushElement':
|
|
return handlePushElementNode(node);
|
|
case 'popElement':
|
|
return handlePopElementNode(node);
|
|
}
|
|
}
|
|
function handleTagName(node) {
|
|
const variants = {};
|
|
for (const variant of node.variants) {
|
|
variants[variant.value] = handleArray(variant.cont);
|
|
}
|
|
return (el, ...tail) => {
|
|
const continuation = variants[el.name];
|
|
return (continuation) ? continuation(el, ...tail) : [];
|
|
};
|
|
}
|
|
function handleAttrPresenceName(node) {
|
|
const attrName = node.name;
|
|
const continuation = handleArray(node.cont);
|
|
return (el, ...tail) => (Object.prototype.hasOwnProperty.call(el.attribs, attrName))
|
|
? continuation(el, ...tail)
|
|
: [];
|
|
}
|
|
function handleAttrValueName(node) {
|
|
const callbacks = [];
|
|
for (const matcher of node.matchers) {
|
|
const predicate = matcher.predicate;
|
|
const continuation = handleArray(matcher.cont);
|
|
callbacks.push((attr, el, ...tail) => (predicate(attr) ? continuation(el, ...tail) : []));
|
|
}
|
|
const attrName = node.name;
|
|
return (el, ...tail) => {
|
|
const attr = el.attribs[attrName];
|
|
return (attr || attr === '')
|
|
? callbacks.flatMap(cb => cb(attr, el, ...tail))
|
|
: [];
|
|
};
|
|
}
|
|
function handlePushElementNode(node) {
|
|
const continuation = handleArray(node.cont);
|
|
const leftElementGetter = (node.combinator === '+')
|
|
? getPrecedingElement
|
|
: getParentElement;
|
|
return (el, ...tail) => {
|
|
const next = leftElementGetter(el);
|
|
if (next === null) {
|
|
return [];
|
|
}
|
|
return continuation(next, el, ...tail);
|
|
};
|
|
}
|
|
const getPrecedingElement = (el) => {
|
|
const prev = el.prev;
|
|
if (prev === null) {
|
|
return null;
|
|
}
|
|
return (isTag(prev)) ? prev : getPrecedingElement(prev);
|
|
};
|
|
const getParentElement = (el) => {
|
|
const parent = el.parent;
|
|
return (parent && isTag(parent)) ? parent : null;
|
|
};
|
|
function handlePopElementNode(node) {
|
|
const continuation = handleArray(node.cont);
|
|
return (el, next, ...tail) => continuation(next, ...tail);
|
|
}
|
|
|
|
export { hp2Builder };
|