diff options
Diffstat (limited to 'internal/handlers/handlers.go')
| -rw-r--r-- | internal/handlers/handlers.go | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index e70650d..c5df00d 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -1251,6 +1251,34 @@ func templateFuncMap() template.FuncMap { return template.FuncMap{ "subtract": func(a, b int) int { return a - b }, "add": func(a, b int) int { return a + b }, + // slug turns a free-text name into an id-safe fragment, so a template + // can give a section a stable anchor and target it by selector. Store + // names are user data ("Trader Joe's", "Hardware Store") and go + // straight into element ids, so anything outside [a-z0-9-] has to go. + "slug": func(s string) string { + var b strings.Builder + lastDash := true // leading dashes are suppressed + for _, r := range strings.ToLower(s) { + switch { + case (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9'): + b.WriteRune(r) + lastDash = false + default: + // Collapse any run of separators/punctuation into one dash. + if !lastDash { + b.WriteByte('-') + lastDash = true + } + } + } + out := strings.Trim(b.String(), "-") + if out == "" { + // An empty id would make every such section share the same + // (invalid) anchor, so give it something addressable. + return "unnamed" + } + return out + }, // multiDayLabel returns the "(starts/ends HH:MM)" suffix for a // multi-day calendar event's row on the day it's rendered, or "" // for a normal item or a "spans" (pass-through) day, which shows |
