export interface Callbacks {
ontext(start: number, endIndex: number): void
- ontextentity(char: string, endIndex: number): void
+ ontextentity(char: string, start: number, endIndex: number): void
oninterpolation(start: number, endIndex: number): void
onclosetag(start: number, endIndex: number): void
onattribdata(start: number, endIndex: number): void
- onattribentity(char: string): void
+ onattribentity(char: string, start: number, end: number): void
onattribend(quote: QuoteType, endIndex: number): void
onattribname(start: number, endIndex: number): void
onattribnameend(endIndex: number): void
} else {
this.delimiterIndex++
}
+ } else if (this.inRCDATA) {
+ this.state = State.InSpecialTag
+ this.stateInSpecialTag(c)
} else {
this.state = State.Text
this.stateText(c)
if (c === this.delimiterClose[this.delimiterIndex]) {
if (this.delimiterIndex === this.delimiterClose.length - 1) {
this.cbs.oninterpolation(this.sectionStart, this.index + 1)
- this.state = State.Text
+ if (this.inRCDATA) {
+ this.state = State.InSpecialTag
+ } else {
+ this.state = State.Text
+ }
this.sectionStart = this.index + 1
} else {
this.delimiterIndex++
// We have to parse entities in <title> and <textarea> tags.
if (!__BROWSER__ && c === CharCodes.Amp) {
this.startEntity()
+ } else if (c === this.delimiterOpen[0]) {
+ // We also need to handle interpolation
+ this.state = State.InterpolationOpen
+ this.delimiterIndex = 0
+ this.stateInterpolationOpen(c)
}
} else if (this.fastForwardTo(CharCodes.Lt)) {
// Outside of <title> and <textarea> tags, we can fast-forward.
this.sectionStart = this.entityStart + consumed
this.index = this.sectionStart - 1
- this.cbs.onattribentity(fromCodePoint(cp))
+ this.cbs.onattribentity(
+ fromCodePoint(cp),
+ this.entityStart,
+ this.sectionStart
+ )
} else {
if (this.sectionStart < this.entityStart) {
this.cbs.ontext(this.sectionStart, this.entityStart)
this.sectionStart = this.entityStart + consumed
this.index = this.sectionStart - 1
- this.cbs.ontextentity(fromCodePoint(cp), this.sectionStart)
+ this.cbs.ontextentity(
+ fromCodePoint(cp),
+ this.entityStart,
+ this.sectionStart
+ )
}
}
}
import { NO, extend } from '@vue/shared'
import { defaultOnError, defaultOnWarn } from '../errors'
import { forAliasRE, isCoreComponent } from '../utils'
+import { decodeHTML } from 'entities/lib/decode.js'
type OptionalOptions =
| 'decodeEntities'
onText(getSlice(start, end), start, end)
},
- ontextentity(char, end) {
- onText(char, end - 1, end)
+ ontextentity(char, start, end) {
+ onText(char, start, end)
},
oninterpolation(start, end) {
while (isWhitespace(currentInput.charCodeAt(innerEnd - 1))) {
innerEnd--
}
+ let exp = getSlice(innerStart, innerEnd)
+ // decode entities for backwards compat
+ if (exp.includes('&')) {
+ if (__BROWSER__) {
+ exp = currentOptions.decodeEntities!(exp, false)
+ } else {
+ exp = decodeHTML(exp)
+ }
+ }
addNode({
type: NodeTypes.INTERPOLATION,
- content: createSimpleExpression(
- getSlice(innerStart, innerEnd),
- false,
- getLoc(innerStart, innerEnd)
- ),
+ content: createSimpleExpression(exp, false, getLoc(innerStart, innerEnd)),
loc: getLoc(start, end)
})
},
currentElement = {
type: NodeTypes.ELEMENT,
tag: name,
- ns: currentOptions.getNamespace(name, getParent()),
+ ns: currentOptions.getNamespace(name, stack[0]),
tagType: ElementTypes.ELEMENT, // will be refined on tag close
props: [],
children: [],
currentAttrEndIndex = end
},
- onattribentity(char) {
+ onattribentity(char, start, end) {
currentAttrValue += char
+ if (currentAttrStartIndex < 0) currentAttrStartIndex = start
+ currentAttrEndIndex = end
},
onattribnameend(end) {
},
oncdata(start, end) {
- // TODO throw error
+ if (stack[0].ns !== Namespaces.HTML) {
+ onText(getSlice(start, end), start, end)
+ } else {
+ // TODO throw error if ns is html
+ }
}
})
// TODO do not do this in <script> or <style>
content = currentOptions.decodeEntities!(content, false)
}
- const parent = getParent()
+ const parent = stack[0] || currentRoot
const lastNode = parent.children[parent.children.length - 1]
if (lastNode?.type === NodeTypes.TEXT) {
// merge
function onCloseTag(el: ElementNode, end: number) {
// attach end position
let offset = 0
- while (currentInput.charCodeAt(end + offset) !== CharCodes.Gt) {
+ while (
+ currentInput.charCodeAt(end + offset) !== CharCodes.Gt &&
+ end + offset < currentInput.length
+ ) {
offset++
}
el.loc.end = tokenizer.getPos(end + offset + 1)
}
function addNode(node: TemplateChildNode) {
- getParent().children.push(node)
-}
-
-function getParent() {
- return stack[0] || currentRoot
+ ;(stack[0] || currentRoot).children.push(node)
}
function getLoc(start: number, end?: number): SourceLocation {