this repo has no description
1package pages
2
3import (
4 "fmt"
5 "html"
6 "html/template"
7 "log"
8 "path/filepath"
9 "reflect"
10 "strings"
11
12 "github.com/dustin/go-humanize"
13)
14
15func funcMap() template.FuncMap {
16 return template.FuncMap{
17 "split": func(s string) []string {
18 return strings.Split(s, "\n")
19 },
20 "truncateAt30": func(s string) string {
21 if len(s) <= 30 {
22 return s
23 }
24 return s[:30] + "…"
25 },
26 "splitOn": func(s, sep string) []string {
27 return strings.Split(s, sep)
28 },
29 "add": func(a, b int) int {
30 return a + b
31 },
32 "sub": func(a, b int) int {
33 return a - b
34 },
35 "cond": func(cond interface{}, a, b string) string {
36 if cond == nil {
37 return b
38 }
39
40 if boolean, ok := cond.(bool); boolean && ok {
41 return a
42 }
43
44 return b
45 },
46 "didOrHandle": func(did, handle string) string {
47 if handle != "" {
48 return fmt.Sprintf("@%s", handle)
49 } else {
50 return did
51 }
52 },
53 "assoc": func(values ...string) ([][]string, error) {
54 if len(values)%2 != 0 {
55 return nil, fmt.Errorf("invalid assoc call, must have an even number of arguments")
56 }
57 pairs := make([][]string, 0)
58 for i := 0; i < len(values); i += 2 {
59 pairs = append(pairs, []string{values[i], values[i+1]})
60 }
61 return pairs, nil
62 },
63 "append": func(s []string, values ...string) []string {
64 s = append(s, values...)
65 return s
66 },
67 "timeFmt": humanize.Time,
68 "byteFmt": humanize.Bytes,
69 "length": func(slice any) int {
70 v := reflect.ValueOf(slice)
71 if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
72 return v.Len()
73 }
74 return 0
75 },
76 "splitN": func(s, sep string, n int) []string {
77 return strings.SplitN(s, sep, n)
78 },
79 "escapeHtml": func(s string) template.HTML {
80 if s == "" {
81 return template.HTML("<br>")
82 }
83 return template.HTML(s)
84 },
85 "unescapeHtml": func(s string) string {
86 return html.UnescapeString(s)
87 },
88 "nl2br": func(text string) template.HTML {
89 return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
90 },
91 "unwrapText": func(text string) string {
92 paragraphs := strings.Split(text, "\n\n")
93
94 for i, p := range paragraphs {
95 lines := strings.Split(p, "\n")
96 paragraphs[i] = strings.Join(lines, " ")
97 }
98
99 return strings.Join(paragraphs, "\n\n")
100 },
101 "sequence": func(n int) []struct{} {
102 return make([]struct{}, n)
103 },
104 "subslice": func(slice any, start, end int) any {
105 v := reflect.ValueOf(slice)
106 if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
107 return nil
108 }
109 if start < 0 || start > v.Len() || end > v.Len() || start > end {
110 return nil
111 }
112 return v.Slice(start, end).Interface()
113 },
114 "markdown": func(text string) template.HTML {
115 return template.HTML(renderMarkdown(text))
116 },
117 "isNil": func(t any) bool {
118 // returns false for other "zero" values
119 return t == nil
120 },
121 "list": func(args ...any) []any {
122 return args
123 },
124 "i": func(name string, classes ...string) template.HTML {
125 data, err := icon(name, classes)
126 if err != nil {
127 log.Printf("icon %s does not exist", name)
128 data, _ = icon("airplay", classes)
129 }
130 return template.HTML(data)
131 },
132 }
133}
134
135func icon(name string, classes []string) (template.HTML, error) {
136 iconPath := filepath.Join("static", "icons", name)
137
138 if filepath.Ext(name) == "" {
139 iconPath += ".svg"
140 }
141
142 data, err := Files.ReadFile(iconPath)
143 if err != nil {
144 return "", fmt.Errorf("icon %s not found: %w", name, err)
145 }
146
147 // Convert SVG data to string
148 svgStr := string(data)
149
150 svgTagEnd := strings.Index(svgStr, ">")
151 if svgTagEnd == -1 {
152 return "", fmt.Errorf("invalid SVG format for icon %s", name)
153 }
154
155 classTag := ` class="` + strings.Join(classes, " ") + `"`
156
157 modifiedSVG := svgStr[:svgTagEnd] + classTag + svgStr[svgTagEnd:]
158 return template.HTML(modifiedSVG), nil
159}