internal/httpd/rendered.go
51 lines · 1515 bytes
5 symbols in this file
1package httpd
2
3import (
4 "html/template"
5 "path"
6 "regexp"
7 "strings"
8)
9
10var (
11 preTag = regexp.MustCompile(`<pre\b[^>]*>`)
12 imgTag = regexp.MustCompile(`<img\b[^>]*>`)
13 srcAttr = regexp.MustCompile(`\ssrc="([^"]*)"`)
14)
15
16// focusableBlocks gives every <pre> a tab stop. A block wider than its
17// column scrolls sideways, and without one a keyboard has no way to reach
18// the end of a long line (#232).
19func focusableBlocks(h template.HTML) template.HTML {
20 return template.HTML(preTag.ReplaceAllStringFunc(string(h), func(m string) string {
21 if strings.Contains(m, "tabindex") {
22 return m
23 }
24 return `<pre tabindex="0"` + m[len("<pre"):]
25 }))
26}
27
28// imageAlt names an image that arrived without alt text after its file.
29// go-org writes none for a bare image link, so a README badge was an image
30// with no text inside a link with no name (#232). Markdown always carries
31// alt, empty or not, and an empty alt is left alone: it means decorative.
32func imageAlt(h template.HTML) template.HTML {
33 return template.HTML(imgTag.ReplaceAllStringFunc(string(h), func(m string) string {
34 if strings.Contains(m, " alt=") {
35 return m
36 }
37 name := ""
38 if sub := srcAttr.FindStringSubmatch(m); sub != nil {
39 src := sub[1]
40 if i := strings.IndexAny(src, "?#"); i >= 0 {
41 src = src[:i]
42 }
43 name = path.Base(src)
44 name = strings.TrimSuffix(name, path.Ext(name))
45 if name == "." || name == "/" {
46 name = ""
47 }
48 }
49 return `<img alt="` + template.HTMLEscapeString(name) + `"` + m[len("<img"):]
50 }))
51}