diff --git a/framework/h/command_test.go b/framework/h/command_test.go index 2442506..ff331eb 100644 --- a/framework/h/command_test.go +++ b/framework/h/command_test.go @@ -7,6 +7,7 @@ import ( "regexp" "strings" "testing" + "time" ) func findScriptById(n *html.Node, id string) *html.Node { @@ -87,7 +88,7 @@ func TestJsEval(t *testing.T) { } func TestSetText(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, SetText("Hello World")), "this.innerText = 'Hello World';") + compareIgnoreSpaces(t, renderJs(t, SetText("Hello World")), "(self||this).innerText = 'Hello World';") } func TestSetTextOnChildren(t *testing.T) { @@ -100,42 +101,42 @@ func TestSetTextOnChildren(t *testing.T) { } func TestIncrement(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, Increment(5)), "this.innerText = parseInt(this.innerText) + 5;") + compareIgnoreSpaces(t, renderJs(t, Increment(5)), "(self||this).innerText = parseInt((self||this).innerText) + 5;") } func TestSetInnerHtml(t *testing.T) { htmlContent := Div(Span(UnsafeRaw("inner content"))) - compareIgnoreSpaces(t, renderJs(t, SetInnerHtml(htmlContent)), "this.innerHTML = `
inner content
`;") + compareIgnoreSpaces(t, renderJs(t, SetInnerHtml(htmlContent)), "(self||this).innerHTML = `
inner content
`;") } func TestSetOuterHtml(t *testing.T) { htmlContent := Div(Span(UnsafeRaw("outer content"))) - compareIgnoreSpaces(t, renderJs(t, SetOuterHtml(htmlContent)), "this.outerHTML = `
outer content
`;") + compareIgnoreSpaces(t, renderJs(t, SetOuterHtml(htmlContent)), "(self||this).outerHTML = `
outer content
`;") } func TestAddAttribute(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, AddAttribute("data-id", "123")), "this.setAttribute('data-id', '123');") + compareIgnoreSpaces(t, renderJs(t, AddAttribute("data-id", "123")), "(self||this).setAttribute('data-id', '123');") } func TestSetDisabled(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, SetDisabled(true)), "this.setAttribute('disabled', 'true');") - compareIgnoreSpaces(t, renderJs(t, SetDisabled(false)), "this.removeAttribute('disabled');") + compareIgnoreSpaces(t, renderJs(t, SetDisabled(true)), "(self||this).setAttribute('disabled', 'true');") + compareIgnoreSpaces(t, renderJs(t, SetDisabled(false)), "(self||this).removeAttribute('disabled');") } func TestRemoveAttribute(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, RemoveAttribute("data-id")), "this.removeAttribute('data-id');") + compareIgnoreSpaces(t, renderJs(t, RemoveAttribute("data-id")), "(self||this).removeAttribute('data-id');") } func TestAddClass(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, AddClass("active")), "this.classList.add('active');") + compareIgnoreSpaces(t, renderJs(t, AddClass("active")), "(self||this).classList.add('active');") } func TestRemoveClass(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, RemoveClass("active")), "this.classList.remove('active');") + compareIgnoreSpaces(t, renderJs(t, RemoveClass("active")), "(self||this).classList.remove('active');") } func TestToggleClass(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, ToggleClass("hidden")), "this.classList.toggle('hidden');") + compareIgnoreSpaces(t, renderJs(t, ToggleClass("hidden")), "(self||this).classList.toggle('hidden');") } func TestToggleClassOnElement(t *testing.T) { @@ -206,7 +207,7 @@ func TestAlert(t *testing.T) { } func TestRemove(t *testing.T) { - compareIgnoreSpaces(t, renderJs(t, Remove()), "this.remove();") + compareIgnoreSpaces(t, renderJs(t, Remove()), "(self||this).remove();") } func TestSubmitFormOnEnter(t *testing.T) { @@ -394,5 +395,23 @@ func TestConsoleLog(t *testing.T) { func TestSetValue(t *testing.T) { t.Parallel() - compareIgnoreSpaces(t, renderJs(t, SetValue("New Value")), "this.value = 'New Value';") + compareIgnoreSpaces(t, renderJs(t, SetValue("New Value")), "(self||this).value = 'New Value';") +} + +func TestRunAfterTimeout(t *testing.T) { + t.Parallel() + compareIgnoreSpaces(t, renderJs(t, RunAfterTimeout(time.Second*5, SetText("Hello"))), ` + setTimeout(function() { + (self||this).innerText = 'Hello' + }, 5000) + `) +} + +func TestRunOnInterval(t *testing.T) { + t.Parallel() + compareIgnoreSpaces(t, renderJs(t, RunOnInterval(time.Second, SetText("Hello"))), ` + setInterval(function() { + (self||this).innerText = 'Hello' + }, 1000) + `) } diff --git a/framework/h/lifecycle.go b/framework/h/lifecycle.go index 1aed70b..393c228 100644 --- a/framework/h/lifecycle.go +++ b/framework/h/lifecycle.go @@ -6,6 +6,7 @@ import ( "github.com/maddalax/htmgo/framework/hx" "github.com/maddalax/htmgo/framework/internal/util" "strings" + "time" ) type LifeCycle struct { @@ -163,7 +164,7 @@ func NewComplexJsCommand(command string) ComplexJsCommand { // SetText sets the inner text of the element. func SetText(text string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.innerText = '%s'", text)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = '%s'", text)} } // SetTextOnChildren sets the inner text of all the children of the element that match the selector. @@ -180,25 +181,25 @@ func SetTextOnChildren(selector, text string) ComplexJsCommand { // Increment increments the inner text of the element by the given amount. func Increment(amount int) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.innerText = parseInt(this.innerText) + %d", amount)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = parseInt((self || this).innerText) + %d", amount)} } // SetInnerHtml sets the inner HTML of the element. func SetInnerHtml(r Ren) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", Render(r))} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerHTML = `%s`", Render(r))} } // SetOuterHtml sets the outer HTML of the element. func SetOuterHtml(r Ren) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", Render(r))} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).outerHTML = `%s`", Render(r))} } // AddAttribute adds the given attribute to the element. func AddAttribute(name, value string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.setAttribute('%s', '%s')", name, value)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).setAttribute('%s', '%s')", name, value)} } // SetDisabled sets the disabled attribute on the element. @@ -213,25 +214,25 @@ func SetDisabled(disabled bool) SimpleJsCommand { // RemoveAttribute removes the given attribute from the element. func RemoveAttribute(name string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.removeAttribute('%s')", name)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).removeAttribute('%s')", name)} } // AddClass adds the given class to the element. func AddClass(class string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.classList.add('%s')", class)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.add('%s')", class)} } // RemoveClass removes the given class from the element. func RemoveClass(class string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.classList.remove('%s')", class)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.remove('%s')", class)} } // ToggleClass toggles the given class on the element. func ToggleClass(class string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.classList.toggle('%s')", class)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.toggle('%s')", class)} } // ToggleText toggles the given text on the element. @@ -391,7 +392,7 @@ func Alert(text string) SimpleJsCommand { // Remove removes the element from the DOM. func Remove() SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: "this.remove()"} + return SimpleJsCommand{Command: "(self || this).remove()"} } // EvalJs evaluates the given JavaScript code. @@ -399,15 +400,21 @@ func EvalJs(js string) ComplexJsCommand { return NewComplexJsCommand(js) } -func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand { +func CombineCommands(cmds ...Command) string { lines := make([]string, len(cmds)) for i, cmd := range cmds { lines[i] = Render(cmd) + lines[i] = strings.ReplaceAll(lines[i], "(self || this).", "self.") lines[i] = strings.ReplaceAll(lines[i], "this.", "self.") // some commands set the element we need to fix it so we arent redeclaring it lines[i] = strings.ReplaceAll(lines[i], "let element =", "element =") } code := strings.Join(lines, "\n") + return code +} + +func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand { + code := CombineCommands(cmds...) return EvalJs(fmt.Sprintf(` let element = document.querySelector("%s"); @@ -444,7 +451,7 @@ func ConsoleLog(text string) SimpleJsCommand { // SetValue sets the value of the element. func SetValue(value string) SimpleJsCommand { // language=JavaScript - return SimpleJsCommand{Command: fmt.Sprintf("this.value = '%s'", value)} + return SimpleJsCommand{Command: fmt.Sprintf("(self || this).value = '%s'", value)} } // SubmitFormOnEnter submits the form when the user presses the enter key. @@ -478,3 +485,31 @@ func InjectScriptIfNotExist(src string) ComplexJsCommand { } `, src, src)) } + +func RunOnInterval(time time.Duration, cmds ...Command) ComplexJsCommand { + code := strings.Builder{} + + for _, cmd := range cmds { + code.WriteString(fmt.Sprintf(` + setInterval(function() { + %s + }, %d) + `, Render(cmd), time.Milliseconds())) + } + + return EvalJs(code.String()) +} + +func RunAfterTimeout(time time.Duration, cmds ...Command) ComplexJsCommand { + code := strings.Builder{} + + for _, cmd := range cmds { + code.WriteString(fmt.Sprintf(` + setTimeout(function() { + %s + }, %d) + `, Render(cmd), time.Milliseconds())) + } + + return EvalJs(code.String()) +} diff --git a/framework/h/render_test.go b/framework/h/render_test.go index 83a4cf4..4ddfa03 100644 --- a/framework/h/render_test.go +++ b/framework/h/render_test.go @@ -48,7 +48,7 @@ func TestRender(t *testing.T) { div.attributes.Set("data-attr-1", "value") - expected := `
hello, world
hello, child
` + expected := `
hello, world
hello, child
` result := Render(div) assert.Equal(t, diff --git a/framework/js/commands.go b/framework/js/commands.go index b1703e3..18a6ce2 100644 --- a/framework/js/commands.go +++ b/framework/js/commands.go @@ -47,3 +47,5 @@ var ToggleText = h.ToggleText var ToggleTextOnSibling = h.ToggleTextOnSibling var ToggleTextOnChildren = h.ToggleTextOnChildren var ToggleTextOnParent = h.ToggleTextOnParent +var RunAfterTimeout = h.RunAfterTimeout +var RunOnInterval = h.RunOnInterval