Last active
September 1, 2017 06:53
-
-
Save iocat/35eb32eb8077ed41c6fd20628158759d to your computer and use it in GitHub Desktop.
A simple Gopherjs resource loader for dynamic resources added to the head of the document
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package resloader loads resources and invokes a single callback for all packages | |
// useful for checking availability of loaded resources on the fly or all at once | |
package resloader | |
import ( | |
"errors" | |
"path/filepath" | |
"time" | |
"github.com/gopherjs/gopherjs/js" | |
) | |
/* | |
Usage | |
loader := resloader.New() | |
loader.Add("https://code.jquery.com/jquery-3.2.1.min.js", nil) | |
loader.Add("https://cdn.jsdelivr.net/semantic-ui/2.2.9/semantic.min.css") | |
loader.SetOnAllLoaded(func(){ | |
println("loaded all") | |
}) | |
loader.LoadAll() | |
*/ | |
type Type int | |
const ( | |
JS Type = iota | |
CSS | |
) | |
const ( | |
defaultCheckFreqForAll = 1 * time.Millisecond | |
) | |
type ResData struct { | |
Type Type | |
// The source of this resource | |
URL string | |
// Callback when this particular resource is loaded | |
Callback func() | |
} | |
type Loader struct { | |
resources []ResData | |
checkAllFreq time.Duration | |
onAllLoadedFn func() | |
count int | |
invokeCount int | |
} | |
func New() *Loader { | |
return &Loader{ | |
resources: make([]ResData, 0), | |
checkAllFreq: defaultCheckFreqForAll, | |
onAllLoadedFn: nil, | |
count: 0, | |
invokeCount: 0, | |
} | |
} | |
func getType(path string) (Type, error) { | |
switch filepath.Ext(path) { | |
case ".js": | |
return JS, nil | |
case ".css": | |
return CSS, nil | |
default: | |
return -1, errors.New("Can't determine the type of file based on the path") | |
} | |
} | |
// AddWithType adds a resource where type is not a part of the url, callback | |
// can be nil | |
func (l *Loader) AddWithType(t Type, url string, callback func()) { | |
resData := ResData{ | |
Type: t, | |
URL: url, | |
Callback: nil, | |
} | |
resData.Callback = func() { | |
l.count++ // safe because all executions are single threaded | |
if callback != nil { | |
callback() | |
} | |
} | |
l.resources = append(l.resources, resData) | |
} | |
// Add adds a resource where type can be inferred from the URL, callback can be nil | |
func (l *Loader) Add(url string, callback func()) { | |
t, err := getType(url) | |
if err != nil { | |
panic("can't determine type from the provided URL") | |
} | |
l.AddWithType(t, url, callback) | |
} | |
// ErrCallLoadTwice occurs when the loader loads everything twice which | |
// is not a purpose of this package | |
var ErrCallLoadTwice = errors.New("WARNING: Can't call LoadAll more than one") | |
// LoadAll starts loading all resources | |
func (l *Loader) LoadAll() { | |
l.invokeCount++ | |
if l.invokeCount > 1 { | |
println(ErrCallLoadTwice) | |
panic(ErrCallLoadTwice) | |
} | |
if l.onAllLoadedFn != nil { | |
var repeatIfNotAll func() | |
repeatIfNotAll = func() { | |
if l.count != len(l.resources) { | |
time.AfterFunc(l.checkAllFreq, repeatIfNotAll) | |
} else { | |
l.onAllLoadedFn() | |
} | |
} | |
time.AfterFunc(l.checkAllFreq, repeatIfNotAll) | |
} | |
for _, res := range l.resources { | |
switch res.Type { | |
case JS: | |
AddScript(res.URL, res.Callback) | |
case CSS: | |
AddStyleSheet(res.URL, res.Callback) | |
default: | |
continue | |
} | |
} | |
} | |
// SetOnAllLoaded sets the callback when all resources are | |
// available | |
func (l *Loader) SetOnAllLoaded(callback func()) { | |
l.onAllLoadedFn = callback | |
} | |
// SetOnAllLoadedCheckFreq sets the frequency of which we check | |
// the availability of all loaded resources | |
func (l *Loader) SetOnAllLoadedCheckFreq(checkFreq time.Duration) { | |
l.checkAllFreq = checkFreq | |
} | |
func AddStyleSheet(url string, callback func()) { | |
link := js.Global.Get("document").Call("createElement", "link") | |
link.Set("href", url) | |
link.Set("type", "text/css") | |
link.Set("rel", "stylesheet") | |
link.Set("onload", callback) | |
js.Global.Get("document").Get("head").Call("appendChild", link) | |
} | |
func AddScript(url string, callback func()) { | |
script := js.Global.Get("document").Call("createElement", "script") | |
script.Set("src", url) | |
script.Set("type", "text/javascript") | |
script.Set("onload", callback) | |
js.Global.Get("document").Get("head").Call("appendChild", script) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment