| 3 | import { trimRight } from "../../js/util.js"; |
| 4 | |
| 5 | export default class HTMLParser { |
| 6 | root = { type: "root", children: [] }; |
| 7 | |
| 8 | constructor() { |
| 9 | this.tokenizer = new Tokenizer(); |
| 10 | this.setListener(); |
| 11 | } |
| 12 | |
| 13 | /** @param {string} tagName */ |
| 14 | openNewElement(tagName) { |
| 15 | const element = new Element(tagName); |
| 16 | this.addChildElement(element); |
| 17 | this.nodeStack.push(element); |
| 18 | this.openElem = element; |
| 19 | } |
| 20 | |
| 21 | /**@public @param {string} buffer*/ |
| 22 | parse(buffer) { |
| 23 | /**@type {Element[]} */ |
| 24 | this.nodeStack = [this.root]; |
| 25 | this.tokenizer.consume(buffer); |
| 26 | |
| 27 | const htmlElements = { type: "root", children: this.root.children }; |
| 28 | this.root = { type: "root", children: [] }; |
| 29 | return htmlElements; |
| 30 | } |
| 31 | |
| 32 | /** @param {Element|Text} element*/ |
| 33 | addChildElement(element) { |
| 34 | this.nodeStack.at(-1).children.push(element); |
| 35 | } |
| 36 | |
| 37 | /** @param {string} attrName */ |
| 38 | setAttrName(attrName) { |
| 39 | if (attrName.at(-1) === "=") this.attrName = attrName.slice(0, -1); |
| 40 | else { |
| 41 | this.openElem.attributes ??= {}; |
| 42 | this.openElem.attributes[attrName] = null; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | /** @param {string} attrVal*/ |
| 47 | setAttrValue(attrVal) { |
| 48 | if (this.attrName === "style") return (this.attrName = null); |
| 49 | this.openElem.attributes ??= {}; |
| 50 | const attrValue = attrVal; |
| 51 | this.openElem.attributes[this.attrName || attrVal] = attrValue; |
| 52 | this.attrName = null; |
| 53 | } |
| 54 | |
| 55 | /** @param {string} data */ |
| 56 | addTextNode(data) { |
| 57 | data = trimRight(data); |
| 58 | data.length === 0 || this.addChildElement(new Text(data)); |
| 59 | } |
| 60 | |
| 61 | setSelfCloseElem() { |
| 62 | this.openElem.children = undefined; |
nothing calls this directly
no outgoing calls
no test coverage detected