You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.6 KiB
JavaScript
123 lines
3.6 KiB
JavaScript
import * as HTMLParser2 from 'htmlparser2';
|
|
|
|
import {ELEMENT_NODE, SVG_NAMESPACE} from './constants.js';
|
|
import {CUSTOM_ELEMENTS, PREV, END, VALUE} from './symbols.js';
|
|
import {keys} from './object.js';
|
|
|
|
import {knownBoundaries, knownSiblings} from './utils.js';
|
|
import {attributeChangedCallback, connectedCallback} from '../interface/custom-element-registry.js';
|
|
|
|
const {Parser} = HTMLParser2;
|
|
|
|
// import {Mime} from './mime.js';
|
|
// const VOID_SOURCE = Mime['text/html'].voidElements.source.slice(4, -2);
|
|
// const VOID_ELEMENTS = new RegExp(`<(${VOID_SOURCE})([^>]*?)>`, 'gi');
|
|
// const VOID_SANITIZER = (_, $1, $2) => `<${$1}${$2}${/\/$/.test($2) ? '' : ' /'}>`;
|
|
// const voidSanitizer = html => html.replace(VOID_ELEMENTS, VOID_SANITIZER);
|
|
|
|
let notParsing = true;
|
|
|
|
const append = (self, node, active) => {
|
|
const end = self[END];
|
|
node.parentNode = self;
|
|
knownBoundaries(end[PREV], node, end);
|
|
if (active && node.nodeType === ELEMENT_NODE)
|
|
connectedCallback(node);
|
|
return node;
|
|
};
|
|
|
|
const attribute = (element, end, attribute, value, active) => {
|
|
attribute[VALUE] = value;
|
|
attribute.ownerElement = element;
|
|
knownSiblings(end[PREV], attribute, end);
|
|
if (attribute.name === 'class')
|
|
element.className = value;
|
|
if (active)
|
|
attributeChangedCallback(element, attribute.name, null, value);
|
|
};
|
|
|
|
export const isNotParsing = () => notParsing;
|
|
|
|
export const parseFromString = (document, isHTML, markupLanguage) => {
|
|
const {active, registry} = document[CUSTOM_ELEMENTS];
|
|
|
|
let node = document;
|
|
let ownerSVGElement = null;
|
|
let parsingCData = false;
|
|
|
|
notParsing = false;
|
|
|
|
const content = new Parser({
|
|
// <!DOCTYPE ...>
|
|
onprocessinginstruction(name, data) {
|
|
if (name.toLowerCase() === '!doctype')
|
|
document.doctype = data.slice(name.length).trim();
|
|
},
|
|
|
|
// <tagName>
|
|
onopentag(name, attributes) {
|
|
let create = true;
|
|
if (isHTML) {
|
|
if (ownerSVGElement) {
|
|
node = append(node, document.createElementNS(SVG_NAMESPACE, name), active);
|
|
node.ownerSVGElement = ownerSVGElement;
|
|
create = false;
|
|
}
|
|
else if (name === 'svg' || name === 'SVG') {
|
|
ownerSVGElement = document.createElementNS(SVG_NAMESPACE, name);
|
|
node = append(node, ownerSVGElement, active);
|
|
create = false;
|
|
}
|
|
else if (active) {
|
|
const ce = name.includes('-') ? name : (attributes.is || '');
|
|
if (ce && registry.has(ce)) {
|
|
const {Class} = registry.get(ce);
|
|
node = append(node, new Class, active);
|
|
delete attributes.is;
|
|
create = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (create)
|
|
node = append(node, document.createElement(name), false);
|
|
|
|
let end = node[END];
|
|
for (const name of keys(attributes))
|
|
attribute(node, end, document.createAttribute(name), attributes[name], active);
|
|
},
|
|
|
|
// #text, #comment
|
|
oncomment(data) { append(node, document.createComment(data), active); },
|
|
ontext(text) {
|
|
if (parsingCData) {
|
|
append(node, document.createCDATASection(text), active);
|
|
} else {
|
|
append(node, document.createTextNode(text), active);
|
|
}
|
|
},
|
|
|
|
// #cdata
|
|
oncdatastart() { parsingCData = true; },
|
|
oncdataend() { parsingCData = false; },
|
|
|
|
// </tagName>
|
|
onclosetag() {
|
|
if (isHTML && node === ownerSVGElement)
|
|
ownerSVGElement = null;
|
|
node = node.parentNode;
|
|
}
|
|
}, {
|
|
lowerCaseAttributeNames: false,
|
|
decodeEntities: true,
|
|
xmlMode: !isHTML
|
|
});
|
|
|
|
content.write(markupLanguage);
|
|
content.end();
|
|
|
|
notParsing = true;
|
|
|
|
return document;
|
|
};
|