-
Notifications
You must be signed in to change notification settings - Fork 3
/
linksbuilder.go
168 lines (135 loc) · 5.33 KB
/
linksbuilder.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package andrew
import (
"bytes"
"fmt"
"path"
"regexp"
"sort"
"strings"
"text/template"
"time"
)
// RenderTemplates receives the path to a file, currently normally an index file.
// It traverses the file system starting at the directory containing
// that file, finds all html files that are _not_ index.html files and returns them
// as a list of html links to those pages.
func RenderTemplates(siblings []Page, startingPage Page) ([]byte, error) {
tableOfContents, err := regexp.Compile(`.*{{\s*\.AndrewTableOfContents\s*}}.*`)
if err != nil {
return nil, err
}
if tableOfContents.FindString(startingPage.Content) != "" {
return renderAndrewTableOfContents(siblings, startingPage)
}
tableOfContentsWithDirs, err := regexp.Compile(`.*{{\s*\.AndrewTableOfContentsWithDirectories\s*}}.*`)
if err != nil {
return nil, err
}
if tableOfContentsWithDirs.FindString(startingPage.Content) != "" {
return renderAndrewTableOfContentsWithDirectories(siblings, startingPage)
}
return []byte(startingPage.Content), nil
}
func countSlashes(s string) int {
return strings.Count(s, "/")
}
func renderAndrewTableOfContentsWithDirectories(siblings []Page, startingPage Page) ([]byte, error) {
var html bytes.Buffer
var templateBuffer bytes.Buffer
directoriesAndContents := mapFromPagePaths(siblings)
directoriesInDepthOrder := keysOrderedByNumberOfSlashes(directoriesAndContents)
linkCount := 0
html.Write([]byte("<div class=\"AndrewTableOfContentsWithDirectories\">\n"))
for _, parentDir := range directoriesInDepthOrder {
// Skip the root directory if it only contains the starting page
if parentDir == "" && len(directoriesAndContents[parentDir]) == 1 && directoriesAndContents[parentDir][0] == startingPage {
continue
}
// Start the list for the directory
html.Write([]byte("<ul>\n"))
// Add the directory heading inside the <ul>
if parentDir != "" {
if countSlashes(parentDir) == 1 {
html.Write([]byte("<h5>" + parentDir + "</h5>\n"))
} else {
dirs := strings.Split(parentDir, "/")
html.Write([]byte("<h5><span class=\"AndrewParentDir\">" + dirs[0] + "/</span>" + strings.Join(dirs[1:], "/") + "</h5>\n"))
}
}
// Add the links to the list
for _, sibling := range directoriesAndContents[parentDir] {
// Skip the starting page
if sibling == startingPage {
continue
}
html.Write(buildAndrewTableOfContentsLink(sibling.UrlPath, sibling.Title, sibling.PublishTime.Format(time.DateOnly), linkCount))
linkCount++
}
html.Write([]byte("</ul>\n"))
}
html.Write([]byte("</div>\n"))
t, err := template.New(startingPage.UrlPath).Parse(startingPage.Content)
if err != nil {
panic(err)
}
err = t.Execute(&templateBuffer, map[string]string{"AndrewTableOfContentsWithDirectories": html.String()})
if err != nil {
return templateBuffer.Bytes(), err
}
return templateBuffer.Bytes(), nil
}
// mapFromPagePaths takes an array of pages and returns a map of those pages in which the keys
// are the directories containing a specific page and the value is the path inside the directory
// to that page.
// So pages at page.html, parent/page1.html, parent/page2.html and parent/child/page.html
// become {"": "page.html", "parent": ["page1.html","page2.html"], "parent/child": ["page.html"]}
// The indexes are directory names as strings; the values are arrays of Pages.
func mapFromPagePaths(siblings []Page) map[string][]Page {
directoriesAndContents := make(map[string][]Page)
for _, sibling := range siblings {
path, _ := path.Split(sibling.UrlPath)
directoriesAndContents[path] = append(directoriesAndContents[path], sibling)
}
return directoriesAndContents
}
func keysOrderedByNumberOfSlashes(directoriesAndContents map[string][]Page) []string {
keysOrderedByLength := make([]string, 0, len(directoriesAndContents))
for k := range directoriesAndContents {
keysOrderedByLength = append(keysOrderedByLength, k)
}
sort.Slice(keysOrderedByLength, func(i, j int) bool {
slashesI := countSlashes(keysOrderedByLength[i])
slashesJ := countSlashes(keysOrderedByLength[j])
if slashesI == slashesJ {
return keysOrderedByLength[i] < keysOrderedByLength[j]
}
return slashesI < slashesJ
})
return keysOrderedByLength
}
func renderAndrewTableOfContents(siblings []Page, startingPage Page) ([]byte, error) {
var html bytes.Buffer
html.Write([]byte("<div class=\"AndrewTableOfContents\">\n"))
html.Write([]byte("<ul>\n"))
for i, sibling := range siblings {
html.Write(buildAndrewTableOfContentsLink(sibling.UrlPath, sibling.Title, sibling.PublishTime.Format(time.DateOnly), i))
}
html.Write([]byte("</ul>\n"))
html.Write([]byte("</div>\n"))
templateBuffer := bytes.Buffer{}
t, err := template.New(startingPage.UrlPath).Parse(startingPage.Content)
if err != nil {
panic(err)
}
err = t.Execute(&templateBuffer, map[string]string{"AndrewTableOfContents": html.String()})
if err != nil {
return templateBuffer.Bytes(), err
}
return templateBuffer.Bytes(), nil
}
// buildAndrewTableOfContentsLink encapsulates the format of the link
func buildAndrewTableOfContentsLink(urlPath string, title string, publishDate string, cssIdNumber int) []byte {
link := fmt.Sprintf("<li><a class=\"andrewtableofcontentslink\" id=\"andrewtableofcontentslink%s\" href=\"%s\">%s</a> - <span class=\"andrew-page-publish-date\">%s</span></li>\n", fmt.Sprint(cssIdNumber), urlPath, title, publishDate)
b := []byte(link)
return b
}