// 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 (
	
	
	
	
)

// htmlNospaceEscaper escapes for inclusion in unquoted attribute values.
func htmlNospaceEscaper( ...any) string {
	,  := stringify(...)
	if  == "" {
		return filterFailsafe
	}
	if  == contentTypeHTML {
		return htmlReplacer(stripTags(), htmlNospaceNormReplacementTable, false)
	}
	return htmlReplacer(, htmlNospaceReplacementTable, false)
}

// attrEscaper escapes for inclusion in quoted attribute values.
func attrEscaper( ...any) string {
	,  := stringify(...)
	if  == contentTypeHTML {
		return htmlReplacer(stripTags(), htmlNormReplacementTable, true)
	}
	return htmlReplacer(, htmlReplacementTable, true)
}

// rcdataEscaper escapes for inclusion in an RCDATA element body.
func rcdataEscaper( ...any) string {
	,  := stringify(...)
	if  == contentTypeHTML {
		return htmlReplacer(, htmlNormReplacementTable, true)
	}
	return htmlReplacer(, htmlReplacementTable, true)
}

// htmlEscaper escapes for inclusion in HTML text.
func htmlEscaper( ...any) string {
	,  := stringify(...)
	if  == contentTypeHTML {
		return 
	}
	return htmlReplacer(, htmlReplacementTable, true)
}

// htmlReplacementTable contains the runes that need to be escaped
// inside a quoted attribute value or in a text node.
var htmlReplacementTable = []string{
	// https://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
	// U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT
	// CHARACTER character to the current attribute's value.
	// "
	// and similarly
	// https://www.w3.org/TR/html5/syntax.html#before-attribute-value-state
	0:    "\uFFFD",
	'"':  """,
	'&':  "&",
	'\'': "'",
	'+':  "+",
	'<':  "&lt;",
	'>':  "&gt;",
}

// htmlNormReplacementTable is like htmlReplacementTable but without '&' to
// avoid over-encoding existing entities.
var htmlNormReplacementTable = []string{
	0:    "\uFFFD",
	'"':  "&#34;",
	'\'': "&#39;",
	'+':  "&#43;",
	'<':  "&lt;",
	'>':  "&gt;",
}

// htmlNospaceReplacementTable contains the runes that need to be escaped
// inside an unquoted attribute value.
// The set of runes escaped is the union of the HTML specials and
// those determined by running the JS below in browsers:
// <div id=d></div>
// <script>(function () {
// var a = [], d = document.getElementById("d"), i, c, s;
// for (i = 0; i < 0x10000; ++i) {
//
//	c = String.fromCharCode(i);
//	d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
//	s = d.getElementsByTagName("SPAN")[0];
//	if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
//
// }
// document.write(a.join(", "));
// })()</script>
var htmlNospaceReplacementTable = []string{
	0:    "&#xfffd;",
	'\t': "&#9;",
	'\n': "&#10;",
	'\v': "&#11;",
	'\f': "&#12;",
	'\r': "&#13;",
	' ':  "&#32;",
	'"':  "&#34;",
	'&':  "&amp;",
	'\'': "&#39;",
	'+':  "&#43;",
	'<':  "&lt;",
	'=':  "&#61;",
	'>':  "&gt;",
	// A parse error in the attribute value (unquoted) and
	// before attribute value states.
	// Treated as a quoting character by IE.
	'`': "&#96;",
}

// htmlNospaceNormReplacementTable is like htmlNospaceReplacementTable but
// without '&' to avoid over-encoding existing entities.
var htmlNospaceNormReplacementTable = []string{
	0:    "&#xfffd;",
	'\t': "&#9;",
	'\n': "&#10;",
	'\v': "&#11;",
	'\f': "&#12;",
	'\r': "&#13;",
	' ':  "&#32;",
	'"':  "&#34;",
	'\'': "&#39;",
	'+':  "&#43;",
	'<':  "&lt;",
	'=':  "&#61;",
	'>':  "&gt;",
	// A parse error in the attribute value (unquoted) and
	// before attribute value states.
	// Treated as a quoting character by IE.
	'`': "&#96;",
}

// htmlReplacer returns s with runes replaced according to replacementTable
// and when badRunes is true, certain bad runes are allowed through unescaped.
func htmlReplacer( string,  []string,  bool) string {
	,  := 0, new(strings.Builder)
	,  := rune(0), 0
	for  := 0;  < len();  +=  {
		// Cannot use 'for range s' because we need to preserve the width
		// of the runes in the input. If we see a decoding error, the input
		// width will not be utf8.Runelen(r) and we will overrun the buffer.
		,  = utf8.DecodeRuneInString([:])
		if int() < len() {
			if  := []; len() != 0 {
				if  == 0 {
					.Grow(len())
				}
				.WriteString([:])
				.WriteString()
				 =  + 
			}
		} else if  {
			// No-op.
			// IE does not allow these ranges in unquoted attrs.
		} else if 0xfdd0 <=  &&  <= 0xfdef || 0xfff0 <=  &&  <= 0xffff {
			if  == 0 {
				.Grow(len())
			}
			fmt.Fprintf(, "%s&#x%x;", [:], )
			 =  + 
		}
	}
	if  == 0 {
		return 
	}
	.WriteString([:])
	return .String()
}

// stripTags takes a snippet of HTML and returns only the text content.
// For example, `<b>&iexcl;Hi!</b> <script>...</script>` -> `&iexcl;Hi! `.
func stripTags( string) string {
	var  strings.Builder
	, , ,  := []byte(), context{}, 0, true
	// Using the transition funcs helps us avoid mangling
	// `<div title="1>2">` or `I <3 Ponies!`.
	for  != len() {
		if .delim == delimNone {
			 := .state
			// Use RCDATA instead of parsing into JS or CSS styles.
			if .element != elementNone && !isInTag() {
				 = stateRCDATA
			}
			,  := transitionFunc[](, [:])
			 :=  + 
			if .state == stateText || .state == stateRCDATA {
				// Emit text up to the start of the tag or comment.
				 := 
				if .state != .state {
					for  :=  - 1;  >= ; -- {
						if [] == '<' {
							 = 
							break
						}
					}
				}
				.Write([:])
			} else {
				 = false
			}
			,  = , 
			continue
		}
		 :=  + bytes.IndexAny([:], delimEnds[.delim])
		if  <  {
			break
		}
		if .delim != delimSpaceOrTagEnd {
			// Consume any quote.
			++
		}
		,  = context{state: stateTag, element: .element}, 
	}
	if  {
		return 
	} else if .state == stateText || .state == stateRCDATA {
		.Write([:])
	}
	return .String()
}

// htmlNameFilter accepts valid parts of an HTML attribute or tag name or
// a known-safe HTML attribute.
func htmlNameFilter( ...any) string {
	,  := stringify(...)
	if  == contentTypeHTMLAttr {
		return 
	}
	if len() == 0 {
		// Avoid violation of structure preservation.
		// <input checked {{.K}}={{.V}}>.
		// Without this, if .K is empty then .V is the value of
		// checked, but otherwise .V is the value of the attribute
		// named .K.
		return filterFailsafe
	}
	 = strings.ToLower()
	if  := attrType();  != contentTypePlain {
		// TODO: Split attr and element name part filters so we can recognize known attributes.
		return filterFailsafe
	}
	for ,  := range  {
		switch {
		case '0' <=  &&  <= '9':
		case 'a' <=  &&  <= 'z':
		default:
			return filterFailsafe
		}
	}
	return 
}

// commentEscaper returns the empty string regardless of input.
// Comment content does not correspond to any parsed structure or
// human-readable content, so the simplest and most secure policy is to drop
// content interpolated into comments.
// This approach is equally valid whether or not static comment content is
// removed from the template.
func commentEscaper( ...any) string {
	return ""
}