2024-09-13 01:31:18 +00:00
|
|
|
package h
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-10-29 13:44:52 +00:00
|
|
|
"github.com/google/uuid"
|
2024-09-21 03:59:07 +00:00
|
|
|
"github.com/maddalax/htmgo/framework/hx"
|
2024-09-22 15:46:38 +00:00
|
|
|
"github.com/maddalax/htmgo/framework/internal/util"
|
2024-09-22 16:53:41 +00:00
|
|
|
"strings"
|
2024-11-16 14:52:00 +00:00
|
|
|
"time"
|
2024-09-13 01:31:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type LifeCycle struct {
|
2024-09-21 03:59:07 +00:00
|
|
|
handlers map[hx.Event][]Command
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewLifeCycle() *LifeCycle {
|
|
|
|
|
return &LifeCycle{
|
2024-09-21 03:59:07 +00:00
|
|
|
handlers: make(map[hx.Event][]Command),
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-21 03:59:07 +00:00
|
|
|
func validateCommands(cmds []Command) {
|
|
|
|
|
for _, cmd := range cmds {
|
|
|
|
|
switch t := cmd.(type) {
|
2024-09-22 15:46:38 +00:00
|
|
|
case SimpleJsCommand:
|
|
|
|
|
break
|
|
|
|
|
case ComplexJsCommand:
|
2024-09-21 03:59:07 +00:00
|
|
|
break
|
2024-09-30 17:39:48 +00:00
|
|
|
case *AttributeMapOrdered:
|
2024-09-21 03:59:07 +00:00
|
|
|
break
|
|
|
|
|
case *Element:
|
|
|
|
|
panic(fmt.Sprintf("element is not allowed in lifecycle events. Got: %v", t))
|
|
|
|
|
default:
|
|
|
|
|
panic(fmt.Sprintf("type is not allowed in lifecycle events. Got: %v", t))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LifeCycle) OnEvent(event hx.Event, cmd ...Command) *LifeCycle {
|
|
|
|
|
validateCommands(cmd)
|
|
|
|
|
|
2024-09-22 16:53:41 +00:00
|
|
|
if strings.HasPrefix(event, "htmx:") {
|
|
|
|
|
event = event[5:]
|
|
|
|
|
event = util.ConvertCamelToDash(fmt.Sprintf("hx-on::%s", event))
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-16 22:41:46 +00:00
|
|
|
if l.handlers[event] == nil {
|
2024-09-21 03:59:07 +00:00
|
|
|
l.handlers[event] = []Command{}
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
2024-09-21 03:59:07 +00:00
|
|
|
|
2024-09-16 22:41:46 +00:00
|
|
|
l.handlers[event] = append(l.handlers[event], cmd...)
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// OnLoad executes the given commands when the element is loaded into the DOM, it also executes when the element is replaced / swapped in.
|
2024-10-26 03:01:04 +00:00
|
|
|
// This will work on any element because of the htmgo htmx extension to trigger it, instead of the browser.
|
2024-09-23 01:59:44 +00:00
|
|
|
func OnLoad(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.LoadDomEvent, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *LifeCycle) HxBeforeRequest(cmd ...Command) *LifeCycle {
|
2024-09-21 03:59:07 +00:00
|
|
|
l.OnEvent(hx.BeforeRequestEvent, cmd...)
|
2024-09-13 01:31:18 +00:00
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnLoad executes the given commands when the element is loaded into the DOM.
|
|
|
|
|
// Deprecated: Use OnLoad instead.
|
2024-09-23 01:59:44 +00:00
|
|
|
func HxOnLoad(cmd ...Command) *LifeCycle {
|
2024-09-21 03:59:07 +00:00
|
|
|
return NewLifeCycle().OnEvent(hx.LoadEvent, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnAfterSwap executes the given commands when the element is swapped in.
|
2024-09-23 01:59:44 +00:00
|
|
|
func HxOnAfterSwap(cmd ...Command) *LifeCycle {
|
2024-09-21 03:59:07 +00:00
|
|
|
return NewLifeCycle().OnEvent(hx.AfterSwapEvent, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// OnClick executes the given commands when the element is clicked.
|
2024-09-21 03:59:07 +00:00
|
|
|
func OnClick(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.ClickEvent, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// OnEvent executes the given commands when the given event is triggered.
|
2024-09-21 03:59:07 +00:00
|
|
|
func OnEvent(event hx.Event, cmd ...Command) *LifeCycle {
|
2024-09-16 22:41:46 +00:00
|
|
|
return NewLifeCycle().OnEvent(event, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxBeforeSseMessage executes the given commands when a message is received from the server via SSE, but before it is processed.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxBeforeSseMessage(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseBeforeMessageEvent, cmd...)
|
2024-10-01 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxAfterSseMessage executes the given commands when a message is received from the server via SSE, and after it is processed.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxAfterSseMessage(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseAfterMessageEvent, cmd...)
|
2024-10-01 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// OnSubmit executes the given commands when the form is submitted.
|
2024-10-01 17:09:22 +00:00
|
|
|
func OnSubmit(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SubmitEvent, cmd...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnSseError executes the given commands when an error occurs while connecting to the server via SSE.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxOnSseError(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseErrorEvent, cmd...)
|
2024-10-01 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnSseClose executes the given commands when the connection to the server via SSE is closed.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxOnSseClose(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseClosedEvent, cmd...)
|
2024-10-01 17:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnSseConnecting executes the given commands when the connection to the server via SSE is being established.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxOnSseConnecting(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseConnectingEvent, cmd...)
|
2024-09-30 21:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnSseOpen executes the given commands when the connection to the server via SSE is established.
|
2024-10-02 03:26:03 +00:00
|
|
|
func HxOnSseOpen(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().OnEvent(hx.SseConnectedEvent, cmd...)
|
2024-09-30 21:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxBeforeRequest executes the given commands before the request is sent.
|
2024-09-23 01:59:44 +00:00
|
|
|
func HxBeforeRequest(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().HxBeforeRequest(cmd...)
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxAfterRequest executes the given commands after the request is sent.
|
2024-09-23 01:59:44 +00:00
|
|
|
func HxAfterRequest(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().HxAfterRequest(cmd...)
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxOnMutationError executes the given commands when a mutation error of a request occurs.
|
2024-09-23 01:59:44 +00:00
|
|
|
func HxOnMutationError(cmd ...Command) *LifeCycle {
|
|
|
|
|
return NewLifeCycle().HxOnMutationError(cmd...)
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-23 01:59:44 +00:00
|
|
|
func (l *LifeCycle) HxAfterRequest(cmd ...Command) *LifeCycle {
|
2024-09-21 03:59:07 +00:00
|
|
|
l.OnEvent(hx.AfterRequestEvent, cmd...)
|
2024-09-13 01:31:18 +00:00
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 01:59:44 +00:00
|
|
|
func (l *LifeCycle) HxOnMutationError(cmd ...Command) *LifeCycle {
|
2024-09-21 03:59:07 +00:00
|
|
|
l.OnEvent(hx.OnMutationErrorEvent, cmd...)
|
2024-09-13 01:31:18 +00:00
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-21 03:59:07 +00:00
|
|
|
type Command = Ren
|
|
|
|
|
|
2024-09-22 15:46:38 +00:00
|
|
|
type SimpleJsCommand struct {
|
2024-09-13 01:31:18 +00:00
|
|
|
Command string
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-22 15:46:38 +00:00
|
|
|
type ComplexJsCommand struct {
|
|
|
|
|
Command string
|
|
|
|
|
TempFuncName string
|
2024-09-21 03:59:07 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// NewComplexJsCommand creates a new complex JavaScript command.
|
2024-09-23 01:59:44 +00:00
|
|
|
func NewComplexJsCommand(command string) ComplexJsCommand {
|
|
|
|
|
name := fmt.Sprintf("__eval_%s", util.RandSeq(6))
|
|
|
|
|
return ComplexJsCommand{Command: command, TempFuncName: name}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetText sets the inner text of the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func SetText(text string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = '%s'", text)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetTextOnChildren sets the inner text of all the children of the element that match the selector.
|
2024-09-29 13:21:58 +00:00
|
|
|
func SetTextOnChildren(selector, text string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
|
|
|
|
var children = self.querySelectorAll('%s');
|
|
|
|
|
children.forEach(function(child) {
|
|
|
|
|
child.innerText = '%s';
|
|
|
|
|
});
|
|
|
|
|
`, selector, text))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// Increment increments the inner text of the element by the given amount.
|
2024-09-22 15:46:38 +00:00
|
|
|
func Increment(amount int) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = parseInt((self || this).innerText) + %d", amount)}
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetInnerHtml sets the inner HTML of the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func SetInnerHtml(r Ren) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerHTML = `%s`", Render(r))}
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetOuterHtml sets the outer HTML of the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func SetOuterHtml(r Ren) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).outerHTML = `%s`", Render(r))}
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// AddAttribute adds the given attribute to the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func AddAttribute(name, value string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).setAttribute('%s', '%s')", name, value)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetDisabled sets the disabled attribute on the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func SetDisabled(disabled bool) SimpleJsCommand {
|
2024-09-16 22:41:46 +00:00
|
|
|
if disabled {
|
|
|
|
|
return AddAttribute("disabled", "true")
|
|
|
|
|
} else {
|
|
|
|
|
return RemoveAttribute("disabled")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// RemoveAttribute removes the given attribute from the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func RemoveAttribute(name string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).removeAttribute('%s')", name)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// AddClass adds the given class to the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func AddClass(class string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.add('%s')", class)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// RemoveClass removes the given class from the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func RemoveClass(class string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.remove('%s')", class)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// ToggleClass toggles the given class on the element.
|
2024-09-22 15:46:38 +00:00
|
|
|
func ToggleClass(class string) SimpleJsCommand {
|
2024-09-19 23:11:12 +00:00
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.toggle('%s')", class)}
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 13:44:52 +00:00
|
|
|
// ToggleText toggles the given text on the element.
|
|
|
|
|
func ToggleText(text string, textTwo string) Command {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
|
|
|
|
if(self.innerText === "%s") {
|
|
|
|
|
self.innerText = "%s";
|
|
|
|
|
} else {
|
|
|
|
|
self.innerText = "%s";
|
|
|
|
|
}
|
|
|
|
|
`, text, textTwo, text))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToggleTextOnSibling toggles the given text on the siblings of the element.
|
|
|
|
|
func ToggleTextOnSibling(selector, text string, textTwo string) Command {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnSibling(selector, fmt.Sprintf(`
|
|
|
|
|
if(element.innerText === "%s") {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
} else {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
}
|
|
|
|
|
`, text, textTwo, text))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToggleTextOnChildren toggles the given text on the children of the element.
|
|
|
|
|
func ToggleTextOnChildren(selector, text string, textTwo string) Command {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnChildren(selector, fmt.Sprintf(`
|
|
|
|
|
if(element.innerText === "%s") {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
} else {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
}
|
|
|
|
|
`, text, textTwo, text))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToggleTextOnParent toggles the given text on the parent of the element.
|
|
|
|
|
func ToggleTextOnParent(text string, textTwo string) Command {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnParent(fmt.Sprintf(`
|
|
|
|
|
if(element.innerText === "%s") {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
} else {
|
|
|
|
|
element.innerText = "%s";
|
|
|
|
|
}
|
|
|
|
|
`, text, textTwo, text))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// ToggleClassOnElement toggles the given class on the elements returned by the selector.
|
2024-09-22 15:46:38 +00:00
|
|
|
func ToggleClassOnElement(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
|
|
|
|
var el = document.querySelector('%s');
|
|
|
|
|
if(el) { el.classList.toggle('%s'); }`,
|
2024-09-22 16:25:18 +00:00
|
|
|
selector, class,
|
2024-09-22 15:46:38 +00:00
|
|
|
))
|
2024-09-13 01:31:18 +00:00
|
|
|
}
|
2024-09-20 20:15:12 +00:00
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// EvalJsOnParent evaluates the given JavaScript code on the parent of the element. Reference the element using 'element'.
|
2024-09-29 16:45:52 +00:00
|
|
|
func EvalJsOnParent(js string) ComplexJsCommand {
|
2024-09-29 13:21:58 +00:00
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
2024-10-29 13:44:52 +00:00
|
|
|
if(self.parentElement) {
|
|
|
|
|
let element = self.parentElement;
|
|
|
|
|
%s
|
|
|
|
|
}
|
2024-09-29 16:45:52 +00:00
|
|
|
`, js))
|
2024-09-29 13:21:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// EvalJsOnChildren evaluates the given JavaScript code on the children of the element. Reference the element using 'element'.
|
2024-09-29 16:45:52 +00:00
|
|
|
func EvalJsOnChildren(selector, js string) ComplexJsCommand {
|
2024-09-29 13:21:58 +00:00
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
2024-09-29 16:45:52 +00:00
|
|
|
let children = self.querySelectorAll('%s');
|
|
|
|
|
children.forEach(function(element) {
|
|
|
|
|
%s
|
2024-09-29 13:21:58 +00:00
|
|
|
});
|
2024-09-29 16:45:52 +00:00
|
|
|
`, selector, js))
|
2024-09-29 13:21:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// EvalJsOnSibling evaluates the given JavaScript code on the siblings of the element. Reference the element using 'element'.
|
2024-09-29 16:45:52 +00:00
|
|
|
func EvalJsOnSibling(selector, js string) ComplexJsCommand {
|
2024-09-29 13:21:58 +00:00
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
2024-10-29 13:44:52 +00:00
|
|
|
if(self.parentElement) {
|
|
|
|
|
let siblings = self.parentElement.querySelectorAll('%s');
|
|
|
|
|
siblings.forEach(function(element) {
|
|
|
|
|
%s
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-09-29 16:45:52 +00:00
|
|
|
`, selector, js))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 13:44:52 +00:00
|
|
|
// SetClassOnParent sets the given class on the parent of the element.
|
2024-09-29 16:50:30 +00:00
|
|
|
func SetClassOnParent(class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnParent(fmt.Sprintf("element.classList.add('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 13:44:52 +00:00
|
|
|
// RemoveClassOnParent removes the given class from the parent of the element.
|
2024-09-29 16:50:30 +00:00
|
|
|
func RemoveClassOnParent(class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnParent(fmt.Sprintf("element.classList.remove('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 13:44:52 +00:00
|
|
|
// SetClassOnChildren sets the given class on the children of the element.
|
2024-09-29 16:45:52 +00:00
|
|
|
func SetClassOnChildren(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.add('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 13:44:52 +00:00
|
|
|
// ToggleClassOnChildren toggles the given class on the children of the element.
|
|
|
|
|
func ToggleClassOnChildren(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.toggle('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToggleClassOnParent toggles the given class on the parent of the element.
|
|
|
|
|
func ToggleClassOnParent(class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnParent(fmt.Sprintf("element.classList.toggle('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToggleClassOnSibling toggles the given class on the siblings of the element.
|
|
|
|
|
func ToggleClassOnSibling(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnSibling(selector, fmt.Sprintf("element.classList.toggle('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetClassOnSibling sets the given class on the siblings of the element. Reference the element using 'element'.
|
2024-09-29 16:45:52 +00:00
|
|
|
func SetClassOnSibling(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnSibling(selector, fmt.Sprintf("element.classList.add('%s')", class))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// RemoveClassOnSibling removes the given class from the siblings of the element. Reference the element using 'element'.
|
2024-09-29 16:45:52 +00:00
|
|
|
func RemoveClassOnSibling(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJsOnSibling(selector, fmt.Sprintf("element.classList.remove('%s')", class))
|
|
|
|
|
|
2024-09-29 13:21:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// RemoveClassOnChildren removes the given class from the children of the element. Reference the element using 'element'.
|
2024-09-29 13:21:58 +00:00
|
|
|
func RemoveClassOnChildren(selector, class string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
2024-09-29 16:45:52 +00:00
|
|
|
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.remove('%s')", class))
|
2024-09-29 13:21:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// Alert displays an alert dialog with the given text.
|
2024-09-22 15:46:38 +00:00
|
|
|
func Alert(text string) SimpleJsCommand {
|
2024-09-20 20:15:12 +00:00
|
|
|
// language=JavaScript
|
2024-09-22 15:46:38 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("alert('%s')", text)}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// Remove removes the element from the DOM.
|
2024-09-26 14:23:22 +00:00
|
|
|
func Remove() SimpleJsCommand {
|
|
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: "(self || this).remove()"}
|
2024-09-26 14:23:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// EvalJs evaluates the given JavaScript code.
|
2024-09-22 15:46:38 +00:00
|
|
|
func EvalJs(js string) ComplexJsCommand {
|
2024-09-23 01:59:44 +00:00
|
|
|
return NewComplexJsCommand(js)
|
2024-09-22 15:46:38 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-16 14:52:00 +00:00
|
|
|
func CombineCommands(cmds ...Command) string {
|
2024-10-29 13:44:52 +00:00
|
|
|
lines := make([]string, len(cmds))
|
|
|
|
|
for i, cmd := range cmds {
|
|
|
|
|
lines[i] = Render(cmd)
|
2024-11-16 14:52:00 +00:00
|
|
|
lines[i] = strings.ReplaceAll(lines[i], "(self || this).", "self.")
|
2024-10-29 13:44:52 +00:00
|
|
|
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")
|
2024-11-16 14:52:00 +00:00
|
|
|
return code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand {
|
|
|
|
|
code := CombineCommands(cmds...)
|
2024-10-29 13:44:52 +00:00
|
|
|
return EvalJs(fmt.Sprintf(`
|
|
|
|
|
let element = document.querySelector("%s");
|
|
|
|
|
|
|
|
|
|
if(!element) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self = element;
|
|
|
|
|
%s
|
|
|
|
|
`, selector, code))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func EvalCommands(element *Element, cmds ...Command) ComplexJsCommand {
|
|
|
|
|
id := strings.ReplaceAll(uuid.NewString(), "-", "")
|
|
|
|
|
element.AppendChildren(
|
|
|
|
|
Attribute("data-eval-commands-id", id),
|
|
|
|
|
)
|
|
|
|
|
return EvalCommandsOnSelector(
|
|
|
|
|
fmt.Sprintf(`[data-eval-commands-id='%s']`, id), cmds...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// PreventDefault prevents the default action of the event.
|
2024-10-01 17:09:22 +00:00
|
|
|
func PreventDefault() SimpleJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return SimpleJsCommand{Command: "event.preventDefault()"}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// ConsoleLog logs a message to the console.
|
2024-10-01 17:09:22 +00:00
|
|
|
func ConsoleLog(text string) SimpleJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("console.log('%s')", text)}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SetValue sets the value of the element.
|
2024-09-30 21:05:06 +00:00
|
|
|
func SetValue(value string) SimpleJsCommand {
|
|
|
|
|
// language=JavaScript
|
2024-11-16 14:52:00 +00:00
|
|
|
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).value = '%s'", value)}
|
2024-09-30 21:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// SubmitFormOnEnter submits the form when the user presses the enter key.
|
2024-09-29 13:47:39 +00:00
|
|
|
func SubmitFormOnEnter() ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(`
|
2024-10-03 20:35:19 +00:00
|
|
|
if (event.code === 'Enter') { self.form.dispatchEvent(new Event('submit', { cancelable: true })); }
|
2024-09-29 13:47:39 +00:00
|
|
|
`)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// InjectScript injects a script tag into the document.
|
2024-09-22 15:46:38 +00:00
|
|
|
func InjectScript(src string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
2024-09-23 01:59:44 +00:00
|
|
|
return NewComplexJsCommand(fmt.Sprintf(`
|
2024-09-20 20:15:12 +00:00
|
|
|
var script = document.createElement('script');
|
|
|
|
|
script.src = '%s';
|
2024-09-23 01:59:44 +00:00
|
|
|
script.async = true;
|
2024-09-20 20:15:12 +00:00
|
|
|
document.head.appendChild(script);
|
2024-09-23 01:59:44 +00:00
|
|
|
`, src))
|
2024-09-20 20:15:12 +00:00
|
|
|
}
|
2024-09-22 15:46:38 +00:00
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// InjectScriptIfNotExist injects a script tag into the document if it does not already exist.
|
2024-09-22 15:46:38 +00:00
|
|
|
func InjectScriptIfNotExist(src string) ComplexJsCommand {
|
|
|
|
|
// language=JavaScript
|
|
|
|
|
return EvalJs(fmt.Sprintf(`
|
|
|
|
|
if(!document.querySelector('script[src="%s"]')) {
|
|
|
|
|
var script = document.createElement('script');
|
|
|
|
|
script.src = '%s';
|
|
|
|
|
script.async = true;
|
|
|
|
|
document.head.appendChild(script);
|
|
|
|
|
}
|
|
|
|
|
`, src, src))
|
|
|
|
|
}
|
2024-11-16 14:52:00 +00:00
|
|
|
|
|
|
|
|
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())
|
|
|
|
|
}
|