Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.
/ vecty Public archive
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions dom.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package vecty

import (
"errors"
"reflect"
"sync"
"time"
)

// batch renderer singleton
Expand Down Expand Up @@ -1282,6 +1285,62 @@ func AddStylesheet(url string) {
global().Get("document").Get("head").Call("appendChild", link)
}

var ErrTimeout = errors.New("timeout while obtaining script from URL")
var ErrTimeoutGreaterThan = errors.New("timeout value has to be greater than zero")

// AddScript adds an external script to the document
func AddScript(url string, timeout time.Duration, attributes map[string]interface{}) error {
if timeout <= 0 {
return ErrTimeoutGreaterThan
}
script := global().Get("document").Call("createElement", "script")
script.Set("src", url)
for k, v := range attributes {
script.Set(k, v)
}

quitCh := make(chan struct{})

var f jsFunc
f = funcOf(func(this jsObject, args []jsObject) interface{} {
defer f.Release()
close(quitCh)
script.Call("removeEventListener", "load", f)
return nil
})
script.Call("addEventListener", "load", f)
global().Get("document").Get("head").Call("appendChild", script)

var err error
var wg sync.WaitGroup
wg.Add(1)
go func(t time.Duration, script jsObject, f jsFunc) {

tic := time.NewTicker(10 * time.Millisecond)
start := time.Now()
loop:
for {
select {
// Stop the execution of this goroutine when the script has loaded
case <-quitCh:
wg.Done()
break loop
case <-tic.C:
if time.Since(start) > t {
defer f.Release()
defer wg.Done()
err = ErrTimeout
script.Call("removeEventListener", "load", f)
break loop
}
}
}
}(timeout,script, f)
wg.Wait()

return err
}

type jsFunc interface {
Release()
}
Expand Down
10 changes: 10 additions & 0 deletions dom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vecty
import (
"fmt"
"testing"
"time"
)

type testCore struct{ Core }
Expand Down Expand Up @@ -1124,6 +1125,15 @@ func TestAddStylesheet(t *testing.T) {
AddStylesheet("https://google.com/foobar.css")
}

// TestAddScript tests that the AddScript performs the correct DOM
// operations.
func TestAddScript(t *testing.T) {
ts := testSuite(t)
defer ts.done()

AddScript("https://google.com/foobar.js", time.Millisecond * 100,map[string]interface{}{"crossorigin": "anonymous"})
}

func TestKeyedChild_DifferentType(t *testing.T) {
ts := testSuite(t)
defer ts.done()
Expand Down
9 changes: 9 additions & 0 deletions testdata/TestAddScript.want.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global.Get("document")
global.Get("document").Call("createElement", "script")
global.Get("document").Call("createElement", "script").Set("src", "https://google.com/foobar.js")
global.Get("document").Call("createElement", "script").Set("crossorigin", "anonymous")
global.Get("document").Call("createElement", "script").Call("addEventListener", "load", func)
global.Get("document")
global.Get("document").Get("head")
global.Get("document").Get("head").Call("appendChild", jsObject(global.Get("document").Call("createElement", "script")))
global.Get("document").Call("createElement", "script").Call("removeEventListener", "load", func)