From 8f98e72e47c3426bcd18ec352f745959e8937067 Mon Sep 17 00:00:00 2001 From: Noah Date: Thu, 9 Apr 2020 19:16:41 -0700 Subject: [PATCH] Lazy load blog post images by default * Post.HTML() and PreviewHTML() both mogrify the resulting HTML code to ensure all tags have loading="lazy" unless a loading attribute is already present. --- pkg/controllers/posts.go | 6 +---- pkg/models/posts.go | 15 ++++++++----- pkg/mogrify/lazy_load.go | 35 +++++++++++++++++++++++++++++ pkg/mogrify/mogrify_test.go | 44 +++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 pkg/mogrify/lazy_load.go create mode 100644 pkg/mogrify/mogrify_test.go diff --git a/pkg/controllers/posts.go b/pkg/controllers/posts.go index f183da2..68128ee 100644 --- a/pkg/controllers/posts.go +++ b/pkg/controllers/posts.go @@ -160,11 +160,7 @@ func PostFragment(w http.ResponseWriter, r *http.Request) { v.V["post"] = post // Render the body. - if post.ContentType == models.Markdown { - v.V["rendered"] = template.HTML(markdown.RenderTrustedMarkdown(post.Body)) - } else { - v.V["rendered"] = template.HTML(post.Body) - } + v.V["rendered"] = post.HTML() responses.RenderTemplate(w, r, "_builtin/blog/view-post.gohtml", v) } diff --git a/pkg/models/posts.go b/pkg/models/posts.go index 4201d42..7caa6f5 100644 --- a/pkg/models/posts.go +++ b/pkg/models/posts.go @@ -12,6 +12,7 @@ import ( "git.kirsle.net/apps/gophertype/pkg/console" "git.kirsle.net/apps/gophertype/pkg/markdown" + "git.kirsle.net/apps/gophertype/pkg/mogrify" "git.kirsle.net/apps/gophertype/pkg/rng" "github.com/albrow/forms" ) @@ -383,12 +384,13 @@ func (p Post) PreviewHTML() template.HTML { if hasMore { body += fmt.Sprintf("\n\n[Read more...](/%s)", p.Fragment) } - return template.HTML(markdown.RenderTrustedMarkdown(body)) - } - - if hasMore { + body = markdown.RenderTrustedMarkdown(body) + } else if hasMore { body += fmt.Sprintf(`

Read more...

`, p.Fragment) } + + // Make all images lazy loaded. TODO: make this configurable behavior? + body = mogrify.LazyLoadImages(body) return template.HTML(body) } @@ -396,8 +398,11 @@ func (p Post) PreviewHTML() template.HTML { func (p Post) HTML() template.HTML { body := strings.ReplaceAll(p.Body, "", "") if p.ContentType == Markdown { - return template.HTML(markdown.RenderTrustedMarkdown(body)) + body = markdown.RenderTrustedMarkdown(body) } + + // Make all images lazy loaded. TODO: make this configurable behavior? + body = mogrify.LazyLoadImages(body) return template.HTML(body) } diff --git a/pkg/mogrify/lazy_load.go b/pkg/mogrify/lazy_load.go new file mode 100644 index 0000000..05585ae --- /dev/null +++ b/pkg/mogrify/lazy_load.go @@ -0,0 +1,35 @@ +package mogrify + +import ( + "fmt" + "regexp" + "strings" +) + +// Outgoing HTML filters for responses (experimental). + +var reImgTag = regexp.MustCompile(`]+)>`) + +// LazyLoadImages modifies tags to add loading="lazy" attribute to them. +// Ignores image tags that already include the attribute. +func LazyLoadImages(input string) string { + m := reImgTag.FindAllStringSubmatch(input, -1) + + if m == nil { + return input + } + + for _, match := range m { + var ( + tag = match[0] + attrs = match[1] + replace = fmt.Sprintf(``, attrs) + ) + if strings.Contains(attrs, "loading=") { + continue + } + input = strings.Replace(input, tag, replace, -1) + } + + return input +} diff --git a/pkg/mogrify/mogrify_test.go b/pkg/mogrify/mogrify_test.go new file mode 100644 index 0000000..94e5541 --- /dev/null +++ b/pkg/mogrify/mogrify_test.go @@ -0,0 +1,44 @@ +package mogrify_test + +import ( + "testing" + + "git.kirsle.net/apps/gophertype/pkg/mogrify" +) + +func TestLazyLoadImages(t *testing.T) { + var tests = []struct { + In string + Expect string + }{ + { + In: ``, + Expect: ``, + }, + { + In: ``, + Expect: ``, + }, + { + In: `Hello world`, + Expect: `Hello world`, + }, + { + In: ``, + Expect: ``, + }, + { + In: ``, + Expect: ``, + }, + } + + for _, test := range tests { + actual := mogrify.LazyLoadImages(test.In) + if actual != test.Expect { + t.Errorf("for input {%s} I expected {%s} but got: {%s}", + test.In, test.Expect, actual, + ) + } + } +}