func (p *Page) ExtractElement(act *Action, out map[string]string) error {
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	switch act.GetArg("target") {
	case "attribute":
		attrName := act.GetArg("attribute")
		if attrName == "" {
			return errors.New("attribute can't be empty")
		}
		attrValue, err := element.Attribute(attrName)
		if err != nil {
			return errors.Wrap(err, "could not get attribute")
		}
		if act.Name != "" {
			out[act.Name] = *attrValue
		}
	default:
		text, err := element.Text()
		if err != nil {
			return errors.Wrap(err, "could not get element text node")
		}
		if act.Name != "" {
			out[act.Name] = text
		}
	}
	return nil
}
ExtractElement extracts from an element on the page.
package engine
import (
	"context"
	"io/ioutil"
	"net"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/proto"
	"github.com/go-rod/rod/lib/utils"
	"github.com/pkg/errors"
	"github.com/segmentio/ksuid"
	"github.com/valyala/fasttemplate"
)
func (p *Page) ExecuteActions(baseURL *url.URL, actions []*Action) (map[string]string, error) {
	var err error
	outData := make(map[string]string)
	for _, act := range actions {
		actionType := ActionStringToAction[act.ActionType]
		switch actionType {
		case ActionNavigate:
			err = p.NavigateURL(act, outData, baseURL)
		case ActionScript:
			err = p.RunScript(act, outData)
		case ActionClick:
			err = p.ClickElement(act, outData)
		case ActionRightClick:
			err = p.RightClickElement(act, outData)
		case ActionTextInput:
			err = p.InputElement(act, outData)
		case ActionScreenshot:
			err = p.Screenshot(act, outData)
		case ActionTimeInput:
			err = p.TimeInputElement(act, outData)
		case ActionSelectInput:
			err = p.SelectInputElement(act, outData)
		case ActionWaitLoad:
			err = p.WaitLoad(act, outData)
		case ActionGetResource:
			err = p.GetResource(act, outData)
		case ActionExtract:
			err = p.ExtractElement(act, outData)
		case ActionWaitEvent:
			err = p.WaitEvent(act, outData)
		case ActionFilesInput:
			err = p.FilesInput(act, outData)
		case ActionAddHeader:
			err = p.ActionAddHeader(act, outData)
		case ActionSetHeader:
			err = p.ActionSetHeader(act, outData)
		case ActionDeleteHeader:
			err = p.ActionDeleteHeader(act, outData)
		case ActionSetBody:
			err = p.ActionSetBody(act, outData)
		case ActionSetMethod:
			err = p.ActionSetMethod(act, outData)
		case ActionKeyboard:
			err = p.KeyboardAction(act, outData)
		case ActionDebug:
			err = p.DebugAction(act, outData)
		case ActionSleep:
			err = p.SleepAction(act, outData)
		case ActionWaitVisible:
			err = p.WaitVisible(act, outData)
		default:
			continue
		}
		if err != nil {
			return nil, errors.Wrap(err, "error occurred executing action")
		}
	}
	return outData, nil
}
type requestRule struct {
	Action ActionType
	Part   string
	Args   map[string]string
}
const elementDidNotAppearMessage = "Element did not appear in the given amount of time"
func (p *Page) WaitVisible(act *Action, out map[string]string) error {
	timeout, err := getTimeout(act)
	if err != nil {
		return errors.Wrap(err, "Wrong timeout given")
	}
	pollTime, err := getPollTime(act)
	if err != nil {
		return errors.Wrap(err, "Wrong polling time given")
	}
	element, _ := p.Sleeper(pollTime, timeout).
		Timeout(timeout).
		pageElementBy(act.Data)
	if element != nil {
		if err := element.WaitVisible(); err != nil {
			return errors.Wrap(err, elementDidNotAppearMessage)
		}
	} else {
		return errors.New(elementDidNotAppearMessage)
	}
	return nil
}
func (p *Page) Sleeper(pollTimeout, timeout time.Duration) *Page {
	page := *p
	page.page = page.Page().Sleeper(func() utils.Sleeper {
		return createBackOffSleeper(pollTimeout, timeout)
	})
	return &page
}
func (p *Page) Timeout(timeout time.Duration) *Page {
	page := *p
	page.page = page.Page().Timeout(timeout)
	return &page
}
func createBackOffSleeper(pollTimeout, timeout time.Duration) utils.Sleeper {
	backoffSleeper := utils.BackoffSleeper(pollTimeout, timeout, func(duration time.Duration) time.Duration {
		return duration
	})
	return func(ctx context.Context) error {
		if ctx.Err() != nil {
			return ctx.Err()
		}
		return backoffSleeper(ctx)
	}
}
func getTimeout(act *Action) (time.Duration, error) {
	return geTimeParameter(act, "timeout", 3, time.Second)
}
func getPollTime(act *Action) (time.Duration, error) {
	return geTimeParameter(act, "pollTime", 100, time.Millisecond)
}
func geTimeParameter(act *Action, parameterName string, defaultValue time.Duration, duration time.Duration) (time.Duration, error) {
	pollTimeString := act.GetArg(parameterName)
	if pollTimeString == "" {
		return defaultValue * duration, nil
	}
	timeout, err := strconv.Atoi(pollTimeString)
	if err != nil {
		return time.Duration(0), err
	}
	return time.Duration(timeout) * duration, nil
}
func (p *Page) ActionAddHeader(act *Action, out map[string]string  ) error {
	in := act.GetArg("part")
	args := make(map[string]string)
	args["key"] = act.GetArg("key")
	args["value"] = act.GetArg("value")
	rule := requestRule{
		Action: ActionAddHeader,
		Part:   in,
		Args:   args,
	}
	p.rules = append(p.rules, rule)
	return nil
}
func (p *Page) ActionSetHeader(act *Action, out map[string]string  ) error {
	in := act.GetArg("part")
	args := make(map[string]string)
	args["key"] = act.GetArg("key")
	args["value"] = act.GetArg("value")
	rule := requestRule{
		Action: ActionSetHeader,
		Part:   in,
		Args:   args,
	}
	p.rules = append(p.rules, rule)
	return nil
}
func (p *Page) ActionDeleteHeader(act *Action, out map[string]string  ) error {
	in := act.GetArg("part")
	args := make(map[string]string)
	args["key"] = act.GetArg("key")
	rule := requestRule{
		Action: ActionDeleteHeader,
		Part:   in,
		Args:   args,
	}
	p.rules = append(p.rules, rule)
	return nil
}
func (p *Page) ActionSetBody(act *Action, out map[string]string  ) error {
	in := act.GetArg("part")
	args := make(map[string]string)
	args["body"] = act.GetArg("body")
	rule := requestRule{
		Action: ActionSetBody,
		Part:   in,
		Args:   args,
	}
	p.rules = append(p.rules, rule)
	return nil
}
func (p *Page) ActionSetMethod(act *Action, out map[string]string  ) error {
	in := act.GetArg("part")
	args := make(map[string]string)
	args["method"] = act.GetArg("method")
	rule := requestRule{
		Action: ActionSetMethod,
		Part:   in,
		Args:   args,
	}
	p.rules = append(p.rules, rule)
	return nil
}
func (p *Page) NavigateURL(action *Action, out map[string]string, parsed *url.URL  ) error {
	URL := action.GetArg("url")
	if URL == "" {
		return errors.New("invalid arguments provided")
	}
	URL, parsed = baseURLWithTemplatePrefs(URL, parsed)
	values := map[string]interface{}{"Hostname": parsed.Hostname()}
	if strings.HasSuffix(parsed.Path, "/") && strings.Contains(URL, "{{BaseURL}}/") {
		parsed.Path = strings.TrimSuffix(parsed.Path, "/")
	}
	parsedString := parsed.String()
	values["BaseURL"] = parsedString
	final := fasttemplate.ExecuteStringStd(URL, "{{", "}}", values)
	if err := p.page.Navigate(final); err != nil {
		return errors.Wrap(err, "could not navigate")
	}
	return nil
}
func (p *Page) RunScript(action *Action, out map[string]string) error {
	code := action.GetArg("code")
	if code == "" {
		return errors.New("invalid arguments provided")
	}
	if action.GetArg("hook") == "true" {
		if _, err := p.page.EvalOnNewDocument(code); err != nil {
			return err
		}
	}
	data, err := p.page.Eval(code)
	if err != nil {
		return err
	}
	if data != nil && action.Name != "" {
		out[action.Name] = data.Value.String()
	}
	return nil
}
func (p *Page) ClickElement(act *Action, out map[string]string  ) error {
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	if err = element.Click(proto.InputMouseButtonLeft); err != nil {
		return errors.Wrap(err, "could not click element")
	}
	return nil
}
func (p *Page) KeyboardAction(act *Action, out map[string]string  ) error {
	return p.page.Keyboard.Press([]rune(act.GetArg("keys"))...)
}
func (p *Page) RightClickElement(act *Action, out map[string]string  ) error {
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	if err = element.Click(proto.InputMouseButtonRight); err != nil {
		return errors.Wrap(err, "could not right click element")
	}
	return nil
}
func (p *Page) Screenshot(act *Action, out map[string]string) error {
	to := act.GetArg("to")
	if to == "" {
		to = ksuid.New().String()
		if act.Name != "" {
			out[act.Name] = to
		}
	}
	var data []byte
	var err error
	if act.GetArg("fullpage") == "true" {
		data, err = p.page.Screenshot(true, &proto.PageCaptureScreenshot{})
	} else {
		data, err = p.page.Screenshot(false, &proto.PageCaptureScreenshot{})
	}
	if err != nil {
		return errors.Wrap(err, "could not take screenshot")
	}
	err = ioutil.WriteFile(to+".png", data, 0540)
	if err != nil {
		return errors.Wrap(err, "could not write screenshot")
	}
	return nil
}
func (p *Page) InputElement(act *Action, out map[string]string  ) error {
	value := act.GetArg("value")
	if value == "" {
		return errors.New("invalid arguments provided")
	}
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	if err = element.Input(value); err != nil {
		return errors.Wrap(err, "could not input element")
	}
	return nil
}
func (p *Page) TimeInputElement(act *Action, out map[string]string  ) error {
	value := act.GetArg("value")
	if value == "" {
		return errors.New("invalid arguments provided")
	}
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	t, err := time.Parse(time.RFC3339, value)
	if err != nil {
		return errors.Wrap(err, "could not parse time")
	}
	if err := element.InputTime(t); err != nil {
		return errors.Wrap(err, "could not input element")
	}
	return nil
}
func (p *Page) SelectInputElement(act *Action, out map[string]string  ) error {
	value := act.GetArg("value")
	if value == "" {
		return errors.New("invalid arguments provided")
	}
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	selectedbool := false
	if act.GetArg("selected") == "true" {
		selectedbool = true
	}
	by := act.GetArg("selector")
	if err := element.Select([]string{value}, selectedbool, selectorBy(by)); err != nil {
		return errors.Wrap(err, "could not select input")
	}
	return nil
}
func (p *Page) WaitLoad(act *Action, out map[string]string  ) error {
	p.page.Timeout(1 * time.Second).WaitNavigation(proto.PageLifecycleEventNameDOMContentLoaded)()
	if err := p.page.WaitLoad(); err != nil {
		return errors.Wrap(err, "could not reset mouse")
	}
	_ = p.page.WaitIdle(1 * time.Second)
	return nil
}
func (p *Page) GetResource(act *Action, out map[string]string) error {
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	resource, err := element.Resource()
	if err != nil {
		return errors.Wrap(err, "could not get src for element")
	}
	if act.Name != "" {
		out[act.Name] = string(resource)
	}
	return nil
}
func (p *Page) FilesInput(act *Action, out map[string]string  ) error {
	element, err := p.pageElementBy(act.Data)
	if err != nil {
		return errors.Wrap(err, "could not get element")
	}
	if err = element.ScrollIntoView(); err != nil {
		return errors.Wrap(err, "could not scroll into view")
	}
	value := act.GetArg("value")
	filesPaths := strings.Split(value, ",")
	if err := element.SetFiles(filesPaths); err != nil {
		return errors.Wrap(err, "could not set files")
	}
	return nil
}