1
0
mirror of https://github.com/golang/go synced 2024-11-25 09:07:58 -07:00

exp/template/html: string replacement refactoring.

R=mikesamuel
CC=golang-dev
https://golang.org/cl/4968063
This commit is contained in:
Nigel Tao 2011-09-03 10:30:05 +10:00
parent 9854fd2a0e
commit 2b6d3b498c

View File

@ -166,53 +166,43 @@ func jsValEscaper(args ...interface{}) string {
// JavaScript source, in JavaScript embedded in an HTML5 <script> element, // JavaScript source, in JavaScript embedded in an HTML5 <script> element,
// or in an HTML5 event handler attribute such as onclick. // or in an HTML5 event handler attribute such as onclick.
func jsStrEscaper(args ...interface{}) string { func jsStrEscaper(args ...interface{}) string {
ok := false return replace(stringify(args...), jsStrReplacementTable)
var s string }
// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
// specials so the result is treated literally when included in a regular
// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
// the literal text of {{.X}} followed by the string "bar".
func jsRegexpEscaper(args ...interface{}) string {
return replace(stringify(args...), jsRegexpReplacementTable)
}
// stringify is an optimized form of fmt.Sprint.
func stringify(args ...interface{}) string {
if len(args) == 1 { if len(args) == 1 {
s, ok = args[0].(string) if s, ok := args[0].(string); ok {
} return s
if !ok { }
s = fmt.Sprint(args...)
} }
return fmt.Sprint(args...)
}
// replace replaces each rune r of s with replacementTable[r], provided that
// r < len(replacementTable). If replacementTable[r] is the empty string then
// no replacement is made.
// It also replaces the runes '\u2028' and '\u2029' with the strings
// `\u2028` and `\u2029`. Note the different quotes used.
func replace(s string, replacementTable []string) string {
var b bytes.Buffer var b bytes.Buffer
written := 0 written := 0
for i, r := range s { for i, r := range s {
var repl string var repl string
switch r { switch {
case 0: case r < len(replacementTable) && replacementTable[r] != "":
repl = `\0` repl = replacementTable[r]
case '\t': case r == '\u2028':
repl = `\t`
case '\n':
repl = `\n`
case '\v':
// "\v" == "v" on IE 6.
repl = `\x0b`
case '\f':
repl = `\f`
case '\r':
repl = `\r`
// Encode HTML specials as hex so the output can be embedded
// in HTML attributes without further encoding.
case '"':
repl = `\x22`
case '&':
repl = `\x26`
case '\'':
repl = `\x27`
case '+':
repl = `\x2b`
case '/':
repl = `\/`
case '<':
repl = `\x3c`
case '>':
repl = `\x3e`
case '\\':
repl = `\\`
case '\u2028':
repl = `\u2028` repl = `\u2028`
case '\u2029': case r == '\u2029':
repl = `\u2029` repl = `\u2029`
default: default:
continue continue
@ -228,97 +218,55 @@ func jsStrEscaper(args ...interface{}) string {
return b.String() return b.String()
} }
// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression var jsStrReplacementTable = []string{
// specials so the result is treated literally when included in a regular 0: `\0`,
// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by '\t': `\t`,
// the literal text of {{.X}} followed by the string "bar". '\n': `\n`,
func jsRegexpEscaper(args ...interface{}) string { '\v': `\x0b`, // "\v" == "v" on IE 6.
ok := false '\f': `\f`,
var s string '\r': `\r`,
if len(args) == 1 { // Encode HTML specials as hex so the output can be embedded
s, ok = args[0].(string) // in HTML attributes without further encoding.
} '"': `\x22`,
if !ok { '&': `\x26`,
s = fmt.Sprint(args...) '\'': `\x27`,
} '+': `\x2b`,
var b bytes.Buffer '/': `\/`,
written := 0 '<': `\x3c`,
for i, r := range s { '>': `\x3e`,
var repl string '\\': `\\`,
switch r { }
case 0:
repl = `\0` var jsRegexpReplacementTable = []string{
case '\t': 0: `\0`,
repl = `\t` '\t': `\t`,
case '\n': '\n': `\n`,
repl = `\n` '\v': `\x0b`, // "\v" == "v" on IE 6.
case '\v': '\f': `\f`,
// "\v" == "v" on IE 6. '\r': `\r`,
repl = `\x0b` // Encode HTML specials as hex so the output can be embedded
case '\f': // in HTML attributes without further encoding.
repl = `\f` '"': `\x22`,
case '\r': '$': `\$`,
repl = `\r` '&': `\x26`,
// Encode HTML specials as hex so the output can be embedded '\'': `\x27`,
// in HTML attributes without further encoding. '(': `\(`,
case '"': ')': `\)`,
repl = `\x22` '*': `\*`,
case '$': '+': `\x2b`,
repl = `\$` '-': `\-`,
case '&': '.': `\.`,
repl = `\x26` '/': `\/`,
case '\'': '<': `\x3c`,
repl = `\x27` '>': `\x3e`,
case '(': '?': `\?`,
repl = `\(` '[': `\[`,
case ')': '\\': `\\`,
repl = `\)` ']': `\]`,
case '*': '^': `\^`,
repl = `\*` '{': `\{`,
case '+': '|': `\|`,
repl = `\x2b` '}': `\}`,
case '-':
repl = `\-`
case '.':
repl = `\.`
case '/':
repl = `\/`
case '<':
repl = `\x3c`
case '>':
repl = `\x3e`
case '?':
repl = `\?`
case '[':
repl = `\[`
case '\\':
repl = `\\`
case ']':
repl = `\]`
case '^':
repl = `\^`
case '{':
repl = `\{`
case '|':
repl = `\|`
case '}':
repl = `\}`
case '\u2028':
repl = `\u2028`
case '\u2029':
repl = `\u2029`
default:
continue
}
b.WriteString(s[written:i])
b.WriteString(repl)
written = i + utf8.RuneLen(r)
}
if written == 0 {
return s
}
b.WriteString(s[written:])
return b.String()
} }
// isJSIdentPart is true if the given rune is a JS identifier part. // isJSIdentPart is true if the given rune is a JS identifier part.