mirror of
https://github.com/MontFerret/ferret.git
synced 2025-11-06 08:39:09 +02:00
pull master
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/parser"
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/stdlib"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FqlCompiler struct {
|
||||
type Compiler struct {
|
||||
*NamespaceContainer
|
||||
}
|
||||
|
||||
func New(setters ...Option) *FqlCompiler {
|
||||
c := &FqlCompiler{}
|
||||
func New(setters ...Option) *Compiler {
|
||||
c := &Compiler{}
|
||||
c.NamespaceContainer = newRootNamespace()
|
||||
c.funcs = make(map[string]core.Function)
|
||||
|
||||
@@ -32,7 +33,7 @@ func New(setters ...Option) *FqlCompiler {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error) {
|
||||
func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
|
||||
if query == "" {
|
||||
return nil, ErrEmptyQuery
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error
|
||||
return program, err
|
||||
}
|
||||
|
||||
func (c *FqlCompiler) MustCompile(query string) *runtime.Program {
|
||||
func (c *Compiler) MustCompile(query string) *runtime.Program {
|
||||
program, err := c.Compile(query)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -119,14 +119,110 @@ func TestMember(t *testing.T) {
|
||||
|
||||
So(string(out), ShouldEqual, `"wsx"`)
|
||||
})
|
||||
|
||||
Convey("Deep path", func() {
|
||||
c := compiler.New()
|
||||
|
||||
p, err := c.Compile(`
|
||||
LET obj = {
|
||||
first: {
|
||||
second: {
|
||||
third: {
|
||||
fourth: {
|
||||
fifth: {
|
||||
bottom: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN obj.first.second.third.fourth.fifth.bottom
|
||||
`)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
out, err := p.Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(out), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("Deep computed path", func() {
|
||||
c := compiler.New()
|
||||
|
||||
p, err := c.Compile(`
|
||||
LET obj = {
|
||||
first: {
|
||||
second: {
|
||||
third: {
|
||||
fourth: {
|
||||
fifth: {
|
||||
bottom: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN obj["first"]["second"]["third"]["fourth"]["fifth"].bottom
|
||||
`)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
out, err := p.Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(out), ShouldEqual, `true`)
|
||||
})
|
||||
|
||||
Convey("Prop after a func call", func() {
|
||||
c := compiler.New()
|
||||
|
||||
p, err := c.Compile(`
|
||||
LET arr = [{ name: "Bob" }]
|
||||
|
||||
RETURN FIRST(arr).name
|
||||
`)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
out, err := p.Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(out), ShouldEqual, `"Bob"`)
|
||||
})
|
||||
|
||||
Convey("Computed prop after a func call", func() {
|
||||
c := compiler.New()
|
||||
|
||||
p, err := c.Compile(`
|
||||
LET arr = [{ name: { first: "Bob" } }]
|
||||
|
||||
RETURN FIRST(arr)['name'].first
|
||||
`)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
out, err := p.Run(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(out), ShouldEqual, `"Bob"`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkMemberArray(b *testing.B) {
|
||||
p := compiler.New().MustCompile(`
|
||||
LET arr = [1]
|
||||
LET arr = [[[[1]]]]
|
||||
|
||||
RETURN arr[0]
|
||||
RETURN arr[0][0][0][0]
|
||||
`)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
@@ -136,9 +232,21 @@ func BenchmarkMemberArray(b *testing.B) {
|
||||
|
||||
func BenchmarkMemberObject(b *testing.B) {
|
||||
p := compiler.New().MustCompile(`
|
||||
LET obj = { "foo": "bar"}
|
||||
LET obj = {
|
||||
first: {
|
||||
second: {
|
||||
third: {
|
||||
fourth: {
|
||||
fifth: {
|
||||
bottom: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN obj.foo
|
||||
RETURN obj.first.second.third.fourth.fifth.bottom
|
||||
`)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
||||
@@ -63,4 +63,54 @@ func TestParam(t *testing.T) {
|
||||
So(string(out), ShouldEqual, `[1,2,3,4]`)
|
||||
|
||||
})
|
||||
|
||||
Convey("Should be possible to use in member expression", t, func() {
|
||||
prog := compiler.New().
|
||||
MustCompile(`
|
||||
RETURN @param.value
|
||||
`)
|
||||
|
||||
out := prog.MustRun(
|
||||
context.Background(),
|
||||
runtime.WithParam("param", map[string]interface{}{
|
||||
"value": "foobar",
|
||||
}),
|
||||
)
|
||||
|
||||
So(string(out), ShouldEqual, `"foobar"`)
|
||||
|
||||
})
|
||||
|
||||
Convey("Should be possible to use in member expression as a computed property", t, func() {
|
||||
prog := compiler.New().
|
||||
MustCompile(`
|
||||
LET obj = { foo: "bar" }
|
||||
RETURN obj[@param]
|
||||
`)
|
||||
|
||||
out := prog.MustRun(
|
||||
context.Background(),
|
||||
runtime.WithParam("param", "foo"),
|
||||
)
|
||||
|
||||
So(string(out), ShouldEqual, `"bar"`)
|
||||
})
|
||||
|
||||
Convey("Should be possible to use in member expression as segments", t, func() {
|
||||
prog := compiler.New().
|
||||
MustCompile(`
|
||||
LET doc = { foo: { bar: "baz" } }
|
||||
|
||||
RETURN doc.@attr.@subattr
|
||||
`)
|
||||
|
||||
out := prog.MustRun(
|
||||
context.Background(),
|
||||
runtime.WithParam("attr", "foo"),
|
||||
runtime.WithParam("subattr", "bar"),
|
||||
)
|
||||
|
||||
So(string(out), ShouldEqual, `"baz"`)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ BAR
|
||||
So(string(out), ShouldEqual, `"\nFOO\nBAR\n"`)
|
||||
})
|
||||
|
||||
Convey("Should be possible to use multi line string with nested strings", t, func() {
|
||||
Convey("Should be possible to use multi line string with nested strings using backtick", t, func() {
|
||||
compiler.New().
|
||||
MustCompile(fmt.Sprintf(`
|
||||
RETURN %s<!DOCTYPE html>
|
||||
@@ -54,6 +54,38 @@ RETURN %s<!DOCTYPE html>
|
||||
|
||||
So(string(out), ShouldEqual, string(out))
|
||||
})
|
||||
|
||||
Convey("Should be possible to use multi line string with nested strings using tick", t, func() {
|
||||
compiler.New().
|
||||
MustCompile(fmt.Sprintf(`
|
||||
RETURN %s<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>GetTitle</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello world
|
||||
</body>
|
||||
</html>%s
|
||||
`, "´", "´")).
|
||||
MustRun(context.Background())
|
||||
|
||||
out, err := json.Marshal(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>GetTitle</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello world
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(out), ShouldEqual, string(out))
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkStringLiteral(b *testing.B) {
|
||||
|
||||
@@ -736,15 +736,13 @@ func (v *visitor) doVisitForExpressionStatement(ctx *fql.ForExpressionStatementC
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scope *scope) (core.Expression, error) {
|
||||
varName := ctx.Identifier().GetText()
|
||||
|
||||
_, err := scope.GetVariable(varName)
|
||||
member, err := v.doVisitMember(ctx.Member().(*fql.MemberContext), scope)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
children := ctx.GetChildren()
|
||||
children := ctx.MemberPath().GetChildren()
|
||||
path := make([]core.Expression, 0, len(children))
|
||||
|
||||
for _, child := range children {
|
||||
@@ -784,9 +782,9 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
|
||||
path = append(path, exp)
|
||||
}
|
||||
|
||||
member, err := expressions.NewMemberExpression(
|
||||
exp, err := expressions.NewMemberExpression(
|
||||
v.getSourceMap(ctx),
|
||||
varName,
|
||||
member,
|
||||
path,
|
||||
)
|
||||
|
||||
@@ -794,7 +792,51 @@ func (v *visitor) doVisitMemberExpression(ctx *fql.MemberExpressionContext, scop
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return member, nil
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitMember(ctx *fql.MemberContext, scope *scope) (core.Expression, error) {
|
||||
identifier := ctx.Identifier()
|
||||
|
||||
if identifier != nil {
|
||||
varName := ctx.Identifier().GetText()
|
||||
|
||||
_, err := scope.GetVariable(varName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exp, err := expressions.NewVariableExpression(v.getSourceMap(ctx), varName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
fnCall := ctx.FunctionCallExpression()
|
||||
|
||||
if fnCall != nil {
|
||||
exp, err := v.doVisitFunctionCallExpression(fnCall.(*fql.FunctionCallExpressionContext), scope)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
param := ctx.Param()
|
||||
|
||||
exp, err := v.doVisitParamContext(param.(*fql.ParamContext), scope)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return exp, nil
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitObjectLiteral(ctx *fql.ObjectLiteralContext, scope *scope) (core.Expression, error) {
|
||||
@@ -847,7 +889,7 @@ func (v *visitor) doVisitObjectLiteral(ctx *fql.ObjectLiteralContext, scope *sco
|
||||
return literals.NewObjectLiteralWith(props...), nil
|
||||
}
|
||||
|
||||
func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, _ *scope) (core.Expression, error) {
|
||||
func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, scope *scope) (core.Expression, error) {
|
||||
var name string
|
||||
|
||||
identifier := ctx.Identifier()
|
||||
@@ -860,6 +902,14 @@ func (v *visitor) doVisitPropertyNameContext(ctx *fql.PropertyNameContext, _ *sc
|
||||
if stringLiteral != nil {
|
||||
runes := []rune(stringLiteral.GetText())
|
||||
name = string(runes[1 : len(runes)-1])
|
||||
} else {
|
||||
param, err := v.doVisitParamContext(ctx.Param().(*fql.ParamContext), scope)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return param, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,8 +987,6 @@ func (v *visitor) doVisitStringLiteral(ctx *fql.StringLiteralContext) (core.Expr
|
||||
|
||||
if strLiteral != nil {
|
||||
text = strLiteral.GetText()
|
||||
} else {
|
||||
text = ctx.TemplateStringLiteral().GetText()
|
||||
}
|
||||
|
||||
// remove extra quotes
|
||||
|
||||
@@ -243,23 +243,23 @@ func (doc *HTMLDocument) GetNodeName() values.String {
|
||||
return "#document"
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) core.Value {
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
return doc.element.GetChildNodes(ctx)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) core.Value {
|
||||
func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
|
||||
return doc.element.GetChildNode(ctx, idx)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) core.Value {
|
||||
func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) {
|
||||
return doc.element.QuerySelector(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
|
||||
func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
||||
return doc.element.QuerySelectorAll(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) values.Int {
|
||||
func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) (values.Int, error) {
|
||||
return doc.element.CountBySelector(ctx, selector)
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector values.S
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetTitle() values.String {
|
||||
value, err := doc.exec.ReadProperty(context.Background(), doc.element.id.objectID, "title")
|
||||
value, err := doc.exec.ReadProperty(context.Background(), doc.element.id.ObjectID, "title")
|
||||
|
||||
if err != nil {
|
||||
doc.logError(errors.Wrap(err, "failed to read document title"))
|
||||
@@ -317,32 +317,8 @@ func (doc *HTMLDocument) GetURL() values.String {
|
||||
return values.NewString(doc.frames.Frame.URL)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ClickBySelector(ctx context.Context, selector values.String) error {
|
||||
return doc.element.ClickBySelector(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ClickBySelectorAll(ctx context.Context, selector values.String) error {
|
||||
return doc.element.ClickBySelectorAll(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) error {
|
||||
return doc.input.TypeBySelector(ctx, doc.element.id.nodeID, selector, value, delay)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error) {
|
||||
return doc.input.SelectBySelector(ctx, doc.element.id.nodeID, selector, value)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) FocusBySelector(ctx context.Context, selector values.String) error {
|
||||
return doc.input.FocusBySelector(ctx, doc.element.id.nodeID, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) MoveMouseBySelector(ctx context.Context, selector values.String) error {
|
||||
return doc.input.MoveMouseBySelector(ctx, doc.element.id.nodeID, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) MoveMouseByXY(ctx context.Context, x, y values.Float) error {
|
||||
return doc.input.MoveMouseByXY(ctx, x, y)
|
||||
return doc.input.MoveMouseByXY(ctx, float64(x), float64(y))
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForElement(ctx context.Context, selector values.String, when drivers.WaitEvent) error {
|
||||
@@ -501,11 +477,11 @@ func (doc *HTMLDocument) ScrollBottom(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollBySelector(ctx context.Context, selector values.String) error {
|
||||
return doc.input.ScrollIntoViewBySelector(ctx, selector)
|
||||
return doc.input.ScrollIntoViewBySelector(ctx, selector.String())
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollByXY(ctx context.Context, x, y values.Float) error {
|
||||
return doc.input.ScrollByXY(ctx, x, y)
|
||||
return doc.input.ScrollByXY(ctx, float64(x), float64(y))
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) loadChildren(ctx context.Context) (value core.Value, e error) {
|
||||
|
||||
@@ -2,15 +2,13 @@ package cdp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/devtool"
|
||||
"github.com/mafredri/cdp/protocol/target"
|
||||
"github.com/mafredri/cdp/rpcc"
|
||||
"github.com/mafredri/cdp/session"
|
||||
"github.com/pkg/errors"
|
||||
"sync"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/logging"
|
||||
@@ -18,7 +16,6 @@ import (
|
||||
|
||||
const DriverName = "cdp"
|
||||
const BlankPageURL = "about:blank"
|
||||
const DefaultTimeout = 5000 * time.Millisecond
|
||||
|
||||
var defaultViewport = &drivers.Viewport{
|
||||
Width: 1600,
|
||||
@@ -107,6 +104,34 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
||||
params.Viewport = defaultViewport
|
||||
}
|
||||
|
||||
if drv.options.Headers != nil && params.Headers == nil {
|
||||
params.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
// set default headers
|
||||
for k, v := range drv.options.Headers {
|
||||
_, exists := params.Headers[k]
|
||||
|
||||
// do not override user's set values
|
||||
if !exists {
|
||||
params.Headers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if drv.options.Cookies != nil && params.Cookies == nil {
|
||||
params.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
// set default cookies
|
||||
for k, v := range drv.options.Cookies {
|
||||
_, exists := params.Cookies[k]
|
||||
|
||||
// do not override user's set values
|
||||
if !exists {
|
||||
params.Cookies[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return LoadHTMLPage(ctx, conn, params)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,14 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/protocol/dom"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
@@ -18,21 +26,14 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/protocol/dom"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var emptyNodeID = dom.NodeID(0)
|
||||
|
||||
type (
|
||||
HTMLElementIdentity struct {
|
||||
nodeID dom.NodeID
|
||||
objectID runtime.RemoteObjectID
|
||||
NodeID dom.NodeID
|
||||
ObjectID runtime.RemoteObjectID
|
||||
}
|
||||
|
||||
HTMLElement struct {
|
||||
@@ -48,7 +49,6 @@ type (
|
||||
nodeName values.String
|
||||
innerHTML *common.LazyValue
|
||||
innerText *common.LazyValue
|
||||
value core.Value
|
||||
attributes *common.LazyValue
|
||||
style *common.LazyValue
|
||||
children []HTMLElementIdentity
|
||||
@@ -82,10 +82,6 @@ func LoadHTMLElement(
|
||||
return nil, core.Error(core.ErrNotFound, fmt.Sprintf("element %d", nodeID))
|
||||
}
|
||||
|
||||
id := HTMLElementIdentity{}
|
||||
id.nodeID = nodeID
|
||||
id.objectID = *obj.Object.ObjectID
|
||||
|
||||
return LoadHTMLElementWithID(
|
||||
ctx,
|
||||
logger,
|
||||
@@ -93,7 +89,10 @@ func LoadHTMLElement(
|
||||
broker,
|
||||
input,
|
||||
exec,
|
||||
id,
|
||||
HTMLElementIdentity{
|
||||
NodeID: nodeID,
|
||||
ObjectID: *obj.Object.ObjectID,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,18 +109,12 @@ func LoadHTMLElementWithID(
|
||||
ctx,
|
||||
dom.
|
||||
NewDescribeNodeArgs().
|
||||
SetObjectID(id.objectID).
|
||||
SetObjectID(id.ObjectID).
|
||||
SetDepth(1),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, core.Error(err, strconv.Itoa(int(id.nodeID)))
|
||||
}
|
||||
|
||||
var val string
|
||||
|
||||
if node.Node.Value != nil {
|
||||
val = *node.Node.Value
|
||||
return nil, core.Error(err, strconv.Itoa(int(id.NodeID)))
|
||||
}
|
||||
|
||||
return NewHTMLElement(
|
||||
@@ -133,7 +126,6 @@ func LoadHTMLElementWithID(
|
||||
id,
|
||||
node.Node.NodeType,
|
||||
node.Node.NodeName,
|
||||
val,
|
||||
createChildrenArray(node.Node.Children),
|
||||
), nil
|
||||
}
|
||||
@@ -147,7 +139,6 @@ func NewHTMLElement(
|
||||
id HTMLElementIdentity,
|
||||
nodeType int,
|
||||
nodeName string,
|
||||
value string,
|
||||
children []HTMLElementIdentity,
|
||||
) *HTMLElement {
|
||||
el := new(HTMLElement)
|
||||
@@ -164,9 +155,7 @@ func NewHTMLElement(
|
||||
el.innerText = common.NewLazyValue(el.loadInnerText)
|
||||
el.attributes = common.NewLazyValue(el.loadAttrs)
|
||||
el.style = common.NewLazyValue(el.parseStyle)
|
||||
el.value = values.EmptyString
|
||||
el.loadedChildren = common.NewLazyValue(el.loadChildren)
|
||||
el.value = values.NewString(value)
|
||||
el.children = children
|
||||
|
||||
broker.AddEventListener(events.EventReload, el.handlePageReload)
|
||||
@@ -208,7 +197,7 @@ func (el *HTMLElement) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (el *HTMLElement) String() string {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(drivers.DefaultWaitTimeout)*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
res, err := el.GetInnerHTML(ctx)
|
||||
@@ -242,7 +231,7 @@ func (el *HTMLElement) Hash() uint64 {
|
||||
|
||||
h.Write([]byte(el.Type().String()))
|
||||
h.Write([]byte(":"))
|
||||
h.Write([]byte(strconv.Itoa(int(el.id.nodeID))))
|
||||
h.Write([]byte(strconv.Itoa(int(el.id.NodeID))))
|
||||
|
||||
return h.Sum64()
|
||||
}
|
||||
@@ -263,31 +252,20 @@ func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.
|
||||
return common.SetInElement(ctx, el, path, value)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetValue(ctx context.Context) core.Value {
|
||||
func (el *HTMLElement) GetValue(ctx context.Context) (core.Value, error) {
|
||||
if el.IsDetached() {
|
||||
return el.value
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
val, err := el.exec.ReadProperty(ctx, el.id.objectID, "value")
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to get node value")
|
||||
|
||||
return el.value
|
||||
}
|
||||
|
||||
el.value = val
|
||||
|
||||
return val
|
||||
return el.exec.ReadProperty(ctx, el.id.ObjectID, "value")
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetValue(ctx context.Context, value core.Value) error {
|
||||
if el.IsDetached() {
|
||||
// TODO: Return an error
|
||||
return nil
|
||||
return drivers.ErrDetached
|
||||
}
|
||||
|
||||
return el.client.DOM.SetNodeValue(ctx, dom.NewSetNodeValueArgs(el.id.nodeID, value.String()))
|
||||
return el.client.DOM.SetNodeValue(ctx, dom.NewSetNodeValueArgs(el.id.NodeID, value.String()))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetNodeType() values.Int {
|
||||
@@ -395,33 +373,33 @@ func (el *HTMLElement) RemoveStyle(ctx context.Context, names ...values.String)
|
||||
return el.SetAttribute(ctx, "style", str)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttributes(ctx context.Context) *values.Object {
|
||||
func (el *HTMLElement) GetAttributes(ctx context.Context) (*values.Object, error) {
|
||||
val, err := el.attributes.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.NewObject()
|
||||
return values.NewObject(), err
|
||||
}
|
||||
|
||||
attrs := val.(*values.Object)
|
||||
|
||||
// returning shallow copy
|
||||
return attrs.Copy().(*values.Object)
|
||||
return attrs.Copy().(*values.Object), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) core.Value {
|
||||
func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) (core.Value, error) {
|
||||
attrs, err := el.attributes.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
val, found := attrs.(*values.Object).Get(name)
|
||||
|
||||
if !found {
|
||||
return values.None
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
return val
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object) error {
|
||||
@@ -439,7 +417,7 @@ func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object)
|
||||
func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.String) error {
|
||||
return el.client.DOM.SetAttributeValue(
|
||||
ctx,
|
||||
dom.NewSetAttributeValueArgs(el.id.nodeID, string(name), string(value)),
|
||||
dom.NewSetAttributeValueArgs(el.id.NodeID, string(name), string(value)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -447,7 +425,7 @@ func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.Stri
|
||||
for _, name := range names {
|
||||
err := el.client.DOM.RemoveAttribute(
|
||||
ctx,
|
||||
dom.NewRemoveAttributeArgs(el.id.nodeID, name.String()),
|
||||
dom.NewRemoveAttributeArgs(el.id.NodeID, name.String()),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -458,79 +436,61 @@ func (el *HTMLElement) RemoveAttribute(ctx context.Context, names ...values.Stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNodes(ctx context.Context) core.Value {
|
||||
func (el *HTMLElement) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
val, err := el.loadedChildren.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.NewArray(0)
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
return val
|
||||
return val.Copy().(*values.Array), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) core.Value {
|
||||
func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
|
||||
val, err := el.loadedChildren.Read(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return val.(*values.Array).Get(idx)
|
||||
return val.(*values.Array).Get(idx), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) core.Value {
|
||||
func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) {
|
||||
if el.IsDetached() {
|
||||
return values.None
|
||||
return values.None, drivers.ErrDetached
|
||||
}
|
||||
|
||||
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
|
||||
selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())
|
||||
selectorArgs := dom.NewQuerySelectorArgs(el.id.NodeID, selector.String())
|
||||
found, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to retrieve a node by selector")
|
||||
|
||||
return values.None
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if found.NodeID == emptyNodeID {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to find a node by selector. returned 0 NodeID")
|
||||
|
||||
return values.None
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
res, err := LoadHTMLElement(ctx, el.logger, el.client, el.events, el.input, el.exec, found.NodeID)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to load a child node by selector")
|
||||
|
||||
return values.None
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
return res
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
|
||||
func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
||||
if el.IsDetached() {
|
||||
return values.NewArray(0)
|
||||
return values.NewArray(0), drivers.ErrDetached
|
||||
}
|
||||
|
||||
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
|
||||
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())
|
||||
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.NodeID, selector.String())
|
||||
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to retrieve nodes by selector")
|
||||
|
||||
return values.None
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
arr := values.NewArray(len(res.NodeIDs))
|
||||
@@ -547,10 +507,6 @@ func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.Str
|
||||
childEl, err := LoadHTMLElement(ctx, el.logger, el.client, el.events, el.input, el.exec, id)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to load nodes by selector")
|
||||
|
||||
// close elements that are already loaded, but won't be used because of the error
|
||||
if arr.Length() > 0 {
|
||||
arr.ForEach(func(e core.Value, _ int) bool {
|
||||
@@ -560,13 +516,13 @@ func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.Str
|
||||
})
|
||||
}
|
||||
|
||||
return values.None
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
arr.Push(childEl)
|
||||
}
|
||||
|
||||
return arr
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (result core.Value, err error) {
|
||||
@@ -578,7 +534,7 @@ func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (res
|
||||
|
||||
out, err := el.exec.EvalWithArgumentsAndReturnReference(ctx, templates.XPath(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: json.RawMessage(exp),
|
||||
@@ -657,8 +613,8 @@ func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (res
|
||||
el.input,
|
||||
el.exec,
|
||||
HTMLElementIdentity{
|
||||
nodeID: repl.NodeID,
|
||||
objectID: *descr.Value.ObjectID,
|
||||
NodeID: repl.NodeID,
|
||||
ObjectID: *descr.Value.ObjectID,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -689,8 +645,8 @@ func (el *HTMLElement) XPath(ctx context.Context, expression values.String) (res
|
||||
el.input,
|
||||
el.exec,
|
||||
HTMLElementIdentity{
|
||||
nodeID: repl.NodeID,
|
||||
objectID: *out.ObjectID,
|
||||
NodeID: repl.NodeID,
|
||||
ObjectID: *out.ObjectID,
|
||||
},
|
||||
)
|
||||
default:
|
||||
@@ -737,7 +693,7 @@ func (el *HTMLElement) GetInnerTextBySelector(ctx context.Context, selector valu
|
||||
ctx,
|
||||
templates.GetInnerTextBySelector(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -772,7 +728,7 @@ func (el *HTMLElement) SetInnerTextBySelector(ctx context.Context, selector, inn
|
||||
ctx,
|
||||
templates.SetInnerTextBySelector(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -798,7 +754,7 @@ func (el *HTMLElement) GetInnerTextBySelectorAll(ctx context.Context, selector v
|
||||
ctx,
|
||||
templates.GetInnerTextBySelectorAll(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -857,7 +813,7 @@ func (el *HTMLElement) GetInnerHTMLBySelector(ctx context.Context, selector valu
|
||||
ctx,
|
||||
templates.GetInnerHTMLBySelector(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -892,7 +848,7 @@ func (el *HTMLElement) SetInnerHTMLBySelector(ctx context.Context, selector, inn
|
||||
ctx,
|
||||
templates.SetInnerHTMLBySelector(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -918,7 +874,7 @@ func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector v
|
||||
ctx,
|
||||
templates.GetInnerHTMLBySelectorAll(),
|
||||
runtime.CallArgument{
|
||||
ObjectID: &el.id.objectID,
|
||||
ObjectID: &el.id.ObjectID,
|
||||
},
|
||||
runtime.CallArgument{
|
||||
Value: sel,
|
||||
@@ -938,24 +894,19 @@ func (el *HTMLElement) GetInnerHTMLBySelectorAll(ctx context.Context, selector v
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) CountBySelector(ctx context.Context, selector values.String) values.Int {
|
||||
func (el *HTMLElement) CountBySelector(ctx context.Context, selector values.String) (values.Int, error) {
|
||||
if el.IsDetached() {
|
||||
return values.ZeroInt
|
||||
return values.ZeroInt, drivers.ErrDetached
|
||||
}
|
||||
|
||||
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
|
||||
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())
|
||||
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.NodeID, selector.String())
|
||||
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
|
||||
|
||||
if err != nil {
|
||||
el.logError(err).
|
||||
Str("selector", selector.String()).
|
||||
Msg("failed to retrieve nodes by selector")
|
||||
|
||||
return values.ZeroInt
|
||||
return values.ZeroInt, err
|
||||
}
|
||||
|
||||
return values.NewInt(len(res.NodeIDs))
|
||||
return values.NewInt(len(res.NodeIDs)), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.String) (values.Boolean, error) {
|
||||
@@ -964,7 +915,7 @@ func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.Str
|
||||
}
|
||||
|
||||
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
|
||||
selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())
|
||||
selectorArgs := dom.NewQuerySelectorArgs(el.id.NodeID, selector.String())
|
||||
res, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
|
||||
|
||||
if err != nil {
|
||||
@@ -981,7 +932,11 @@ func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.Str
|
||||
func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String, when drivers.WaitEvent) error {
|
||||
task := events.NewWaitTask(
|
||||
func(ctx2 context.Context) (core.Value, error) {
|
||||
current := el.GetAttribute(ctx2, "class")
|
||||
current, err := el.GetAttribute(ctx2, "class")
|
||||
|
||||
if err != nil {
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
if current.Type() != types.String {
|
||||
return values.None, nil
|
||||
@@ -1033,7 +988,7 @@ func (el *HTMLElement) WaitForAttribute(
|
||||
when drivers.WaitEvent,
|
||||
) error {
|
||||
task := events.NewValueWaitTask(when, value, func(ctx context.Context) (core.Value, error) {
|
||||
return el.GetAttribute(ctx, name), nil
|
||||
return el.GetAttribute(ctx, name)
|
||||
}, events.DefaultPolling)
|
||||
|
||||
_, err := task.Run(ctx)
|
||||
@@ -1051,16 +1006,16 @@ func (el *HTMLElement) WaitForStyle(ctx context.Context, name values.String, val
|
||||
return err
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Click(ctx context.Context) error {
|
||||
return el.input.Click(ctx, el.id.objectID)
|
||||
func (el *HTMLElement) Click(ctx context.Context, count values.Int) error {
|
||||
return el.input.Click(ctx, el.id.ObjectID, int(count))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClickBySelector(ctx context.Context, selector values.String) error {
|
||||
return el.input.ClickBySelector(ctx, el.id.nodeID, selector)
|
||||
func (el *HTMLElement) ClickBySelector(ctx context.Context, selector values.String, count values.Int) error {
|
||||
return el.input.ClickBySelector(ctx, el.id.NodeID, selector.String(), int(count))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClickBySelectorAll(ctx context.Context, selector values.String) error {
|
||||
return el.input.ClickBySelectorAll(ctx, el.id.nodeID, selector)
|
||||
func (el *HTMLElement) ClickBySelectorAll(ctx context.Context, selector values.String, count values.Int) error {
|
||||
return el.input.ClickBySelectorAll(ctx, el.id.NodeID, selector.String(), int(count))
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Input(ctx context.Context, value core.Value, delay values.Int) error {
|
||||
@@ -1068,23 +1023,63 @@ func (el *HTMLElement) Input(ctx context.Context, value core.Value, delay values
|
||||
return core.Error(core.ErrInvalidOperation, "element is not an <input> element.")
|
||||
}
|
||||
|
||||
return el.input.Type(ctx, el.id.objectID, value, delay)
|
||||
return el.input.Type(ctx, el.id.ObjectID, input.TypeParams{
|
||||
Text: value.String(),
|
||||
Clear: false,
|
||||
Delay: time.Duration(delay) * time.Millisecond,
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) error {
|
||||
return el.input.TypeBySelector(ctx, el.id.NodeID, selector.String(), input.TypeParams{
|
||||
Text: value.String(),
|
||||
Clear: false,
|
||||
Delay: time.Duration(delay) * time.Millisecond,
|
||||
})
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Clear(ctx context.Context) error {
|
||||
return el.input.Clear(ctx, el.id.ObjectID)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClearBySelector(ctx context.Context, selector values.String) error {
|
||||
return el.input.ClearBySelector(ctx, el.id.NodeID, selector.String())
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Select(ctx context.Context, value *values.Array) (*values.Array, error) {
|
||||
return el.input.Select(ctx, el.id.objectID, value)
|
||||
return el.input.Select(ctx, el.id.ObjectID, value)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error) {
|
||||
return el.input.SelectBySelector(ctx, el.id.NodeID, selector.String(), value)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ScrollIntoView(ctx context.Context) error {
|
||||
return el.input.ScrollIntoView(ctx, el.id.objectID)
|
||||
return el.input.ScrollIntoView(ctx, el.id.ObjectID)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Focus(ctx context.Context) error {
|
||||
return el.input.Focus(ctx, el.id.objectID)
|
||||
return el.input.Focus(ctx, el.id.ObjectID)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) FocusBySelector(ctx context.Context, selector values.String) error {
|
||||
return el.input.FocusBySelector(ctx, el.id.NodeID, selector.String())
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Blur(ctx context.Context) error {
|
||||
return el.input.Blur(ctx, el.id.ObjectID)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) BlurBySelector(ctx context.Context, selector values.String) error {
|
||||
return el.input.BlurBySelector(ctx, el.id.ObjectID, selector.String())
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Hover(ctx context.Context) error {
|
||||
return el.input.MoveMouse(ctx, el.id.objectID)
|
||||
return el.input.MoveMouse(ctx, el.id.ObjectID)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) HoverBySelector(ctx context.Context, selector values.String) error {
|
||||
return el.input.MoveMouseBySelector(ctx, el.id.NodeID, selector.String())
|
||||
}
|
||||
|
||||
func (el *HTMLElement) IsDetached() values.Boolean {
|
||||
@@ -1139,7 +1134,7 @@ func (el *HTMLElement) loadInnerText(ctx context.Context) (core.Value, error) {
|
||||
}
|
||||
|
||||
func (el *HTMLElement) loadAttrs(ctx context.Context) (core.Value, error) {
|
||||
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.nodeID))
|
||||
repl, err := el.client.DOM.GetAttributes(ctx, dom.NewGetAttributesArgs(el.id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
@@ -1163,7 +1158,7 @@ func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) {
|
||||
el.events,
|
||||
el.input,
|
||||
el.exec,
|
||||
childID.nodeID,
|
||||
childID.NodeID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -1179,7 +1174,11 @@ func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) {
|
||||
}
|
||||
|
||||
func (el *HTMLElement) parseStyle(ctx context.Context) (core.Value, error) {
|
||||
value := el.GetAttribute(ctx, "style")
|
||||
value, err := el.GetAttribute(ctx, "style")
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if value == values.None {
|
||||
return values.NewObject(), nil
|
||||
@@ -1205,7 +1204,7 @@ func (el *HTMLElement) handleAttrModified(ctx context.Context, message interface
|
||||
}
|
||||
|
||||
// it's not for this el
|
||||
if reply.NodeID != el.id.nodeID {
|
||||
if reply.NodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1215,6 +1214,10 @@ func (el *HTMLElement) handleAttrModified(ctx context.Context, message interface
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.attributes.Mutate(ctx, func(v core.Value, err error) {
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to update element")
|
||||
@@ -1245,7 +1248,7 @@ func (el *HTMLElement) handleAttrRemoved(ctx context.Context, message interface{
|
||||
}
|
||||
|
||||
// it's not for this el
|
||||
if reply.NodeID != el.id.nodeID {
|
||||
if reply.NodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1255,6 +1258,10 @@ func (el *HTMLElement) handleAttrRemoved(ctx context.Context, message interface{
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.attributes.Mutate(ctx, func(v core.Value, err error) {
|
||||
if err != nil {
|
||||
el.logError(err).Msg("failed to update element")
|
||||
@@ -1283,13 +1290,20 @@ func (el *HTMLElement) handleChildrenCountChanged(ctx context.Context, message i
|
||||
return
|
||||
}
|
||||
|
||||
if reply.NodeID != el.id.nodeID {
|
||||
if reply.NodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.mu.Lock()
|
||||
defer el.mu.Unlock()
|
||||
|
||||
node, err := el.client.DOM.DescribeNode(
|
||||
ctx,
|
||||
dom.NewDescribeNodeArgs().SetObjectID(el.id.objectID),
|
||||
dom.NewDescribeNodeArgs().SetObjectID(el.id.ObjectID),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -1298,9 +1312,6 @@ func (el *HTMLElement) handleChildrenCountChanged(ctx context.Context, message i
|
||||
return
|
||||
}
|
||||
|
||||
el.mu.Lock()
|
||||
defer el.mu.Unlock()
|
||||
|
||||
el.children = createChildrenArray(node.Node.Children)
|
||||
}
|
||||
|
||||
@@ -1311,7 +1322,7 @@ func (el *HTMLElement) handleChildInserted(ctx context.Context, message interfac
|
||||
return
|
||||
}
|
||||
|
||||
if reply.ParentNodeID != el.id.nodeID {
|
||||
if reply.ParentNodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1319,11 +1330,15 @@ func (el *HTMLElement) handleChildInserted(ctx context.Context, message interfac
|
||||
prevID := reply.PreviousNodeID
|
||||
nextID := reply.Node.NodeID
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.mu.Lock()
|
||||
defer el.mu.Unlock()
|
||||
|
||||
for idx, id := range el.children {
|
||||
if id.nodeID == prevID {
|
||||
if id.NodeID == prevID {
|
||||
targetIDx = idx
|
||||
break
|
||||
}
|
||||
@@ -1334,7 +1349,7 @@ func (el *HTMLElement) handleChildInserted(ctx context.Context, message interfac
|
||||
}
|
||||
|
||||
nextIdentity := HTMLElementIdentity{
|
||||
nodeID: reply.Node.NodeID,
|
||||
NodeID: reply.Node.NodeID,
|
||||
}
|
||||
|
||||
arr := el.children
|
||||
@@ -1368,18 +1383,22 @@ func (el *HTMLElement) handleChildRemoved(ctx context.Context, message interface
|
||||
return
|
||||
}
|
||||
|
||||
if reply.ParentNodeID != el.id.nodeID {
|
||||
if reply.ParentNodeID != el.id.NodeID {
|
||||
return
|
||||
}
|
||||
|
||||
targetIDx := -1
|
||||
targetID := reply.NodeID
|
||||
|
||||
if el.IsDetached() {
|
||||
return
|
||||
}
|
||||
|
||||
el.mu.Lock()
|
||||
defer el.mu.Unlock()
|
||||
|
||||
for idx, id := range el.children {
|
||||
if id.nodeID == targetID {
|
||||
if id.NodeID == targetID {
|
||||
targetIDx = idx
|
||||
break
|
||||
}
|
||||
@@ -1401,7 +1420,7 @@ func (el *HTMLElement) handleChildRemoved(ctx context.Context, message interface
|
||||
el.logger.Error().
|
||||
Timestamp().
|
||||
Err(err).
|
||||
Int("nodeID", int(el.id.nodeID)).
|
||||
Int("nodeID", int(el.id.NodeID)).
|
||||
Msg("failed to update element")
|
||||
|
||||
return
|
||||
@@ -1419,7 +1438,7 @@ func (el *HTMLElement) logError(err error) *zerolog.Event {
|
||||
return el.logger.
|
||||
Error().
|
||||
Timestamp().
|
||||
Int("nodeID", int(el.id.nodeID)).
|
||||
Str("objectID", string(el.id.objectID)).
|
||||
Int("nodeID", int(el.id.NodeID)).
|
||||
Str("objectID", string(el.id.ObjectID)).
|
||||
Err(err)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mafredri/cdp/protocol/dom"
|
||||
"github.com/mafredri/cdp/protocol/page"
|
||||
@@ -289,7 +290,7 @@ func (broker *EventBroker) emit(ctx context.Context, event Event, message interf
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
ctx2, fn := drivers.WithDefaultTimeout(ctx)
|
||||
ctx2, fn := context.WithTimeout(ctx, time.Duration(drivers.DefaultTimeout)*time.Millisecond)
|
||||
|
||||
listener(ctx2, message)
|
||||
|
||||
|
||||
@@ -71,10 +71,10 @@ func parseAttrs(attrs []string) *values.Object {
|
||||
func setInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.ExecutionContext, id HTMLElementIdentity, innerHTML values.String) error {
|
||||
var objID *runtime.RemoteObjectID
|
||||
|
||||
if id.objectID != "" {
|
||||
objID = &id.objectID
|
||||
if id.ObjectID != "" {
|
||||
objID = &id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.nodeID))
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -110,10 +110,10 @@ func getInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.ExecutionC
|
||||
if nodeType != html.DocumentNode {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.objectID != "" {
|
||||
objID = id.objectID
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.nodeID))
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -147,10 +147,10 @@ func getInnerHTML(ctx context.Context, client *cdp.Client, exec *eval.ExecutionC
|
||||
func setInnerText(ctx context.Context, client *cdp.Client, exec *eval.ExecutionContext, id HTMLElementIdentity, innerText values.String) error {
|
||||
var objID *runtime.RemoteObjectID
|
||||
|
||||
if id.objectID != "" {
|
||||
objID = &id.objectID
|
||||
if id.ObjectID != "" {
|
||||
objID = &id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.nodeID))
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -186,10 +186,10 @@ func getInnerText(ctx context.Context, client *cdp.Client, exec *eval.ExecutionC
|
||||
if nodeType != html.DocumentNode {
|
||||
var objID runtime.RemoteObjectID
|
||||
|
||||
if id.objectID != "" {
|
||||
objID = id.objectID
|
||||
if id.ObjectID != "" {
|
||||
objID = id.ObjectID
|
||||
} else {
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.nodeID))
|
||||
repl, err := client.DOM.ResolveNode(ctx, dom.NewResolveNodeArgs().SetNodeID(id.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -238,7 +238,7 @@ func createChildrenArray(nodes []dom.Node) []HTMLElementIdentity {
|
||||
for idx, child := range nodes {
|
||||
child := child
|
||||
children[idx] = HTMLElementIdentity{
|
||||
nodeID: child.NodeID,
|
||||
NodeID: child.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,45 @@ package input
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/pkg/errors"
|
||||
"time"
|
||||
|
||||
"github.com/mafredri/cdp"
|
||||
"github.com/mafredri/cdp/protocol/input"
|
||||
)
|
||||
|
||||
type Keyboard struct {
|
||||
client *cdp.Client
|
||||
}
|
||||
const DefaultDelay = 25
|
||||
|
||||
type (
|
||||
KeyboardModifier int
|
||||
|
||||
KeyboardLocation int
|
||||
|
||||
KeyboardKey struct {
|
||||
KeyCode int
|
||||
Key string
|
||||
Code string
|
||||
Modifier KeyboardModifier
|
||||
Location KeyboardLocation
|
||||
}
|
||||
|
||||
Keyboard struct {
|
||||
client *cdp.Client
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
KeyboardModifierNone KeyboardModifier = 0
|
||||
KeyboardModifierAlt KeyboardModifier = 1
|
||||
KeyboardModifierCtrl KeyboardModifier = 2
|
||||
KeyboardModifierCmd KeyboardModifier = 4
|
||||
KeyboardModifierShift KeyboardModifier = 8
|
||||
|
||||
// 1=Left, 2=Right
|
||||
KeyboardLocationNone KeyboardLocation = 0
|
||||
KeyboardLocationLeft KeyboardLocation = 1
|
||||
KeyboardLocationRight KeyboardLocation = 2
|
||||
)
|
||||
|
||||
func NewKeyboard(client *cdp.Client) *Keyboard {
|
||||
return &Keyboard{client}
|
||||
@@ -32,7 +62,7 @@ func (k *Keyboard) Up(ctx context.Context, char string) error {
|
||||
)
|
||||
}
|
||||
|
||||
func (k *Keyboard) Type(ctx context.Context, text string, delay int) error {
|
||||
func (k *Keyboard) Type(ctx context.Context, text string, delay time.Duration) error {
|
||||
for _, ch := range text {
|
||||
ch := string(ch)
|
||||
|
||||
@@ -40,7 +70,7 @@ func (k *Keyboard) Type(ctx context.Context, text string, delay int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseDelay := randomDuration(delay)
|
||||
releaseDelay := randomDuration(int(delay))
|
||||
time.Sleep(releaseDelay)
|
||||
|
||||
if err := k.Up(ctx, ch); err != nil {
|
||||
@@ -50,3 +80,34 @@ func (k *Keyboard) Type(ctx context.Context, text string, delay int) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Keyboard) Press(ctx context.Context, name string) error {
|
||||
key, found := usKeyboardLayout[name]
|
||||
|
||||
if !found {
|
||||
return errors.New("invalid key")
|
||||
}
|
||||
|
||||
err := k.client.Input.DispatchKeyEvent(
|
||||
ctx,
|
||||
input.NewDispatchKeyEventArgs("keyDown").
|
||||
SetCode(key.Code).
|
||||
SetKey(key.Key).
|
||||
SetWindowsVirtualKeyCode(key.KeyCode),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseDelay := randomDuration(DefaultDelay)
|
||||
time.Sleep(releaseDelay)
|
||||
|
||||
return k.client.Input.DispatchKeyEvent(
|
||||
ctx,
|
||||
input.NewDispatchKeyEventArgs("keyUp").
|
||||
SetCode(key.Code).
|
||||
SetKey(key.Key).
|
||||
SetWindowsVirtualKeyCode(key.KeyCode),
|
||||
)
|
||||
}
|
||||
|
||||
1352
pkg/drivers/cdp/input/layout.go
Normal file
1352
pkg/drivers/cdp/input/layout.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,18 +8,27 @@ import (
|
||||
"github.com/mafredri/cdp/protocol/dom"
|
||||
"github.com/mafredri/cdp/protocol/runtime"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/templates"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
client *cdp.Client
|
||||
exec *eval.ExecutionContext
|
||||
keyboard *Keyboard
|
||||
mouse *Mouse
|
||||
}
|
||||
type (
|
||||
TypeParams struct {
|
||||
Text string
|
||||
Clear bool
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
Manager struct {
|
||||
client *cdp.Client
|
||||
exec *eval.ExecutionContext
|
||||
keyboard *Keyboard
|
||||
mouse *Mouse
|
||||
}
|
||||
)
|
||||
|
||||
func NewManager(
|
||||
client *cdp.Client,
|
||||
@@ -61,14 +70,14 @@ func (m *Manager) ScrollIntoView(ctx context.Context, objectID runtime.RemoteObj
|
||||
)
|
||||
}
|
||||
|
||||
func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, selector values.String) error {
|
||||
return m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(selector.String()))
|
||||
func (m *Manager) ScrollIntoViewBySelector(ctx context.Context, selector string) error {
|
||||
return m.exec.Eval(ctx, templates.ScrollIntoViewBySelector(selector))
|
||||
}
|
||||
|
||||
func (m *Manager) ScrollByXY(ctx context.Context, x, y values.Float) error {
|
||||
func (m *Manager) ScrollByXY(ctx context.Context, x, y float64) error {
|
||||
return m.exec.Eval(
|
||||
ctx,
|
||||
templates.Scroll(eval.ParamFloat(float64(x)), eval.ParamFloat(float64(y))),
|
||||
templates.Scroll(eval.ParamFloat(x), eval.ParamFloat(y)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,14 +91,14 @@ func (m *Manager) Focus(ctx context.Context, objectID runtime.RemoteObjectID) er
|
||||
return m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
|
||||
}
|
||||
|
||||
func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID, selector values.String) error {
|
||||
func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector.String()))
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -98,6 +107,18 @@ func (m *Manager) FocusBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
||||
}
|
||||
|
||||
func (m *Manager) Blur(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
||||
return m.exec.EvalWithArguments(ctx, templates.Blur(), runtime.CallArgument{
|
||||
ObjectID: &objectID,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) BlurBySelector(ctx context.Context, parentObjectID runtime.RemoteObjectID, selector string) error {
|
||||
return m.exec.EvalWithArguments(ctx, templates.BlurBySelector(selector), runtime.CallArgument{
|
||||
ObjectID: &parentObjectID,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Manager) MoveMouse(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
||||
if err := m.ScrollIntoView(ctx, objectID); err != nil {
|
||||
return err
|
||||
@@ -112,12 +133,12 @@ func (m *Manager) MoveMouse(ctx context.Context, objectID runtime.RemoteObjectID
|
||||
return m.mouse.Move(ctx, q.X, q.Y)
|
||||
}
|
||||
|
||||
func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.NodeID, selector values.String) error {
|
||||
func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector.String()))
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -132,15 +153,15 @@ func (m *Manager) MoveMouseBySelector(ctx context.Context, parentNodeID dom.Node
|
||||
return m.mouse.Move(ctx, q.X, q.Y)
|
||||
}
|
||||
|
||||
func (m *Manager) MoveMouseByXY(ctx context.Context, x, y values.Float) error {
|
||||
func (m *Manager) MoveMouseByXY(ctx context.Context, x, y float64) error {
|
||||
if err := m.ScrollByXY(ctx, x, y); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.mouse.Move(ctx, float64(x), float64(y))
|
||||
return m.mouse.Move(ctx, x, y)
|
||||
}
|
||||
|
||||
func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
||||
func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID, count int) error {
|
||||
if err := m.ScrollIntoView(ctx, objectID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,19 +172,21 @@ func (m *Manager) Click(ctx context.Context, objectID runtime.RemoteObjectID) er
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.mouse.Click(ctx, points.X, points.Y, 50); err != nil {
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID, selector values.String) error {
|
||||
func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector.String()))
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -175,29 +198,30 @@ func (m *Manager) ClickBySelector(ctx context.Context, parentNodeID dom.NodeID,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.mouse.Click(ctx, points.X, points.Y, 50); err != nil {
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeID, selector values.String) error {
|
||||
func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeID, selector string, count int) error {
|
||||
if err := m.ScrollIntoViewBySelector(ctx, selector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(parentNodeID, selector.String()))
|
||||
found, err := m.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, nodeID := range found.NodeIDs {
|
||||
_, min := core.NumberBoundaries(100)
|
||||
beforeTypeDelay := time.Duration(min)
|
||||
beforeTypeDelay := time.Duration(core.NumberLowerBoundary(drivers.DefaultMouseDelay*10)) * time.Millisecond
|
||||
|
||||
time.Sleep(beforeTypeDelay * time.Millisecond)
|
||||
time.Sleep(beforeTypeDelay)
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, nodeID)
|
||||
|
||||
@@ -205,7 +229,9 @@ func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeI
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.mouse.Click(ctx, points.X, points.Y, 50); err != nil {
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
|
||||
if err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, count); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -213,7 +239,7 @@ func (m *Manager) ClickBySelectorAll(ctx context.Context, parentNodeID dom.NodeI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, text core.Value, delay values.Int) error {
|
||||
func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, params TypeParams) error {
|
||||
err := m.ScrollIntoView(ctx, objectID)
|
||||
|
||||
if err != nil {
|
||||
@@ -226,22 +252,34 @@ func (m *Manager) Type(ctx context.Context, objectID runtime.RemoteObjectID, tex
|
||||
return err
|
||||
}
|
||||
|
||||
_, min := core.NumberBoundaries(float64(delay))
|
||||
beforeTypeDelay := time.Duration(min)
|
||||
if params.Clear {
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
||||
|
||||
time.Sleep(beforeTypeDelay * time.Millisecond)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.keyboard.Type(ctx, text.String(), int(delay))
|
||||
if err := m.ClearByXY(ctx, points); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d := core.NumberLowerBoundary(float64(params.Delay))
|
||||
beforeTypeDelay := time.Duration(d)
|
||||
|
||||
time.Sleep(beforeTypeDelay)
|
||||
|
||||
return m.keyboard.Type(ctx, params.Text, params.Delay)
|
||||
}
|
||||
|
||||
func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, selector values.String, text core.Value, delay values.Int) error {
|
||||
func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, params TypeParams) error {
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector.String()))
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -253,12 +291,85 @@ func (m *Manager) TypeBySelector(ctx context.Context, parentNodeID dom.NodeID, s
|
||||
return err
|
||||
}
|
||||
|
||||
_, min := core.NumberBoundaries(float64(delay))
|
||||
beforeTypeDelay := time.Duration(min)
|
||||
if params.Clear {
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
|
||||
time.Sleep(beforeTypeDelay * time.Millisecond)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.keyboard.Type(ctx, text.String(), int(delay))
|
||||
if err := m.ClearByXY(ctx, points); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d := core.NumberLowerBoundary(float64(params.Delay))
|
||||
beforeTypeDelay := time.Duration(d)
|
||||
|
||||
time.Sleep(beforeTypeDelay)
|
||||
|
||||
return m.keyboard.Type(ctx, params.Text, params.Delay)
|
||||
}
|
||||
|
||||
func (m *Manager) Clear(ctx context.Context, objectID runtime.RemoteObjectID) error {
|
||||
err := m.ScrollIntoView(ctx, objectID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
points, err := GetClickablePointByObjectID(ctx, m.client, objectID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(objectID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.ClearByXY(ctx, points)
|
||||
}
|
||||
|
||||
func (m *Manager) ClearBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string) error {
|
||||
err := m.ScrollIntoViewBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := m.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(parentNodeID, selector))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
points, err := GetClickablePointByNodeID(ctx, m.client, found.NodeID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.client.DOM.Focus(ctx, dom.NewFocusArgs().SetNodeID(found.NodeID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.ClearByXY(ctx, points)
|
||||
}
|
||||
|
||||
func (m *Manager) ClearByXY(ctx context.Context, points Quad) error {
|
||||
delay := time.Duration(drivers.DefaultMouseDelay) * time.Millisecond
|
||||
err := m.mouse.ClickWithCount(ctx, points.X, points.Y, delay, 2)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.keyboard.Press(ctx, "Backspace")
|
||||
}
|
||||
|
||||
func (m *Manager) Select(ctx context.Context, objectID runtime.RemoteObjectID, value *values.Array) (*values.Array, error) {
|
||||
@@ -283,12 +394,12 @@ func (m *Manager) Select(ctx context.Context, objectID runtime.RemoteObjectID, v
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (m *Manager) SelectBySelector(ctx context.Context, parentNodeID dom.NodeID, selector values.String, value *values.Array) (*values.Array, error) {
|
||||
func (m *Manager) SelectBySelector(ctx context.Context, parentNodeID dom.NodeID, selector string, value *values.Array) (*values.Array, error) {
|
||||
if err := m.FocusBySelector(ctx, parentNodeID, selector); err != nil {
|
||||
return values.NewArray(0), err
|
||||
}
|
||||
|
||||
res, err := m.exec.EvalWithReturnValue(ctx, templates.SelectBySelector(selector.String(), value.String()))
|
||||
res, err := m.exec.EvalWithReturnValue(ctx, templates.SelectBySelector(selector, value.String()))
|
||||
|
||||
if err != nil {
|
||||
return values.NewArray(0), err
|
||||
|
||||
@@ -18,37 +18,47 @@ func NewMouse(client *cdp.Client) *Mouse {
|
||||
return &Mouse{client, 0, 0}
|
||||
}
|
||||
|
||||
func (m *Mouse) Click(ctx context.Context, x, y float64, delay int) error {
|
||||
func (m *Mouse) Click(ctx context.Context, x, y float64, delay time.Duration) error {
|
||||
return m.ClickWithCount(ctx, x, y, delay, 1)
|
||||
}
|
||||
|
||||
func (m *Mouse) ClickWithCount(ctx context.Context, x, y float64, delay time.Duration, count int) error {
|
||||
if err := m.Move(ctx, x, y); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.Down(ctx, "left"); err != nil {
|
||||
if err := m.DownWithCount(ctx, "left", count); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseDelay := randomDuration(delay)
|
||||
time.Sleep(randomDuration(int(delay)))
|
||||
|
||||
time.Sleep(releaseDelay * time.Millisecond)
|
||||
|
||||
return m.Up(ctx, "left")
|
||||
return m.UpWithCount(ctx, "left", count)
|
||||
}
|
||||
|
||||
func (m *Mouse) Down(ctx context.Context, button string) error {
|
||||
return m.DownWithCount(ctx, button, 1)
|
||||
}
|
||||
|
||||
func (m *Mouse) DownWithCount(ctx context.Context, button string, count int) error {
|
||||
return m.client.Input.DispatchMouseEvent(
|
||||
ctx,
|
||||
input.NewDispatchMouseEventArgs("mousePressed", m.x, m.y).
|
||||
SetClickCount(1).
|
||||
SetButton(button),
|
||||
SetButton(button).
|
||||
SetClickCount(count),
|
||||
)
|
||||
}
|
||||
|
||||
func (m *Mouse) Up(ctx context.Context, button string) error {
|
||||
return m.UpWithCount(ctx, button, 1)
|
||||
}
|
||||
|
||||
func (m *Mouse) UpWithCount(ctx context.Context, button string, count int) error {
|
||||
return m.client.Input.DispatchMouseEvent(
|
||||
ctx,
|
||||
input.NewDispatchMouseEventArgs("mouseReleased", m.x, m.y).
|
||||
SetClickCount(1).
|
||||
SetButton(button),
|
||||
SetButton(button).
|
||||
SetClickCount(count),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cdp
|
||||
|
||||
import "github.com/MontFerret/ferret/pkg/drivers"
|
||||
|
||||
type (
|
||||
Options struct {
|
||||
Name string
|
||||
@@ -7,6 +9,8 @@ type (
|
||||
UserAgent string
|
||||
Address string
|
||||
KeepCookies bool
|
||||
Headers drivers.HTTPHeaders
|
||||
Cookies drivers.HTTPCookies
|
||||
}
|
||||
|
||||
Option func(opts *Options)
|
||||
@@ -57,3 +61,47 @@ func WithCustomName(name string) Option {
|
||||
opts.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeader(name string, value []string) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Headers == nil {
|
||||
opts.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
opts.Headers[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeaders(headers drivers.HTTPHeaders) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Headers == nil {
|
||||
opts.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
opts.Headers[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookie(cookie drivers.HTTPCookie) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Cookies == nil {
|
||||
opts.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
opts.Cookies[cookie.Name] = cookie
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookies(cookies []drivers.HTTPCookie) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Cookies == nil {
|
||||
opts.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
for _, c := range cookies {
|
||||
opts.Cookies[c.Name] = c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ func LoadHTMLPage(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.Cookies != nil {
|
||||
if len(params.Cookies) > 0 {
|
||||
cookies := make([]network.CookieParam, 0, len(params.Cookies))
|
||||
|
||||
for _, c := range params.Cookies {
|
||||
@@ -167,7 +167,7 @@ func LoadHTMLPage(
|
||||
}
|
||||
}
|
||||
|
||||
if params.Headers != nil {
|
||||
if len(params.Headers) > 0 {
|
||||
j, err := json.Marshal(params.Headers)
|
||||
|
||||
if err != nil {
|
||||
|
||||
28
pkg/drivers/cdp/templates/blur.go
Normal file
28
pkg/drivers/cdp/templates/blur.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
)
|
||||
|
||||
func Blur() string {
|
||||
return `
|
||||
(el) => {
|
||||
el.blur()
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
func BlurBySelector(selector string) string {
|
||||
return fmt.Sprintf(`
|
||||
(parent) => {
|
||||
const el = parent.querySelector('%s');
|
||||
|
||||
if (el == null) {
|
||||
throw new Error('%s')
|
||||
}
|
||||
|
||||
el.blur();
|
||||
}
|
||||
`, selector, drivers.ErrNotFound)
|
||||
}
|
||||
@@ -121,7 +121,11 @@ func GetInDocument(ctx context.Context, doc drivers.HTMLDocument, path []core.Va
|
||||
|
||||
return GetInDocument(ctx, parent, path[1:])
|
||||
case "body", "head":
|
||||
out := doc.QuerySelector(ctx, segment)
|
||||
out, err := doc.QuerySelector(ctx, segment)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if out == values.None {
|
||||
return out, nil
|
||||
@@ -166,9 +170,13 @@ func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
|
||||
case "innerHTML":
|
||||
return el.GetInnerHTML(ctx)
|
||||
case "value":
|
||||
return el.GetValue(ctx), nil
|
||||
return el.GetValue(ctx)
|
||||
case "attributes":
|
||||
attrs := el.GetAttributes(ctx)
|
||||
attrs, err := el.GetAttributes(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if len(path) == 1 {
|
||||
return attrs, nil
|
||||
@@ -209,7 +217,7 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
|
||||
if nt == drivers.HTMLElementType || nt == drivers.HTMLDocumentType {
|
||||
re := node.(drivers.HTMLNode)
|
||||
|
||||
return re.GetChildNode(ctx, segment.(values.Int)), nil
|
||||
return re.GetChildNode(ctx, values.ToInt(segment))
|
||||
}
|
||||
|
||||
return values.GetIn(ctx, node, path[1:])
|
||||
@@ -224,7 +232,11 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
|
||||
case "nodeName":
|
||||
return node.GetNodeName(), nil
|
||||
case "children":
|
||||
children := node.GetChildNodes(ctx)
|
||||
children, err := node.GetChildNodes(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if len(path) == 1 {
|
||||
return children, nil
|
||||
|
||||
@@ -26,7 +26,11 @@ func NewIterator(
|
||||
func (iterator *Iterator) Next(ctx context.Context) (value core.Value, key core.Value, err error) {
|
||||
if iterator.node.Length() > iterator.pos {
|
||||
idx := iterator.pos
|
||||
val := iterator.node.GetChildNode(ctx, idx)
|
||||
val, err := iterator.node.GetChildNode(ctx, idx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, values.None, err
|
||||
}
|
||||
|
||||
iterator.pos++
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
|
||||
return err
|
||||
}
|
||||
|
||||
curr := el.GetAttributes(ctx)
|
||||
curr, err := el.GetAttributes(ctx)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove all previous attributes
|
||||
err = el.RemoveAttribute(ctx, curr.Keys()...)
|
||||
|
||||
9
pkg/drivers/consts.go
Normal file
9
pkg/drivers/consts.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package drivers
|
||||
|
||||
const (
|
||||
DefaultPageLoadTimeout = 60000
|
||||
DefaultWaitTimeout = 5000
|
||||
DefaultKeyboardDelay = 25
|
||||
DefaultMouseDelay = 10
|
||||
DefaultTimeout = 30000
|
||||
)
|
||||
@@ -30,6 +30,8 @@ type (
|
||||
HTTPOnly bool
|
||||
SameSite SameSite
|
||||
}
|
||||
|
||||
HTTPCookies map[string]HTTPCookie
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -2,14 +2,10 @@ package drivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"io"
|
||||
)
|
||||
|
||||
const DefaultTimeout = time.Second * 30
|
||||
|
||||
type (
|
||||
ctxKey struct{}
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
)
|
||||
|
||||
func WithDefaultTimeout(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
return context.WithTimeout(ctx, DefaultTimeout)
|
||||
}
|
||||
|
||||
func ToPage(value core.Value) (HTMLPage, error) {
|
||||
err := core.ValidateType(value, HTMLPageType)
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ func (doc *HTMLDocument) Copy() core.Value {
|
||||
return cp
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) Clone() core.Value {
|
||||
func (doc *HTMLDocument) Clone() core.Cloneable {
|
||||
cloned, err := NewHTMLDocument(doc.doc, doc.url.String(), doc.parent)
|
||||
|
||||
if err != nil {
|
||||
@@ -149,23 +149,23 @@ func (doc *HTMLDocument) GetNodeName() values.String {
|
||||
return "#document"
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) core.Value {
|
||||
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) (*values.Array, error) {
|
||||
return doc.element.GetChildNodes(ctx)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) core.Value {
|
||||
func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) (core.Value, error) {
|
||||
return doc.element.GetChildNode(ctx, idx)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) core.Value {
|
||||
func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) (core.Value, error) {
|
||||
return doc.element.QuerySelector(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
|
||||
func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) (*values.Array, error) {
|
||||
return doc.element.QuerySelectorAll(ctx, selector)
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) values.Int {
|
||||
func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) (values.Int, error) {
|
||||
return doc.element.CountBySelector(ctx, selector)
|
||||
}
|
||||
|
||||
@@ -207,30 +207,6 @@ func (doc *HTMLDocument) GetParentDocument() drivers.HTMLDocument {
|
||||
return doc.parent
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ClickBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ClickBySelectorAll(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) InputBySelector(_ context.Context, _ values.String, _ core.Value, _ values.Int) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) SelectBySelector(_ context.Context, _ values.String, _ *values.Array) (*values.Array, error) {
|
||||
return nil, core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) PrintToPDF(_ context.Context, _ drivers.PDFParams) (values.Binary, error) {
|
||||
return nil, core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) CaptureScreenshot(_ context.Context, _ drivers.ScreenshotParams) (values.Binary, error) {
|
||||
return nil, core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) ScrollTop(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
@@ -247,22 +223,10 @@ func (doc *HTMLDocument) ScrollByXY(_ context.Context, _, _ values.Float) error
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) FocusBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) MoveMouseBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) MoveMouseByXY(_ context.Context, _, _ values.Float) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForNavigation(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (doc *HTMLDocument) WaitForElement(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewDriver(opts ...Option) *Driver {
|
||||
drv := new(Driver)
|
||||
drv.options = newOptions(opts)
|
||||
|
||||
if drv.options.proxy == "" {
|
||||
if drv.options.Proxy == "" {
|
||||
drv.client = pester.New()
|
||||
} else {
|
||||
client, err := newClientWithProxy(drv.options)
|
||||
@@ -38,15 +38,15 @@ func NewDriver(opts ...Option) *Driver {
|
||||
}
|
||||
}
|
||||
|
||||
drv.client.Concurrency = drv.options.concurrency
|
||||
drv.client.MaxRetries = drv.options.maxRetries
|
||||
drv.client.Backoff = drv.options.backoff
|
||||
drv.client.Concurrency = drv.options.Concurrency
|
||||
drv.client.MaxRetries = drv.options.MaxRetries
|
||||
drv.client.Backoff = drv.options.Backoff
|
||||
|
||||
return drv
|
||||
}
|
||||
|
||||
func newClientWithProxy(options *Options) (*http.Client, error) {
|
||||
proxyURL, err := url.Parse(options.proxy)
|
||||
proxyURL, err := url.Parse(options.Proxy)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -59,7 +59,7 @@ func newClientWithProxy(options *Options) (*http.Client, error) {
|
||||
}
|
||||
|
||||
func (drv *Driver) Name() string {
|
||||
return DriverName
|
||||
return drv.options.Name
|
||||
}
|
||||
|
||||
func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTMLPage, error) {
|
||||
@@ -76,33 +76,54 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
|
||||
if params.Headers != nil {
|
||||
for k := range params.Headers {
|
||||
req.Header.Add(k, params.Headers.Get(k))
|
||||
if drv.options.Headers != nil && params.Headers == nil {
|
||||
params.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
logger.
|
||||
Debug().
|
||||
Timestamp().
|
||||
Str("header", k).
|
||||
Msg("set header")
|
||||
// Set default headers
|
||||
for k, v := range drv.options.Headers {
|
||||
_, exists := params.Headers[k]
|
||||
|
||||
// do not override user's set values
|
||||
if !exists {
|
||||
params.Headers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if params.Cookies != nil {
|
||||
for _, c := range params.Cookies {
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: c.Name,
|
||||
Value: c.Value,
|
||||
})
|
||||
for k := range params.Headers {
|
||||
req.Header.Add(k, params.Headers.Get(k))
|
||||
|
||||
logger.
|
||||
Debug().
|
||||
Timestamp().
|
||||
Str("cookie", c.Name).
|
||||
Msg("set cookie")
|
||||
logger.
|
||||
Debug().
|
||||
Timestamp().
|
||||
Str("header", k).
|
||||
Msg("set header")
|
||||
}
|
||||
|
||||
if drv.options.Cookies != nil && params.Cookies == nil {
|
||||
params.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
// set default cookies
|
||||
for k, v := range drv.options.Cookies {
|
||||
_, exists := params.Cookies[k]
|
||||
|
||||
// do not override user's set values
|
||||
if !exists {
|
||||
params.Cookies[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range params.Cookies {
|
||||
req.AddCookie(fromDriverCookie(c))
|
||||
|
||||
logger.
|
||||
Debug().
|
||||
Timestamp().
|
||||
Str("cookie", c.Name).
|
||||
Msg("set cookie")
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
var ua string
|
||||
@@ -110,7 +131,7 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
||||
if params.UserAgent != "" {
|
||||
ua = common.GetUserAgent(params.UserAgent)
|
||||
} else {
|
||||
ua = common.GetUserAgent(drv.options.userAgent)
|
||||
ua = common.GetUserAgent(drv.options.UserAgent)
|
||||
}
|
||||
|
||||
logger.
|
||||
@@ -141,6 +162,12 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
||||
return nil, errors.Wrapf(err, "failed to parse a document %s", params.URL)
|
||||
}
|
||||
|
||||
cookies, err := toDriverCookies(resp.Cookies())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// HTTPResponse is temporarily unavailable when the status code != OK
|
||||
r := drivers.HTTPResponse{
|
||||
StatusCode: resp.StatusCode,
|
||||
@@ -148,7 +175,7 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
||||
Headers: drivers.HTTPHeaders(resp.Header),
|
||||
}
|
||||
|
||||
return NewHTMLPage(doc, params.URL, params.Cookies, &r)
|
||||
return NewHTMLPage(doc, params.URL, &r, cookies)
|
||||
}
|
||||
|
||||
func (drv *Driver) Parse(_ context.Context, str values.String) (drivers.HTMLPage, error) {
|
||||
|
||||
@@ -116,14 +116,14 @@ func (el *HTMLElement) Length() values.Int {
|
||||
return el.children.Length()
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetValue(_ context.Context) core.Value {
|
||||
func (el *HTMLElement) GetValue(_ context.Context) (core.Value, error) {
|
||||
val, ok := el.selection.Attr("value")
|
||||
|
||||
if ok {
|
||||
return values.NewString(val)
|
||||
return values.NewString(val), nil
|
||||
}
|
||||
|
||||
return values.EmptyString
|
||||
return values.EmptyString, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetValue(_ context.Context, value core.Value) error {
|
||||
@@ -242,16 +242,16 @@ func (el *HTMLElement) SetAttributes(ctx context.Context, attrs *values.Object)
|
||||
return err
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttributes(_ context.Context) *values.Object {
|
||||
func (el *HTMLElement) GetAttributes(_ context.Context) (*values.Object, error) {
|
||||
el.ensureAttrs()
|
||||
|
||||
return el.attrs.Copy().(*values.Object)
|
||||
return el.attrs.Copy().(*values.Object), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetAttribute(_ context.Context, name values.String) core.Value {
|
||||
func (el *HTMLElement) GetAttribute(_ context.Context, name values.String) (core.Value, error) {
|
||||
el.ensureAttrs()
|
||||
|
||||
return el.attrs.MustGet(name)
|
||||
return el.attrs.MustGet(name), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SetAttribute(_ context.Context, name, value values.String) error {
|
||||
@@ -274,43 +274,43 @@ func (el *HTMLElement) RemoveAttribute(_ context.Context, name ...values.String)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNodes(_ context.Context) core.Value {
|
||||
func (el *HTMLElement) GetChildNodes(_ context.Context) (*values.Array, error) {
|
||||
if el.children == nil {
|
||||
el.children = el.parseChildren()
|
||||
}
|
||||
|
||||
return el.children
|
||||
return el.children.Copy().(*values.Array), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) GetChildNode(_ context.Context, idx values.Int) core.Value {
|
||||
func (el *HTMLElement) GetChildNode(_ context.Context, idx values.Int) (core.Value, error) {
|
||||
if el.children == nil {
|
||||
el.children = el.parseChildren()
|
||||
}
|
||||
|
||||
return el.children.Get(idx)
|
||||
return el.children.Get(idx), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) QuerySelector(_ context.Context, selector values.String) core.Value {
|
||||
func (el *HTMLElement) QuerySelector(_ context.Context, selector values.String) (core.Value, error) {
|
||||
selection := el.selection.Find(selector.String())
|
||||
|
||||
if selection == nil {
|
||||
return values.None
|
||||
return values.None, nil
|
||||
}
|
||||
|
||||
res, err := NewHTMLElement(selection)
|
||||
|
||||
if err != nil {
|
||||
return values.None
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return res
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) QuerySelectorAll(_ context.Context, selector values.String) core.Value {
|
||||
func (el *HTMLElement) QuerySelectorAll(_ context.Context, selector values.String) (*values.Array, error) {
|
||||
selection := el.selection.Find(selector.String())
|
||||
|
||||
if selection == nil {
|
||||
return values.None
|
||||
return values.NewArray(0), nil
|
||||
}
|
||||
|
||||
arr := values.NewArray(selection.Length())
|
||||
@@ -323,7 +323,7 @@ func (el *HTMLElement) QuerySelectorAll(_ context.Context, selector values.Strin
|
||||
}
|
||||
})
|
||||
|
||||
return arr
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) XPath(_ context.Context, expression values.String) (core.Value, error) {
|
||||
@@ -457,14 +457,14 @@ func (el *HTMLElement) GetInnerTextBySelectorAll(_ context.Context, selector val
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) CountBySelector(_ context.Context, selector values.String) values.Int {
|
||||
func (el *HTMLElement) CountBySelector(_ context.Context, selector values.String) (values.Int, error) {
|
||||
selection := el.selection.Find(selector.String())
|
||||
|
||||
if selection == nil {
|
||||
return values.ZeroInt
|
||||
return values.ZeroInt, nil
|
||||
}
|
||||
|
||||
return values.NewInt(selection.Size())
|
||||
return values.NewInt(selection.Size()), nil
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ExistsBySelector(_ context.Context, selector values.String) (values.Boolean, error) {
|
||||
@@ -489,15 +489,23 @@ func (el *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) {
|
||||
return common.NewIterator(el)
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Click(_ context.Context) error {
|
||||
func (el *HTMLElement) Click(_ context.Context, _ values.Int) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClickBySelector(_ context.Context, _ values.String) error {
|
||||
func (el *HTMLElement) ClickBySelector(_ context.Context, _ values.String, _ values.Int) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClickBySelectorAll(_ context.Context, _ values.String) error {
|
||||
func (el *HTMLElement) ClickBySelectorAll(_ context.Context, _ values.String, _ values.Int) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Clear(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ClearBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -505,10 +513,18 @@ func (el *HTMLElement) Input(_ context.Context, _ core.Value, _ values.Int) erro
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) InputBySelector(_ context.Context, _ values.String, _ core.Value, _ values.Int) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) {
|
||||
return nil, core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) SelectBySelector(_ context.Context, _ values.String, _ *values.Array) (*values.Array, error) {
|
||||
return nil, core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) ScrollIntoView(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
@@ -517,10 +533,26 @@ func (el *HTMLElement) Focus(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) FocusBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Blur(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) BlurBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) Hover(_ context.Context) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) HoverBySelector(_ context.Context, _ values.String) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
|
||||
func (el *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
|
||||
return core.ErrNotSupported
|
||||
}
|
||||
@@ -548,7 +580,11 @@ func (el *HTMLElement) ensureStyles(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (el *HTMLElement) parseStyles(ctx context.Context) (*values.Object, error) {
|
||||
str := el.GetAttribute(ctx, "style")
|
||||
str, err := el.GetAttribute(ctx, "style")
|
||||
|
||||
if err != nil {
|
||||
return values.NewObject(), err
|
||||
}
|
||||
|
||||
if str == values.None {
|
||||
return values.NewObject(), nil
|
||||
|
||||
@@ -322,8 +322,9 @@ func TestElement(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
v := el.GetValue(context.Background())
|
||||
v, err := el.GetValue(context.Background())
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldEqual, "find")
|
||||
})
|
||||
|
||||
@@ -393,8 +394,9 @@ func TestElement(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
found := el.QuerySelector(context.Background(), values.NewString("body .card-img-top:nth-child(1)"))
|
||||
found, err := el.QuerySelector(context.Background(), values.NewString("body .card-img-top:nth-child(1)"))
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(found, ShouldNotEqual, values.None)
|
||||
|
||||
v := found.(drivers.HTMLNode).GetNodeName()
|
||||
@@ -415,8 +417,9 @@ func TestElement(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
v := el.CountBySelector(context.Background(), values.NewString("head meta"))
|
||||
v, err := el.CountBySelector(context.Background(), values.NewString("head meta"))
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldEqual, 4)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
HTTP "net/http"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/antchfx/htmlquery"
|
||||
"github.com/antchfx/xpath"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
func parseXPathNode(nav *htmlquery.NodeNavigator) (core.Value, error) {
|
||||
@@ -45,3 +47,71 @@ func outerHTML(s *goquery.Selection) (string, error) {
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func toDriverCookies(cookies []*HTTP.Cookie) (drivers.HTTPCookies, error) {
|
||||
res := make(drivers.HTTPCookies)
|
||||
|
||||
for _, c := range cookies {
|
||||
dc, err := toDriverCookie(c)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res[dc.Name] = dc
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func toDriverCookie(cookie *HTTP.Cookie) (drivers.HTTPCookie, error) {
|
||||
res := drivers.HTTPCookie{}
|
||||
|
||||
if cookie == nil {
|
||||
return res, core.Error(core.ErrMissedArgument, "cookie")
|
||||
}
|
||||
|
||||
res.Name = cookie.Name
|
||||
res.Value = cookie.Value
|
||||
res.Path = cookie.Path
|
||||
res.Domain = cookie.Domain
|
||||
res.Expires = cookie.Expires
|
||||
res.MaxAge = cookie.MaxAge
|
||||
res.Secure = cookie.Secure
|
||||
res.HTTPOnly = cookie.HttpOnly
|
||||
|
||||
switch cookie.SameSite {
|
||||
case HTTP.SameSiteLaxMode:
|
||||
res.SameSite = drivers.SameSiteLaxMode
|
||||
case HTTP.SameSiteStrictMode:
|
||||
res.SameSite = drivers.SameSiteStrictMode
|
||||
default:
|
||||
res.SameSite = drivers.SameSiteDefaultMode
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func fromDriverCookie(cookie drivers.HTTPCookie) *HTTP.Cookie {
|
||||
res := &HTTP.Cookie{}
|
||||
|
||||
res.Name = cookie.Name
|
||||
res.Value = cookie.Value
|
||||
res.Path = cookie.Path
|
||||
res.Domain = cookie.Domain
|
||||
res.Expires = cookie.Expires
|
||||
res.MaxAge = cookie.MaxAge
|
||||
res.Secure = cookie.Secure
|
||||
res.HttpOnly = cookie.HTTPOnly
|
||||
|
||||
switch cookie.SameSite {
|
||||
case drivers.SameSiteLaxMode:
|
||||
res.SameSite = HTTP.SameSiteLaxMode
|
||||
case drivers.SameSiteStrictMode:
|
||||
res.SameSite = HTTP.SameSiteStrictMode
|
||||
default:
|
||||
res.SameSite = HTTP.SameSiteDefaultMode
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/sethgrid/pester"
|
||||
)
|
||||
|
||||
@@ -8,19 +9,23 @@ type (
|
||||
Option func(opts *Options)
|
||||
|
||||
Options struct {
|
||||
backoff pester.BackoffStrategy
|
||||
maxRetries int
|
||||
concurrency int
|
||||
proxy string
|
||||
userAgent string
|
||||
Name string
|
||||
Backoff pester.BackoffStrategy
|
||||
MaxRetries int
|
||||
Concurrency int
|
||||
Proxy string
|
||||
UserAgent string
|
||||
Headers drivers.HTTPHeaders
|
||||
Cookies drivers.HTTPCookies
|
||||
}
|
||||
)
|
||||
|
||||
func newOptions(setters []Option) *Options {
|
||||
opts := new(Options)
|
||||
opts.backoff = pester.ExponentialBackoff
|
||||
opts.concurrency = 3
|
||||
opts.maxRetries = 5
|
||||
opts.Name = DriverName
|
||||
opts.Backoff = pester.ExponentialBackoff
|
||||
opts.Concurrency = 3
|
||||
opts.MaxRetries = 5
|
||||
|
||||
for _, setter := range setters {
|
||||
setter(opts)
|
||||
@@ -31,42 +36,92 @@ func newOptions(setters []Option) *Options {
|
||||
|
||||
func WithDefaultBackoff() Option {
|
||||
return func(opts *Options) {
|
||||
opts.backoff = pester.DefaultBackoff
|
||||
opts.Backoff = pester.DefaultBackoff
|
||||
}
|
||||
}
|
||||
|
||||
func WithLinearBackoff() Option {
|
||||
return func(opts *Options) {
|
||||
opts.backoff = pester.LinearBackoff
|
||||
opts.Backoff = pester.LinearBackoff
|
||||
}
|
||||
}
|
||||
|
||||
func WithExponentialBackoff() Option {
|
||||
return func(opts *Options) {
|
||||
opts.backoff = pester.ExponentialBackoff
|
||||
opts.Backoff = pester.ExponentialBackoff
|
||||
}
|
||||
}
|
||||
|
||||
func WithMaxRetries(value int) Option {
|
||||
return func(opts *Options) {
|
||||
opts.maxRetries = value
|
||||
opts.MaxRetries = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithConcurrency(value int) Option {
|
||||
return func(opts *Options) {
|
||||
opts.concurrency = value
|
||||
opts.Concurrency = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithProxy(address string) Option {
|
||||
return func(opts *Options) {
|
||||
opts.proxy = address
|
||||
opts.Proxy = address
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserAgent(value string) Option {
|
||||
return func(opts *Options) {
|
||||
opts.userAgent = value
|
||||
opts.UserAgent = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomName(name string) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeader(name string, value []string) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Headers == nil {
|
||||
opts.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
opts.Headers[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeaders(headers drivers.HTTPHeaders) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Headers == nil {
|
||||
opts.Headers = make(drivers.HTTPHeaders)
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
opts.Headers[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookie(cookie drivers.HTTPCookie) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Cookies == nil {
|
||||
opts.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
opts.Cookies[cookie.Name] = cookie
|
||||
}
|
||||
}
|
||||
|
||||
func WithCookies(cookies []drivers.HTTPCookie) Option {
|
||||
return func(opts *Options) {
|
||||
if opts.Cookies == nil {
|
||||
opts.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
for _, c := range cookies {
|
||||
opts.Cookies[c.Name] = c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@ import (
|
||||
"context"
|
||||
"hash/fnv"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/drivers/common"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
type HTMLPage struct {
|
||||
document *HTMLDocument
|
||||
cookies []drivers.HTTPCookie
|
||||
cookies drivers.HTTPCookies
|
||||
frames *values.Array
|
||||
response *drivers.HTTPResponse
|
||||
}
|
||||
@@ -21,8 +22,8 @@ type HTMLPage struct {
|
||||
func NewHTMLPage(
|
||||
qdoc *goquery.Document,
|
||||
url string,
|
||||
cookies []drivers.HTTPCookie,
|
||||
response *drivers.HTTPResponse,
|
||||
cookies drivers.HTTPCookies,
|
||||
) (*HTMLPage, error) {
|
||||
doc, err := NewRootHTMLDocument(qdoc, url)
|
||||
|
||||
@@ -83,11 +84,17 @@ func (p *HTMLPage) Hash() uint64 {
|
||||
}
|
||||
|
||||
func (p *HTMLPage) Copy() core.Value {
|
||||
cookies := make(drivers.HTTPCookies)
|
||||
|
||||
for k, v := range p.cookies {
|
||||
cookies[k] = v
|
||||
}
|
||||
|
||||
page, err := NewHTMLPage(
|
||||
p.document.doc,
|
||||
p.document.GetURL().String(),
|
||||
p.cookies,
|
||||
p.response,
|
||||
cookies,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ type (
|
||||
URL string
|
||||
UserAgent string
|
||||
KeepCookies bool
|
||||
Cookies []HTTPCookie
|
||||
Cookies HTTPCookies
|
||||
Headers HTTPHeaders
|
||||
Viewport *Viewport
|
||||
}
|
||||
|
||||
@@ -30,23 +30,19 @@ type (
|
||||
|
||||
GetNodeName() values.String
|
||||
|
||||
GetChildNodes(ctx context.Context) core.Value
|
||||
GetChildNodes(ctx context.Context) (*values.Array, error)
|
||||
|
||||
GetChildNode(ctx context.Context, idx values.Int) core.Value
|
||||
GetChildNode(ctx context.Context, idx values.Int) (core.Value, error)
|
||||
|
||||
QuerySelector(ctx context.Context, selector values.String) core.Value
|
||||
QuerySelector(ctx context.Context, selector values.String) (core.Value, error)
|
||||
|
||||
QuerySelectorAll(ctx context.Context, selector values.String) core.Value
|
||||
QuerySelectorAll(ctx context.Context, selector values.String) (*values.Array, error)
|
||||
|
||||
CountBySelector(ctx context.Context, selector values.String) values.Int
|
||||
CountBySelector(ctx context.Context, selector values.String) (values.Int, error)
|
||||
|
||||
ExistsBySelector(ctx context.Context, selector values.String) (values.Boolean, error)
|
||||
|
||||
XPath(ctx context.Context, expression values.String) (core.Value, error)
|
||||
|
||||
ClickBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
ClickBySelectorAll(ctx context.Context, selector values.String) error
|
||||
}
|
||||
|
||||
// HTMLElement is the most general base interface which most objects in a GetMainFrame implement.
|
||||
@@ -61,7 +57,7 @@ type (
|
||||
|
||||
SetInnerHTML(ctx context.Context, innerHTML values.String) error
|
||||
|
||||
GetValue(ctx context.Context) core.Value
|
||||
GetValue(ctx context.Context) (core.Value, error)
|
||||
|
||||
SetValue(ctx context.Context, value core.Value) error
|
||||
|
||||
@@ -75,9 +71,9 @@ type (
|
||||
|
||||
RemoveStyle(ctx context.Context, name ...values.String) error
|
||||
|
||||
GetAttributes(ctx context.Context) *values.Object
|
||||
GetAttributes(ctx context.Context) (*values.Object, error)
|
||||
|
||||
GetAttribute(ctx context.Context, name values.String) core.Value
|
||||
GetAttribute(ctx context.Context, name values.String) (core.Value, error)
|
||||
|
||||
SetAttributes(ctx context.Context, values *values.Object) error
|
||||
|
||||
@@ -97,18 +93,38 @@ type (
|
||||
|
||||
GetInnerTextBySelectorAll(ctx context.Context, selector values.String) (*values.Array, error)
|
||||
|
||||
Click(ctx context.Context) error
|
||||
Click(ctx context.Context, count values.Int) error
|
||||
|
||||
ClickBySelector(ctx context.Context, selector values.String, count values.Int) error
|
||||
|
||||
ClickBySelectorAll(ctx context.Context, selector values.String, count values.Int) error
|
||||
|
||||
Clear(ctx context.Context) error
|
||||
|
||||
ClearBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
Input(ctx context.Context, value core.Value, delay values.Int) error
|
||||
|
||||
InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) error
|
||||
|
||||
Select(ctx context.Context, value *values.Array) (*values.Array, error)
|
||||
|
||||
SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error)
|
||||
|
||||
ScrollIntoView(ctx context.Context) error
|
||||
|
||||
Focus(ctx context.Context) error
|
||||
|
||||
FocusBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
Blur(ctx context.Context) error
|
||||
|
||||
BlurBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
Hover(ctx context.Context) error
|
||||
|
||||
HoverBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
WaitForAttribute(ctx context.Context, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
WaitForStyle(ctx context.Context, name values.String, value core.Value, when WaitEvent) error
|
||||
@@ -131,10 +147,6 @@ type (
|
||||
|
||||
GetChildDocuments(ctx context.Context) (*values.Array, error)
|
||||
|
||||
InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) error
|
||||
|
||||
SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error)
|
||||
|
||||
ScrollTop(ctx context.Context) error
|
||||
|
||||
ScrollBottom(ctx context.Context) error
|
||||
@@ -143,12 +155,8 @@ type (
|
||||
|
||||
ScrollByXY(ctx context.Context, x, y values.Float) error
|
||||
|
||||
FocusBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
MoveMouseByXY(ctx context.Context, x, y values.Float) error
|
||||
|
||||
MoveMouseBySelector(ctx context.Context, selector values.String) error
|
||||
|
||||
WaitForElement(ctx context.Context, selector values.String, when WaitEvent) error
|
||||
|
||||
WaitForAttributeBySelector(ctx context.Context, selector, name values.String, value core.Value, when WaitEvent) error
|
||||
|
||||
@@ -78,8 +78,7 @@ In: 'IN';
|
||||
// Literals
|
||||
Param: '@';
|
||||
Identifier: Letter+ (Symbols (Identifier)*)* (Digit (Identifier)*)*;
|
||||
StringLiteral: SQString | DQSring;
|
||||
TemplateStringLiteral: '`' ('\\`' | ~'`')* '`';
|
||||
StringLiteral: SQString | DQSring | BacktickString | TickString;
|
||||
IntegerLiteral: [0-9]+;
|
||||
FloatLiteral
|
||||
: DecimalIntegerLiteral Dot [0-9]+ ExponentPart?
|
||||
@@ -108,4 +107,6 @@ fragment Digit
|
||||
;
|
||||
fragment DQSring: '"' ( '\\'. | '""' | ~('"'| '\\') )* '"';
|
||||
fragment SQString: '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\'';
|
||||
fragment BacktickString: '`' ('\\`' | ~'`')* '`';
|
||||
fragment TickString: '´' ('\\´' | ~'´')* '´';
|
||||
fragment NamespaceSeparator: '::';
|
||||
@@ -57,10 +57,9 @@ In=56
|
||||
Param=57
|
||||
Identifier=58
|
||||
StringLiteral=59
|
||||
TemplateStringLiteral=60
|
||||
IntegerLiteral=61
|
||||
FloatLiteral=62
|
||||
NamespaceSegment=63
|
||||
IntegerLiteral=60
|
||||
FloatLiteral=61
|
||||
NamespaceSegment=62
|
||||
':'=5
|
||||
';'=6
|
||||
'.'=7
|
||||
|
||||
@@ -159,7 +159,6 @@ booleanLiteral
|
||||
|
||||
stringLiteral
|
||||
: StringLiteral
|
||||
| TemplateStringLiteral
|
||||
;
|
||||
|
||||
integerLiteral
|
||||
@@ -185,11 +184,6 @@ propertyAssignment
|
||||
| shorthandPropertyName
|
||||
;
|
||||
|
||||
memberExpression
|
||||
: Identifier (Dot propertyName (computedPropertyName)*)+
|
||||
| Identifier computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
||||
;
|
||||
|
||||
shorthandPropertyName
|
||||
: variable
|
||||
;
|
||||
@@ -201,6 +195,7 @@ computedPropertyName
|
||||
propertyName
|
||||
: Identifier
|
||||
| stringLiteral
|
||||
| param
|
||||
;
|
||||
|
||||
expressionGroup
|
||||
@@ -215,6 +210,21 @@ functionCallExpression
|
||||
: namespace Identifier arguments
|
||||
;
|
||||
|
||||
member
|
||||
: Identifier
|
||||
| functionCallExpression
|
||||
| param
|
||||
;
|
||||
|
||||
memberPath
|
||||
: (Dot propertyName (computedPropertyName)*)+
|
||||
| computedPropertyName (Dot propertyName (computedPropertyName)*)* (computedPropertyName (Dot propertyName)*)*
|
||||
;
|
||||
|
||||
memberExpression
|
||||
: member memberPath
|
||||
;
|
||||
|
||||
arguments
|
||||
: OpenParen(expression (Comma expression)*)?CloseParen
|
||||
;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -57,10 +57,9 @@ In=56
|
||||
Param=57
|
||||
Identifier=58
|
||||
StringLiteral=59
|
||||
TemplateStringLiteral=60
|
||||
IntegerLiteral=61
|
||||
FloatLiteral=62
|
||||
NamespaceSegment=63
|
||||
IntegerLiteral=60
|
||||
FloatLiteral=61
|
||||
NamespaceSegment=62
|
||||
':'=5
|
||||
';'=6
|
||||
'.'=7
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -57,10 +57,9 @@ In=56
|
||||
Param=57
|
||||
Identifier=58
|
||||
StringLiteral=59
|
||||
TemplateStringLiteral=60
|
||||
IntegerLiteral=61
|
||||
FloatLiteral=62
|
||||
NamespaceSegment=63
|
||||
IntegerLiteral=60
|
||||
FloatLiteral=61
|
||||
NamespaceSegment=62
|
||||
':'=5
|
||||
';'=6
|
||||
'.'=7
|
||||
|
||||
@@ -14,7 +14,7 @@ var _ = fmt.Printf
|
||||
var _ = unicode.IsLetter
|
||||
|
||||
var serializedLexerAtn = []uint16{
|
||||
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 65, 529,
|
||||
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 64, 544,
|
||||
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
|
||||
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
|
||||
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
|
||||
@@ -28,59 +28,61 @@ var serializedLexerAtn = []uint16{
|
||||
4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4,
|
||||
60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65,
|
||||
9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9,
|
||||
70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 3, 2, 3, 2, 3, 2, 3, 2, 7,
|
||||
2, 152, 10, 2, 12, 2, 14, 2, 155, 11, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 166, 10, 3, 12, 3, 14, 3, 169, 11, 3, 3,
|
||||
3, 3, 3, 3, 4, 6, 4, 174, 10, 4, 13, 4, 14, 4, 175, 3, 4, 3, 4, 3, 5, 3,
|
||||
5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3,
|
||||
10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15,
|
||||
3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3,
|
||||
20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24,
|
||||
3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3,
|
||||
28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 5, 29, 241, 10, 29, 3, 30, 3, 30,
|
||||
3, 30, 3, 30, 5, 30, 247, 10, 30, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3,
|
||||
33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36,
|
||||
3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3,
|
||||
38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39,
|
||||
3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3,
|
||||
41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43,
|
||||
3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3,
|
||||
44, 3, 44, 5, 44, 319, 10, 44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 46,
|
||||
3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3,
|
||||
47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47,
|
||||
3, 47, 5, 47, 349, 10, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3,
|
||||
49, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51,
|
||||
3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3,
|
||||
53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54,
|
||||
3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 56, 5,
|
||||
56, 399, 10, 56, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 6, 59, 407,
|
||||
10, 59, 13, 59, 14, 59, 408, 3, 59, 3, 59, 7, 59, 413, 10, 59, 12, 59,
|
||||
14, 59, 416, 11, 59, 7, 59, 418, 10, 59, 12, 59, 14, 59, 421, 11, 59, 3,
|
||||
59, 3, 59, 7, 59, 425, 10, 59, 12, 59, 14, 59, 428, 11, 59, 7, 59, 430,
|
||||
10, 59, 12, 59, 14, 59, 433, 11, 59, 3, 60, 3, 60, 5, 60, 437, 10, 60,
|
||||
3, 61, 3, 61, 3, 61, 3, 61, 7, 61, 443, 10, 61, 12, 61, 14, 61, 446, 11,
|
||||
61, 3, 61, 3, 61, 3, 62, 6, 62, 451, 10, 62, 13, 62, 14, 62, 452, 3, 63,
|
||||
3, 63, 3, 63, 6, 63, 458, 10, 63, 13, 63, 14, 63, 459, 3, 63, 5, 63, 463,
|
||||
10, 63, 3, 63, 3, 63, 5, 63, 467, 10, 63, 5, 63, 469, 10, 63, 3, 64, 3,
|
||||
64, 3, 64, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 7, 66, 479, 10, 66, 12, 66,
|
||||
14, 66, 482, 11, 66, 5, 66, 484, 10, 66, 3, 67, 3, 67, 5, 67, 488, 10,
|
||||
67, 3, 67, 6, 67, 491, 10, 67, 13, 67, 14, 67, 492, 3, 68, 3, 68, 3, 69,
|
||||
3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 7, 71, 507,
|
||||
10, 71, 12, 71, 14, 71, 510, 11, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72,
|
||||
3, 72, 3, 72, 3, 72, 7, 72, 520, 10, 72, 12, 72, 14, 72, 523, 11, 72, 3,
|
||||
72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 153, 2, 74, 3, 3, 5, 4, 7, 5, 9, 6,
|
||||
11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29,
|
||||
16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47,
|
||||
25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65,
|
||||
34, 67, 35, 69, 36, 71, 37, 73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83,
|
||||
43, 85, 44, 87, 45, 89, 46, 91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101,
|
||||
52, 103, 53, 105, 54, 107, 55, 109, 56, 111, 57, 113, 58, 115, 59, 117,
|
||||
60, 119, 61, 121, 62, 123, 63, 125, 64, 127, 65, 129, 2, 131, 2, 133, 2,
|
||||
135, 2, 137, 2, 139, 2, 141, 2, 143, 2, 145, 2, 3, 2, 13, 5, 2, 12, 12,
|
||||
15, 15, 8234, 8235, 6, 2, 11, 11, 13, 14, 34, 34, 162, 162, 3, 2, 98, 98,
|
||||
3, 2, 50, 59, 5, 2, 50, 59, 67, 72, 99, 104, 3, 2, 51, 59, 4, 2, 71, 71,
|
||||
103, 103, 4, 2, 45, 45, 47, 47, 4, 2, 67, 92, 99, 124, 4, 2, 36, 36, 94,
|
||||
94, 4, 2, 41, 41, 94, 94, 2, 552, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2,
|
||||
70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 3, 2, 3, 2,
|
||||
3, 2, 3, 2, 7, 2, 154, 10, 2, 12, 2, 14, 2, 157, 11, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 168, 10, 3, 12, 3, 14, 3,
|
||||
171, 11, 3, 3, 3, 3, 3, 3, 4, 6, 4, 176, 10, 4, 13, 4, 14, 4, 177, 3, 4,
|
||||
3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9,
|
||||
3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3,
|
||||
14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 19,
|
||||
3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3,
|
||||
23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27,
|
||||
3, 28, 3, 28, 3, 28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 5, 29, 243, 10,
|
||||
29, 3, 30, 3, 30, 3, 30, 3, 30, 5, 30, 249, 10, 30, 3, 31, 3, 31, 3, 31,
|
||||
3, 32, 3, 32, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3,
|
||||
36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37,
|
||||
3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3,
|
||||
39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40,
|
||||
3, 41, 3, 41, 3, 41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3,
|
||||
43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44,
|
||||
3, 44, 3, 44, 3, 44, 3, 44, 5, 44, 321, 10, 44, 3, 45, 3, 45, 3, 45, 3,
|
||||
45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 47,
|
||||
3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3,
|
||||
47, 3, 47, 3, 47, 3, 47, 5, 47, 351, 10, 47, 3, 48, 3, 48, 3, 48, 3, 48,
|
||||
3, 48, 3, 49, 3, 49, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3,
|
||||
50, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52,
|
||||
3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3,
|
||||
54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56,
|
||||
3, 56, 3, 56, 5, 56, 401, 10, 56, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 3,
|
||||
59, 6, 59, 409, 10, 59, 13, 59, 14, 59, 410, 3, 59, 3, 59, 7, 59, 415,
|
||||
10, 59, 12, 59, 14, 59, 418, 11, 59, 7, 59, 420, 10, 59, 12, 59, 14, 59,
|
||||
423, 11, 59, 3, 59, 3, 59, 7, 59, 427, 10, 59, 12, 59, 14, 59, 430, 11,
|
||||
59, 7, 59, 432, 10, 59, 12, 59, 14, 59, 435, 11, 59, 3, 60, 3, 60, 3, 60,
|
||||
3, 60, 5, 60, 441, 10, 60, 3, 61, 6, 61, 444, 10, 61, 13, 61, 14, 61, 445,
|
||||
3, 62, 3, 62, 3, 62, 6, 62, 451, 10, 62, 13, 62, 14, 62, 452, 3, 62, 5,
|
||||
62, 456, 10, 62, 3, 62, 3, 62, 5, 62, 460, 10, 62, 5, 62, 462, 10, 62,
|
||||
3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 7, 65, 472, 10,
|
||||
65, 12, 65, 14, 65, 475, 11, 65, 5, 65, 477, 10, 65, 3, 66, 3, 66, 5, 66,
|
||||
481, 10, 66, 3, 66, 6, 66, 484, 10, 66, 13, 66, 14, 66, 485, 3, 67, 3,
|
||||
67, 3, 68, 3, 68, 3, 69, 3, 69, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70,
|
||||
7, 70, 500, 10, 70, 12, 70, 14, 70, 503, 11, 70, 3, 70, 3, 70, 3, 71, 3,
|
||||
71, 3, 71, 3, 71, 3, 71, 3, 71, 7, 71, 513, 10, 71, 12, 71, 14, 71, 516,
|
||||
11, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 72, 7, 72, 524, 10, 72, 12,
|
||||
72, 14, 72, 527, 11, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 73, 7, 73,
|
||||
535, 10, 73, 12, 73, 14, 73, 538, 11, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3,
|
||||
74, 3, 155, 2, 75, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10,
|
||||
19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19,
|
||||
37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28,
|
||||
55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65, 34, 67, 35, 69, 36, 71, 37,
|
||||
73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83, 43, 85, 44, 87, 45, 89, 46,
|
||||
91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101, 52, 103, 53, 105, 54, 107,
|
||||
55, 109, 56, 111, 57, 113, 58, 115, 59, 117, 60, 119, 61, 121, 62, 123,
|
||||
63, 125, 64, 127, 2, 129, 2, 131, 2, 133, 2, 135, 2, 137, 2, 139, 2, 141,
|
||||
2, 143, 2, 145, 2, 147, 2, 3, 2, 14, 5, 2, 12, 12, 15, 15, 8234, 8235,
|
||||
6, 2, 11, 11, 13, 14, 34, 34, 162, 162, 3, 2, 50, 59, 5, 2, 50, 59, 67,
|
||||
72, 99, 104, 3, 2, 51, 59, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47,
|
||||
4, 2, 67, 92, 99, 124, 4, 2, 36, 36, 94, 94, 4, 2, 41, 41, 94, 94, 3, 2,
|
||||
98, 98, 3, 2, 182, 182, 2, 569, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2,
|
||||
7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2,
|
||||
2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2,
|
||||
2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2,
|
||||
@@ -96,160 +98,166 @@ var serializedLexerAtn = []uint16{
|
||||
99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2,
|
||||
2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113,
|
||||
3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2,
|
||||
2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3,
|
||||
2, 2, 2, 3, 147, 3, 2, 2, 2, 5, 161, 3, 2, 2, 2, 7, 173, 3, 2, 2, 2, 9,
|
||||
179, 3, 2, 2, 2, 11, 183, 3, 2, 2, 2, 13, 185, 3, 2, 2, 2, 15, 187, 3,
|
||||
2, 2, 2, 17, 189, 3, 2, 2, 2, 19, 191, 3, 2, 2, 2, 21, 193, 3, 2, 2, 2,
|
||||
23, 195, 3, 2, 2, 2, 25, 197, 3, 2, 2, 2, 27, 199, 3, 2, 2, 2, 29, 201,
|
||||
3, 2, 2, 2, 31, 203, 3, 2, 2, 2, 33, 205, 3, 2, 2, 2, 35, 207, 3, 2, 2,
|
||||
2, 37, 210, 3, 2, 2, 2, 39, 213, 3, 2, 2, 2, 41, 216, 3, 2, 2, 2, 43, 219,
|
||||
3, 2, 2, 2, 45, 221, 3, 2, 2, 2, 47, 223, 3, 2, 2, 2, 49, 225, 3, 2, 2,
|
||||
2, 51, 227, 3, 2, 2, 2, 53, 229, 3, 2, 2, 2, 55, 232, 3, 2, 2, 2, 57, 240,
|
||||
3, 2, 2, 2, 59, 246, 3, 2, 2, 2, 61, 248, 3, 2, 2, 2, 63, 251, 3, 2, 2,
|
||||
2, 65, 253, 3, 2, 2, 2, 67, 255, 3, 2, 2, 2, 69, 258, 3, 2, 2, 2, 71, 261,
|
||||
3, 2, 2, 2, 73, 265, 3, 2, 2, 2, 75, 272, 3, 2, 2, 2, 77, 281, 3, 2, 2,
|
||||
2, 79, 288, 3, 2, 2, 2, 81, 293, 3, 2, 2, 2, 83, 299, 3, 2, 2, 2, 85, 303,
|
||||
3, 2, 2, 2, 87, 318, 3, 2, 2, 2, 89, 320, 3, 2, 2, 2, 91, 325, 3, 2, 2,
|
||||
2, 93, 348, 3, 2, 2, 2, 95, 350, 3, 2, 2, 2, 97, 355, 3, 2, 2, 2, 99, 360,
|
||||
3, 2, 2, 2, 101, 365, 3, 2, 2, 2, 103, 371, 3, 2, 2, 2, 105, 375, 3, 2,
|
||||
2, 2, 107, 379, 3, 2, 2, 2, 109, 389, 3, 2, 2, 2, 111, 398, 3, 2, 2, 2,
|
||||
113, 400, 3, 2, 2, 2, 115, 403, 3, 2, 2, 2, 117, 406, 3, 2, 2, 2, 119,
|
||||
436, 3, 2, 2, 2, 121, 438, 3, 2, 2, 2, 123, 450, 3, 2, 2, 2, 125, 468,
|
||||
3, 2, 2, 2, 127, 470, 3, 2, 2, 2, 129, 473, 3, 2, 2, 2, 131, 483, 3, 2,
|
||||
2, 2, 133, 485, 3, 2, 2, 2, 135, 494, 3, 2, 2, 2, 137, 496, 3, 2, 2, 2,
|
||||
139, 498, 3, 2, 2, 2, 141, 500, 3, 2, 2, 2, 143, 513, 3, 2, 2, 2, 145,
|
||||
526, 3, 2, 2, 2, 147, 148, 7, 49, 2, 2, 148, 149, 7, 44, 2, 2, 149, 153,
|
||||
3, 2, 2, 2, 150, 152, 11, 2, 2, 2, 151, 150, 3, 2, 2, 2, 152, 155, 3, 2,
|
||||
2, 2, 153, 154, 3, 2, 2, 2, 153, 151, 3, 2, 2, 2, 154, 156, 3, 2, 2, 2,
|
||||
155, 153, 3, 2, 2, 2, 156, 157, 7, 44, 2, 2, 157, 158, 7, 49, 2, 2, 158,
|
||||
159, 3, 2, 2, 2, 159, 160, 8, 2, 2, 2, 160, 4, 3, 2, 2, 2, 161, 162, 7,
|
||||
49, 2, 2, 162, 163, 7, 49, 2, 2, 163, 167, 3, 2, 2, 2, 164, 166, 10, 2,
|
||||
2, 2, 165, 164, 3, 2, 2, 2, 166, 169, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2,
|
||||
167, 168, 3, 2, 2, 2, 168, 170, 3, 2, 2, 2, 169, 167, 3, 2, 2, 2, 170,
|
||||
171, 8, 3, 2, 2, 171, 6, 3, 2, 2, 2, 172, 174, 9, 3, 2, 2, 173, 172, 3,
|
||||
2, 2, 2, 174, 175, 3, 2, 2, 2, 175, 173, 3, 2, 2, 2, 175, 176, 3, 2, 2,
|
||||
2, 176, 177, 3, 2, 2, 2, 177, 178, 8, 4, 2, 2, 178, 8, 3, 2, 2, 2, 179,
|
||||
180, 9, 2, 2, 2, 180, 181, 3, 2, 2, 2, 181, 182, 8, 5, 2, 2, 182, 10, 3,
|
||||
2, 2, 2, 183, 184, 7, 60, 2, 2, 184, 12, 3, 2, 2, 2, 185, 186, 7, 61, 2,
|
||||
2, 186, 14, 3, 2, 2, 2, 187, 188, 7, 48, 2, 2, 188, 16, 3, 2, 2, 2, 189,
|
||||
190, 7, 46, 2, 2, 190, 18, 3, 2, 2, 2, 191, 192, 7, 93, 2, 2, 192, 20,
|
||||
3, 2, 2, 2, 193, 194, 7, 95, 2, 2, 194, 22, 3, 2, 2, 2, 195, 196, 7, 42,
|
||||
2, 2, 196, 24, 3, 2, 2, 2, 197, 198, 7, 43, 2, 2, 198, 26, 3, 2, 2, 2,
|
||||
199, 200, 7, 125, 2, 2, 200, 28, 3, 2, 2, 2, 201, 202, 7, 127, 2, 2, 202,
|
||||
30, 3, 2, 2, 2, 203, 204, 7, 64, 2, 2, 204, 32, 3, 2, 2, 2, 205, 206, 7,
|
||||
62, 2, 2, 206, 34, 3, 2, 2, 2, 207, 208, 7, 63, 2, 2, 208, 209, 7, 63,
|
||||
2, 2, 209, 36, 3, 2, 2, 2, 210, 211, 7, 64, 2, 2, 211, 212, 7, 63, 2, 2,
|
||||
212, 38, 3, 2, 2, 2, 213, 214, 7, 62, 2, 2, 214, 215, 7, 63, 2, 2, 215,
|
||||
40, 3, 2, 2, 2, 216, 217, 7, 35, 2, 2, 217, 218, 7, 63, 2, 2, 218, 42,
|
||||
3, 2, 2, 2, 219, 220, 7, 44, 2, 2, 220, 44, 3, 2, 2, 2, 221, 222, 7, 49,
|
||||
2, 2, 222, 46, 3, 2, 2, 2, 223, 224, 7, 39, 2, 2, 224, 48, 3, 2, 2, 2,
|
||||
225, 226, 7, 45, 2, 2, 226, 50, 3, 2, 2, 2, 227, 228, 7, 47, 2, 2, 228,
|
||||
52, 3, 2, 2, 2, 229, 230, 7, 47, 2, 2, 230, 231, 7, 47, 2, 2, 231, 54,
|
||||
3, 2, 2, 2, 232, 233, 7, 45, 2, 2, 233, 234, 7, 45, 2, 2, 234, 56, 3, 2,
|
||||
2, 2, 235, 236, 7, 67, 2, 2, 236, 237, 7, 80, 2, 2, 237, 241, 7, 70, 2,
|
||||
2, 238, 239, 7, 40, 2, 2, 239, 241, 7, 40, 2, 2, 240, 235, 3, 2, 2, 2,
|
||||
240, 238, 3, 2, 2, 2, 241, 58, 3, 2, 2, 2, 242, 243, 7, 81, 2, 2, 243,
|
||||
247, 7, 84, 2, 2, 244, 245, 7, 126, 2, 2, 245, 247, 7, 126, 2, 2, 246,
|
||||
242, 3, 2, 2, 2, 246, 244, 3, 2, 2, 2, 247, 60, 3, 2, 2, 2, 248, 249, 5,
|
||||
15, 8, 2, 249, 250, 5, 15, 8, 2, 250, 62, 3, 2, 2, 2, 251, 252, 7, 63,
|
||||
2, 2, 252, 64, 3, 2, 2, 2, 253, 254, 7, 65, 2, 2, 254, 66, 3, 2, 2, 2,
|
||||
255, 256, 7, 35, 2, 2, 256, 257, 7, 128, 2, 2, 257, 68, 3, 2, 2, 2, 258,
|
||||
259, 7, 63, 2, 2, 259, 260, 7, 128, 2, 2, 260, 70, 3, 2, 2, 2, 261, 262,
|
||||
7, 72, 2, 2, 262, 263, 7, 81, 2, 2, 263, 264, 7, 84, 2, 2, 264, 72, 3,
|
||||
2, 2, 2, 265, 266, 7, 84, 2, 2, 266, 267, 7, 71, 2, 2, 267, 268, 7, 86,
|
||||
2, 2, 268, 269, 7, 87, 2, 2, 269, 270, 7, 84, 2, 2, 270, 271, 7, 80, 2,
|
||||
2, 271, 74, 3, 2, 2, 2, 272, 273, 7, 70, 2, 2, 273, 274, 7, 75, 2, 2, 274,
|
||||
275, 7, 85, 2, 2, 275, 276, 7, 86, 2, 2, 276, 277, 7, 75, 2, 2, 277, 278,
|
||||
7, 80, 2, 2, 278, 279, 7, 69, 2, 2, 279, 280, 7, 86, 2, 2, 280, 76, 3,
|
||||
2, 2, 2, 281, 282, 7, 72, 2, 2, 282, 283, 7, 75, 2, 2, 283, 284, 7, 78,
|
||||
2, 2, 284, 285, 7, 86, 2, 2, 285, 286, 7, 71, 2, 2, 286, 287, 7, 84, 2,
|
||||
2, 287, 78, 3, 2, 2, 2, 288, 289, 7, 85, 2, 2, 289, 290, 7, 81, 2, 2, 290,
|
||||
291, 7, 84, 2, 2, 291, 292, 7, 86, 2, 2, 292, 80, 3, 2, 2, 2, 293, 294,
|
||||
7, 78, 2, 2, 294, 295, 7, 75, 2, 2, 295, 296, 7, 79, 2, 2, 296, 297, 7,
|
||||
75, 2, 2, 297, 298, 7, 86, 2, 2, 298, 82, 3, 2, 2, 2, 299, 300, 7, 78,
|
||||
2, 2, 300, 301, 7, 71, 2, 2, 301, 302, 7, 86, 2, 2, 302, 84, 3, 2, 2, 2,
|
||||
303, 304, 7, 69, 2, 2, 304, 305, 7, 81, 2, 2, 305, 306, 7, 78, 2, 2, 306,
|
||||
307, 7, 78, 2, 2, 307, 308, 7, 71, 2, 2, 308, 309, 7, 69, 2, 2, 309, 310,
|
||||
7, 86, 2, 2, 310, 86, 3, 2, 2, 2, 311, 312, 7, 67, 2, 2, 312, 313, 7, 85,
|
||||
2, 2, 313, 319, 7, 69, 2, 2, 314, 315, 7, 70, 2, 2, 315, 316, 7, 71, 2,
|
||||
2, 316, 317, 7, 85, 2, 2, 317, 319, 7, 69, 2, 2, 318, 311, 3, 2, 2, 2,
|
||||
318, 314, 3, 2, 2, 2, 319, 88, 3, 2, 2, 2, 320, 321, 7, 80, 2, 2, 321,
|
||||
322, 7, 81, 2, 2, 322, 323, 7, 80, 2, 2, 323, 324, 7, 71, 2, 2, 324, 90,
|
||||
3, 2, 2, 2, 325, 326, 7, 80, 2, 2, 326, 327, 7, 87, 2, 2, 327, 328, 7,
|
||||
78, 2, 2, 328, 329, 7, 78, 2, 2, 329, 92, 3, 2, 2, 2, 330, 331, 7, 86,
|
||||
2, 2, 331, 332, 7, 84, 2, 2, 332, 333, 7, 87, 2, 2, 333, 349, 7, 71, 2,
|
||||
2, 334, 335, 7, 118, 2, 2, 335, 336, 7, 116, 2, 2, 336, 337, 7, 119, 2,
|
||||
2, 337, 349, 7, 103, 2, 2, 338, 339, 7, 72, 2, 2, 339, 340, 7, 67, 2, 2,
|
||||
340, 341, 7, 78, 2, 2, 341, 342, 7, 85, 2, 2, 342, 349, 7, 71, 2, 2, 343,
|
||||
344, 7, 104, 2, 2, 344, 345, 7, 99, 2, 2, 345, 346, 7, 110, 2, 2, 346,
|
||||
347, 7, 117, 2, 2, 347, 349, 7, 103, 2, 2, 348, 330, 3, 2, 2, 2, 348, 334,
|
||||
3, 2, 2, 2, 348, 338, 3, 2, 2, 2, 348, 343, 3, 2, 2, 2, 349, 94, 3, 2,
|
||||
2, 2, 350, 351, 7, 75, 2, 2, 351, 352, 7, 80, 2, 2, 352, 353, 7, 86, 2,
|
||||
2, 353, 354, 7, 81, 2, 2, 354, 96, 3, 2, 2, 2, 355, 356, 7, 77, 2, 2, 356,
|
||||
357, 7, 71, 2, 2, 357, 358, 7, 71, 2, 2, 358, 359, 7, 82, 2, 2, 359, 98,
|
||||
3, 2, 2, 2, 360, 361, 7, 89, 2, 2, 361, 362, 7, 75, 2, 2, 362, 363, 7,
|
||||
86, 2, 2, 363, 364, 7, 74, 2, 2, 364, 100, 3, 2, 2, 2, 365, 366, 7, 69,
|
||||
2, 2, 366, 367, 7, 81, 2, 2, 367, 368, 7, 87, 2, 2, 368, 369, 7, 80, 2,
|
||||
2, 369, 370, 7, 86, 2, 2, 370, 102, 3, 2, 2, 2, 371, 372, 7, 67, 2, 2,
|
||||
372, 373, 7, 78, 2, 2, 373, 374, 7, 78, 2, 2, 374, 104, 3, 2, 2, 2, 375,
|
||||
376, 7, 67, 2, 2, 376, 377, 7, 80, 2, 2, 377, 378, 7, 91, 2, 2, 378, 106,
|
||||
3, 2, 2, 2, 379, 380, 7, 67, 2, 2, 380, 381, 7, 73, 2, 2, 381, 382, 7,
|
||||
73, 2, 2, 382, 383, 7, 84, 2, 2, 383, 384, 7, 71, 2, 2, 384, 385, 7, 73,
|
||||
2, 2, 385, 386, 7, 67, 2, 2, 386, 387, 7, 86, 2, 2, 387, 388, 7, 71, 2,
|
||||
2, 388, 108, 3, 2, 2, 2, 389, 390, 7, 78, 2, 2, 390, 391, 7, 75, 2, 2,
|
||||
391, 392, 7, 77, 2, 2, 392, 393, 7, 71, 2, 2, 393, 110, 3, 2, 2, 2, 394,
|
||||
395, 7, 80, 2, 2, 395, 396, 7, 81, 2, 2, 396, 399, 7, 86, 2, 2, 397, 399,
|
||||
7, 35, 2, 2, 398, 394, 3, 2, 2, 2, 398, 397, 3, 2, 2, 2, 399, 112, 3, 2,
|
||||
2, 2, 400, 401, 7, 75, 2, 2, 401, 402, 7, 80, 2, 2, 402, 114, 3, 2, 2,
|
||||
2, 403, 404, 7, 66, 2, 2, 404, 116, 3, 2, 2, 2, 405, 407, 5, 135, 68, 2,
|
||||
406, 405, 3, 2, 2, 2, 407, 408, 3, 2, 2, 2, 408, 406, 3, 2, 2, 2, 408,
|
||||
409, 3, 2, 2, 2, 409, 419, 3, 2, 2, 2, 410, 414, 5, 137, 69, 2, 411, 413,
|
||||
5, 117, 59, 2, 412, 411, 3, 2, 2, 2, 413, 416, 3, 2, 2, 2, 414, 412, 3,
|
||||
2, 2, 2, 414, 415, 3, 2, 2, 2, 415, 418, 3, 2, 2, 2, 416, 414, 3, 2, 2,
|
||||
2, 417, 410, 3, 2, 2, 2, 418, 421, 3, 2, 2, 2, 419, 417, 3, 2, 2, 2, 419,
|
||||
420, 3, 2, 2, 2, 420, 431, 3, 2, 2, 2, 421, 419, 3, 2, 2, 2, 422, 426,
|
||||
5, 139, 70, 2, 423, 425, 5, 117, 59, 2, 424, 423, 3, 2, 2, 2, 425, 428,
|
||||
3, 2, 2, 2, 426, 424, 3, 2, 2, 2, 426, 427, 3, 2, 2, 2, 427, 430, 3, 2,
|
||||
2, 2, 428, 426, 3, 2, 2, 2, 429, 422, 3, 2, 2, 2, 430, 433, 3, 2, 2, 2,
|
||||
431, 429, 3, 2, 2, 2, 431, 432, 3, 2, 2, 2, 432, 118, 3, 2, 2, 2, 433,
|
||||
431, 3, 2, 2, 2, 434, 437, 5, 143, 72, 2, 435, 437, 5, 141, 71, 2, 436,
|
||||
434, 3, 2, 2, 2, 436, 435, 3, 2, 2, 2, 437, 120, 3, 2, 2, 2, 438, 444,
|
||||
7, 98, 2, 2, 439, 440, 7, 94, 2, 2, 440, 443, 7, 98, 2, 2, 441, 443, 10,
|
||||
4, 2, 2, 442, 439, 3, 2, 2, 2, 442, 441, 3, 2, 2, 2, 443, 446, 3, 2, 2,
|
||||
2, 444, 442, 3, 2, 2, 2, 444, 445, 3, 2, 2, 2, 445, 447, 3, 2, 2, 2, 446,
|
||||
444, 3, 2, 2, 2, 447, 448, 7, 98, 2, 2, 448, 122, 3, 2, 2, 2, 449, 451,
|
||||
9, 5, 2, 2, 450, 449, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 450, 3, 2,
|
||||
2, 2, 452, 453, 3, 2, 2, 2, 453, 124, 3, 2, 2, 2, 454, 455, 5, 131, 66,
|
||||
2, 455, 457, 5, 15, 8, 2, 456, 458, 9, 5, 2, 2, 457, 456, 3, 2, 2, 2, 458,
|
||||
459, 3, 2, 2, 2, 459, 457, 3, 2, 2, 2, 459, 460, 3, 2, 2, 2, 460, 462,
|
||||
3, 2, 2, 2, 461, 463, 5, 133, 67, 2, 462, 461, 3, 2, 2, 2, 462, 463, 3,
|
||||
2, 2, 2, 463, 469, 3, 2, 2, 2, 464, 466, 5, 131, 66, 2, 465, 467, 5, 133,
|
||||
67, 2, 466, 465, 3, 2, 2, 2, 466, 467, 3, 2, 2, 2, 467, 469, 3, 2, 2, 2,
|
||||
468, 454, 3, 2, 2, 2, 468, 464, 3, 2, 2, 2, 469, 126, 3, 2, 2, 2, 470,
|
||||
471, 5, 117, 59, 2, 471, 472, 5, 145, 73, 2, 472, 128, 3, 2, 2, 2, 473,
|
||||
474, 9, 6, 2, 2, 474, 130, 3, 2, 2, 2, 475, 484, 7, 50, 2, 2, 476, 480,
|
||||
9, 7, 2, 2, 477, 479, 9, 5, 2, 2, 478, 477, 3, 2, 2, 2, 479, 482, 3, 2,
|
||||
2, 2, 480, 478, 3, 2, 2, 2, 480, 481, 3, 2, 2, 2, 481, 484, 3, 2, 2, 2,
|
||||
482, 480, 3, 2, 2, 2, 483, 475, 3, 2, 2, 2, 483, 476, 3, 2, 2, 2, 484,
|
||||
132, 3, 2, 2, 2, 485, 487, 9, 8, 2, 2, 486, 488, 9, 9, 2, 2, 487, 486,
|
||||
3, 2, 2, 2, 487, 488, 3, 2, 2, 2, 488, 490, 3, 2, 2, 2, 489, 491, 9, 5,
|
||||
2, 2, 490, 489, 3, 2, 2, 2, 491, 492, 3, 2, 2, 2, 492, 490, 3, 2, 2, 2,
|
||||
492, 493, 3, 2, 2, 2, 493, 134, 3, 2, 2, 2, 494, 495, 9, 10, 2, 2, 495,
|
||||
136, 3, 2, 2, 2, 496, 497, 7, 97, 2, 2, 497, 138, 3, 2, 2, 2, 498, 499,
|
||||
4, 50, 59, 2, 499, 140, 3, 2, 2, 2, 500, 508, 7, 36, 2, 2, 501, 502, 7,
|
||||
94, 2, 2, 502, 507, 11, 2, 2, 2, 503, 504, 7, 36, 2, 2, 504, 507, 7, 36,
|
||||
2, 2, 505, 507, 10, 11, 2, 2, 506, 501, 3, 2, 2, 2, 506, 503, 3, 2, 2,
|
||||
2, 506, 505, 3, 2, 2, 2, 507, 510, 3, 2, 2, 2, 508, 506, 3, 2, 2, 2, 508,
|
||||
509, 3, 2, 2, 2, 509, 511, 3, 2, 2, 2, 510, 508, 3, 2, 2, 2, 511, 512,
|
||||
7, 36, 2, 2, 512, 142, 3, 2, 2, 2, 513, 521, 7, 41, 2, 2, 514, 515, 7,
|
||||
94, 2, 2, 515, 520, 11, 2, 2, 2, 516, 517, 7, 41, 2, 2, 517, 520, 7, 41,
|
||||
2, 2, 518, 520, 10, 12, 2, 2, 519, 514, 3, 2, 2, 2, 519, 516, 3, 2, 2,
|
||||
2, 519, 518, 3, 2, 2, 2, 520, 523, 3, 2, 2, 2, 521, 519, 3, 2, 2, 2, 521,
|
||||
522, 3, 2, 2, 2, 522, 524, 3, 2, 2, 2, 523, 521, 3, 2, 2, 2, 524, 525,
|
||||
7, 41, 2, 2, 525, 144, 3, 2, 2, 2, 526, 527, 7, 60, 2, 2, 527, 528, 7,
|
||||
60, 2, 2, 528, 146, 3, 2, 2, 2, 32, 2, 153, 167, 175, 240, 246, 318, 348,
|
||||
398, 408, 414, 419, 426, 431, 436, 442, 444, 452, 459, 462, 466, 468, 480,
|
||||
483, 487, 492, 506, 508, 519, 521, 3, 2, 3, 2,
|
||||
2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 3, 149, 3,
|
||||
2, 2, 2, 5, 163, 3, 2, 2, 2, 7, 175, 3, 2, 2, 2, 9, 181, 3, 2, 2, 2, 11,
|
||||
185, 3, 2, 2, 2, 13, 187, 3, 2, 2, 2, 15, 189, 3, 2, 2, 2, 17, 191, 3,
|
||||
2, 2, 2, 19, 193, 3, 2, 2, 2, 21, 195, 3, 2, 2, 2, 23, 197, 3, 2, 2, 2,
|
||||
25, 199, 3, 2, 2, 2, 27, 201, 3, 2, 2, 2, 29, 203, 3, 2, 2, 2, 31, 205,
|
||||
3, 2, 2, 2, 33, 207, 3, 2, 2, 2, 35, 209, 3, 2, 2, 2, 37, 212, 3, 2, 2,
|
||||
2, 39, 215, 3, 2, 2, 2, 41, 218, 3, 2, 2, 2, 43, 221, 3, 2, 2, 2, 45, 223,
|
||||
3, 2, 2, 2, 47, 225, 3, 2, 2, 2, 49, 227, 3, 2, 2, 2, 51, 229, 3, 2, 2,
|
||||
2, 53, 231, 3, 2, 2, 2, 55, 234, 3, 2, 2, 2, 57, 242, 3, 2, 2, 2, 59, 248,
|
||||
3, 2, 2, 2, 61, 250, 3, 2, 2, 2, 63, 253, 3, 2, 2, 2, 65, 255, 3, 2, 2,
|
||||
2, 67, 257, 3, 2, 2, 2, 69, 260, 3, 2, 2, 2, 71, 263, 3, 2, 2, 2, 73, 267,
|
||||
3, 2, 2, 2, 75, 274, 3, 2, 2, 2, 77, 283, 3, 2, 2, 2, 79, 290, 3, 2, 2,
|
||||
2, 81, 295, 3, 2, 2, 2, 83, 301, 3, 2, 2, 2, 85, 305, 3, 2, 2, 2, 87, 320,
|
||||
3, 2, 2, 2, 89, 322, 3, 2, 2, 2, 91, 327, 3, 2, 2, 2, 93, 350, 3, 2, 2,
|
||||
2, 95, 352, 3, 2, 2, 2, 97, 357, 3, 2, 2, 2, 99, 362, 3, 2, 2, 2, 101,
|
||||
367, 3, 2, 2, 2, 103, 373, 3, 2, 2, 2, 105, 377, 3, 2, 2, 2, 107, 381,
|
||||
3, 2, 2, 2, 109, 391, 3, 2, 2, 2, 111, 400, 3, 2, 2, 2, 113, 402, 3, 2,
|
||||
2, 2, 115, 405, 3, 2, 2, 2, 117, 408, 3, 2, 2, 2, 119, 440, 3, 2, 2, 2,
|
||||
121, 443, 3, 2, 2, 2, 123, 461, 3, 2, 2, 2, 125, 463, 3, 2, 2, 2, 127,
|
||||
466, 3, 2, 2, 2, 129, 476, 3, 2, 2, 2, 131, 478, 3, 2, 2, 2, 133, 487,
|
||||
3, 2, 2, 2, 135, 489, 3, 2, 2, 2, 137, 491, 3, 2, 2, 2, 139, 493, 3, 2,
|
||||
2, 2, 141, 506, 3, 2, 2, 2, 143, 519, 3, 2, 2, 2, 145, 530, 3, 2, 2, 2,
|
||||
147, 541, 3, 2, 2, 2, 149, 150, 7, 49, 2, 2, 150, 151, 7, 44, 2, 2, 151,
|
||||
155, 3, 2, 2, 2, 152, 154, 11, 2, 2, 2, 153, 152, 3, 2, 2, 2, 154, 157,
|
||||
3, 2, 2, 2, 155, 156, 3, 2, 2, 2, 155, 153, 3, 2, 2, 2, 156, 158, 3, 2,
|
||||
2, 2, 157, 155, 3, 2, 2, 2, 158, 159, 7, 44, 2, 2, 159, 160, 7, 49, 2,
|
||||
2, 160, 161, 3, 2, 2, 2, 161, 162, 8, 2, 2, 2, 162, 4, 3, 2, 2, 2, 163,
|
||||
164, 7, 49, 2, 2, 164, 165, 7, 49, 2, 2, 165, 169, 3, 2, 2, 2, 166, 168,
|
||||
10, 2, 2, 2, 167, 166, 3, 2, 2, 2, 168, 171, 3, 2, 2, 2, 169, 167, 3, 2,
|
||||
2, 2, 169, 170, 3, 2, 2, 2, 170, 172, 3, 2, 2, 2, 171, 169, 3, 2, 2, 2,
|
||||
172, 173, 8, 3, 2, 2, 173, 6, 3, 2, 2, 2, 174, 176, 9, 3, 2, 2, 175, 174,
|
||||
3, 2, 2, 2, 176, 177, 3, 2, 2, 2, 177, 175, 3, 2, 2, 2, 177, 178, 3, 2,
|
||||
2, 2, 178, 179, 3, 2, 2, 2, 179, 180, 8, 4, 2, 2, 180, 8, 3, 2, 2, 2, 181,
|
||||
182, 9, 2, 2, 2, 182, 183, 3, 2, 2, 2, 183, 184, 8, 5, 2, 2, 184, 10, 3,
|
||||
2, 2, 2, 185, 186, 7, 60, 2, 2, 186, 12, 3, 2, 2, 2, 187, 188, 7, 61, 2,
|
||||
2, 188, 14, 3, 2, 2, 2, 189, 190, 7, 48, 2, 2, 190, 16, 3, 2, 2, 2, 191,
|
||||
192, 7, 46, 2, 2, 192, 18, 3, 2, 2, 2, 193, 194, 7, 93, 2, 2, 194, 20,
|
||||
3, 2, 2, 2, 195, 196, 7, 95, 2, 2, 196, 22, 3, 2, 2, 2, 197, 198, 7, 42,
|
||||
2, 2, 198, 24, 3, 2, 2, 2, 199, 200, 7, 43, 2, 2, 200, 26, 3, 2, 2, 2,
|
||||
201, 202, 7, 125, 2, 2, 202, 28, 3, 2, 2, 2, 203, 204, 7, 127, 2, 2, 204,
|
||||
30, 3, 2, 2, 2, 205, 206, 7, 64, 2, 2, 206, 32, 3, 2, 2, 2, 207, 208, 7,
|
||||
62, 2, 2, 208, 34, 3, 2, 2, 2, 209, 210, 7, 63, 2, 2, 210, 211, 7, 63,
|
||||
2, 2, 211, 36, 3, 2, 2, 2, 212, 213, 7, 64, 2, 2, 213, 214, 7, 63, 2, 2,
|
||||
214, 38, 3, 2, 2, 2, 215, 216, 7, 62, 2, 2, 216, 217, 7, 63, 2, 2, 217,
|
||||
40, 3, 2, 2, 2, 218, 219, 7, 35, 2, 2, 219, 220, 7, 63, 2, 2, 220, 42,
|
||||
3, 2, 2, 2, 221, 222, 7, 44, 2, 2, 222, 44, 3, 2, 2, 2, 223, 224, 7, 49,
|
||||
2, 2, 224, 46, 3, 2, 2, 2, 225, 226, 7, 39, 2, 2, 226, 48, 3, 2, 2, 2,
|
||||
227, 228, 7, 45, 2, 2, 228, 50, 3, 2, 2, 2, 229, 230, 7, 47, 2, 2, 230,
|
||||
52, 3, 2, 2, 2, 231, 232, 7, 47, 2, 2, 232, 233, 7, 47, 2, 2, 233, 54,
|
||||
3, 2, 2, 2, 234, 235, 7, 45, 2, 2, 235, 236, 7, 45, 2, 2, 236, 56, 3, 2,
|
||||
2, 2, 237, 238, 7, 67, 2, 2, 238, 239, 7, 80, 2, 2, 239, 243, 7, 70, 2,
|
||||
2, 240, 241, 7, 40, 2, 2, 241, 243, 7, 40, 2, 2, 242, 237, 3, 2, 2, 2,
|
||||
242, 240, 3, 2, 2, 2, 243, 58, 3, 2, 2, 2, 244, 245, 7, 81, 2, 2, 245,
|
||||
249, 7, 84, 2, 2, 246, 247, 7, 126, 2, 2, 247, 249, 7, 126, 2, 2, 248,
|
||||
244, 3, 2, 2, 2, 248, 246, 3, 2, 2, 2, 249, 60, 3, 2, 2, 2, 250, 251, 5,
|
||||
15, 8, 2, 251, 252, 5, 15, 8, 2, 252, 62, 3, 2, 2, 2, 253, 254, 7, 63,
|
||||
2, 2, 254, 64, 3, 2, 2, 2, 255, 256, 7, 65, 2, 2, 256, 66, 3, 2, 2, 2,
|
||||
257, 258, 7, 35, 2, 2, 258, 259, 7, 128, 2, 2, 259, 68, 3, 2, 2, 2, 260,
|
||||
261, 7, 63, 2, 2, 261, 262, 7, 128, 2, 2, 262, 70, 3, 2, 2, 2, 263, 264,
|
||||
7, 72, 2, 2, 264, 265, 7, 81, 2, 2, 265, 266, 7, 84, 2, 2, 266, 72, 3,
|
||||
2, 2, 2, 267, 268, 7, 84, 2, 2, 268, 269, 7, 71, 2, 2, 269, 270, 7, 86,
|
||||
2, 2, 270, 271, 7, 87, 2, 2, 271, 272, 7, 84, 2, 2, 272, 273, 7, 80, 2,
|
||||
2, 273, 74, 3, 2, 2, 2, 274, 275, 7, 70, 2, 2, 275, 276, 7, 75, 2, 2, 276,
|
||||
277, 7, 85, 2, 2, 277, 278, 7, 86, 2, 2, 278, 279, 7, 75, 2, 2, 279, 280,
|
||||
7, 80, 2, 2, 280, 281, 7, 69, 2, 2, 281, 282, 7, 86, 2, 2, 282, 76, 3,
|
||||
2, 2, 2, 283, 284, 7, 72, 2, 2, 284, 285, 7, 75, 2, 2, 285, 286, 7, 78,
|
||||
2, 2, 286, 287, 7, 86, 2, 2, 287, 288, 7, 71, 2, 2, 288, 289, 7, 84, 2,
|
||||
2, 289, 78, 3, 2, 2, 2, 290, 291, 7, 85, 2, 2, 291, 292, 7, 81, 2, 2, 292,
|
||||
293, 7, 84, 2, 2, 293, 294, 7, 86, 2, 2, 294, 80, 3, 2, 2, 2, 295, 296,
|
||||
7, 78, 2, 2, 296, 297, 7, 75, 2, 2, 297, 298, 7, 79, 2, 2, 298, 299, 7,
|
||||
75, 2, 2, 299, 300, 7, 86, 2, 2, 300, 82, 3, 2, 2, 2, 301, 302, 7, 78,
|
||||
2, 2, 302, 303, 7, 71, 2, 2, 303, 304, 7, 86, 2, 2, 304, 84, 3, 2, 2, 2,
|
||||
305, 306, 7, 69, 2, 2, 306, 307, 7, 81, 2, 2, 307, 308, 7, 78, 2, 2, 308,
|
||||
309, 7, 78, 2, 2, 309, 310, 7, 71, 2, 2, 310, 311, 7, 69, 2, 2, 311, 312,
|
||||
7, 86, 2, 2, 312, 86, 3, 2, 2, 2, 313, 314, 7, 67, 2, 2, 314, 315, 7, 85,
|
||||
2, 2, 315, 321, 7, 69, 2, 2, 316, 317, 7, 70, 2, 2, 317, 318, 7, 71, 2,
|
||||
2, 318, 319, 7, 85, 2, 2, 319, 321, 7, 69, 2, 2, 320, 313, 3, 2, 2, 2,
|
||||
320, 316, 3, 2, 2, 2, 321, 88, 3, 2, 2, 2, 322, 323, 7, 80, 2, 2, 323,
|
||||
324, 7, 81, 2, 2, 324, 325, 7, 80, 2, 2, 325, 326, 7, 71, 2, 2, 326, 90,
|
||||
3, 2, 2, 2, 327, 328, 7, 80, 2, 2, 328, 329, 7, 87, 2, 2, 329, 330, 7,
|
||||
78, 2, 2, 330, 331, 7, 78, 2, 2, 331, 92, 3, 2, 2, 2, 332, 333, 7, 86,
|
||||
2, 2, 333, 334, 7, 84, 2, 2, 334, 335, 7, 87, 2, 2, 335, 351, 7, 71, 2,
|
||||
2, 336, 337, 7, 118, 2, 2, 337, 338, 7, 116, 2, 2, 338, 339, 7, 119, 2,
|
||||
2, 339, 351, 7, 103, 2, 2, 340, 341, 7, 72, 2, 2, 341, 342, 7, 67, 2, 2,
|
||||
342, 343, 7, 78, 2, 2, 343, 344, 7, 85, 2, 2, 344, 351, 7, 71, 2, 2, 345,
|
||||
346, 7, 104, 2, 2, 346, 347, 7, 99, 2, 2, 347, 348, 7, 110, 2, 2, 348,
|
||||
349, 7, 117, 2, 2, 349, 351, 7, 103, 2, 2, 350, 332, 3, 2, 2, 2, 350, 336,
|
||||
3, 2, 2, 2, 350, 340, 3, 2, 2, 2, 350, 345, 3, 2, 2, 2, 351, 94, 3, 2,
|
||||
2, 2, 352, 353, 7, 75, 2, 2, 353, 354, 7, 80, 2, 2, 354, 355, 7, 86, 2,
|
||||
2, 355, 356, 7, 81, 2, 2, 356, 96, 3, 2, 2, 2, 357, 358, 7, 77, 2, 2, 358,
|
||||
359, 7, 71, 2, 2, 359, 360, 7, 71, 2, 2, 360, 361, 7, 82, 2, 2, 361, 98,
|
||||
3, 2, 2, 2, 362, 363, 7, 89, 2, 2, 363, 364, 7, 75, 2, 2, 364, 365, 7,
|
||||
86, 2, 2, 365, 366, 7, 74, 2, 2, 366, 100, 3, 2, 2, 2, 367, 368, 7, 69,
|
||||
2, 2, 368, 369, 7, 81, 2, 2, 369, 370, 7, 87, 2, 2, 370, 371, 7, 80, 2,
|
||||
2, 371, 372, 7, 86, 2, 2, 372, 102, 3, 2, 2, 2, 373, 374, 7, 67, 2, 2,
|
||||
374, 375, 7, 78, 2, 2, 375, 376, 7, 78, 2, 2, 376, 104, 3, 2, 2, 2, 377,
|
||||
378, 7, 67, 2, 2, 378, 379, 7, 80, 2, 2, 379, 380, 7, 91, 2, 2, 380, 106,
|
||||
3, 2, 2, 2, 381, 382, 7, 67, 2, 2, 382, 383, 7, 73, 2, 2, 383, 384, 7,
|
||||
73, 2, 2, 384, 385, 7, 84, 2, 2, 385, 386, 7, 71, 2, 2, 386, 387, 7, 73,
|
||||
2, 2, 387, 388, 7, 67, 2, 2, 388, 389, 7, 86, 2, 2, 389, 390, 7, 71, 2,
|
||||
2, 390, 108, 3, 2, 2, 2, 391, 392, 7, 78, 2, 2, 392, 393, 7, 75, 2, 2,
|
||||
393, 394, 7, 77, 2, 2, 394, 395, 7, 71, 2, 2, 395, 110, 3, 2, 2, 2, 396,
|
||||
397, 7, 80, 2, 2, 397, 398, 7, 81, 2, 2, 398, 401, 7, 86, 2, 2, 399, 401,
|
||||
7, 35, 2, 2, 400, 396, 3, 2, 2, 2, 400, 399, 3, 2, 2, 2, 401, 112, 3, 2,
|
||||
2, 2, 402, 403, 7, 75, 2, 2, 403, 404, 7, 80, 2, 2, 404, 114, 3, 2, 2,
|
||||
2, 405, 406, 7, 66, 2, 2, 406, 116, 3, 2, 2, 2, 407, 409, 5, 133, 67, 2,
|
||||
408, 407, 3, 2, 2, 2, 409, 410, 3, 2, 2, 2, 410, 408, 3, 2, 2, 2, 410,
|
||||
411, 3, 2, 2, 2, 411, 421, 3, 2, 2, 2, 412, 416, 5, 135, 68, 2, 413, 415,
|
||||
5, 117, 59, 2, 414, 413, 3, 2, 2, 2, 415, 418, 3, 2, 2, 2, 416, 414, 3,
|
||||
2, 2, 2, 416, 417, 3, 2, 2, 2, 417, 420, 3, 2, 2, 2, 418, 416, 3, 2, 2,
|
||||
2, 419, 412, 3, 2, 2, 2, 420, 423, 3, 2, 2, 2, 421, 419, 3, 2, 2, 2, 421,
|
||||
422, 3, 2, 2, 2, 422, 433, 3, 2, 2, 2, 423, 421, 3, 2, 2, 2, 424, 428,
|
||||
5, 137, 69, 2, 425, 427, 5, 117, 59, 2, 426, 425, 3, 2, 2, 2, 427, 430,
|
||||
3, 2, 2, 2, 428, 426, 3, 2, 2, 2, 428, 429, 3, 2, 2, 2, 429, 432, 3, 2,
|
||||
2, 2, 430, 428, 3, 2, 2, 2, 431, 424, 3, 2, 2, 2, 432, 435, 3, 2, 2, 2,
|
||||
433, 431, 3, 2, 2, 2, 433, 434, 3, 2, 2, 2, 434, 118, 3, 2, 2, 2, 435,
|
||||
433, 3, 2, 2, 2, 436, 441, 5, 141, 71, 2, 437, 441, 5, 139, 70, 2, 438,
|
||||
441, 5, 143, 72, 2, 439, 441, 5, 145, 73, 2, 440, 436, 3, 2, 2, 2, 440,
|
||||
437, 3, 2, 2, 2, 440, 438, 3, 2, 2, 2, 440, 439, 3, 2, 2, 2, 441, 120,
|
||||
3, 2, 2, 2, 442, 444, 9, 4, 2, 2, 443, 442, 3, 2, 2, 2, 444, 445, 3, 2,
|
||||
2, 2, 445, 443, 3, 2, 2, 2, 445, 446, 3, 2, 2, 2, 446, 122, 3, 2, 2, 2,
|
||||
447, 448, 5, 129, 65, 2, 448, 450, 5, 15, 8, 2, 449, 451, 9, 4, 2, 2, 450,
|
||||
449, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 450, 3, 2, 2, 2, 452, 453,
|
||||
3, 2, 2, 2, 453, 455, 3, 2, 2, 2, 454, 456, 5, 131, 66, 2, 455, 454, 3,
|
||||
2, 2, 2, 455, 456, 3, 2, 2, 2, 456, 462, 3, 2, 2, 2, 457, 459, 5, 129,
|
||||
65, 2, 458, 460, 5, 131, 66, 2, 459, 458, 3, 2, 2, 2, 459, 460, 3, 2, 2,
|
||||
2, 460, 462, 3, 2, 2, 2, 461, 447, 3, 2, 2, 2, 461, 457, 3, 2, 2, 2, 462,
|
||||
124, 3, 2, 2, 2, 463, 464, 5, 117, 59, 2, 464, 465, 5, 147, 74, 2, 465,
|
||||
126, 3, 2, 2, 2, 466, 467, 9, 5, 2, 2, 467, 128, 3, 2, 2, 2, 468, 477,
|
||||
7, 50, 2, 2, 469, 473, 9, 6, 2, 2, 470, 472, 9, 4, 2, 2, 471, 470, 3, 2,
|
||||
2, 2, 472, 475, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 473, 474, 3, 2, 2, 2,
|
||||
474, 477, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 476, 468, 3, 2, 2, 2, 476,
|
||||
469, 3, 2, 2, 2, 477, 130, 3, 2, 2, 2, 478, 480, 9, 7, 2, 2, 479, 481,
|
||||
9, 8, 2, 2, 480, 479, 3, 2, 2, 2, 480, 481, 3, 2, 2, 2, 481, 483, 3, 2,
|
||||
2, 2, 482, 484, 9, 4, 2, 2, 483, 482, 3, 2, 2, 2, 484, 485, 3, 2, 2, 2,
|
||||
485, 483, 3, 2, 2, 2, 485, 486, 3, 2, 2, 2, 486, 132, 3, 2, 2, 2, 487,
|
||||
488, 9, 9, 2, 2, 488, 134, 3, 2, 2, 2, 489, 490, 7, 97, 2, 2, 490, 136,
|
||||
3, 2, 2, 2, 491, 492, 4, 50, 59, 2, 492, 138, 3, 2, 2, 2, 493, 501, 7,
|
||||
36, 2, 2, 494, 495, 7, 94, 2, 2, 495, 500, 11, 2, 2, 2, 496, 497, 7, 36,
|
||||
2, 2, 497, 500, 7, 36, 2, 2, 498, 500, 10, 10, 2, 2, 499, 494, 3, 2, 2,
|
||||
2, 499, 496, 3, 2, 2, 2, 499, 498, 3, 2, 2, 2, 500, 503, 3, 2, 2, 2, 501,
|
||||
499, 3, 2, 2, 2, 501, 502, 3, 2, 2, 2, 502, 504, 3, 2, 2, 2, 503, 501,
|
||||
3, 2, 2, 2, 504, 505, 7, 36, 2, 2, 505, 140, 3, 2, 2, 2, 506, 514, 7, 41,
|
||||
2, 2, 507, 508, 7, 94, 2, 2, 508, 513, 11, 2, 2, 2, 509, 510, 7, 41, 2,
|
||||
2, 510, 513, 7, 41, 2, 2, 511, 513, 10, 11, 2, 2, 512, 507, 3, 2, 2, 2,
|
||||
512, 509, 3, 2, 2, 2, 512, 511, 3, 2, 2, 2, 513, 516, 3, 2, 2, 2, 514,
|
||||
512, 3, 2, 2, 2, 514, 515, 3, 2, 2, 2, 515, 517, 3, 2, 2, 2, 516, 514,
|
||||
3, 2, 2, 2, 517, 518, 7, 41, 2, 2, 518, 142, 3, 2, 2, 2, 519, 525, 7, 98,
|
||||
2, 2, 520, 521, 7, 94, 2, 2, 521, 524, 7, 98, 2, 2, 522, 524, 10, 12, 2,
|
||||
2, 523, 520, 3, 2, 2, 2, 523, 522, 3, 2, 2, 2, 524, 527, 3, 2, 2, 2, 525,
|
||||
523, 3, 2, 2, 2, 525, 526, 3, 2, 2, 2, 526, 528, 3, 2, 2, 2, 527, 525,
|
||||
3, 2, 2, 2, 528, 529, 7, 98, 2, 2, 529, 144, 3, 2, 2, 2, 530, 536, 7, 182,
|
||||
2, 2, 531, 532, 7, 94, 2, 2, 532, 535, 7, 182, 2, 2, 533, 535, 10, 13,
|
||||
2, 2, 534, 531, 3, 2, 2, 2, 534, 533, 3, 2, 2, 2, 535, 538, 3, 2, 2, 2,
|
||||
536, 534, 3, 2, 2, 2, 536, 537, 3, 2, 2, 2, 537, 539, 3, 2, 2, 2, 538,
|
||||
536, 3, 2, 2, 2, 539, 540, 7, 182, 2, 2, 540, 146, 3, 2, 2, 2, 541, 542,
|
||||
7, 60, 2, 2, 542, 543, 7, 60, 2, 2, 543, 148, 3, 2, 2, 2, 34, 2, 155, 169,
|
||||
177, 242, 248, 320, 350, 400, 410, 416, 421, 428, 433, 440, 445, 452, 455,
|
||||
459, 461, 473, 476, 480, 485, 499, 501, 512, 514, 523, 525, 534, 536, 3,
|
||||
2, 3, 2,
|
||||
}
|
||||
|
||||
var lexerDeserializer = antlr.NewATNDeserializer(nil)
|
||||
@@ -281,8 +289,7 @@ var lexerSymbolicNames = []string{
|
||||
"For", "Return", "Distinct", "Filter", "Sort", "Limit", "Let", "Collect",
|
||||
"SortDirection", "None", "Null", "BooleanLiteral", "Into", "Keep", "With",
|
||||
"Count", "All", "Any", "Aggregate", "Like", "Not", "In", "Param", "Identifier",
|
||||
"StringLiteral", "TemplateStringLiteral", "IntegerLiteral", "FloatLiteral",
|
||||
"NamespaceSegment",
|
||||
"StringLiteral", "IntegerLiteral", "FloatLiteral", "NamespaceSegment",
|
||||
}
|
||||
|
||||
var lexerRuleNames = []string{
|
||||
@@ -294,9 +301,9 @@ var lexerRuleNames = []string{
|
||||
"For", "Return", "Distinct", "Filter", "Sort", "Limit", "Let", "Collect",
|
||||
"SortDirection", "None", "Null", "BooleanLiteral", "Into", "Keep", "With",
|
||||
"Count", "All", "Any", "Aggregate", "Like", "Not", "In", "Param", "Identifier",
|
||||
"StringLiteral", "TemplateStringLiteral", "IntegerLiteral", "FloatLiteral",
|
||||
"NamespaceSegment", "HexDigit", "DecimalIntegerLiteral", "ExponentPart",
|
||||
"Letter", "Symbols", "Digit", "DQSring", "SQString", "NamespaceSeparator",
|
||||
"StringLiteral", "IntegerLiteral", "FloatLiteral", "NamespaceSegment",
|
||||
"HexDigit", "DecimalIntegerLiteral", "ExponentPart", "Letter", "Symbols",
|
||||
"Digit", "DQSring", "SQString", "BacktickString", "TickString", "NamespaceSeparator",
|
||||
}
|
||||
|
||||
type FqlLexer struct {
|
||||
@@ -334,67 +341,66 @@ func NewFqlLexer(input antlr.CharStream) *FqlLexer {
|
||||
|
||||
// FqlLexer tokens.
|
||||
const (
|
||||
FqlLexerMultiLineComment = 1
|
||||
FqlLexerSingleLineComment = 2
|
||||
FqlLexerWhiteSpaces = 3
|
||||
FqlLexerLineTerminator = 4
|
||||
FqlLexerColon = 5
|
||||
FqlLexerSemiColon = 6
|
||||
FqlLexerDot = 7
|
||||
FqlLexerComma = 8
|
||||
FqlLexerOpenBracket = 9
|
||||
FqlLexerCloseBracket = 10
|
||||
FqlLexerOpenParen = 11
|
||||
FqlLexerCloseParen = 12
|
||||
FqlLexerOpenBrace = 13
|
||||
FqlLexerCloseBrace = 14
|
||||
FqlLexerGt = 15
|
||||
FqlLexerLt = 16
|
||||
FqlLexerEq = 17
|
||||
FqlLexerGte = 18
|
||||
FqlLexerLte = 19
|
||||
FqlLexerNeq = 20
|
||||
FqlLexerMulti = 21
|
||||
FqlLexerDiv = 22
|
||||
FqlLexerMod = 23
|
||||
FqlLexerPlus = 24
|
||||
FqlLexerMinus = 25
|
||||
FqlLexerMinusMinus = 26
|
||||
FqlLexerPlusPlus = 27
|
||||
FqlLexerAnd = 28
|
||||
FqlLexerOr = 29
|
||||
FqlLexerRange = 30
|
||||
FqlLexerAssign = 31
|
||||
FqlLexerQuestionMark = 32
|
||||
FqlLexerRegexNotMatch = 33
|
||||
FqlLexerRegexMatch = 34
|
||||
FqlLexerFor = 35
|
||||
FqlLexerReturn = 36
|
||||
FqlLexerDistinct = 37
|
||||
FqlLexerFilter = 38
|
||||
FqlLexerSort = 39
|
||||
FqlLexerLimit = 40
|
||||
FqlLexerLet = 41
|
||||
FqlLexerCollect = 42
|
||||
FqlLexerSortDirection = 43
|
||||
FqlLexerNone = 44
|
||||
FqlLexerNull = 45
|
||||
FqlLexerBooleanLiteral = 46
|
||||
FqlLexerInto = 47
|
||||
FqlLexerKeep = 48
|
||||
FqlLexerWith = 49
|
||||
FqlLexerCount = 50
|
||||
FqlLexerAll = 51
|
||||
FqlLexerAny = 52
|
||||
FqlLexerAggregate = 53
|
||||
FqlLexerLike = 54
|
||||
FqlLexerNot = 55
|
||||
FqlLexerIn = 56
|
||||
FqlLexerParam = 57
|
||||
FqlLexerIdentifier = 58
|
||||
FqlLexerStringLiteral = 59
|
||||
FqlLexerTemplateStringLiteral = 60
|
||||
FqlLexerIntegerLiteral = 61
|
||||
FqlLexerFloatLiteral = 62
|
||||
FqlLexerNamespaceSegment = 63
|
||||
FqlLexerMultiLineComment = 1
|
||||
FqlLexerSingleLineComment = 2
|
||||
FqlLexerWhiteSpaces = 3
|
||||
FqlLexerLineTerminator = 4
|
||||
FqlLexerColon = 5
|
||||
FqlLexerSemiColon = 6
|
||||
FqlLexerDot = 7
|
||||
FqlLexerComma = 8
|
||||
FqlLexerOpenBracket = 9
|
||||
FqlLexerCloseBracket = 10
|
||||
FqlLexerOpenParen = 11
|
||||
FqlLexerCloseParen = 12
|
||||
FqlLexerOpenBrace = 13
|
||||
FqlLexerCloseBrace = 14
|
||||
FqlLexerGt = 15
|
||||
FqlLexerLt = 16
|
||||
FqlLexerEq = 17
|
||||
FqlLexerGte = 18
|
||||
FqlLexerLte = 19
|
||||
FqlLexerNeq = 20
|
||||
FqlLexerMulti = 21
|
||||
FqlLexerDiv = 22
|
||||
FqlLexerMod = 23
|
||||
FqlLexerPlus = 24
|
||||
FqlLexerMinus = 25
|
||||
FqlLexerMinusMinus = 26
|
||||
FqlLexerPlusPlus = 27
|
||||
FqlLexerAnd = 28
|
||||
FqlLexerOr = 29
|
||||
FqlLexerRange = 30
|
||||
FqlLexerAssign = 31
|
||||
FqlLexerQuestionMark = 32
|
||||
FqlLexerRegexNotMatch = 33
|
||||
FqlLexerRegexMatch = 34
|
||||
FqlLexerFor = 35
|
||||
FqlLexerReturn = 36
|
||||
FqlLexerDistinct = 37
|
||||
FqlLexerFilter = 38
|
||||
FqlLexerSort = 39
|
||||
FqlLexerLimit = 40
|
||||
FqlLexerLet = 41
|
||||
FqlLexerCollect = 42
|
||||
FqlLexerSortDirection = 43
|
||||
FqlLexerNone = 44
|
||||
FqlLexerNull = 45
|
||||
FqlLexerBooleanLiteral = 46
|
||||
FqlLexerInto = 47
|
||||
FqlLexerKeep = 48
|
||||
FqlLexerWith = 49
|
||||
FqlLexerCount = 50
|
||||
FqlLexerAll = 51
|
||||
FqlLexerAny = 52
|
||||
FqlLexerAggregate = 53
|
||||
FqlLexerLike = 54
|
||||
FqlLexerNot = 55
|
||||
FqlLexerIn = 56
|
||||
FqlLexerParam = 57
|
||||
FqlLexerIdentifier = 58
|
||||
FqlLexerStringLiteral = 59
|
||||
FqlLexerIntegerLiteral = 60
|
||||
FqlLexerFloatLiteral = 61
|
||||
FqlLexerNamespaceSegment = 62
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -250,12 +250,6 @@ func (s *BaseFqlParserListener) EnterPropertyAssignment(ctx *PropertyAssignmentC
|
||||
// ExitPropertyAssignment is called when production propertyAssignment is exited.
|
||||
func (s *BaseFqlParserListener) ExitPropertyAssignment(ctx *PropertyAssignmentContext) {}
|
||||
|
||||
// EnterMemberExpression is called when production memberExpression is entered.
|
||||
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
|
||||
|
||||
// ExitMemberExpression is called when production memberExpression is exited.
|
||||
func (s *BaseFqlParserListener) ExitMemberExpression(ctx *MemberExpressionContext) {}
|
||||
|
||||
// EnterShorthandPropertyName is called when production shorthandPropertyName is entered.
|
||||
func (s *BaseFqlParserListener) EnterShorthandPropertyName(ctx *ShorthandPropertyNameContext) {}
|
||||
|
||||
@@ -292,6 +286,24 @@ func (s *BaseFqlParserListener) EnterFunctionCallExpression(ctx *FunctionCallExp
|
||||
// ExitFunctionCallExpression is called when production functionCallExpression is exited.
|
||||
func (s *BaseFqlParserListener) ExitFunctionCallExpression(ctx *FunctionCallExpressionContext) {}
|
||||
|
||||
// EnterMember is called when production member is entered.
|
||||
func (s *BaseFqlParserListener) EnterMember(ctx *MemberContext) {}
|
||||
|
||||
// ExitMember is called when production member is exited.
|
||||
func (s *BaseFqlParserListener) ExitMember(ctx *MemberContext) {}
|
||||
|
||||
// EnterMemberPath is called when production memberPath is entered.
|
||||
func (s *BaseFqlParserListener) EnterMemberPath(ctx *MemberPathContext) {}
|
||||
|
||||
// ExitMemberPath is called when production memberPath is exited.
|
||||
func (s *BaseFqlParserListener) ExitMemberPath(ctx *MemberPathContext) {}
|
||||
|
||||
// EnterMemberExpression is called when production memberExpression is entered.
|
||||
func (s *BaseFqlParserListener) EnterMemberExpression(ctx *MemberExpressionContext) {}
|
||||
|
||||
// ExitMemberExpression is called when production memberExpression is exited.
|
||||
func (s *BaseFqlParserListener) ExitMemberExpression(ctx *MemberExpressionContext) {}
|
||||
|
||||
// EnterArguments is called when production arguments is entered.
|
||||
func (s *BaseFqlParserListener) EnterArguments(ctx *ArgumentsContext) {}
|
||||
|
||||
|
||||
@@ -159,10 +159,6 @@ func (v *BaseFqlParserVisitor) VisitPropertyAssignment(ctx *PropertyAssignmentCo
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
@@ -187,6 +183,18 @@ func (v *BaseFqlParserVisitor) VisitFunctionCallExpression(ctx *FunctionCallExpr
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMember(ctx *MemberContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMemberPath(ctx *MemberPathContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitMemberExpression(ctx *MemberExpressionContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFqlParserVisitor) VisitArguments(ctx *ArgumentsContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
@@ -121,9 +121,6 @@ type FqlParserListener interface {
|
||||
// EnterPropertyAssignment is called when entering the propertyAssignment production.
|
||||
EnterPropertyAssignment(c *PropertyAssignmentContext)
|
||||
|
||||
// EnterMemberExpression is called when entering the memberExpression production.
|
||||
EnterMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// EnterShorthandPropertyName is called when entering the shorthandPropertyName production.
|
||||
EnterShorthandPropertyName(c *ShorthandPropertyNameContext)
|
||||
|
||||
@@ -142,6 +139,15 @@ type FqlParserListener interface {
|
||||
// EnterFunctionCallExpression is called when entering the functionCallExpression production.
|
||||
EnterFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||
|
||||
// EnterMember is called when entering the member production.
|
||||
EnterMember(c *MemberContext)
|
||||
|
||||
// EnterMemberPath is called when entering the memberPath production.
|
||||
EnterMemberPath(c *MemberPathContext)
|
||||
|
||||
// EnterMemberExpression is called when entering the memberExpression production.
|
||||
EnterMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// EnterArguments is called when entering the arguments production.
|
||||
EnterArguments(c *ArgumentsContext)
|
||||
|
||||
@@ -292,9 +298,6 @@ type FqlParserListener interface {
|
||||
// ExitPropertyAssignment is called when exiting the propertyAssignment production.
|
||||
ExitPropertyAssignment(c *PropertyAssignmentContext)
|
||||
|
||||
// ExitMemberExpression is called when exiting the memberExpression production.
|
||||
ExitMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// ExitShorthandPropertyName is called when exiting the shorthandPropertyName production.
|
||||
ExitShorthandPropertyName(c *ShorthandPropertyNameContext)
|
||||
|
||||
@@ -313,6 +316,15 @@ type FqlParserListener interface {
|
||||
// ExitFunctionCallExpression is called when exiting the functionCallExpression production.
|
||||
ExitFunctionCallExpression(c *FunctionCallExpressionContext)
|
||||
|
||||
// ExitMember is called when exiting the member production.
|
||||
ExitMember(c *MemberContext)
|
||||
|
||||
// ExitMemberPath is called when exiting the memberPath production.
|
||||
ExitMemberPath(c *MemberPathContext)
|
||||
|
||||
// ExitMemberExpression is called when exiting the memberExpression production.
|
||||
ExitMemberExpression(c *MemberExpressionContext)
|
||||
|
||||
// ExitArguments is called when exiting the arguments production.
|
||||
ExitArguments(c *ArgumentsContext)
|
||||
|
||||
|
||||
@@ -121,9 +121,6 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#propertyAssignment.
|
||||
VisitPropertyAssignment(ctx *PropertyAssignmentContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#memberExpression.
|
||||
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#shorthandPropertyName.
|
||||
VisitShorthandPropertyName(ctx *ShorthandPropertyNameContext) interface{}
|
||||
|
||||
@@ -142,6 +139,15 @@ type FqlParserVisitor interface {
|
||||
// Visit a parse tree produced by FqlParser#functionCallExpression.
|
||||
VisitFunctionCallExpression(ctx *FunctionCallExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#member.
|
||||
VisitMember(ctx *MemberContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#memberPath.
|
||||
VisitMemberPath(ctx *MemberPathContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#memberExpression.
|
||||
VisitMemberExpression(ctx *MemberExpressionContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FqlParser#arguments.
|
||||
VisitArguments(ctx *ArgumentsContext) interface{}
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@ func NumberBoundaries(input float64) (max float64, min float64) {
|
||||
return
|
||||
}
|
||||
|
||||
func NumberUpperBoundary(input float64) float64 {
|
||||
return input * 2
|
||||
}
|
||||
|
||||
func NumberLowerBoundary(input float64) float64 {
|
||||
return input / 2
|
||||
}
|
||||
|
||||
func Random(max float64, min float64) float64 {
|
||||
r := rand.Float64()
|
||||
i := r * (max - min + 1)
|
||||
|
||||
@@ -8,25 +8,25 @@ import (
|
||||
)
|
||||
|
||||
type MemberExpression struct {
|
||||
src core.SourceMap
|
||||
variableName string
|
||||
path []core.Expression
|
||||
src core.SourceMap
|
||||
source core.Expression
|
||||
path []core.Expression
|
||||
}
|
||||
|
||||
func NewMemberExpression(src core.SourceMap, variableName string, path []core.Expression) (*MemberExpression, error) {
|
||||
if variableName == "" {
|
||||
return nil, core.Error(core.ErrMissedArgument, "variable name")
|
||||
func NewMemberExpression(src core.SourceMap, source core.Expression, path []core.Expression) (*MemberExpression, error) {
|
||||
if source == nil {
|
||||
return nil, core.Error(core.ErrMissedArgument, "source")
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
return nil, core.Error(core.ErrMissedArgument, "path expressions")
|
||||
}
|
||||
|
||||
return &MemberExpression{src, variableName, path}, nil
|
||||
return &MemberExpression{src, source, path}, nil
|
||||
}
|
||||
|
||||
func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
|
||||
val, err := scope.GetVariable(e.variableName)
|
||||
val, err := e.source.Exec(ctx, scope)
|
||||
|
||||
if err != nil {
|
||||
return values.None, core.SourceError(
|
||||
@@ -35,22 +35,24 @@ func (e *MemberExpression) Exec(ctx context.Context, scope *core.Scope) (core.Va
|
||||
)
|
||||
}
|
||||
|
||||
strPath := make([]core.Value, len(e.path))
|
||||
out := val
|
||||
path := make([]core.Value, 1)
|
||||
|
||||
for idx, exp := range e.path {
|
||||
for _, exp := range e.path {
|
||||
segment, err := exp.Exec(ctx, scope)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
strPath[idx] = segment
|
||||
}
|
||||
path[0] = segment
|
||||
c, err := values.GetIn(ctx, out, path)
|
||||
|
||||
out, err := values.GetIn(ctx, val, strPath)
|
||||
if err != nil {
|
||||
return values.None, core.SourceError(e.src, err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return values.None, core.SourceError(e.src, err)
|
||||
out = c
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
||||
@@ -329,7 +329,7 @@ func ToInt(input core.Value) Int {
|
||||
}
|
||||
}
|
||||
|
||||
func ToArray(ctx context.Context, input core.Value) core.Value {
|
||||
func ToArray(ctx context.Context, input core.Value) *Array {
|
||||
switch value := input.(type) {
|
||||
case Boolean,
|
||||
Int,
|
||||
@@ -339,7 +339,7 @@ func ToArray(ctx context.Context, input core.Value) core.Value {
|
||||
|
||||
return NewArrayWith(value)
|
||||
case *Array:
|
||||
return value.Copy()
|
||||
return value.Copy().(*Array)
|
||||
case *Object:
|
||||
arr := NewArray(int(value.Length()))
|
||||
|
||||
@@ -354,7 +354,7 @@ func ToArray(ctx context.Context, input core.Value) core.Value {
|
||||
iterator, err := value.Iterate(ctx)
|
||||
|
||||
if err != nil {
|
||||
return None
|
||||
return NewArray(0)
|
||||
}
|
||||
|
||||
arr := NewArray(10)
|
||||
@@ -363,7 +363,7 @@ func ToArray(ctx context.Context, input core.Value) core.Value {
|
||||
val, _, err := iterator.Next(ctx)
|
||||
|
||||
if err != nil {
|
||||
return None
|
||||
return NewArray(0)
|
||||
}
|
||||
|
||||
if val == None {
|
||||
|
||||
@@ -390,9 +390,7 @@ func TestHelpers(t *testing.T) {
|
||||
}
|
||||
|
||||
input := values.NewArrayWith(vals...)
|
||||
output := values.ToArray(context.Background(), input)
|
||||
|
||||
arr := output.(*values.Array)
|
||||
arr := values.ToArray(context.Background(), input)
|
||||
|
||||
So(input == arr, ShouldBeFalse)
|
||||
So(arr.Length() == input.Length(), ShouldBeTrue)
|
||||
@@ -414,9 +412,7 @@ func TestHelpers(t *testing.T) {
|
||||
values.NewObjectProperty("qaz", values.NewObject()),
|
||||
)
|
||||
|
||||
output := values.ToArray(context.Background(), input)
|
||||
|
||||
arr := output.(*values.Array).Sort()
|
||||
arr := values.ToArray(context.Background(), input).Sort()
|
||||
|
||||
So(arr.String(), ShouldEqual, "[1,\"bar\",{}]")
|
||||
So(arr.Get(values.NewInt(2)) == input.MustGet("qaz"), ShouldBeTrue)
|
||||
|
||||
@@ -40,3 +40,7 @@ func (t *none) Hash() uint64 {
|
||||
func (t *none) Copy() core.Value {
|
||||
return None
|
||||
}
|
||||
|
||||
func (t *none) Clone() core.Cloneable {
|
||||
return None
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// AttributeGet gets single or more attribute(s) of a given element.
|
||||
// ATTR_GET gets single or more attribute(s) of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param names (...String) - Attribute name(s).
|
||||
// @returns Object - Key-value pairs of attribute values.
|
||||
@@ -27,7 +27,11 @@ func AttributeGet(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
names := args[1:]
|
||||
result := values.NewObject()
|
||||
attrs := el.GetAttributes(ctx)
|
||||
attrs, err := el.GetAttributes(ctx)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
for _, n := range names {
|
||||
name := values.NewString(n.String())
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// AttributeRemove removes single or more attribute(s) of a given element.
|
||||
// ATTR_REMOVE removes single or more attribute(s) of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param names (...String) - Attribute name(s).
|
||||
func AttributeRemove(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// AttributeSet sets or updates a single or more attribute(s) of a given element.
|
||||
// ATTR_SET sets or updates a single or more attribute(s) of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param nameOrObj (String | Object) - Attribute name or an object representing a key-value pair of attributes.
|
||||
// @param value (String) - If a second parameter is a string value, this parameter represent an attribute value.
|
||||
|
||||
32
pkg/stdlib/html/blur.go
Normal file
32
pkg/stdlib/html/blur.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
)
|
||||
|
||||
// BLUR Calls blur on the element.
|
||||
// @param target (HTMLPage | HTMLDocument | HTMLElement) - Target node.
|
||||
// @param selector (String, optional) - Optional CSS selector.
|
||||
func Blur(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 2)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Blur(ctx)
|
||||
}
|
||||
|
||||
return values.None, el.BlurBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
33
pkg/stdlib/html/clear.go
Normal file
33
pkg/stdlib/html/clear.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// INPUT_CLEAR clears a value from an underlying input element.
|
||||
// @param source (HTMLPage | HTMLDocument | HTMLElement) - Event target.
|
||||
// @param selector (String, options) - Selector.
|
||||
func InputClear(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 2)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
// CLEAR(el)
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Clear(ctx)
|
||||
}
|
||||
|
||||
return values.None, el.ClearBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
@@ -6,13 +6,15 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Click dispatches click event on a given element
|
||||
// CLICK dispatches click event on a given element
|
||||
// @param source (Open | GetElement) - Event source.
|
||||
// @param selector (String, optional) - Optional selector.
|
||||
// @param selectorOrCount (String | Int, optional) - Optional selector or count of clicks.
|
||||
// @param count (Int, optional) - Optional count of clicks.
|
||||
func Click(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 2)
|
||||
err := core.ValidateArgs(args, 1, 3)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
@@ -26,7 +28,44 @@ func Click(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
// CLICK(elOrDoc)
|
||||
if len(args) == 1 {
|
||||
return values.True, el.Click(ctx)
|
||||
return values.True, el.Click(ctx, 1)
|
||||
}
|
||||
|
||||
if len(args) == 2 {
|
||||
err := core.ValidateType(args[1], types.String, types.Int)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
if args[1].Type() == types.String {
|
||||
selector := values.ToString(args[1])
|
||||
exists, err := el.ExistsBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
return exists, el.ClickBySelector(ctx, selector, 1)
|
||||
}
|
||||
|
||||
return values.True, el.Click(ctx, values.ToInt(args[1]))
|
||||
}
|
||||
|
||||
err = core.ValidateType(args[1], types.String)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
err = core.ValidateType(args[2], types.Int)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
// CLICK(doc, selector)
|
||||
@@ -41,5 +80,7 @@ func Click(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
return exists, el.ClickBySelector(ctx, selector)
|
||||
count := values.ToInt(args[2])
|
||||
|
||||
return exists, el.ClickBySelector(ctx, selector, count)
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// ClickAll dispatches click event on all matched element
|
||||
// CLICK_ALL dispatches click event on all matched element
|
||||
// @param source (Open) - Open.
|
||||
// @param selector (String) - Selector.
|
||||
// @param count (Int, optional) - Optional count of clicks.
|
||||
// @returns (Boolean) - Returns true if matched at least one element.
|
||||
func ClickAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 2, 2)
|
||||
err := core.ValidateArgs(args, 2, 3)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
@@ -22,7 +24,7 @@ func ClickAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
selector := values.ToString(args[1])
|
||||
@@ -37,5 +39,17 @@ func ClickAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.False, nil
|
||||
}
|
||||
|
||||
return values.True, el.ClickBySelectorAll(ctx, selector)
|
||||
count := values.NewInt(1)
|
||||
|
||||
if len(args) == 3 {
|
||||
err := core.ValidateType(args[2], types.Int)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
count = values.ToInt(args[2])
|
||||
}
|
||||
|
||||
return values.True, el.ClickBySelectorAll(ctx, selector, count)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// CookieSet gets a cookie from a given page by name.
|
||||
// COOKIE_DEL gets a cookie from a given page by name.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
// @param cookie (...HTTPCookie|String) - Cookie or cookie name to delete.
|
||||
func CookieDel(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// CookieSet gets a cookie from a given page by name.
|
||||
// COOKIE_GET gets a cookie from a given page by name.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
// @param name (String) - Cookie or cookie name to delete.
|
||||
func CookieGet(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// CookieSet sets cookies to a given page
|
||||
// COOKIE_SET sets cookies to a given page
|
||||
// @param page (HTMLPage) - Target page.
|
||||
// @param cookie... (HTTPCookie) - Target cookies.
|
||||
func CookieSet(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -18,7 +18,7 @@ type PageLoadParams struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// Open opens an HTML page by a given url.
|
||||
// DOCUMENT opens an HTML page by a given url.
|
||||
// By default, loads a page by http call - resulted page does not support any interactions.
|
||||
// @param params (Object) - Optional, An object containing the following properties :
|
||||
// driver (String) - Optional, driver name.
|
||||
@@ -26,7 +26,7 @@ type PageLoadParams struct {
|
||||
// userAgent (String) - Optional, user agent.
|
||||
// keepCookies (Boolean) - Optional, boolean value indicating whether to use cookies from previous sessions.
|
||||
// i.e. not to open a page in the Incognito mode.
|
||||
// cookies (HTTPCookie) - Optional, set of HTTP cookies.
|
||||
// cookies (HTTPCookies) - Optional, set of HTTP cookies.
|
||||
// headers (HTTPHeaders) - Optional, HTTP headers.
|
||||
// viewport (Viewport) - Optional, viewport params.
|
||||
// @returns (HTMLPage) - Returns loaded HTML page.
|
||||
@@ -76,7 +76,7 @@ func newDefaultDocLoadParams(url values.String) PageLoadParams {
|
||||
Params: drivers.Params{
|
||||
URL: url.String(),
|
||||
},
|
||||
Timeout: time.Second * 30,
|
||||
Timeout: drivers.DefaultPageLoadTimeout * time.Millisecond,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,17 +134,30 @@ func newPageLoadParams(url values.String, arg core.Value) (PageLoadParams, error
|
||||
cookies, exists := obj.Get(values.NewString("cookies"))
|
||||
|
||||
if exists {
|
||||
if err := core.ValidateType(cookies, types.Array); err != nil {
|
||||
if err := core.ValidateType(cookies, types.Array, types.Object); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
cookies, err := parseCookies(cookies.(*values.Array))
|
||||
switch c := cookies.(type) {
|
||||
case *values.Array:
|
||||
cookies, err := parseCookieArray(c)
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res.Cookies = cookies
|
||||
case *values.Object:
|
||||
cookies, err := parseCookieObject(c)
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
res.Cookies = cookies
|
||||
default:
|
||||
res.Cookies = make(drivers.HTTPCookies)
|
||||
}
|
||||
|
||||
res.Cookies = cookies
|
||||
}
|
||||
|
||||
headers, exists := obj.Get(values.NewString("headers"))
|
||||
@@ -183,11 +196,11 @@ func newPageLoadParams(url values.String, arg core.Value) (PageLoadParams, error
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseCookies(arr *values.Array) ([]drivers.HTTPCookie, error) {
|
||||
func parseCookieObject(obj *values.Object) (drivers.HTTPCookies, error) {
|
||||
var err error
|
||||
res := make([]drivers.HTTPCookie, 0, arr.Length())
|
||||
res := make(drivers.HTTPCookies)
|
||||
|
||||
arr.ForEach(func(value core.Value, idx int) bool {
|
||||
obj.ForEach(func(value core.Value, _ string) bool {
|
||||
cookie, e := parseCookie(value)
|
||||
|
||||
if e != nil {
|
||||
@@ -196,7 +209,28 @@ func parseCookies(arr *values.Array) ([]drivers.HTTPCookie, error) {
|
||||
return false
|
||||
}
|
||||
|
||||
res = append(res, cookie)
|
||||
res[cookie.Name] = cookie
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func parseCookieArray(arr *values.Array) (drivers.HTTPCookies, error) {
|
||||
var err error
|
||||
res := make(drivers.HTTPCookies)
|
||||
|
||||
arr.ForEach(func(value core.Value, _ int) bool {
|
||||
cookie, e := parseCookie(value)
|
||||
|
||||
if e != nil {
|
||||
err = e
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
res[cookie.Name] = cookie
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Download a resource from the given GetURL.
|
||||
// DOWNLOAD downloads a resource from the given GetURL.
|
||||
// @param GetURL (String) - GetURL to download.
|
||||
// @returns data (Binary) - Returns a base64 encoded string in binary format.
|
||||
func Download(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetElement finds an element by a given CSS selector.
|
||||
// ELEMENT finds an element by a given CSS selector.
|
||||
// Returns NONE if element not found.
|
||||
// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element.
|
||||
// @param selector (String) - CSS selector.
|
||||
@@ -21,7 +21,7 @@ func Element(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return el.QuerySelector(ctx, selector), nil
|
||||
return el.QuerySelector(ctx, selector)
|
||||
}
|
||||
|
||||
func queryArgs(args []core.Value) (drivers.HTMLElement, values.String, error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// ElementExists returns a boolean value indicating whether there is an element matched by selector.
|
||||
// ELEMENT_EXISTS returns a boolean value indicating whether there is an element matched by selector.
|
||||
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
|
||||
// @param selector (String) - CSS selector.
|
||||
// @returns (Boolean) - A boolean value indicating whether there is an element matched by selector.
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// Elements finds HTML elements by a given CSS selector.
|
||||
// ELEMENTS finds HTML elements by a given CSS selector.
|
||||
// Returns an empty array if element not found.
|
||||
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
|
||||
// @param selector (String) - CSS selector.
|
||||
@@ -19,5 +19,5 @@ func Elements(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return el.QuerySelectorAll(ctx, selector), nil
|
||||
return el.QuerySelectorAll(ctx, selector)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// ElementsCount returns a number of found HTML elements by a given CSS selector.
|
||||
// ELEMENTS_COUNT returns a number of found HTML elements by a given CSS selector.
|
||||
// Returns an empty array if element not found.
|
||||
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
|
||||
// @param selector (String) - CSS selector.
|
||||
@@ -19,5 +19,5 @@ func ElementsCount(ctx context.Context, args ...core.Value) (core.Value, error)
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return el.CountBySelector(ctx, selector), nil
|
||||
return el.CountBySelector(ctx, selector)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package html
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// FOCUS Calls focus on the element.
|
||||
// FOCUS Sets focus on the element.
|
||||
// @param target (HTMLPage | HTMLDocument | HTMLElement) - Target node.
|
||||
// @param selector (String, optional) - Optional CSS selector. Required when target is HTMLPage or HTMLDocument.
|
||||
// @param selector (String, optional) - Optional CSS selector.
|
||||
func Focus(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 2)
|
||||
|
||||
@@ -17,22 +18,15 @@ func Focus(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
// Document with selector
|
||||
if len(args) == 2 {
|
||||
doc, err := drivers.ToDocument(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return values.None, doc.FocusBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return values.None, el.Focus(ctx)
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Focus(ctx)
|
||||
}
|
||||
|
||||
return values.None, el.FocusBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetInnerHTML Returns inner HTML string of a given or matched by CSS selector element
|
||||
// INNER_HTML returns inner HTML string of a given or matched by CSS selector element
|
||||
// @param doc (Open|GetElement) - Parent document or element.
|
||||
// @param selector (String, optional) - String of CSS selector.
|
||||
// @returns (String) - Inner HTML string if an element found, otherwise empty string.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetInnerHTMLAll returns an array of inner HTML strings of matched elements.
|
||||
// INNER_HTML_ALL returns an array of inner HTML strings of matched elements.
|
||||
// @param doc (HTMLDocument|HTMLElement) - Parent document or element.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
// @returns (String) - An array of inner HTML strings if any element found, otherwise empty array.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetInnerText returns inner text string of a given or matched by CSS selector element
|
||||
// INNER_TEXT returns inner text string of a given or matched by CSS selector element
|
||||
// @param doc (HTMLDocument|HTMLElement) - Parent document or element.
|
||||
// @param selector (String, optional) - String of CSS selector.
|
||||
// @returns (String) - Inner text if an element found, otherwise empty string.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// GetInnerTextAll returns an array of inner text of matched elements.
|
||||
// INNER_TEXT_ALL returns an array of inner text of matched elements.
|
||||
// @param doc (HTMLDocument|HTMLElement) - Parent document or element.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
// @returns (String) - An array of inner text if any element found, otherwise empty array.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Hover fetches an element with selector, scrolls it into view if needed, and then uses page.mouse to hover over the center of the element.
|
||||
// HOVER fetches an element with selector, scrolls it into view if needed, and then uses page.mouse to hover over the center of the element.
|
||||
// If there's no element matching selector, the method returns an error.
|
||||
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
|
||||
// @param selector (String, options) - If document is passed, this param must represent an element selector.
|
||||
@@ -20,59 +20,21 @@ func Hover(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
// page or document or element
|
||||
err = core.ValidateType(args[0], drivers.HTMLPageType, drivers.HTMLDocumentType, drivers.HTMLElementType)
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
selector := values.EmptyString
|
||||
|
||||
if len(args) > 1 {
|
||||
err = core.ValidateType(args[1], types.String)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
selector = args[1].(values.String)
|
||||
}
|
||||
|
||||
switch n := args[0].(type) {
|
||||
case drivers.HTMLPage:
|
||||
if selector == values.EmptyString {
|
||||
return values.None, core.Error(core.ErrMissedArgument, "selector")
|
||||
}
|
||||
|
||||
return values.None, n.GetMainFrame().MoveMouseBySelector(ctx, selector)
|
||||
case drivers.HTMLDocument:
|
||||
if selector == values.EmptyString {
|
||||
return values.None, core.Error(core.ErrMissedArgument, "selector")
|
||||
}
|
||||
|
||||
return values.None, n.MoveMouseBySelector(ctx, selector)
|
||||
case drivers.HTMLElement:
|
||||
if selector == values.EmptyString {
|
||||
return values.None, n.Hover(ctx)
|
||||
}
|
||||
|
||||
found := n.QuerySelector(ctx, selector)
|
||||
|
||||
if found == values.None {
|
||||
return values.None, core.Errorf(core.ErrNotFound, "element by selector %s", selector)
|
||||
}
|
||||
|
||||
el, ok := found.(drivers.HTMLElement)
|
||||
|
||||
if !ok {
|
||||
return values.None, core.Errorf(core.ErrNotFound, "element by selector %s", selector)
|
||||
}
|
||||
|
||||
defer el.Close()
|
||||
|
||||
if len(args) == 1 {
|
||||
return values.None, el.Hover(ctx)
|
||||
default:
|
||||
return values.None, core.TypeError(n.Type(), drivers.HTMLDocumentType, drivers.HTMLElementType)
|
||||
}
|
||||
|
||||
err = core.ValidateType(args[1], types.String)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return values.None, el.HoverBySelector(ctx, values.ToString(args[1]))
|
||||
}
|
||||
|
||||
@@ -2,101 +2,83 @@ package html
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Input types a value to an underlying input element.
|
||||
// @param source (Open | GetElement) - Event target.
|
||||
// INPUT types a value to an underlying input element.
|
||||
// @param source (HTMLPage | HTMLDocument | HTMLElement) - Event target.
|
||||
// @param valueOrSelector (String) - Selector or a value.
|
||||
// @param value (String) - Target value.
|
||||
// @param delay (Int, optional) - Waits delay milliseconds between keystrokes
|
||||
// @param delay (Int, optional) - Target value.
|
||||
// @returns (Boolean) - Returns true if an element was found.
|
||||
func Input(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 2, 4)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
arg1 := args[0]
|
||||
err = core.ValidateType(arg1, drivers.HTMLPageType, drivers.HTMLDocumentType, drivers.HTMLElementType)
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
if arg1.Type() == drivers.HTMLPageType || arg1.Type() == drivers.HTMLDocumentType {
|
||||
doc, err := drivers.ToDocument(arg1)
|
||||
delay := values.NewInt(drivers.DefaultKeyboardDelay)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
// INPUT(el, value)
|
||||
if len(args) == 2 {
|
||||
return values.True, el.Input(ctx, args[1], delay)
|
||||
}
|
||||
|
||||
// selector
|
||||
arg2 := args[1]
|
||||
err = core.ValidateType(arg2, types.String)
|
||||
var selector values.String
|
||||
var value core.Value
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
// INPUT(el, valueOrSelector, valueOrOpts)
|
||||
if len(args) == 3 {
|
||||
switch v := args[2].(type) {
|
||||
// INPUT(el, value, delay)
|
||||
case values.Int, values.Float:
|
||||
value = args[1]
|
||||
delay = values.ToInt(v)
|
||||
|
||||
selector := values.ToString(arg2)
|
||||
delay := values.Int(0)
|
||||
|
||||
if len(args) == 4 {
|
||||
arg4 := args[3]
|
||||
|
||||
err = core.ValidateType(arg4, types.Int)
|
||||
|
||||
if err != nil {
|
||||
return values.True, el.Input(ctx, value, delay)
|
||||
default:
|
||||
// INPUT(el, selector, value)
|
||||
if err := core.ValidateType(args[1], types.String); err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
delay = values.ToInt(arg4)
|
||||
selector = values.ToString(args[1])
|
||||
value = args[2]
|
||||
}
|
||||
|
||||
exists, err := doc.ExistsBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
} else {
|
||||
// INPUT(el, selector, value, delay)
|
||||
if err := core.ValidateType(args[1], types.String); err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return values.False, nil
|
||||
}
|
||||
|
||||
return values.True, doc.InputBySelector(ctx, selector, args[2], delay)
|
||||
}
|
||||
|
||||
el, err := drivers.ToElement(arg1)
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
delay := values.Int(0)
|
||||
|
||||
if len(args) == 3 {
|
||||
arg3 := args[2]
|
||||
|
||||
err = core.ValidateType(arg3, types.Int)
|
||||
|
||||
if err != nil {
|
||||
if err := core.ValidateType(args[3], types.Int); err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
delay = arg3.(values.Int)
|
||||
selector = values.ToString(args[1])
|
||||
value = args[2]
|
||||
delay = values.ToInt(args[3])
|
||||
}
|
||||
|
||||
err = el.Input(ctx, args[1], delay)
|
||||
exists, err := el.ExistsBySelector(ctx, selector)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
return values.True, nil
|
||||
if !exists {
|
||||
return values.False, nil
|
||||
}
|
||||
|
||||
return values.True, el.InputBySelector(ctx, selector, value, delay)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,12 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
const defaultTimeout = 5000
|
||||
|
||||
func RegisterLib(ns core.Namespace) error {
|
||||
return ns.RegisterFunctions(core.Functions{
|
||||
"ATTR_GET": AttributeGet,
|
||||
"ATTR_REMOVE": AttributeRemove,
|
||||
"ATTR_SET": AttributeSet,
|
||||
"BLUR": Blur,
|
||||
"COOKIE_DEL": CookieDel,
|
||||
"COOKIE_GET": CookieGet,
|
||||
"COOKIE_SET": CookieSet,
|
||||
@@ -37,6 +36,7 @@ func RegisterLib(ns core.Namespace) error {
|
||||
"INNER_TEXT_SET": SetInnerText,
|
||||
"INNER_TEXT_ALL": GetInnerTextAll,
|
||||
"INPUT": Input,
|
||||
"INPUT_CLEAR": InputClear,
|
||||
"MOUSE": MouseMoveXY,
|
||||
"NAVIGATE": Navigate,
|
||||
"NAVIGATE_BACK": NavigateBack,
|
||||
@@ -73,6 +73,7 @@ func RegisterLib(ns core.Namespace) error {
|
||||
|
||||
func OpenOrCastPage(ctx context.Context, value core.Value) (drivers.HTMLPage, bool, error) {
|
||||
err := core.ValidateType(value, drivers.HTMLPageType, types.String)
|
||||
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// MouseMoveXY moves mouse by given coordinates.
|
||||
// MOUSE moves mouse by given coordinates.
|
||||
// @param doc (HTMLDocument) - HTML document.
|
||||
// @param x (Int|Float) - X coordinate.
|
||||
// @param y (Int|Float) - Y coordinate.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Navigate navigates a given page to a new resource.
|
||||
// NAVIGATE navigates a given page to a new resource.
|
||||
// The operation blocks the execution until the page gets loaded.
|
||||
// Which means there is no need in WAIT_NAVIGATION function.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
@@ -34,7 +34,7 @@ func Navigate(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) > 2 {
|
||||
err = core.ValidateType(args[2], types.Int)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// NavigateBack navigates a given page back within its navigation history.
|
||||
// NAVIGATE_BACK navigates a given page back within its navigation history.
|
||||
// The operation blocks the execution until the page gets loaded.
|
||||
// If the history is empty, the function returns FALSE.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
@@ -30,7 +30,7 @@ func NavigateBack(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
}
|
||||
|
||||
skip := values.NewInt(1)
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) > 1 {
|
||||
err = core.ValidateType(args[1], types.Int)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// NavigateForward navigates a given page forward within its navigation history.
|
||||
// NAVIGATE_FORWARD navigates a given page forward within its navigation history.
|
||||
// The operation blocks the execution until the page gets loaded.
|
||||
// If the history is empty, the function returns FALSE.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
@@ -30,7 +30,7 @@ func NavigateForward(ctx context.Context, args ...core.Value) (core.Value, error
|
||||
}
|
||||
|
||||
skip := values.NewInt(1)
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) > 1 {
|
||||
err = core.ValidateType(args[1], types.Int)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Pagination creates an iterator that goes through pages using CSS selector.
|
||||
// PAGINATION creates an iterator that goes through pages using CSS selector.
|
||||
// The iterator starts from the current page i.e. it does not change the page on 1st iteration.
|
||||
// That allows you to keep scraping logic inside FOR loop.
|
||||
// @param doc (Open) - Target document.
|
||||
@@ -102,7 +102,7 @@ func (i *PagingIterator) Next(ctx context.Context) (core.Value, core.Value, erro
|
||||
return values.None, values.None, core.ErrNoMoreData
|
||||
}
|
||||
|
||||
err = i.document.ClickBySelector(ctx, i.selector)
|
||||
err = i.document.GetElement().ClickBySelector(ctx, i.selector, 1)
|
||||
|
||||
if err != nil {
|
||||
return values.None, values.None, err
|
||||
|
||||
@@ -21,7 +21,7 @@ func ValidatePageRanges(pageRanges string) (bool, error) {
|
||||
return match, nil
|
||||
}
|
||||
|
||||
// PDF print a PDF of the current page.
|
||||
// PDF prints a PDF of the current page.
|
||||
// @param target (HTMLPage|String) - Target page or url.
|
||||
// @param params (Object) - Optional, An object containing the following properties :
|
||||
// Landscape (Bool) - Paper orientation. Defaults to false.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Screenshot takes a screenshot of a given page.
|
||||
// SCREENSHOT takes a screenshot of a given page.
|
||||
// @param target (HTMLPage|String) - Target page or url.
|
||||
// @param params (Object) - Optional, An object containing the following properties :
|
||||
// x (Float|Int) - Optional, X position of the viewport.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// ScrollTop scrolls the document's window to its bottom.
|
||||
// SCROLL_BOTTOM scrolls the document's window to its bottom.
|
||||
// @param doc (HTMLDocument) - Target document.
|
||||
func ScrollBottom(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 1)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// ScrollInto scrolls an element on.
|
||||
// SCROLL_ELEMENT scrolls an element on.
|
||||
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
|
||||
// @param selector (String, options) - If document is passed, this param must represent an element selector.
|
||||
func ScrollInto(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// ScrollTop scrolls the document's window to its top.
|
||||
// SCROLL_TOP scrolls the document's window to its top.
|
||||
// @param doc (HTMLDocument) - Target document.
|
||||
func ScrollTop(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
err := core.ValidateArgs(args, 1, 1)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// ScrollXY scrolls by given coordinates.
|
||||
// SCROLL scrolls by given coordinates.
|
||||
// @param doc (HTMLDocument) - HTML document.
|
||||
// @param x (Int|Float) - X coordinate.
|
||||
// @param y (Int|Float) - Y coordinate.
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/drivers"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// Select selects a value from an underlying select element.
|
||||
// SELECT selects a value from an underlying select element.
|
||||
// @param source (Open | GetElement) - Event target.
|
||||
// @param valueOrSelector (String | Array<String>) - Selector or a an array of strings as a value.
|
||||
// @param value (Array<String) - Target value. Optional.
|
||||
@@ -21,46 +20,20 @@ func Select(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
arg1 := args[0]
|
||||
err = core.ValidateType(arg1, drivers.HTMLPageType, drivers.HTMLDocumentType, drivers.HTMLElementType)
|
||||
el, err := drivers.ToElement(args[0])
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
if arg1.Type() == drivers.HTMLPageType || arg1.Type() == drivers.HTMLDocumentType {
|
||||
doc, err := drivers.ToDocument(arg1)
|
||||
if len(args) == 2 {
|
||||
arr := values.ToArray(ctx, args[1])
|
||||
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
// selector
|
||||
arg2 := args[1]
|
||||
err = core.ValidateType(arg2, types.String)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
arg3 := args[2]
|
||||
err = core.ValidateType(arg3, types.Array)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
return doc.SelectBySelector(ctx, arg2.(values.String), arg3.(*values.Array))
|
||||
return el.Select(ctx, arr)
|
||||
}
|
||||
|
||||
el := arg1.(drivers.HTMLElement)
|
||||
arg2 := args[1]
|
||||
selector := values.ToString(args[1])
|
||||
arr := values.ToArray(ctx, args[2])
|
||||
|
||||
err = core.ValidateType(arg2, types.Array)
|
||||
|
||||
if err != nil {
|
||||
return values.False, err
|
||||
}
|
||||
|
||||
return el.Select(ctx, arg2.(*values.Array))
|
||||
return el.SelectBySelector(ctx, selector, arr)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// SetInnerHTML sets inner HTML string to a given or matched by CSS selector element
|
||||
// INNER_HTML_SET sets inner HTML string to a given or matched by CSS selector element
|
||||
// @param doc (Open|GetElement) - Parent document or element.
|
||||
// @param selector (String, optional) - String of CSS selector.
|
||||
// @param innerHTML (String) - String of inner HTML.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// SetInnerText sets inner text string to a given or matched by CSS selector element
|
||||
// INNER_TEXT_SET sets inner text string to a given or matched by CSS selector element
|
||||
// @param doc (Open|GetElement) - Parent document or element.
|
||||
// @param selector (String, optional) - String of CSS selector.
|
||||
// @param innerText (String) - String of inner text.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
)
|
||||
|
||||
// StyleGet gets single or more style attribute value(s) of a given element.
|
||||
// STYLE_GET gets single or more style attribute value(s) of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param names (...String) - Style name(s).
|
||||
// @returns Object - Key-value pairs of style values.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// StyleRemove removes single or more style attribute value(s) of a given element.
|
||||
// STYLE_REMOVE removes single or more style attribute value(s) of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param names (...String) - Style name(s).
|
||||
func StyleRemove(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// StyleSet sets or updates a single or more style attribute value of a given element.
|
||||
// STYLE_SET sets or updates a single or more style attribute value of a given element.
|
||||
// @param el (HTMLElement) - Target element.
|
||||
// @param nameOrObj (String | Object) - Style name or an object representing a key-value pair of attributes.
|
||||
// @param value (String) - If a second parameter is a string value, this parameter represent a style value.
|
||||
|
||||
@@ -9,10 +9,22 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WAIT_ATTR waits until a target attribute's value appears
|
||||
// @param node (HTMLPage | HTMLDocument | HTMLElement) - Parent document.
|
||||
// @param attrNameOrSelector (String) - String of an attr name or CSS selector.
|
||||
// @param attrValueOrAttrName (String | Any) - Attr value or name.
|
||||
// @param attrValueOrTimeout (Any | Int, optional) - Attr value or an optional timeout.
|
||||
// @param timeout (Int, optional) - Optional timeout.
|
||||
func WaitAttribute(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitAttributeWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WAIT_NO_ATTR waits until a target attribute's value disappears
|
||||
// @param node (HTMLPage | HTMLDocument | HTMLElement) - Parent document.
|
||||
// @param attrNameOrSelector (String) - String of an attr name or CSS selector.
|
||||
// @param attrValueOrAttrName (String | Any) - Attr value or name.
|
||||
// @param attrValueOrTimeout (Any | Int, optional) - Attr value or an optional timeout.
|
||||
// @param timeout (Int, optional) - Optional timeout.
|
||||
func WaitNoAttribute(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitAttributeWhen(ctx, args, drivers.WaitEventAbsence)
|
||||
}
|
||||
@@ -39,7 +51,7 @@ func waitAttributeWhen(ctx context.Context, args []core.Value, when drivers.Wait
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
// if a document is passed
|
||||
// WAIT_ATTR(doc, selector, attrName, attrValue, timeout)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WaitClassAll waits for a class to appear on all matched elements.
|
||||
// WAIT_ATTR_ALL waits for an attribute to appear on all matched elements with a given value.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param doc (HTMLDocument) - Parent document.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
@@ -19,7 +19,7 @@ func WaitAttributeAll(ctx context.Context, args ...core.Value) (core.Value, erro
|
||||
return waitAttributeAllWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WaitClassAll waits for a class to disappear on all matched elements.
|
||||
// WAIT_NO_ATTR_ALL waits for an attribute to disappear on all matched elements by a given value.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param doc (HTMLDocument) - Parent document.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
@@ -59,7 +59,7 @@ func waitAttributeAllWhen(ctx context.Context, args []core.Value, when drivers.W
|
||||
selector := args[1].(values.String)
|
||||
name := args[2].(values.String)
|
||||
value := args[3]
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) == 5 {
|
||||
err = core.ValidateType(args[4], types.Int)
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WaitClass waits for a class to appear on a given element.
|
||||
// WAIT_CLASS waits for a class to appear on a given element.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
|
||||
// @param node (HTMLPage | HTMLDocument | HTMLElement) - Target node.
|
||||
// @param selectorOrClass (String) - If document is passed, this param must represent an element selector.
|
||||
// Otherwise target class.
|
||||
// @param classOrTimeout (String|Int, optional) - If document is passed, this param must represent target class name.
|
||||
@@ -22,9 +22,9 @@ func WaitClass(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitClassWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WaitClass waits for a class to disappear on a given element.
|
||||
// WAIT_NO_CLASS waits for a class to disappear on a given element.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
|
||||
// @param node (HTMLPage | HTMLDocument | HTMLElement) - Target node.
|
||||
// @param selectorOrClass (String) - If document is passed, this param must represent an element selector.
|
||||
// Otherwise target class.
|
||||
// @param classOrTimeout (String|Int, optional) - If document is passed, this param must represent target class name.
|
||||
@@ -57,7 +57,7 @@ func waitClassWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
// if a document is passed
|
||||
if arg1.Type() == drivers.HTMLPageType || arg1.Type() == drivers.HTMLDocumentType {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WaitClassAll waits for a class to appear on all matched elements.
|
||||
// WAIT_CLASS_ALL waits for a class to appear on all matched elements.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param doc (HTMLDocument) - Parent document.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
@@ -19,7 +19,7 @@ func WaitClassAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitClassAllWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WaitClassAll waits for a class to disappear on all matched elements.
|
||||
// WAIT_NO_CLASS_ALL waits for a class to disappear on all matched elements.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param doc (HTMLDocument) - Parent document.
|
||||
// @param selector (String) - String of CSS selector.
|
||||
@@ -58,7 +58,7 @@ func waitClassAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
|
||||
selector := args[1].(values.String)
|
||||
class := args[2].(values.String)
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) == 4 {
|
||||
err = core.ValidateType(args[3], types.Int)
|
||||
|
||||
@@ -9,16 +9,16 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WaitElement waits for element to appear in the DOM.
|
||||
// WAIT_ELEMENT waits for element to appear in the DOM.
|
||||
// Stops the execution until it finds an element or operation times out.
|
||||
// @param doc (HTMLDocument) - Driver HTMLDocument.
|
||||
// @param n (HTMLDocument) - Driver HTMLDocument.
|
||||
// @param selector (String) - Target element's selector.
|
||||
// @param timeout (Int, optional) - Optional timeout. Default 5000 ms.
|
||||
func WaitElement(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitElementWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WaitNoElements waits for element to disappear in the DOM.
|
||||
// WAIT_NO_ELEMENT waits for element to disappear in the DOM.
|
||||
// Stops the execution until it does not find an element or operation times out.
|
||||
// @param doc (HTMLDocument) - Driver HTMLDocument.
|
||||
// @param selector (String) - Target element's selector.
|
||||
@@ -41,7 +41,7 @@ func waitElementWhen(ctx context.Context, args []core.Value, when drivers.WaitEv
|
||||
}
|
||||
|
||||
selector := args[1].String()
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) > 2 {
|
||||
err = core.ValidateType(args[2], types.Int)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WaitNavigation waits for a given page to navigate to a new url.
|
||||
// WAIT_NAVIGATION waits for a given page to navigate to a new url.
|
||||
// Stops the execution until the navigation ends or operation times out.
|
||||
// @param page (HTMLPage) - Target page.
|
||||
// @param timeout (Int, optional) - Optional timeout. Default 5000 ms.
|
||||
@@ -26,7 +26,7 @@ func WaitNavigation(ctx context.Context, args ...core.Value) (core.Value, error)
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) > 1 {
|
||||
err = core.ValidateType(args[1], types.Int)
|
||||
|
||||
@@ -9,10 +9,12 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WAIT_STYLE
|
||||
func WaitStyle(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitStyleWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WAIT_NO_STYLE
|
||||
func WaitNoStyle(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitStyleWhen(ctx, args, drivers.WaitEventAbsence)
|
||||
}
|
||||
@@ -39,7 +41,7 @@ func waitStyleWhen(ctx context.Context, args []core.Value, when drivers.WaitEven
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
// if a document is passed
|
||||
// WAIT_ATTR(doc, selector, attrName, attrValue, timeout)
|
||||
|
||||
@@ -9,10 +9,12 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||
)
|
||||
|
||||
// WAIT_STYLE_ALL
|
||||
func WaitStyleAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitStyleAllWhen(ctx, args, drivers.WaitEventPresence)
|
||||
}
|
||||
|
||||
// WAIT_NO_STYLE_ALL
|
||||
func WaitNoStyleAll(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||
return waitStyleAllWhen(ctx, args, drivers.WaitEventAbsence)
|
||||
}
|
||||
@@ -47,7 +49,7 @@ func waitStyleAllWhen(ctx context.Context, args []core.Value, when drivers.WaitE
|
||||
selector := args[1].(values.String)
|
||||
name := args[2].(values.String)
|
||||
value := args[3]
|
||||
timeout := values.NewInt(defaultTimeout)
|
||||
timeout := values.NewInt(drivers.DefaultWaitTimeout)
|
||||
|
||||
if len(args) == 5 {
|
||||
err = core.ValidateType(args[4], types.Int)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user