package common import ( "context" "github.com/pkg/errors" "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" ) func GetInPage(ctx context.Context, page drivers.HTMLPage, path []core.Value) (core.Value, error) { if len(path) == 0 { return page, nil } segment := path[0] if segment.Type() == types.String { segment := segment.(values.String) switch segment { case "response": resp, err := page.GetResponse(ctx) if err != nil { return nil, errors.Wrap(err, "get response") } return resp.GetIn(ctx, path[1:]) case "mainFrame", "document": return GetInDocument(ctx, page.GetMainFrame(), path[1:]) case "frames": if len(path) == 1 { return page.GetFrames(ctx) } idx := path[1] if !values.IsNumber(idx) { return values.None, core.TypeError(idx.Type(), types.Int, types.Float) } value, err := page.GetFrame(ctx, values.ToInt(idx)) if err != nil { return values.None, err } if len(path) == 2 { return value, nil } frame, err := drivers.ToDocument(value) if err != nil { return values.None, err } return GetInDocument(ctx, frame, path[2:]) case "url", "URL": return page.GetMainFrame().GetURL(), nil case "cookies": if len(path) == 1 { return page.GetCookies(ctx) } switch idx := path[1].(type) { case values.Int: cookies, err := page.GetCookies(ctx) if err != nil { return values.None, err } return cookies.Get(idx), nil default: return values.None, core.TypeError(idx.Type(), types.Int) } case "isClosed": return page.IsClosed(), nil case "title": return page.GetMainFrame().GetTitle(), nil default: return GetInDocument(ctx, page.GetMainFrame(), path) } } return GetInDocument(ctx, page.GetMainFrame(), path) } func GetInDocument(ctx context.Context, doc drivers.HTMLDocument, path []core.Value) (core.Value, error) { if len(path) == 0 { return doc, nil } segment := path[0] if segment.Type() == types.String { segment := segment.(values.String) switch segment { case "url", "URL": return doc.GetURL(), nil case "name": return doc.GetName(), nil case "title": return doc.GetTitle(), nil case "parent": parent := doc.GetParentDocument() if parent == nil { return values.None, nil } if len(path) == 1 { return parent, nil } return GetInDocument(ctx, parent, path[1:]) case "body", "head": out, err := doc.QuerySelector(ctx, segment) if err != nil { return values.None, err } if out == values.None { return out, nil } if len(path) == 1 { return out, nil } el, err := drivers.ToElement(out) if err != nil { return values.None, err } return GetInElement(ctx, el, path[1:]) case "innerHTML": return doc.GetElement().GetInnerHTML(ctx) case "innerText": return doc.GetElement().GetInnerText(ctx) default: return GetInNode(ctx, doc.GetElement(), path) } } return GetInNode(ctx, doc.GetElement(), path) } func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value) (core.Value, error) { if len(path) == 0 { return el, nil } segment := path[0] if segment.Type() == types.String { segment := segment.(values.String) switch segment { case "innerText": return el.GetInnerText(ctx) case "innerHTML": return el.GetInnerHTML(ctx) case "value": return el.GetValue(ctx) case "attributes": attrs, err := el.GetAttributes(ctx) if err != nil { return values.None, err } if len(path) == 1 { return attrs, nil } return values.GetIn(ctx, attrs, path[1:]) case "style": styles, err := el.GetStyles(ctx) if err != nil { return values.None, err } if len(path) == 1 { return styles, nil } return values.GetIn(ctx, styles, path[1:]) default: return GetInNode(ctx, el, path) } } return GetInNode(ctx, el, path) } func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (core.Value, error) { if len(path) == 0 { return node, nil } nt := node.Type() segment := path[0] st := segment.Type() switch st { case types.Int: if nt == drivers.HTMLElementType || nt == drivers.HTMLDocumentType { re := node.(drivers.HTMLNode) return re.GetChildNode(ctx, values.ToInt(segment)) } return values.GetIn(ctx, node, path[1:]) case types.String: segment := segment.(values.String) switch segment { case "isDetached": return node.IsDetached(), nil case "nodeType": return node.GetNodeType(), nil case "nodeName": return node.GetNodeName(), nil case "children": children, err := node.GetChildNodes(ctx) if err != nil { return values.None, err } if len(path) == 1 { return children, nil } return values.GetIn(ctx, children, path[1:]) case "length": return node.Length(), nil default: return values.None, nil } default: return values.None, core.TypeError(st, types.Int, types.String) } }