Copyright 2011 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package template

import (
	
	
)
transitionFunc is the array of context transition functions for text nodes. A transition function takes a context and template text input, and returns the updated context and the number of bytes consumed from the front of the input.
tText is the context transition function for the text state.
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
tTag is the context transition function for the tag state.
Find the attribute name.
	 := 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: }, 
}
tAttrName is the context transition function for stateAttrName.
func ( context,  []byte) (context, int) {
	,  := eatAttrName(, 0)
	if  != nil {
		return context{state: stateError, err: }, len()
	} else if  != len() {
		.state = stateAfterName
	}
	return , 
}
tAfterName is the context transition function for stateAfterName.
Look for the start of the value.
	 := eatWhiteSpace(, 0)
	if  == len() {
		return , len()
Occurs due to tag ending '>', and valueless attribute.
		.state = stateTag
		return , 
	}
tBeforeValue is the context transition function for stateBeforeValue.
func ( context,  []byte) (context, int) {
	 := eatWhiteSpace(, 0)
	if  == len() {
		return , len()
Find the attribute delimiter.
	 := delimSpaceOrTagEnd
	switch [] {
	case '\'':
		,  = delimSingleQuote, +1
	case '"':
		,  = delimDoubleQuote, +1
	}
	.state, .delim = attrStartStates[.attr], 
	return , 
}
tHTMLCmt is the context transition function for stateHTMLCmt.
func ( context,  []byte) (context, int) {
	if  := bytes.Index(, commentEnd);  != -1 {
		return context{},  + 3
	}
	return , len()
}
specialTagEndMarkers maps element types to the character sequence that case-insensitively signals the end of the special tag body.
var specialTagEndMarkers = [...][]byte{
	elementScript:   []byte("script"),
	elementStyle:    []byte("style"),
	elementTextarea: []byte("textarea"),
	elementTitle:    []byte("title"),
}

var (
	specialTagEndPrefix = []byte("</")
	tagEndSeparators    = []byte("> \t\n\f/")
)
tSpecialTagEnd is the context transition function for raw text and RCDATA element states.
func ( context,  []byte) (context, int) {
	if .element != elementNone {
		if  := indexTagEnd(, specialTagEndMarkers[.element]);  != -1 {
			return context{}, 
		}
	}
	return , len()
}
indexTagEnd finds the index of a special tag end in a case insensitive way, or returns -1
func ( []byte,  []byte) int {
	 := 0
	 := len(specialTagEndPrefix)
Try to find the tag end prefix first
		 := bytes.Index(, specialTagEndPrefix)
		if  == -1 {
			return 
		}
Try to match the actual tag if there is still space for it
		if len() <= len() && bytes.EqualFold(, [:len()]) {
Check the tag is followed by a proper separator
			if len() > 0 && bytes.IndexByte(tagEndSeparators, [0]) != -1 {
				return  + 
			}
			 += len()
		}
		 +=  + 
	}
	return -1
}
tAttr is the context transition function for the attribute state.
func ( context,  []byte) (context, int) {
	return , len()
}
tURL is the context transition function for the URL state.
func ( context,  []byte) (context, int) {
	if bytes.ContainsAny(, "#?") {
		.urlPart = urlPartQueryOrFrag
HTML5 uses "Valid URL potentially surrounded by spaces" for attrs: https://www.w3.org/TR/html5/index.html#attributes-1
		.urlPart = urlPartPreQuery
	}
	return , len()
}
tJS is the context transition function for the JS state.
func ( context,  []byte) (context, int) {
	 := bytes.IndexAny(, `"'/`)
Entire input is non string, comment, regexp tokens.
		.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
}
tJSDelimited is the context transition function for the JS string and regexp states.
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
end delimiter
			if ! {
				.state, .jsCtx = stateJS, jsCtxDivOp
				return ,  + 1
			}
		}
		 =  + 1
	}

This can be fixed by making context richer if interpolation into charsets is desired.
		return context{
			state: stateError,
			err:   errorf(ErrPartialCharset, nil, 0, "unfinished JS regexp charset: %q", ),
		}, len()
	}

	return , len()
}

var blockCommentEnd = []byte("*/")
tBlockCmt is the context transition function for comment states.
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
}
tLineCmt is the context transition function for //comment states.
func ( context,  []byte) (context, int) {
	var  string
	var  state
	switch .state {
	case stateJSLineCmt:
		,  = "\n\r\u2028\u2029", stateJS
	case stateCSSLineCmt:
Line comments are not part of any published CSS standard but are supported by the 4 major browsers. This defines line comments as LINECOMMENT ::= "//" [^\n\f\d]* since https://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines newlines: nl ::= #xA | #xD #xA | #xD | #xC
	default:
		panic(.state.String())
	}

	 := bytes.IndexAny(, )
	if  == -1 {
		return , len()
	}
Per section 7.4 of EcmaScript 5 : https://es5.github.com/#x7.4 "However, the LineTerminator at the end of the line is not considered to be part of the single-line comment; it is recognized separately by the lexical grammar and becomes part of the stream of input elements for the syntactic grammar."
	return , 
}
tCSS is the context transition function for the CSS state.
CSS quoted strings are almost never used except for: (1) URLs as in background: "/foo.png" (2) Multiword font-names as in font-family: "Times New Roman" (3) List separators in content values as in inline-lists: <style> ul.inlineList { list-style: none; padding:0 } ul.inlineList > li { display: inline } ul.inlineList > li:before { content: ", " } ul.inlineList > li:first-child:before { content: "" } </style> <ul class=inlineList><li>One<li>Two<li>Three</ul> (4) Attribute value selectors as in a[href="http://example.com/"] We conservatively treat all strings as URLs, but make some allowances to avoid confusion. In (1), our conservative assumption is justified. In (2), valid font names do not contain ':', '?', or '#', so our conservative assumption is fine since we will never transition past urlPartPreQuery. In (3), our protocol heuristic should not be tripped, and there should not be non-space content after a '?' or '#', so as long as we only %-encode RFC 3986 reserved characters we are ok. In (4), we should URL escape for URL attributes, and for others we have the attribute name available if our conservative assumption proves problematic for real code.

	 := 0
	for {
		 :=  + bytes.IndexAny([:], `("'/`)
		if  <  {
			return , len()
		}
		switch [] {
Look for url to the left.
			 := 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
	}
}
tCSSStr is the context transition function for the CSS string and URL states.
func ( context,  []byte) (context, int) {
	var  string
	switch .state {
	case stateCSSDqStr, stateCSSDqURL:
		 = `\"`
	case stateCSSSqStr, stateCSSSqURL:
		 = `\'`
Unquoted URLs end with a newline or close parenthesis. The below includes the wc (whitespace character) and nl.
		 = "\\\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
	}
}
tError is the context transition function for the error state.
func ( context,  []byte) (context, int) {
	return , len()
}
eatAttrName returns the largest j such that s[i:j] is an attribute name. It returns an error if s[i:] does not look like it begins with an attribute name, such as encountering a quote mark without a preceding equals sign.
func ( []byte,  int) (int, *Error) {
	for  := ;  < len(); ++ {
		switch [] {
		case ' ', '\t', '\n', '\f', '\r', '=', '>':
			return , nil
These result in a parse warning in HTML5 and are indicative of serious problems if seen in an attr name in a template.
			return -1, errorf(ErrBadHTML, nil, 0, "%q in attribute name: %.32q", [:+1], )
No-op.
		}
	}
	return len(), nil
}

var elementNameMap = map[string]element{
	"script":   elementScript,
	"style":    elementStyle,
	"textarea": elementTextarea,
	"title":    elementTitle,
}
asciiAlpha reports whether c is an ASCII letter.
func ( byte) bool {
	return 'A' <=  &&  <= 'Z' || 'a' <=  &&  <= 'z'
}
asciiAlphaNum reports whether c is an ASCII letter or digit.
func ( byte) bool {
	return asciiAlpha() || '0' <=  &&  <= '9'
}
eatTagName returns the largest j such that s[i:j] is a tag name and the tag type.
func ( []byte,  int) (int, element) {
	if  == len() || !asciiAlpha([]) {
		return , elementNone
	}
	 :=  + 1
	for  < len() {
		 := []
		if asciiAlphaNum() {
			++
			continue
Allow "x-y" or "x:y" but not "x-", "-y", or "x--y".
		if ( == ':' ||  == '-') && +1 < len() && asciiAlphaNum([+1]) {
			 += 2
			continue
		}
		break
	}
	return , elementNameMap[strings.ToLower(string([:]))]
}
eatWhiteSpace returns the largest j such that s[i:j] is white space.
func ( []byte,  int) int {
	for  := ;  < len(); ++ {
		switch [] {
No-op.
		default:
			return 
		}
	}
	return len()