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