Skip to content

Commit

Permalink
HeadHTML -> PreloadHTML
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdata committed May 30, 2024
1 parent 5091f95 commit 52ca4bb
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 52 deletions.
35 changes: 15 additions & 20 deletions jaws.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"context"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"html/template"
"io"
Expand Down Expand Up @@ -367,32 +368,26 @@ const jawsAlertStyle = `
`

// GenerateHeadHTML (re-)generates the HTML code that goes in the HEAD section, ensuring
// that the provided scripts and stylesheets in `extra` are loaded.
// that the provided URL resources in `extra` are loaded, along with the JaWS javascript.
//
// You only need to call this if you want to add your own scripts and stylesheets.
func (jw *Jaws) GenerateHeadHTML(extra ...string) error {
var js, css []string
addedJaws := false
for _, e := range extra {
if u, err := url.Parse(e); err == nil {
if strings.HasSuffix(u.Path, ".js") {
js = append(js, e)
addedJaws = addedJaws || strings.HasSuffix(u.Path, JavascriptPath)
} else if strings.HasSuffix(e, ".css") {
css = append(css, e)
func (jw *Jaws) GenerateHeadHTML(extra ...string) (err error) {
var jawsurl *url.URL
if jawsurl, err = url.Parse(JavascriptPath); err == nil {
var urls []*url.URL
urls = append(urls, jawsurl)
for _, urlstr := range extra {
if u, e := url.Parse(urlstr); e == nil {
if !strings.HasSuffix(u.Path, jawsurl.Path) {
urls = append(urls, u)
}
} else {
return fmt.Errorf("%q: not .js or .css", u.Path)
err = errors.Join(e)
}
} else {
return err
}
jw.headPrefix = PreloadHTML(urls...) + jawsAlertStyle + `<script>var jawsKey="`
}
if !addedJaws {
js = append(js, JavascriptPath)
}
jw.headPrefix = HeadHTML(js, css) + jawsAlertStyle + `<script>var jawsKey="`

return nil
return
}

// Broadcast sends a message to all Requests.
Expand Down
10 changes: 7 additions & 3 deletions jaws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,18 +452,22 @@ func TestJaws_subscribeOnClosedReturnsNil(t *testing.T) {
func TestJaws_GenerateHeadHTML(t *testing.T) {
const extraScript = "someExtraScript.js?disregard"
const extraStyle = "http://other.server/someExtraStyle.css"
const extraImage = "someExtraImage.png"
const extraFont = "someExtraFont.woff2"
th := newTestHelper(t)
jw := New()
jw.Close()

jw.GenerateHeadHTML()
th.NoErr(jw.GenerateHeadHTML())
th.True(strings.Contains(string(jw.headPrefix), JavascriptPath))
jw.GenerateHeadHTML(extraScript, extraStyle)

th.NoErr(jw.GenerateHeadHTML(extraScript, extraStyle, extraImage, extraFont))
th.True(strings.Contains(string(jw.headPrefix), JavascriptPath))
th.True(strings.Contains(string(jw.headPrefix), extraScript))
th.True(strings.Contains(string(jw.headPrefix), extraStyle))
th.True(strings.Contains(string(jw.headPrefix), extraImage))
th.True(strings.Contains(string(jw.headPrefix), extraFont))

th.True(jw.GenerateHeadHTML("random.crap") != nil)
th.True(jw.GenerateHeadHTML("\n") != nil)
}

Expand Down
85 changes: 59 additions & 26 deletions js.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"compress/gzip"
_ "embed"
"hash/fnv"
"mime"
"net/url"
"path/filepath"
"strconv"
"strings"
)
Expand Down Expand Up @@ -61,36 +64,66 @@ func JawsKeyValue(jawsKey string) uint64 {
return 0
}

const jsLoader = `.forEach(function(c){var e=document.createElement("script");e.src=c;e.async=!1;document.head.appendChild(e);});`

// HeadHTML returns HTML code to load the given scripts and CSS files efficiently.
func HeadHTML(js []string, css []string) string {
var s []byte

for _, e := range css {
s = append(s, "<link rel=\"preload\" href="...)
s = strconv.AppendQuote(s, e)
s = append(s, " as=\"style\">\n"...)
}
for _, e := range js {
s = append(s, "<link rel=\"preload\" href="...)
s = strconv.AppendQuote(s, e)
s = append(s, " as=\"script\">\n"...)
// PreloadHTML returns HTML code to load the given resources efficiently.
func PreloadHTML(urls ...*url.URL) string {
var jsurls, cssurls []string
var buf []byte
for _, u := range urls {
var asattr string
ext := strings.ToLower(filepath.Ext(u.Path))
mimetype := mime.TypeByExtension(ext)
if semi := strings.IndexByte(mimetype, ';'); semi > 0 {
mimetype = mimetype[:semi]
}
urlstr := u.String()
switch ext {
case ".js":
jsurls = append(jsurls, urlstr)
asattr = "script"
case ".css":
cssurls = append(cssurls, urlstr)
asattr = "style"
default:
if strings.HasPrefix(mimetype, "image") {
asattr = "image"
} else if strings.HasPrefix(mimetype, "font") {
asattr = "font"
}
}
buf = append(buf, `<link rel="preload" href="`...)
buf = append(buf, urlstr...)
buf = append(buf, '"')
if asattr != "" {
buf = append(buf, ` as="`...)
buf = append(buf, asattr...)
buf = append(buf, '"')
}
if mimetype != "" {
buf = append(buf, ` type="`...)
buf = append(buf, mimetype...)
buf = append(buf, '"')
}
buf = append(buf, ">\n"...)
}
s = append(s, "<script>["...)
for i, e := range js {
if i > 0 {
s = append(s, ',')

if len(jsurls) > 0 {
buf = append(buf, `<script>[`...)
for i, urlstr := range jsurls {
if i > 0 {
buf = append(buf, ',')
}
buf = append(buf, '"')
buf = append(buf, urlstr...)
buf = append(buf, '"')
}
s = strconv.AppendQuote(s, e)
buf = append(buf, "].forEach(function(c){var e=document.createElement(\"script\");e.src=c;e.async=!1;document.head.appendChild(e);});</script>\n"...)
}
s = append(s, "]"+jsLoader+"</script>\n"...)

for _, e := range css {
s = append(s, "<link rel=\"stylesheet\" href="...)
s = strconv.AppendQuote(s, e)
s = append(s, ">\n"...)
for _, urlstr := range cssurls {
buf = append(buf, `<link rel="stylesheet" href="`...)
buf = append(buf, urlstr...)
buf = append(buf, "\">\n"...)
}

return string(s)
return string(buf)
}
27 changes: 24 additions & 3 deletions js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"compress/gzip"
_ "embed"
"hash/fnv"
"net/url"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -32,18 +33,38 @@ func Test_Javascript(t *testing.T) {
th.Equal(b.Bytes(), JavascriptGZip)
}

func Test_HeadHTML(t *testing.T) {
func Test_PreloadHTML(t *testing.T) {
const extraScript = "someExtraScript.js"
const extraStyle = "someExtraStyle.css"
const extraImage = "someExtraImage.png"
const extraFont = "someExtraFont.woff2"
th := newTestHelper(t)
txt := HeadHTML(nil, nil)

txt := PreloadHTML()
th.Equal(strings.Contains(txt, JavascriptPath), false)
th.Equal(strings.Count(txt, "<script>"), strings.Count(txt, "</script>"))
txt = HeadHTML([]string{JavascriptPath, extraScript}, []string{extraStyle})

mustParseUrl := func(urlstr string) *url.URL {
u, err := url.Parse(urlstr)
if err != nil {
t.Fatal(err)
}
return u
}

txt = PreloadHTML(
mustParseUrl(JavascriptPath),
mustParseUrl(extraScript),
mustParseUrl(extraStyle),
mustParseUrl(extraImage),
mustParseUrl(extraFont))
th.Equal(strings.Contains(txt, JavascriptPath), true)
th.Equal(strings.Contains(txt, extraScript), true)
th.Equal(strings.Contains(txt, extraStyle), true)
th.Equal(strings.Contains(txt, extraImage), true)
th.Equal(strings.Contains(txt, extraFont), true)
th.Equal(strings.Count(txt, "<script>"), strings.Count(txt, "</script>"))
t.Log(txt)
}

func TestJawsKeyString(t *testing.T) {
Expand Down

0 comments on commit 52ca4bb

Please sign in to comment.