package web import ( "sort" "strings" "unicode/utf8" "github.com/gramanas/blogspace/internal/slug" "github.com/gramanas/blogspace/internal/store" ) const ( maxTagsPerPost = 20 maxTagLen = 40 // runes: Greek names are two bytes a letter maxTagListCount = 50 // the Tags module's "how many"; 0 = all ) // parseTags normalises what the post form sends (the ticked tags and the // comma-separated new ones): whitespace trimmed and collapsed, blanks and // names with nothing slug-worthy dropped, duplicates removed by slug with the // first spelling kept. ok is false when a name is too long or there are too many. func parseTags(raw []string) (tags []store.Tag, ok bool) { seen := map[string]bool{} for _, r := range raw { name := strings.Join(strings.Fields(r), " ") if name == "" { continue } if utf8.RuneCountInString(name) > maxTagLen { return nil, false } sl := slug.Clean(name) if sl == "" || seen[sl] { continue } seen[sl] = true tags = append(tags, store.Tag{Name: name, Slug: sl}) } return tags, len(tags) <= maxTagsPerPost } // tagsText is the post's tags the way the form's box shows them. func tagsText(tags []store.Tag) string { names := make([]string, len(tags)) for i, t := range tags { names[i] = t.Name } return strings.Join(names, ", ") } // topTags orders tags by use, then name: the Tags module's list. func topTags(tags []store.TagCount) []store.TagCount { out := append([]store.TagCount(nil), tags...) sort.SliceStable(out, func(i, j int) bool { if out[i].Count != out[j].Count { return out[i].Count > out[j].Count } return out[i].Name < out[j].Name }) return out } // cloudTag is a tag with its font-size step for the tag cloud (class tc-1 … tc-5). type cloudTag struct { store.TagCount Size int } // cloudSizes spreads the counts linearly over five sizes between the least and // most used tag; when every tag is used equally they all get the middle size. func cloudSizes(tags []store.TagCount) []cloudTag { out := make([]cloudTag, 0, len(tags)) if len(tags) == 0 { return out } lo, hi := tags[0].Count, tags[0].Count for _, t := range tags { lo, hi = min(lo, t.Count), max(hi, t.Count) } for _, t := range tags { size := 3 if hi > lo { size = 1 + (t.Count-lo)*4/(hi-lo) } out = append(out, cloudTag{t, size}) } return out }