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