package template
import (
)
var transitionFunc = [...]func(context, []byte) (context, int){
stateText: tText,
stateTag: tTag,
stateAttrName: tAttrName,
stateAfterName: tAfterName,
stateBeforeValue: tBeforeValue,
stateHTMLCmt: tHTMLCmt,
stateRCDATA: tSpecialTagEnd,
stateAttr: tAttr,
stateURL: tURL,
stateSrcset: tURL,
stateJS: tJS,
stateJSDqStr: tJSDelimited,
stateJSSqStr: tJSDelimited,
stateJSRegexp: tJSDelimited,
stateJSBlockCmt: tBlockCmt,
stateJSLineCmt: tLineCmt,
stateCSS: tCSS,
stateCSSDqStr: tCSSStr,
stateCSSSqStr: tCSSStr,
stateCSSDqURL: tCSSStr,
stateCSSSqURL: tCSSStr,
stateCSSURL: tCSSStr,
stateCSSBlockCmt: tBlockCmt,
stateCSSLineCmt: tLineCmt,
stateError: tError,
}
var commentStart = []byte("<!--")
var commentEnd = []byte("-->")
func ( context, []byte) (context, int) {
:= 0
for {
:= + bytes.IndexByte([:], '<')
if < || +1 == len() {
return , len()
} else if +4 <= len() && bytes.Equal(commentStart, [:+4]) {
return context{state: stateHTMLCmt}, + 4
}
++
:= false
if [] == '/' {
if +1 == len() {
return , len()
}
, = true, +1
}
, := eatTagName(, )
if != {
if {
= elementNone
}
return context{state: stateTag, element: },
}
=
}
}
var elementContentType = [...]state{
elementNone: stateText,
elementScript: stateJS,
elementStyle: stateCSS,
elementTextarea: stateRCDATA,
elementTitle: stateRCDATA,
}
func ( context, []byte) (context, int) {
:= eatWhiteSpace(, 0)
if == len() {
return , len()
}
if [] == '>' {
return context{
state: elementContentType[.element],
element: .element,
}, + 1
}
, := eatAttrName(, )
if != nil {
return context{state: stateError, err: }, len()
}
, := stateTag, attrNone
if == {
return context{
state: stateError,
err: errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", [:]),
}, len()
}
:= strings.ToLower(string([:]))
if .element == elementScript && == "type" {
= attrScriptType
} else {
switch attrType() {
case contentTypeURL:
= attrURL
case contentTypeCSS:
= attrStyle
case contentTypeJS:
= attrScript
case contentTypeSrcset:
= attrSrcset
}
}
if == len() {
= stateAttrName
} else {
= stateAfterName
}
return context{state: , element: .element, attr: },
}
func ( context, []byte) (context, int) {
, := eatAttrName(, 0)
if != nil {
return context{state: stateError, err: }, len()
} else if != len() {
.state = stateAfterName
}
return ,
}
func ( context, []byte) (context, int) {
:= eatWhiteSpace(, 0)
if == len() {
return , len()
} else if [] != '=' {
.state = stateTag
return ,
}
.state = stateBeforeValue
return , + 1
}
var attrStartStates = [...]state{
attrNone: stateAttr,
attrScript: stateJS,
attrScriptType: stateAttr,
attrStyle: stateCSS,
attrURL: stateURL,
attrSrcset: stateSrcset,
}
func ( context, []byte) (context, int) {
:= eatWhiteSpace(, 0)
if == len() {
return , len()
}
:= delimSpaceOrTagEnd
switch [] {
case '\'':
, = delimSingleQuote, +1
case '"':
, = delimDoubleQuote, +1
}
.state, .delim = attrStartStates[.attr],
return ,
}
func ( context, []byte) (context, int) {
if := bytes.Index(, commentEnd); != -1 {
return context{}, + 3
}
return , len()
}
var specialTagEndMarkers = [...][]byte{
elementScript: []byte("script"),
elementStyle: []byte("style"),
elementTextarea: []byte("textarea"),
elementTitle: []byte("title"),
}
var (
specialTagEndPrefix = []byte("</")
tagEndSeparators = []byte("> \t\n\f/")
)
func ( context, []byte) (context, int) {
if .element != elementNone {
if := indexTagEnd(, specialTagEndMarkers[.element]); != -1 {
return context{},
}
}
return , len()
}
func ( []byte, []byte) int {
:= 0
:= len(specialTagEndPrefix)
for len() > 0 {
:= bytes.Index(, specialTagEndPrefix)
if == -1 {
return
}
= [+:]
if len() <= len() && bytes.EqualFold(, [:len()]) {
= [len():]
if len() > 0 && bytes.IndexByte(tagEndSeparators, [0]) != -1 {
return +
}
+= len()
}
+= +
}
return -1
}
func ( context, []byte) (context, int) {
return , len()
}
func ( context, []byte) (context, int) {
if bytes.ContainsAny(, "#?") {
.urlPart = urlPartQueryOrFrag
} else if len() != eatWhiteSpace(, 0) && .urlPart == urlPartNone {
.urlPart = urlPartPreQuery
}
return , len()
}
func ( context, []byte) (context, int) {
:= bytes.IndexAny(, `"'/`)
if == -1 {
.jsCtx = nextJSCtx(, .jsCtx)
return , len()
}
.jsCtx = nextJSCtx([:], .jsCtx)
switch [] {
case '"':
.state, .jsCtx = stateJSDqStr, jsCtxRegexp
case '\'':
.state, .jsCtx = stateJSSqStr, jsCtxRegexp
case '/':
switch {
case +1 < len() && [+1] == '/':
.state, = stateJSLineCmt, +1
case +1 < len() && [+1] == '*':
.state, = stateJSBlockCmt, +1
case .jsCtx == jsCtxRegexp:
.state = stateJSRegexp
case .jsCtx == jsCtxDivOp:
.jsCtx = jsCtxRegexp
default:
return context{
state: stateError,
err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", [:]),
}, len()
}
default:
panic("unreachable")
}
return , + 1
}
func ( context, []byte) (context, int) {
:= `\"`
switch .state {
case stateJSSqStr:
= `\'`
case stateJSRegexp:
= `\/[]`
}
, := 0, false
for {
:= + bytes.IndexAny([:], )
if < {
break
}
switch [] {
case '\\':
++
if == len() {
return context{
state: stateError,
err: errorf(ErrPartialEscape, nil, 0, "unfinished escape sequence in JS string: %q", ),
}, len()
}
case '[':
= true
case ']':
= false
default:
if ! {
.state, .jsCtx = stateJS, jsCtxDivOp
return , + 1
}
}
= + 1
}
if {
return context{
state: stateError,
err: errorf(ErrPartialCharset, nil, 0, "unfinished JS regexp charset: %q", ),
}, len()
}
return , len()
}
var blockCommentEnd = []byte("*/")
func ( context, []byte) (context, int) {
:= bytes.Index(, blockCommentEnd)
if == -1 {
return , len()
}
switch .state {
case stateJSBlockCmt:
.state = stateJS
case stateCSSBlockCmt:
.state = stateCSS
default:
panic(.state.String())
}
return , + 2
}
func ( context, []byte) (context, int) {
var string
var state
switch .state {
case stateJSLineCmt:
, = "\n\r\u2028\u2029", stateJS
case stateCSSLineCmt:
, = "\n\f\r", stateCSS
default:
panic(.state.String())
}
:= bytes.IndexAny(, )
if == -1 {
return , len()
}
.state =
return ,
}
func ( context, []byte) (context, int) {
:= 0
for {
:= + bytes.IndexAny([:], `("'/`)
if < {
return , len()
}
switch [] {
case '(':
:= bytes.TrimRight([:], "\t\n\f\r ")
if endsWithCSSKeyword(, "url") {
:= len() - len(bytes.TrimLeft([+1:], "\t\n\f\r "))
switch {
case != len() && [] == '"':
.state, = stateCSSDqURL, +1
case != len() && [] == '\'':
.state, = stateCSSSqURL, +1
default:
.state = stateCSSURL
}
return ,
}
case '/':
if +1 < len() {
switch [+1] {
case '/':
.state = stateCSSLineCmt
return , + 2
case '*':
.state = stateCSSBlockCmt
return , + 2
}
}
case '"':
.state = stateCSSDqStr
return , + 1
case '\'':
.state = stateCSSSqStr
return , + 1
}
= + 1
}
}
func ( context, []byte) (context, int) {
var string
switch .state {
case stateCSSDqStr, stateCSSDqURL:
= `\"`
case stateCSSSqStr, stateCSSSqURL:
= `\'`
case stateCSSURL:
= "\\\t\n\f\r )"
default:
panic(.state.String())
}
:= 0
for {
:= + bytes.IndexAny([:], )
if < {
, := tURL(, decodeCSS([:]))
return , +
}
if [] == '\\' {
++
if == len() {
return context{
state: stateError,
err: errorf(ErrPartialEscape, nil, 0, "unfinished escape sequence in CSS string: %q", ),
}, len()
}
} else {
.state = stateCSS
return , + 1
}
, _ = tURL(, decodeCSS([:+1]))
= + 1
}
}
func ( context, []byte) (context, int) {
return , len()
}
func ( []byte, int) (int, *Error) {
for := ; < len(); ++ {
switch [] {
case ' ', '\t', '\n', '\f', '\r', '=', '>':
return , nil
case '\'', '"', '<':
return -1, errorf(ErrBadHTML, nil, 0, "%q in attribute name: %.32q", [:+1], )
default:
}
}
return len(), nil
}
var elementNameMap = map[string]element{
"script": elementScript,
"style": elementStyle,
"textarea": elementTextarea,
"title": elementTitle,
}
func ( byte) bool {
return 'A' <= && <= 'Z' || 'a' <= && <= 'z'
}
func ( byte) bool {
return asciiAlpha() || '0' <= && <= '9'
}
func ( []byte, int) (int, element) {
if == len() || !asciiAlpha([]) {
return , elementNone
}
:= + 1
for < len() {
:= []
if asciiAlphaNum() {
++
continue
}
if ( == ':' || == '-') && +1 < len() && asciiAlphaNum([+1]) {
+= 2
continue
}
break
}
return , elementNameMap[strings.ToLower(string([:]))]
}
func ( []byte, int) int {
for := ; < len(); ++ {
switch [] {
case ' ', '\t', '\n', '\f', '\r':
default:
return
}
}
return len()
}