mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-14 11:23:02 +02:00
pull master
This commit is contained in:
commit
3bb0ab183b
@ -15,21 +15,20 @@ builds:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
archive:
|
||||
name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
replacements:
|
||||
darwin: darwin
|
||||
linux: linux
|
||||
windows: windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
|
||||
# Can be used to change the archive formats for specific GOOSs.
|
||||
# Most common use case is to archive as zip on Windows.
|
||||
# Default is empty.
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
archives:
|
||||
- name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
replacements:
|
||||
darwin: darwin
|
||||
linux: linux
|
||||
windows: windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
# Can be used to change the archive formats for specific GOOSs.
|
||||
# Most common use case is to archive as zip on Windows.
|
||||
# Default is empty.
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}_checksums.txt'
|
||||
|
18
.travis.yml
18
.travis.yml
@ -10,23 +10,25 @@ go:
|
||||
- "1.12.x"
|
||||
- stable
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- openjdk-9-jre-headless
|
||||
chrome: stable
|
||||
|
||||
install:
|
||||
- go get -u github.com/mgechev/revive
|
||||
- go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
- sudo curl -o /usr/local/lib/antlr-4.7.1-complete.jar https://www.antlr.org/download/antlr-4.7.1-complete.jar
|
||||
- export CLASSPATH=".:/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH"
|
||||
- sudo curl -o /usr/local/lib/antlr-4.7.2-complete.jar https://www.antlr.org/download/antlr-4.7.2-complete.jar
|
||||
- export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
|
||||
- mkdir $HOME/travis-bin
|
||||
- echo -e '#!/bin/bash\njava -jar /usr/local/lib/antlr-4.7.1-complete.jar "$@"' > $HOME/travis-bin/antlr
|
||||
- echo -e '#!/bin/bash\njava -jar /usr/local/lib/antlr-4.7.2-complete.jar "$@"' > $HOME/travis-bin/antlr
|
||||
- echo -e '#!/bin/bash\njava org.antlr.v4.gui.TestRig "$@"' > $HOME/travis-bin/grun
|
||||
- chmod +x $HOME/travis-bin/*
|
||||
- export PATH=$PATH:$HOME/travis-bin
|
||||
- export GO111MODULE=on
|
||||
- git reset --hard
|
||||
|
||||
stages:
|
||||
- lint
|
||||
@ -56,11 +58,13 @@ jobs:
|
||||
- stage: e2e
|
||||
go: stable
|
||||
before_script:
|
||||
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 --disable-setuid-sandbox --no-sandbox about:blank &
|
||||
- docker pull microbox/chromium-headless:75.0.3765.1
|
||||
- docker run -d -p 9222:9222 microbox/chromium-headless:75.0.3765.1
|
||||
- docker ps
|
||||
script:
|
||||
- make e2e
|
||||
after_script:
|
||||
- killall google-chrome-stable
|
||||
- docker stop $(docker ps -q)
|
||||
- stage: bench
|
||||
go: stable
|
||||
script:
|
||||
|
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,5 +1,25 @@
|
||||
## Changelog
|
||||
|
||||
### 0.9.0
|
||||
#### Added
|
||||
- ``INPUT_CLEAR`` function to clear input's value. [#366](https://github.com/MontFerret/ferret/pull/366)
|
||||
- Support of tick for string literals. [#367](https://github.com/MontFerret/ferret/pull/367)
|
||||
- Support of default headers and cookies. [#372](https://github.com/MontFerret/ferret/pull/372)
|
||||
- Support of use of params in dot notation. [#378](https://github.com/MontFerret/ferret/pull/378)
|
||||
- Optional count param to ``CLICK`` function. [#377](https://github.com/MontFerret/ferret/pull/377)
|
||||
- ``BLUR`` function. [#379](https://github.com/MontFerret/ferret/pull/379)
|
||||
|
||||
#### Fixed
|
||||
- Tabs don't get closed on page load error. [#359](https://github.com/MontFerret/ferret/pull/359)
|
||||
- ``CLICK`` function does not allow to use element with a selector. [#355](https://github.com/MontFerret/ferret/pull/355)
|
||||
- Unable to use member expression right after a function call. [#368](https://github.com/MontFerret/ferret/pull/368)
|
||||
|
||||
#### Changed
|
||||
- Updated zerolog. [#352](https://github.com/MontFerret/ferret/pull/352)
|
||||
- Runtime ``Object`` and ``Array`` values implement ``core.Getter`` interface. [#353](https://github.com/MontFerret/ferret/pull/353)
|
||||
- Externalized default timeout values. [#371](https://github.com/MontFerret/ferret/pull/371)
|
||||
- Refactored ``drivers.HTMLDocument`` and ``drivers.HTMLElement`` interfaces. [#376](https://github.com/MontFerret/ferret/pull/376), [#375](https://github.com/MontFerret/ferret/pull/375)
|
||||
|
||||
### 0.8.3
|
||||
#### Fixed
|
||||
- Unable to click by selector using an element.
|
||||
|
3
Makefile
3
Makefile
@ -49,8 +49,7 @@ fmt:
|
||||
# https://github.com/mgechev/revive
|
||||
# go get github.com/mgechev/revive
|
||||
lint:
|
||||
revive -config revive.toml -formatter friendly -exclude ./pkg/parser/fql/... -exclude ./vendor/... ./... && \
|
||||
golangci-lint run ./pkg/...
|
||||
revive -config revive.toml -formatter stylish -exclude ./pkg/parser/fql/... -exclude ./vendor/... ./...
|
||||
|
||||
# http://godoc.org/code.google.com/p/go.tools/cmd/vet
|
||||
# go get code.google.com/p/go.tools/cmd/vet
|
||||
|
@ -60,12 +60,108 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)),
|
||||
)
|
||||
|
||||
ctx = drivers.WithContext(
|
||||
ctx,
|
||||
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress),
|
||||
cdp.WithCustomName("cdp_headers"),
|
||||
cdp.WithHeader("Single_header", []string{"single_header_value"}),
|
||||
cdp.WithHeaders(drivers.HTTPHeaders{
|
||||
"Multi_set_header": []string{"multi_set_header_value"},
|
||||
"Multi_set_header2": []string{"multi_set_header2_value"},
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
ctx = drivers.WithContext(
|
||||
ctx,
|
||||
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress),
|
||||
cdp.WithCustomName("cdp_cookies"),
|
||||
cdp.WithCookie(drivers.HTTPCookie{
|
||||
Name: "single_cookie",
|
||||
Value: "single_cookie_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
}),
|
||||
cdp.WithCookies([]drivers.HTTPCookie{
|
||||
{
|
||||
Name: "multi_set_cookie",
|
||||
Value: "multi_set_cookie_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
},
|
||||
{
|
||||
Name: "multi_set_cookie2",
|
||||
Value: "multi_set_cookie2_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
ctx = drivers.WithContext(
|
||||
ctx,
|
||||
http.NewDriver(),
|
||||
drivers.AsDefault(),
|
||||
)
|
||||
|
||||
ctx = drivers.WithContext(
|
||||
ctx,
|
||||
http.NewDriver(
|
||||
http.WithCustomName("http_headers"),
|
||||
http.WithHeader("Single_header", []string{"single_header_value"}),
|
||||
http.WithHeaders(drivers.HTTPHeaders{
|
||||
"Multi_set_header": []string{"multi_set_header_value"},
|
||||
"Multi_set_header2": []string{"multi_set_header2_value"},
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
ctx = drivers.WithContext(
|
||||
ctx,
|
||||
http.NewDriver(
|
||||
http.WithCustomName("http_cookies"),
|
||||
http.WithCookie(drivers.HTTPCookie{
|
||||
Name: "single_cookie",
|
||||
Value: "single_cookie_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
}),
|
||||
http.WithCookies([]drivers.HTTPCookie{
|
||||
{
|
||||
Name: "multi_set_cookie",
|
||||
Value: "multi_set_cookie_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
},
|
||||
{
|
||||
Name: "multi_set_cookie2",
|
||||
Value: "multi_set_cookie2_value",
|
||||
Path: "/",
|
||||
MaxAge: 0,
|
||||
Secure: false,
|
||||
HTTPOnly: false,
|
||||
SameSite: 0,
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
results, err := r.runQueries(ctx, r.settings.Dir)
|
||||
|
||||
if err != nil {
|
||||
@ -184,7 +280,7 @@ func (r *Runner) runQueries(ctx context.Context, dir string) ([]Result, error) {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *Runner) runQuery(ctx context.Context, c *compiler.FqlCompiler, name, script string) Result {
|
||||
func (r *Runner) runQuery(ctx context.Context, c *compiler.Compiler, name, script string) Result {
|
||||
start := time.Now()
|
||||
|
||||
p, err := c.Compile(script)
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -54,6 +54,18 @@ func New(settings Settings) *Server {
|
||||
headers = string(b)
|
||||
}
|
||||
|
||||
var cookies string
|
||||
|
||||
if len(ctx.Request().Cookies()) > 0 {
|
||||
b, err := json.Marshal(ctx.Request().Cookies())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cookies = string(b)
|
||||
}
|
||||
|
||||
ts := time.Now().Format("2006-01-02 15:04:05")
|
||||
|
||||
return ctx.HTML(http.StatusOK, fmt.Sprintf(`
|
||||
@ -65,9 +77,10 @@ func New(settings Settings) *Server {
|
||||
<body>
|
||||
<span id="timestamp">%s</span>
|
||||
<span id="headers">%s</span>
|
||||
<span id="cookies">%s</span>
|
||||
</body>
|
||||
</html>
|
||||
`, ts, headers))
|
||||
`, ts, headers, cookies))
|
||||
})
|
||||
api.GET("/ping", func(ctx echo.Context) error {
|
||||
return ctx.JSON(http.StatusOK, echo.Map{
|
||||
|
21
e2e/tests/dynamic/doc/cookies/default.fql
Normal file
21
e2e/tests/dynamic/doc/cookies/default.fql
Normal file
@ -0,0 +1,21 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "cdp_cookies"
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#cookies")
|
||||
LET actual = (
|
||||
FOR c IN JSON_PARSE(el.innerText)
|
||||
SORT c.Name
|
||||
RETURN c
|
||||
)
|
||||
|
||||
LET expected = {
|
||||
"Single_cookie": "single_cookie_value",
|
||||
"Multi_set_cookie": "multi_set_cookie_value",
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_cookie": actual[2].Value,
|
||||
"Multi_set_cookie": actual[0].Value,
|
||||
})
|
31
e2e/tests/dynamic/doc/cookies/override_default.fql
Normal file
31
e2e/tests/dynamic/doc/cookies/override_default.fql
Normal file
@ -0,0 +1,31 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "cdp_cookies",
|
||||
cookies: [
|
||||
{
|
||||
name: "Single_cookie",
|
||||
value: "Foo"
|
||||
},
|
||||
{
|
||||
name: "Multi_set_cookie",
|
||||
value: "Bar"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#cookies")
|
||||
LET actual = (
|
||||
FOR c IN JSON_PARSE(el.innerText)
|
||||
SORT c.Name
|
||||
RETURN c
|
||||
)
|
||||
|
||||
LET expected = {
|
||||
"Single_cookie": "Foo",
|
||||
"Multi_set_cookie": "Bar",
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_cookie": actual[1].Value,
|
||||
"Multi_set_cookie": actual[0].Value,
|
||||
})
|
17
e2e/tests/dynamic/doc/headers/default.fql
Normal file
17
e2e/tests/dynamic/doc/headers/default.fql
Normal file
@ -0,0 +1,17 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "cdp_headers"
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#headers")
|
||||
LET actual = JSON_PARSE(el.innerText)
|
||||
|
||||
LET expected = {
|
||||
"Single_header": ["single_header_value"],
|
||||
"Multi_set_header":["multi_set_header_value"],
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_header": actual["Single_header"],
|
||||
"Multi_set_header": actual["Multi_set_header"],
|
||||
})
|
20
e2e/tests/dynamic/doc/headers/override_default.fql
Normal file
20
e2e/tests/dynamic/doc/headers/override_default.fql
Normal file
@ -0,0 +1,20 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "cdp_headers",
|
||||
headers: {
|
||||
"single_header": "foo"
|
||||
}
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#headers")
|
||||
LET actual = JSON_PARSE(el.innerText)
|
||||
|
||||
LET expected = {
|
||||
"Single_header": ["foo"],
|
||||
"Multi_set_header":["multi_set_header_value"],
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_header": actual["Single_header"],
|
||||
"Multi_set_header": actual["Multi_set_header"],
|
||||
})
|
14
e2e/tests/dynamic/element/blur/blur.fql
Normal file
14
e2e/tests/dynamic/element/blur/blur.fql
Normal file
@ -0,0 +1,14 @@
|
||||
LET url = @dynamic + "/#/events"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
LET input = ELEMENT(page, "#focus-input")
|
||||
|
||||
FOCUS(input)
|
||||
|
||||
WAIT_CLASS(page, "#focus-content", "alert-success")
|
||||
|
||||
BLUR(input)
|
||||
|
||||
WAIT_NO_CLASS(page, "#focus-content", "alert-success")
|
||||
|
||||
RETURN ""
|
12
e2e/tests/dynamic/element/blur/blur_by_selector.fql
Normal file
12
e2e/tests/dynamic/element/blur/blur_by_selector.fql
Normal file
@ -0,0 +1,12 @@
|
||||
LET url = @dynamic + "/#/events"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
FOCUS(page, "#focus-input")
|
||||
|
||||
WAIT_CLASS(page, "#focus-content", "alert-success")
|
||||
|
||||
BLUR(page, "#focus-input")
|
||||
|
||||
WAIT_NO_CLASS(page, "#focus-content", "alert-success")
|
||||
|
||||
RETURN ""
|
12
e2e/tests/dynamic/element/clear/clear.fql
Normal file
12
e2e/tests/dynamic/element/clear/clear.fql
Normal file
@ -0,0 +1,12 @@
|
||||
LET url = @dynamic + "?redirect=/forms"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(doc, "form")
|
||||
|
||||
LET input = ELEMENT(doc, "#text_input")
|
||||
|
||||
INPUT(input, "Foo", 100)
|
||||
|
||||
INPUT_CLEAR(input)
|
||||
|
||||
RETURN EXPECT("", INNER_TEXT(doc, "#text_output"))
|
14
e2e/tests/dynamic/element/clear/clear_by_selector.fql
Normal file
14
e2e/tests/dynamic/element/clear/clear_by_selector.fql
Normal file
@ -0,0 +1,14 @@
|
||||
LET url = @dynamic + "?redirect=/forms"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(doc, "form")
|
||||
|
||||
LET form = ELEMENT(doc, "#page-form")
|
||||
|
||||
INPUT(form, "#text_input", "foo")
|
||||
INPUT_CLEAR(form, "#text_input")
|
||||
|
||||
LET input = ELEMENT(doc, "#text_input")
|
||||
LET output = ELEMENT(doc, "#text_output")
|
||||
|
||||
RETURN EXPECT("", output.innerText)
|
@ -1,4 +1,4 @@
|
||||
LET url = "http://192.168.1.170:8080/#/events"
|
||||
LET url = @dynamic + "/#/events"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
LET div = ELEMENT(page, "#wait-class-random")
|
||||
@ -7,4 +7,4 @@ CLICK(div, "button")
|
||||
|
||||
WAIT_CLASS(page, "#wait-class-random-content", "alert-success", 10000)
|
||||
|
||||
RETURN ""
|
||||
RETURN ""
|
||||
|
@ -0,0 +1,16 @@
|
||||
LET url = @dynamic + "/#/forms"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(page, "form")
|
||||
|
||||
LET input = ELEMENT(page, "#text_input")
|
||||
|
||||
INPUT(input, "Foo")
|
||||
|
||||
CLICK(page, "#text_input", 2)
|
||||
|
||||
INPUT(input, "Bar")
|
||||
|
||||
WAIT(100)
|
||||
|
||||
RETURN EXPECT("Bar", input.value)
|
16
e2e/tests/dynamic/element/click/click_with_count.fql
Normal file
16
e2e/tests/dynamic/element/click/click_with_count.fql
Normal file
@ -0,0 +1,16 @@
|
||||
LET url = @dynamic + "/#/forms"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(page, "form")
|
||||
|
||||
LET input = ELEMENT(page, "#text_input")
|
||||
|
||||
INPUT(input, "Foo")
|
||||
|
||||
CLICK(input, 2)
|
||||
|
||||
INPUT(input, "Bar")
|
||||
|
||||
WAIT(100)
|
||||
|
||||
RETURN EXPECT("Bar", input.value)
|
8
e2e/tests/dynamic/element/focus/focus_ by_selector.fql
Normal file
8
e2e/tests/dynamic/element/focus/focus_ by_selector.fql
Normal file
@ -0,0 +1,8 @@
|
||||
LET url = @dynamic + "/#/events"
|
||||
LET page = DOCUMENT(url, true)
|
||||
|
||||
FOCUS(page, "#focus-input")
|
||||
|
||||
WAIT_CLASS(page, "#focus-content", "alert-success")
|
||||
|
||||
RETURN ""
|
12
e2e/tests/dynamic/element/input/input_by_selector.fql
Normal file
12
e2e/tests/dynamic/element/input/input_by_selector.fql
Normal file
@ -0,0 +1,12 @@
|
||||
LET url = @dynamic + "?redirect=/forms"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(doc, "form")
|
||||
|
||||
LET form = ELEMENT(doc, "#page-form")
|
||||
|
||||
INPUT(form, "#text_input", "foo")
|
||||
|
||||
LET output = ELEMENT(doc, "#text_output")
|
||||
|
||||
RETURN EXPECT(output.innerText, "foo")
|
@ -0,0 +1,12 @@
|
||||
LET url = @dynamic + "?redirect=/forms"
|
||||
LET doc = DOCUMENT(url, true)
|
||||
|
||||
WAIT_ELEMENT(doc, "form")
|
||||
|
||||
LET form = ELEMENT(doc, "#page-form")
|
||||
|
||||
INPUT(form, "#text_input", "foo", 100)
|
||||
|
||||
LET output = ELEMENT(doc, "#text_output")
|
||||
|
||||
RETURN EXPECT(output.innerText, "foo")
|
21
e2e/tests/static/doc/cookies/default.fql
Normal file
21
e2e/tests/static/doc/cookies/default.fql
Normal file
@ -0,0 +1,21 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "http_cookies"
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#cookies")
|
||||
LET actual = (
|
||||
FOR c IN JSON_PARSE(el.innerText)
|
||||
SORT c.Name
|
||||
RETURN c
|
||||
)
|
||||
|
||||
LET expected = {
|
||||
"Single_cookie": "single_cookie_value",
|
||||
"Multi_set_cookie": "multi_set_cookie_value",
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_cookie": actual[2].Value,
|
||||
"Multi_set_cookie": actual[0].Value,
|
||||
})
|
10
e2e/tests/static/doc/cookies/get.fql
Normal file
10
e2e/tests/static/doc/cookies/get.fql
Normal file
@ -0,0 +1,10 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET doc = DOCUMENT(url, {
|
||||
driver: "http"
|
||||
})
|
||||
|
||||
LET cookiesPath = LENGTH(doc.cookies) > 0 ? "ok" : "false"
|
||||
LET cookie = COOKIE_GET(doc, "x-ferret")
|
||||
LET expected = "ok e2e"
|
||||
|
||||
RETURN EXPECT(expected, cookiesPath + " " + cookie.value)
|
31
e2e/tests/static/doc/cookies/override_default.fql
Normal file
31
e2e/tests/static/doc/cookies/override_default.fql
Normal file
@ -0,0 +1,31 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "http_cookies",
|
||||
cookies: [
|
||||
{
|
||||
name: "Single_cookie",
|
||||
value: "Foo"
|
||||
},
|
||||
{
|
||||
name: "Multi_set_cookie",
|
||||
value: "Bar"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#cookies")
|
||||
LET actual = (
|
||||
FOR c IN JSON_PARSE(el.innerText)
|
||||
SORT c.Name
|
||||
RETURN c
|
||||
)
|
||||
|
||||
LET expected = {
|
||||
"Single_cookie": "Foo",
|
||||
"Multi_set_cookie": "Bar",
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_cookie": actual[1].Value,
|
||||
"Multi_set_cookie": actual[0].Value,
|
||||
})
|
17
e2e/tests/static/doc/headers/default.fql
Normal file
17
e2e/tests/static/doc/headers/default.fql
Normal file
@ -0,0 +1,17 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "http_headers"
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#headers")
|
||||
LET actual = JSON_PARSE(el.innerText)
|
||||
|
||||
LET expected = {
|
||||
"Single_header": ["single_header_value"],
|
||||
"Multi_set_header":["multi_set_header_value"],
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_header": actual["Single_header"],
|
||||
"Multi_set_header": actual["Multi_set_header"],
|
||||
})
|
20
e2e/tests/static/doc/headers/override_default.fql
Normal file
20
e2e/tests/static/doc/headers/override_default.fql
Normal file
@ -0,0 +1,20 @@
|
||||
LET url = @static + "/api/ts"
|
||||
LET page = DOCUMENT(url, {
|
||||
driver: "http_headers",
|
||||
headers: {
|
||||
"single_header": "foo"
|
||||
}
|
||||
})
|
||||
|
||||
LET el = ELEMENT(page, "#headers")
|
||||
LET actual = JSON_PARSE(el.innerText)
|
||||
|
||||
LET expected = {
|
||||
"Single_header": ["foo"],
|
||||
"Multi_set_header":["multi_set_header_value"],
|
||||
}
|
||||
|
||||
RETURN EXPECT(expected, {
|
||||
"Single_header": actual["Single_header"],
|
||||
"Multi_set_header": actual["Multi_set_header"],
|
||||
})
|
37
go.mod
37
go.mod
@ -1,46 +1,25 @@
|
||||
module github.com/MontFerret/ferret
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.5.0
|
||||
github.com/antchfx/htmlquery v1.0.0
|
||||
github.com/antchfx/xpath v1.0.0
|
||||
github.com/antlr/antlr4 v0.0.0-20190325153624-837aa60e2c47
|
||||
github.com/chzyer/logex v1.1.10 // indirect
|
||||
github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
||||
github.com/corpix/uarand v0.0.0
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/derekparker/trie v0.0.0-20190322172448-1ce4922c7ad9
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/corpix/uarand v0.1.1
|
||||
github.com/derekparker/trie v0.0.0-20190812220523-e66023ee76eb
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/google/go-cmp v0.2.0 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f // indirect
|
||||
github.com/gorilla/css v1.0.0
|
||||
github.com/gorilla/websocket v1.4.0 // indirect
|
||||
github.com/k0kubun/pp v3.0.1+incompatible
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/labstack/echo v3.3.10+incompatible
|
||||
github.com/labstack/gommon v0.2.8 // indirect
|
||||
github.com/gorilla/websocket v1.4.1 // indirect
|
||||
github.com/labstack/echo/v4 v4.1.10
|
||||
github.com/mafredri/cdp v0.24.2
|
||||
github.com/mattn/go-colorable v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.7 // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/rs/zerolog v1.15.0
|
||||
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.0.1 // indirect
|
||||
github.com/yudai/pp v2.0.1+incompatible
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c // indirect
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
92
go.sum
92
go.sum
@ -1,5 +1,6 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||
@ -8,74 +9,57 @@ github.com/antchfx/htmlquery v1.0.0 h1:O5IXz8fZF3B3MW+B33MZWbTHBlYmcfw0BAxgErHua
|
||||
github.com/antchfx/htmlquery v1.0.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
|
||||
github.com/antchfx/xpath v1.0.0 h1:Q5gFgh2O40VTSwMOVbFE7nFNRBu3tS21Tn0KAWeEjtk=
|
||||
github.com/antchfx/xpath v1.0.0/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/antlr/antlr4 v0.0.0-20190325153624-837aa60e2c47 h1:Lp5nUoQzppfVmfZadpzAytNyb5IMtxyOJLzoQS5dExg=
|
||||
github.com/antlr/antlr4 v0.0.0-20190325153624-837aa60e2c47/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015 h1:StuiJFxQUsxSCzcby6NFZRdEhPkXD5vxN7TZ4MD6T84=
|
||||
github.com/antlr/antlr4 v0.0.0-20190819145818-b43a4c3a8015/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/corpix/uarand v0.0.0 h1:mNbzro1GwUcZ1hmO2rWXytkR3JBxNxxctzjyuhO+Aig=
|
||||
github.com/corpix/uarand v0.0.0/go.mod h1:JSm890tOkDN+M1jqN8pUGDKnzJrsVbJwSMHBY4zwz7M=
|
||||
github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U=
|
||||
github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/derekparker/trie v0.0.0-20190322172448-1ce4922c7ad9 h1:aSaTVlEXc2QKl4fzXU1tMYCjlrSc2mA4DZtiVfckQHo=
|
||||
github.com/derekparker/trie v0.0.0-20190322172448-1ce4922c7ad9/go.mod h1:D6ICZm05D9VN1n/8iOtBxLpXtoGp6HDFUJ1RNVieOSE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/derekparker/trie v0.0.0-20190812220523-e66023ee76eb h1:HGjKnH6D1WD8sc9SfCkWkeD4OteWPPXD+ayJY+P1Bgk=
|
||||
github.com/derekparker/trie v0.0.0-20190812220523-e66023ee76eb/go.mod h1:D6ICZm05D9VN1n/8iOtBxLpXtoGp6HDFUJ1RNVieOSE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f h1:4Gslotqbs16iAg+1KR/XdabIfq8TlAWHdwS5QJFksLc=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
|
||||
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
|
||||
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||
github.com/labstack/echo/v4 v4.1.10 h1:/yhIpO50CBInUbE/nHJtGIyhBv0dJe2cDAYxc3V3uMo=
|
||||
github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/mafredri/cdp v0.24.2 h1:Rzhj/EQw9opbiwUpNML7P+4Hvf0/nSYPaDbiCEpILOM=
|
||||
github.com/mafredri/cdp v0.24.2/go.mod h1:hgdiA0yp1uqhSaDOHJWPgXpMbh+LAfUdD9vbN2AM8gE=
|
||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 h1:X9XMOYjxEfAYSy3xK1DzO5dMkkWhs9E9UCcS1IERx2k=
|
||||
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||
@ -84,26 +68,24 @@ github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcm
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca h1:hyA6yiAgbUwuWqtscNvWAI7U1CtlaD1KilQ6iudt1aI=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -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]))
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user