From 121cfb66e0bc9de9483d13de7495aba49304cf02 Mon Sep 17 00:00:00 2001 From: juliangojani Date: Mon, 4 Mar 2024 15:31:46 +0100 Subject: [PATCH] Added more Support for more complex HTML like with script tags --- src/template/JDOMTemplateParser.js | 125 ++++++++++++++++++---------- src/template/TemplateDOMAdapter.js | 54 +++++++----- src/template/TemplateJDOMAdapter.js | 3 + src/template/template.js | 1 + 4 files changed, 115 insertions(+), 68 deletions(-) diff --git a/src/template/JDOMTemplateParser.js b/src/template/JDOMTemplateParser.js index 1c57a98..a923179 100644 --- a/src/template/JDOMTemplateParser.js +++ b/src/template/JDOMTemplateParser.js @@ -47,18 +47,23 @@ export default class JDOMTemplateParser { } isWhiteSpace(c) { - return c === '\n' || c === ' ' + if (c === undefined) return false + if (typeof c !== 'string') return false + return c === '\n' || c === ' ' || c === '\t' || c === '\v' || c === '\f' } /** - * @return {{attributes: {}, from: number, tag: string, to: number, type: string, body: *[]}} + * @return {{attributes: {}, from: number, tag: string, to: number, type: string, body: *[]}|null} */ readTag() { let tag = { type: 'element', tag: '', attributes: [], body: [], from: this.index, to: 0 } let opened = '' let tagNameOpened = true let closingTag = false + let i = 0 while (this.hasNext()) { + const index = i++ + const {type, value} = this.get() if (!opened) { @@ -94,38 +99,12 @@ export default class JDOMTemplateParser { opened = true } } else if (closingTag) { - const next = ` - this.next() - } - - break - } + break } else { - tag.body = this.readContent() + tag.body = this.readContent(['script', 'style'].includes(tag.tag), tag) closingTag = true + this.next() + break } this.next() @@ -135,6 +114,40 @@ export default class JDOMTemplateParser { return tag } + isClosingTag(tag, ind = -1) { + const index = this.index - ind + const next = ` + this.next() + } + + return isEnding + } + return false + } + readAttributes() { const attributes = [] let inAttribute = false @@ -189,8 +202,9 @@ export default class JDOMTemplateParser { return true } if (value === '=' || this.isWhiteSpace(value)) { - if (value === '=') + if (value === '=') { return true + } let i = this.index while (this.get(i) && this.isWhiteSpace(this.get(i).value)) { @@ -206,7 +220,7 @@ export default class JDOMTemplateParser { if (name === '>') break if (!out.name) - out.name = name + out.name = name.trim() if (doBreak) break hasReadName = true continue @@ -225,13 +239,22 @@ export default class JDOMTemplateParser { let val = '' let opener = '' let i = 0 + let [{value}] = this.readUntil(({type, value}) => { let doNotAdd = false - if (i++ === 0 && (value === '"' || value === "'")) - opener = value + if (i++ === 0) { + if (value === '"' || value === "'") + opener = value + else + opener = ' ' + } - if (value === opener) { + if (opener === ' ' && (this.isWhiteSpace(value) || value === '>' || this.nextIs('/>'))) { + return true + } + + if (opener !== ' ' && value === opener) { if (opened) { return true } @@ -240,7 +263,8 @@ export default class JDOMTemplateParser { if (!doNotAdd) val += value }) - out.value = value.substring(1) + + out.value = opener === ' ' ? value : value.substring(1) break } } @@ -293,7 +317,7 @@ export default class JDOMTemplateParser { return false } - readContent() { + readContent(ignoreTags = false, tag = false) { const contents = [] let currentString = '' this.skipEmpty() @@ -301,25 +325,33 @@ export default class JDOMTemplateParser { const { type, value } = this.get() if (type === 'char') { - if (value === '<') { + if (value === '<' && !this.isWhiteSpace(this.get(this.index + 1)?.value)) { + const currentIndex = this.index + if (tag !== false && this.isClosingTag(tag, 0)) { + break + } else { + this.index = currentIndex + if (ignoreTags) { + currentString += '<' + this.next() + continue + } + } if (currentString !== '') { contents.push({type: 'text', value: currentString}) currentString = '' } - if (this.get(this.index+1)?.value === '/') { - break - } - if (this.shipComment()) continue - contents.push(this.readTag()) + const newTag = this.readTag() + if (newTag !== null) + contents.push(newTag) this.skipEmpty() continue } - currentString += value } else { if (currentString !== '') { contents.push({type: 'text', value: currentString}) @@ -329,6 +361,7 @@ export default class JDOMTemplateParser { this.next() continue } + currentString += value this.next() } diff --git a/src/template/TemplateDOMAdapter.js b/src/template/TemplateDOMAdapter.js index 8a2f4c1..da559e7 100644 --- a/src/template/TemplateDOMAdapter.js +++ b/src/template/TemplateDOMAdapter.js @@ -10,36 +10,46 @@ export default class TemplateDOMAdapter { let el; let svg = false - if (typeof conf.tag === 'function') { - const elAttribs = {} - if (conf.body.length > 0) { - elAttribs.$slot = (new TemplateDOMAdapter(conf.body)).create(svg) - } - for (const [key, value] of conf.attributes) { - if (key === ':bind') { - elAttribs.value = value - } else { - elAttribs[key] = value + if (typeof conf.tag === 'string') { + if (typeof conf.tag === 'string' && conf.tag.toLowerCase() === '!doctype') + return null + + if (conf.tag === 'svg' || this.inSVG) { + el = document.createElementNS('http://www.w3.org/2000/svg', conf.tag) + svg = true + } else if (conf.tag.includes('-') && window?.customElements) { + const customElement = window.customElements.get(conf.tag) + if (customElement) { + el = new customElement() } } + } else if (conf.tag instanceof Node) { + el = conf.tag + } else if (conf.tag instanceof JDOM) { + el = conf.tag.firstNode() + } else if (typeof conf.tag === 'function') { + if (conf.tag.prototype && conf.tag.prototype instanceof HTMLElement) { + el = new conf.tag() + } else { + const elAttribs = {} - const newEl = conf.tag(elAttribs) + if (conf.body.length > 0) { + elAttribs.$slot = (new TemplateDOMAdapter(conf.body)).create(svg) + } - return this.createFromValue({value: newEl}) - } + for (const [key, value] of conf.attributes) { + if (key === ':bind') { + elAttribs.value = value + } else { + elAttribs[key] = value + } + } - if (conf.tag.toLowerCase() === '!doctype') - return null + const newEl = conf.tag(elAttribs) - if (conf.tag === 'svg' || this.inSVG) { - el = document.createElementNS('http://www.w3.org/2000/svg', conf.tag) - svg = true - } else if (conf.tag.includes('-') && window?.customElements) { - const customElement = window.customElements.get(conf.tag) - if (customElement) { - el = new customElement() + return this.createFromValue({value: newEl}) } } diff --git a/src/template/TemplateJDOMAdapter.js b/src/template/TemplateJDOMAdapter.js index 8b1a4bc..69ba45e 100644 --- a/src/template/TemplateJDOMAdapter.js +++ b/src/template/TemplateJDOMAdapter.js @@ -2,6 +2,9 @@ import TemplateDOMAdapter from './TemplateDOMAdapter.js' import JDOM from '../JDOM.js' export default class TemplateJDOMAdapter extends TemplateDOMAdapter { + /** + * @return {JDOM} + */ create() { return new JDOM(super.create()) } diff --git a/src/template/template.js b/src/template/template.js index 26701e8..fe9e142 100644 --- a/src/template/template.js +++ b/src/template/template.js @@ -15,6 +15,7 @@ export function html(strings, ...values) { const parser = JDOMTemplateParser.fromTemplate(strings, ...values) const parsed = parser.parse() + const adapter = new TemplateJDOMAdapter(parsed) // console.timeEnd("myFunction"); return adapter.create()