-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathhelpers.go
173 lines (142 loc) · 3.83 KB
/
helpers.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
169
170
171
172
173
package xlog
import (
"errors"
"fmt"
"html/template"
"slices"
"strings"
"time"
emojiAst "github.com/yuin/goldmark-emoji/ast"
"github.com/yuin/goldmark/ast"
gast "github.com/yuin/goldmark/ast"
)
var helpers = template.FuncMap{
"ago": ago,
"properties": Properties,
"links": Links,
"widgets": RenderWidget,
"commands": Commands,
"quick_commands": QuickCommands,
"isFontAwesome": IsFontAwesome,
"includeJS": includeJS,
"scripts": scripts,
"banner": Banner,
"emoji": Emoji,
}
var ErrHelperRegistered = errors.New("Helper already registered")
// RegisterHelper registers a new helper function. all helpers are used when compiling
// templates. so registering helpers function must happen before the server
// starts as compiling templates happened right before starting the http server.
func RegisterHelper(name string, f any) error {
if _, ok := helpers[name]; ok {
return ErrHelperRegistered
}
helpers[name] = f
return nil
}
// A function that takes time.duration and return a string representation of the
// duration in human readable way such as "3 seconds ago". "5 hours 30 minutes
// ago". The precision of this function is 2. which means it returns the largest
// unit of time possible and the next one after it. for example days + hours, or
// Hours + minutes or Minutes + seconds...etc
func ago(t time.Time) string {
if Config.Readonly {
return t.Format("Monday 2 January 2006")
}
d := time.Since(t)
const day = time.Hour * 24
const week = day * 7
const month = day * 30
const year = day * 365
const maxPrecision = 2
var o strings.Builder
if d.Seconds() < 1 {
o.WriteString("Less than a second ")
}
for precision := 0; d.Seconds() > 1 && precision < maxPrecision; precision++ {
switch {
case d >= year:
years := d / year
d -= years * year
o.WriteString(fmt.Sprintf("%d years ", years))
case d >= month:
months := d / month
d -= months * month
o.WriteString(fmt.Sprintf("%d months ", months))
case d >= week:
weeks := d / week
d -= weeks * week
o.WriteString(fmt.Sprintf("%d weeks ", weeks))
case d >= day:
days := d / day
d -= days * day
o.WriteString(fmt.Sprintf("%d days ", days))
case d >= time.Hour:
hours := d / time.Hour
d -= hours * time.Hour
o.WriteString(fmt.Sprintf("%d hours ", hours))
case d >= time.Minute:
minutes := d / time.Minute
d -= minutes * time.Minute
o.WriteString(fmt.Sprintf("%d minutes ", minutes))
case d >= time.Second:
seconds := d / time.Second
d -= seconds * time.Second
o.WriteString(fmt.Sprintf("%d seconds ", seconds))
}
}
o.WriteString("ago")
return o.String()
}
var js = []string{}
// RegisterJS adds a Javascript library URL/path to be included in the scripts used by the template
func RegisterJS(f string) {
if slices.Contains(js, f) {
return
}
js = append(js, f)
}
// RequireHTMX registes HTML library, this helps include one version of HTMX
func RequireHTMX() {
RegisterJS("/public/htmx.min.js")
}
func includeJS(f string) template.HTML {
RegisterJS(f)
return ""
}
func scripts() template.HTML {
var b strings.Builder
for _, f := range js {
fmt.Fprintf(&b, `<script src="%s" defer></script>`, f)
}
return template.HTML(b.String())
}
func IsFontAwesome(i string) bool {
return strings.HasPrefix(i, "fa")
}
func Banner(p Page) string {
_, a := p.AST()
if a == nil {
return ""
}
paragraph := a.FirstChild()
if paragraph == nil || paragraph.Kind() != gast.KindParagraph {
return ""
}
img := paragraph.FirstChild()
if img == nil || img.Kind() != gast.KindImage {
return ""
}
image, ok := img.(*ast.Image)
if !ok {
return ""
}
return string(image.Destination)
}
func Emoji(p Page) string {
_, tree := p.AST()
if e, ok := FindInAST[*emojiAst.Emoji](tree); ok && e != nil {
return string(e.Value.Unicode)
}
return ""
}