internal/httpd/rendered.go

dd06e80a071d451ebb7b083363e0580550481114
gitbay/internal/httpd/rendered.go history · blame · raw

51 lines · 1515 bytes

 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}