-
Notifications
You must be signed in to change notification settings - Fork 18
/
html.go
106 lines (84 loc) · 2.4 KB
/
html.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package static
import (
"bufio"
"io"
"strconv"
"strings"
dom "github.com/PuerkitoBio/goquery"
snakecase "github.com/segmentio/go-snakecase"
)
// anchorHTML is the anchor element and SVG icon.
var anchorHTML = `<a class="Anchor" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
</a>`
// anchorEl is the instance of the anchor element which is cloned.
var anchorEl *dom.Selection
// initialize anchor element.
func init() {
doc, err := dom.NewDocumentFromReader(strings.NewReader(anchorHTML))
if err != nil {
panic(err)
}
anchorEl = doc.Find("a")
}
// HeadingAnchors returns a reader with
// HTML heading ids derived from their text.
func HeadingAnchors(r io.Reader) io.ReadCloser {
pr, pw := io.Pipe()
go func() {
doc, err := dom.NewDocumentFromReader(r)
if err != nil {
pw.CloseWithError(err)
return
}
scope := []string{""}
prev := 1
doc.Find("h1, h2, h3, h4, h5, h6").Each(func(i int, s *dom.Selection) {
curr, _ := strconv.Atoi(string(s.Get(0).Data[1]))
change := curr - prev
if change <= 0 {
scope = scope[:len(scope)+(change-1)]
}
scope = append(scope, snakecase.Snakecase(s.Text()))
prev = curr
id := strings.Join(scope, ".")
a := anchorEl.Clone()
a.SetAttr("id", id)
a.SetAttr("href", "#"+id)
s.SetAttr("class", "Heading")
s.PrependSelection(a)
})
html, err := doc.Html()
if err != nil {
pw.CloseWithError(err)
return
}
pw.Write([]byte(html))
pw.Close()
}()
return pr
}
// Notes returns a reader with paragraphs starting
// with "Notes:" to be considered highlighted note.
func Notes(r io.Reader) io.ReadCloser {
pr, pw := io.Pipe()
go func() {
scan := bufio.NewScanner(r)
for scan.Scan() {
io.WriteString(pw, note(scan.Text()))
io.WriteString(pw, "\n")
}
pw.CloseWithError(scan.Err())
}()
return pr
}
// note helper.
func note(s string) string {
if !strings.HasPrefix(s, "<p>Note:") {
return s
}
s = strings.TrimPrefix(s, "<p>Note:")
s = strings.TrimSuffix(s, "</p>")
s = strings.TrimSpace(s)
return `<div class="Message"><p>` + s + `</p></div>`
}