123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- package renderer
- import (
- "html/template"
- "io"
- "regexp"
- "strconv"
- "strings"
- "time"
- "fmt"
- "bloat/mastodon"
- )
- type Page string
- const (
- SigninPage = "signin.tmpl"
- ErrorPage = "error.tmpl"
- NavPage = "nav.tmpl"
- RootPage = "root.tmpl"
- TimelinePage = "timeline.tmpl"
- ListsPage = "lists.tmpl"
- ListPage = "list.tmpl"
- ThreadPage = "thread.tmpl"
- QuickReplyPage = "quickreply.tmpl"
- NotificationPage = "notification.tmpl"
- UserPage = "user.tmpl"
- UserSearchPage = "usersearch.tmpl"
- AboutPage = "about.tmpl"
- AboutInstance = "aboutinstance.tmpl"
- EmojiPage = "emoji.tmpl"
- LikedByPage = "likedby.tmpl"
- ReactedByPage = "reactionspage.tmpl"
- RetweetedByPage = "retweetedby.tmpl"
- SearchPage = "search.tmpl"
- SettingsPage = "settings.tmpl"
- FiltersPage = "filters.tmpl"
- ProfilePage = "profile.tmpl"
- MutePage = "mute.tmpl"
- )
- type TemplateData struct {
- Data interface{}
- Ctx *Context
- }
- func allowed_emoji_page(emoj string, codes string) bool {
- s := strings.Split(codes, " ")
- for _, code := range s {
- if strings.Contains(emoj, code) {
- return true
- }
- }
- return false
- }
-
- func emojiHTML(e mastodon.Emoji, height string) string {
- return `<img class="emoji" src="` + e.URL + `" alt=":` + e.ShortCode + `:" title=":` + e.ShortCode + `:" height="` + height + `"/>`
- }
- func emojiFilter(content string, emojis []mastodon.Emoji) string {
- var replacements []string
- for _, e := range emojis {
- replacements = append(replacements, ":"+e.ShortCode+":", emojiHTML(e, "24"))
- }
- return strings.NewReplacer(replacements...).Replace(content)
- }
- func generatePollOptions() string {
- var pollbuilder string
- for i := 0; i < 20; i++ {
- pollbuilder = pollbuilder + `<div><input id="` + fmt.Sprintf("poll-option-%d", i) + `" name="` + fmt.Sprintf("poll-option-%d", i) + `"></div>`
- }
- return pollbuilder
- }
- func generateMediaDescrForm() string {
- var mediadescrbuilder string
- for i := 0; i < 20; i++ {
- mediadescrbuilder = mediadescrbuilder + `<div><textarea rows="2" id="` + fmt.Sprintf("media-descr-%d", i) + `" name="` + fmt.Sprintf("media-descr-%d", i) + `"></textarea></div>`
- }
- return mediadescrbuilder
- }
- var quoteRE = regexp.MustCompile("(?mU)(^|> *|\n)(>.*)(<br|$)")
- func statusContentFilter(content string, emojis []mastodon.Emoji, mentions []mastodon.Mention) string {
- content = quoteRE.ReplaceAllString(content, `$1<span class="quote">$2</span>$3`)
- var replacements []string
- for _, e := range emojis {
- replacements = append(replacements, ":"+e.ShortCode+":", emojiHTML(e, "32"))
- }
- for _, m := range mentions {
- replacements = append(replacements, `"`+m.URL+`"`, `"/user/`+m.ID+`" title="@`+m.Acct+`"`)
- }
- return strings.NewReplacer(replacements...).Replace(content)
- }
- func displayInteractionCount(c int64) string {
- if c > 0 {
- return strconv.Itoa(int(c))
- }
- return ""
- }
- func durUnit(s int64) (dur int64, unit string) {
- if s < 60 {
- if s < 0 {
- s = 0
- }
- return s, "s"
- }
- m := s / 60
- h := m / 60
- if h < 2 {
- return m, "m"
- }
- d := h / 24
- if d < 2 {
- return h, "h"
- }
- mo := d / 30
- if mo < 2 {
- return d, "d"
- }
- y := d / 365
- if y < 2 {
- return mo, "mo"
- }
- return y, "y"
- }
- func timeSince(t time.Time) string {
- d, u := durUnit(time.Now().Unix() - t.Unix())
- return strconv.FormatInt(d, 10) + u
- }
- func timeUntil(t time.Time) string {
- d, u := durUnit(t.Unix() - time.Now().Unix())
- return strconv.FormatInt(d, 10) + u
- }
- func formatTimeRFC3339(t time.Time) string {
- return t.Format(time.RFC3339)
- }
- func formatTimeRFC822(t time.Time) string {
- return t.Format(time.RFC822)
- }
- func withContext(data interface{}, ctx *Context) TemplateData {
- return TemplateData{data, ctx}
- }
- func raw(s string) template.HTML {
- return template.HTML(s)
- }
- func rawCSS(s string) template.CSS {
- return template.CSS(s)
- }
- type Renderer interface {
- Render(ctx *Context, writer io.Writer, page string, data interface{}) (err error)
- }
- type renderer struct {
- template *template.Template
- }
- func NewRenderer(templateGlobPattern string) (r *renderer, err error) {
- t := template.New("default")
- t, err = t.Funcs(template.FuncMap{
- "EmojiFilter": emojiFilter,
- "Allowed_emoji_page": allowed_emoji_page,
- "GeneratePollOptions": generatePollOptions,
- "GenerateMediaDescrForm": generateMediaDescrForm,
- "StatusContentFilter": statusContentFilter,
- "DisplayInteractionCount": displayInteractionCount,
- "TimeSince": timeSince,
- "TimeUntil": timeUntil,
- "FormatTimeRFC3339": formatTimeRFC3339,
- "FormatTimeRFC822": formatTimeRFC822,
- "WithContext": withContext,
- "HTML": template.HTMLEscapeString,
- "Raw": raw,
- "RawCSS": rawCSS,
- }).ParseGlob(templateGlobPattern)
- if err != nil {
- return
- }
- return &renderer{
- template: t,
- }, nil
- }
- func (r *renderer) Render(ctx *Context, writer io.Writer,
- page string, data interface{}) (err error) {
- return r.template.ExecuteTemplate(writer, page, withContext(data, ctx))
- }
|