Skip to content

Commit

Permalink
Added more Support for more complex HTML like with script tags
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianFun123 committed Mar 4, 2024
1 parent c9c7dca commit 121cfb6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 68 deletions.
125 changes: 79 additions & 46 deletions src/template/JDOMTemplateParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -94,38 +99,12 @@ export default class JDOMTemplateParser {
opened = true
}
} else if (closingTag) {
const next = `</`

// TODO: Check why -1 is needed
if (this.nextIs(next, -1)) {
let isEnding = false
// typeof tag.tag === 'string'
const afterClosingSlash = this.get(this.index + 1)
if (afterClosingSlash.type === 'value') {
if (typeof afterClosingSlash.value === 'function' && afterClosingSlash.value === tag.tag) {
isEnding = true
tag.attributes.slot = tag.body
this.next()
}
} else {
if (!this.nextIs(tag.tag)) {
isEnding = true
this.next(tag.tag.length)
}
}

if (isEnding) {
this.next(next.length - 1)
this.skipEmpty()
// Skipping >
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()
Expand All @@ -135,6 +114,40 @@ export default class JDOMTemplateParser {
return tag
}

isClosingTag(tag, ind = -1) {
const index = this.index - ind
const next = `</`

// TODO: Check why -1 is needed
if (this.nextIs(next, ind)) {
let isEnding = false
const afterClosingSlash = this.get(index + 2)

if (afterClosingSlash.type === 'value') {
if (afterClosingSlash.value === tag.tag) {
isEnding = true
tag.attributes.slot = tag.body
this.next()
}
} else {
if (typeof tag.tag === 'string' && !this.nextIs(tag.tag)) {
isEnding = true
this.next(tag.tag.length)
}
}

if (isEnding) {
this.next(next.length - 1)
this.skipEmpty()
// Skipping >
this.next()
}

return isEnding
}
return false
}

readAttributes() {
const attributes = []
let inAttribute = false
Expand Down Expand Up @@ -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)) {
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
}
}
Expand Down Expand Up @@ -293,33 +317,41 @@ export default class JDOMTemplateParser {
return false
}

readContent() {
readContent(ignoreTags = false, tag = false) {
const contents = []
let currentString = ''
this.skipEmpty()
while (this.hasNext()) {
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})
Expand All @@ -329,6 +361,7 @@ export default class JDOMTemplateParser {
this.next()
continue
}
currentString += value

this.next()
}
Expand Down
54 changes: 32 additions & 22 deletions src/template/TemplateDOMAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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})
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/template/TemplateJDOMAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down
1 change: 1 addition & 0 deletions src/template/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 121cfb6

Please sign in to comment.