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(, )
We've found an HTML tag.
Element name not needed if we are at the end of the element.
			if ! {
				.element = 
			}
			return , 
		}
		 = 
	}
}
specialElements contains the names of elements whose bodies are treated differently by the parser and escaper from stateText.
var specialElements = map[string]bool{
	"script":   true,
	"style":    true,
	"textarea": true,
	"title":    true,
}
voidElements contains the names of all void elements. https://www.w3.org/TR/html5/syntax.html#void-elements
var voidElements = map[string]bool{
	"area":   true,
	"base":   true,
	"br":     true,
	"col":    true,
	"embed":  true,
	"hr":     true,
	"img":    true,
	"input":  true,
	"keygen": true,
	"link":   true,
	"meta":   true,
	"param":  true,
	"source": true,
	"track":  true,
	"wbr":    true,
}
tTag is the context transition function for the tag state.
Find the attribute name.
	 := eatWhiteSpace(, 0)
	if  == len() {
		return , len()
	}
	if [] == '>' {
		 := context{
			state:      stateText,
			element:    .element,
			scriptType: .scriptType,
			linkRel:    .linkRel,
		}
		if specialElements[.element.name] {
			.state = stateSpecialElementBody
		}
Special case: end of start tag of a void element. Discard unnecessary state, since this element have no content.
			.element = element{}
			.scriptType = ""
			.linkRel = ""
		}
		return ,  + 1
	}
	,  := eatAttrName(, )
	if  != nil {
		return context{state: stateError, err: }, len()
	}
	 := stateTag
	if  ==  {
		return context{
			state: stateError,
			err:   errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", [:]),
		}, len()
	}

	if  == len() {
		 = stateAttrName
	} else {
		 = stateAfterName
	}
	return context{
		state:   ,
		element: .element,
		attr:    attr{name: strings.ToLower(string([:]))},
		linkRel: .linkRel,
	}, 
}
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 , 
	}
Consume the "=".
	return ,  + 1
}
tBeforeValue is the context transition function for stateBeforeValue.
func ( context,  []byte) (context, int) {
	 := eatWhiteSpace(, 0)
	if  == len() {
		return , len()
Find the attribute delimiter. TODO: consider disallowing single-quoted or unquoted attribute values completely, even in hardcoded template text.
	 := delimSpaceOrTagEnd
	switch [] {
	case '\'':
		,  = delimSingleQuote, +1
	case '"':
		,  = delimDoubleQuote, +1
	}
	.state, .delim = stateAttr, 
	return , 
}
tHTMLCmt is the context transition function for stateHTMLCmt.
func ( context,  []byte) (context, int) {
	if  := bytes.Index(, commentEnd);  != -1 {
		return context{},  + 3
	}
	return , len()
}

var (
	specialTagEndPrefix = []byte("</")
	tagEndSeparators    = []byte("> \t\n\f/")
)
tSpecialTagEnd is the context transition function for raw text, RCDATA script data, and stylesheet element states.
func ( context,  []byte) (context, int) {
	if specialElements[.element.name] {
		if  := indexTagEnd(, []byte(.element.name));  != -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()
}
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
}
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 name.
func ( []byte,  int) (int, element) {
	if  == len() || !asciiAlpha([]) {
		return , element{}
	}
	 :=  + 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 , element{name: 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()