diff options
Diffstat (limited to 'vendor/github.com')
27 files changed, 617 insertions, 1016 deletions
diff --git a/vendor/github.com/PuerkitoBio/goquery/.gitattributes b/vendor/github.com/PuerkitoBio/goquery/.gitattributes new file mode 100644 index 0000000..0cc26ec --- /dev/null +++ b/vendor/github.com/PuerkitoBio/goquery/.gitattributes @@ -0,0 +1 @@ +testdata/* linguist-vendored diff --git a/vendor/github.com/PuerkitoBio/goquery/.gitignore b/vendor/github.com/PuerkitoBio/goquery/.gitignore new file mode 100644 index 0000000..970381c --- /dev/null +++ b/vendor/github.com/PuerkitoBio/goquery/.gitignore @@ -0,0 +1,16 @@ +# editor temporary files +*.sublime-* +.DS_Store +*.swp +#*.*# +tags + +# direnv config +.env* + +# test binaries +*.test + +# coverage and profilte outputs +*.out + diff --git a/vendor/github.com/PuerkitoBio/goquery/.travis.yml b/vendor/github.com/PuerkitoBio/goquery/.travis.yml new file mode 100644 index 0000000..cc1402d --- /dev/null +++ b/vendor/github.com/PuerkitoBio/goquery/.travis.yml @@ -0,0 +1,16 @@ +language: go + +go: + - 1.1 + - 1.2.x + - 1.3.x + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - "1.10.x" + - 1.11.x + - tip + diff --git a/vendor/github.com/PuerkitoBio/goquery/README.md b/vendor/github.com/PuerkitoBio/goquery/README.md index c9db736..84f9af3 100644 --- a/vendor/github.com/PuerkitoBio/goquery/README.md +++ b/vendor/github.com/PuerkitoBio/goquery/README.md @@ -138,11 +138,9 @@ func main() { - [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags. - [andybalholm/cascadia][cascadia], the CSS selector library used by goquery. - [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors. -- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework +- [asciimoo/colly](https://github.com/asciimoo/colly), a lightning fast and elegant Scraping Framework - [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets. - [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping. -- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins -- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers. ## Support diff --git a/vendor/github.com/PuerkitoBio/purell/.gitignore b/vendor/github.com/PuerkitoBio/purell/.gitignore new file mode 100644 index 0000000..748e4c8 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.gitignore @@ -0,0 +1,5 @@ +*.sublime-* +.DS_Store +*.swp +*.swo +tags diff --git a/vendor/github.com/PuerkitoBio/purell/.travis.yml b/vendor/github.com/PuerkitoBio/purell/.travis.yml new file mode 100644 index 0000000..df332da --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - "1.10.x" + - tip diff --git a/vendor/github.com/PuerkitoBio/urlesc/.travis.yml b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml new file mode 100644 index 0000000..ba6b225 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - tip + +install: + - go build . + +script: + - go test -v diff --git a/vendor/github.com/andybalholm/cascadia/.travis.yml b/vendor/github.com/andybalholm/cascadia/.travis.yml new file mode 100644 index 0000000..6f22751 --- /dev/null +++ b/vendor/github.com/andybalholm/cascadia/.travis.yml @@ -0,0 +1,14 @@ +language: go + +go: + - 1.3 + - 1.4 + +install: + - go get github.com/andybalholm/cascadia + +script: + - go test -v + +notifications: + email: false diff --git a/vendor/github.com/andybalholm/cascadia/LICENSE b/vendor/github.com/andybalholm/cascadia/LICENSE index ee5ad35..ee5ad35 100755..100644 --- a/vendor/github.com/andybalholm/cascadia/LICENSE +++ b/vendor/github.com/andybalholm/cascadia/LICENSE diff --git a/vendor/github.com/andybalholm/cascadia/README.md b/vendor/github.com/andybalholm/cascadia/README.md index 26f4c37..9021cb9 100644 --- a/vendor/github.com/andybalholm/cascadia/README.md +++ b/vendor/github.com/andybalholm/cascadia/README.md @@ -5,5 +5,3 @@ The Cascadia package implements CSS selectors for use with the parse trees produced by the html package. To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package. - -[Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia). diff --git a/vendor/github.com/andybalholm/cascadia/parser.go b/vendor/github.com/andybalholm/cascadia/parser.go index bebf0af..495db9c 100644 --- a/vendor/github.com/andybalholm/cascadia/parser.go +++ b/vendor/github.com/andybalholm/cascadia/parser.go @@ -7,16 +7,14 @@ import ( "regexp" "strconv" "strings" + + "golang.org/x/net/html" ) // a parser for CSS selectors type parser struct { s string // the source text i int // the current position - - // if `false`, parsing a pseudo-element - // returns an error. - acceptPseudoElements bool } // parseEscape parses a backslash escape. @@ -58,26 +56,6 @@ func (p *parser) parseEscape() (result string, err error) { return result, nil } -// toLowerASCII returns s with all ASCII capital letters lowercased. -func toLowerASCII(s string) string { - var b []byte - for i := 0; i < len(s); i++ { - if c := s[i]; 'A' <= c && c <= 'Z' { - if b == nil { - b = make([]byte, len(s)) - copy(b, s) - } - b[i] = s[i] + ('a' - 'A') - } - } - - if b == nil { - return s - } - - return string(b) -} - func hexDigit(c byte) bool { return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' } @@ -302,92 +280,92 @@ func (p *parser) consumeClosingParenthesis() bool { } // parseTypeSelector parses a type selector (one that matches by tag name). -func (p *parser) parseTypeSelector() (result tagSelector, err error) { +func (p *parser) parseTypeSelector() (result Selector, err error) { tag, err := p.parseIdentifier() if err != nil { - return + return nil, err } - return tagSelector{tag: toLowerASCII(tag)}, nil + + return typeSelector(tag), nil } // parseIDSelector parses a selector that matches by id attribute. -func (p *parser) parseIDSelector() (idSelector, error) { +func (p *parser) parseIDSelector() (Selector, error) { if p.i >= len(p.s) { - return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead") + return nil, fmt.Errorf("expected id selector (#id), found EOF instead") } if p.s[p.i] != '#' { - return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i]) + return nil, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i]) } p.i++ id, err := p.parseName() if err != nil { - return idSelector{}, err + return nil, err } - return idSelector{id: id}, nil + return attributeEqualsSelector("id", id), nil } // parseClassSelector parses a selector that matches by class attribute. -func (p *parser) parseClassSelector() (classSelector, error) { +func (p *parser) parseClassSelector() (Selector, error) { if p.i >= len(p.s) { - return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead") + return nil, fmt.Errorf("expected class selector (.class), found EOF instead") } if p.s[p.i] != '.' { - return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i]) + return nil, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i]) } p.i++ class, err := p.parseIdentifier() if err != nil { - return classSelector{}, err + return nil, err } - return classSelector{class: class}, nil + return attributeIncludesSelector("class", class), nil } // parseAttributeSelector parses a selector that matches by attribute value. -func (p *parser) parseAttributeSelector() (attrSelector, error) { +func (p *parser) parseAttributeSelector() (Selector, error) { if p.i >= len(p.s) { - return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead") + return nil, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead") } if p.s[p.i] != '[' { - return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i]) + return nil, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i]) } p.i++ p.skipWhitespace() key, err := p.parseIdentifier() if err != nil { - return attrSelector{}, err + return nil, err } - key = toLowerASCII(key) p.skipWhitespace() if p.i >= len(p.s) { - return attrSelector{}, errors.New("unexpected EOF in attribute selector") + return nil, errors.New("unexpected EOF in attribute selector") } if p.s[p.i] == ']' { p.i++ - return attrSelector{key: key, operation: ""}, nil + return attributeExistsSelector(key), nil } if p.i+2 >= len(p.s) { - return attrSelector{}, errors.New("unexpected EOF in attribute selector") + return nil, errors.New("unexpected EOF in attribute selector") } op := p.s[p.i : p.i+2] if op[0] == '=' { op = "=" } else if op[1] != '=' { - return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op) + return nil, fmt.Errorf(`expected equality operator, found "%s" instead`, op) } p.i += len(op) p.skipWhitespace() if p.i >= len(p.s) { - return attrSelector{}, errors.New("unexpected EOF in attribute selector") + return nil, errors.New("unexpected EOF in attribute selector") } var val string var rx *regexp.Regexp @@ -402,84 +380,88 @@ func (p *parser) parseAttributeSelector() (attrSelector, error) { } } if err != nil { - return attrSelector{}, err + return nil, err } p.skipWhitespace() if p.i >= len(p.s) { - return attrSelector{}, errors.New("unexpected EOF in attribute selector") + return nil, errors.New("unexpected EOF in attribute selector") } if p.s[p.i] != ']' { - return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i]) + return nil, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i]) } p.i++ switch op { - case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=": - return attrSelector{key: key, val: val, operation: op, regexp: rx}, nil - default: - return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op) - } + case "=": + return attributeEqualsSelector(key, val), nil + case "!=": + return attributeNotEqualSelector(key, val), nil + case "~=": + return attributeIncludesSelector(key, val), nil + case "|=": + return attributeDashmatchSelector(key, val), nil + case "^=": + return attributePrefixSelector(key, val), nil + case "$=": + return attributeSuffixSelector(key, val), nil + case "*=": + return attributeSubstringSelector(key, val), nil + case "#=": + return attributeRegexSelector(key, rx), nil + } + + return nil, fmt.Errorf("attribute operator %q is not supported", op) } var errExpectedParenthesis = errors.New("expected '(' but didn't find it") var errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it") var errUnmatchedParenthesis = errors.New("unmatched '('") -// parsePseudoclassSelector parses a pseudoclass selector like :not(p) or a pseudo-element -// For backwards compatibility, both ':' and '::' prefix are allowed for pseudo-elements. -// https://drafts.csswg.org/selectors-3/#pseudo-elements -// Returning a nil `Sel` (and a nil `error`) means we found a pseudo-element. -func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err error) { +// parsePseudoclassSelector parses a pseudoclass selector like :not(p). +func (p *parser) parsePseudoclassSelector() (Selector, error) { if p.i >= len(p.s) { - return nil, "", fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead") + return nil, fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead") } if p.s[p.i] != ':' { - return nil, "", fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i]) + return nil, fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i]) } p.i++ - var mustBePseudoElement bool - if p.i >= len(p.s) { - return nil, "", fmt.Errorf("got empty pseudoclass (or pseudoelement)") - } - if p.s[p.i] == ':' { // we found a pseudo-element - mustBePseudoElement = true - p.i++ - } - name, err := p.parseIdentifier() if err != nil { - return + return nil, err } name = toLowerASCII(name) - if mustBePseudoElement && (name != "after" && name != "backdrop" && name != "before" && - name != "cue" && name != "first-letter" && name != "first-line" && name != "grammar-error" && - name != "marker" && name != "placeholder" && name != "selection" && name != "spelling-error") { - return out, "", fmt.Errorf("unknown pseudoelement :%s", name) - } switch name { case "not", "has", "haschild": if !p.consumeParenthesis() { - return out, "", errExpectedParenthesis + return nil, errExpectedParenthesis } sel, parseErr := p.parseSelectorGroup() if parseErr != nil { - return out, "", parseErr + return nil, parseErr } if !p.consumeClosingParenthesis() { - return out, "", errExpectedClosingParenthesis + return nil, errExpectedClosingParenthesis } - out = relativePseudoClassSelector{name: name, match: sel} + switch name { + case "not": + return negatedSelector(sel), nil + case "has": + return hasDescendantSelector(sel), nil + case "haschild": + return hasChildSelector(sel), nil + } case "contains", "containsown": if !p.consumeParenthesis() { - return out, "", errExpectedParenthesis + return nil, errExpectedParenthesis } if p.i == len(p.s) { - return out, "", errUnmatchedParenthesis + return nil, errUnmatchedParenthesis } var val string switch p.s[p.i] { @@ -489,75 +471,95 @@ func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err val, err = p.parseIdentifier() } if err != nil { - return out, "", err + return nil, err } val = strings.ToLower(val) p.skipWhitespace() if p.i >= len(p.s) { - return out, "", errors.New("unexpected EOF in pseudo selector") + return nil, errors.New("unexpected EOF in pseudo selector") } if !p.consumeClosingParenthesis() { - return out, "", errExpectedClosingParenthesis + return nil, errExpectedClosingParenthesis } - out = containsPseudoClassSelector{own: name == "containsown", value: val} + switch name { + case "contains": + return textSubstrSelector(val), nil + case "containsown": + return ownTextSubstrSelector(val), nil + } case "matches", "matchesown": if !p.consumeParenthesis() { - return out, "", errExpectedParenthesis + return nil, errExpectedParenthesis } rx, err := p.parseRegex() if err != nil { - return out, "", err + return nil, err } if p.i >= len(p.s) { - return out, "", errors.New("unexpected EOF in pseudo selector") + return nil, errors.New("unexpected EOF in pseudo selector") } if !p.consumeClosingParenthesis() { - return out, "", errExpectedClosingParenthesis + return nil, errExpectedClosingParenthesis } - out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx} + switch name { + case "matches": + return textRegexSelector(rx), nil + case "matchesown": + return ownTextRegexSelector(rx), nil + } case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type": if !p.consumeParenthesis() { - return out, "", errExpectedParenthesis + return nil, errExpectedParenthesis } a, b, err := p.parseNth() if err != nil { - return out, "", err + return nil, err } if !p.consumeClosingParenthesis() { - return out, "", errExpectedClosingParenthesis + return nil, errExpectedClosingParenthesis + } + if a == 0 { + switch name { + case "nth-child": + return simpleNthChildSelector(b, false), nil + case "nth-of-type": + return simpleNthChildSelector(b, true), nil + case "nth-last-child": + return simpleNthLastChildSelector(b, false), nil + case "nth-last-of-type": + return simpleNthLastChildSelector(b, true), nil + } } - last := name == "nth-last-child" || name == "nth-last-of-type" - ofType := name == "nth-of-type" || name == "nth-last-of-type" - out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType} + return nthChildSelector(a, b, + name == "nth-last-child" || name == "nth-last-of-type", + name == "nth-of-type" || name == "nth-last-of-type"), + nil case "first-child": - out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false} + return simpleNthChildSelector(1, false), nil case "last-child": - out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true} + return simpleNthLastChildSelector(1, false), nil case "first-of-type": - out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false} + return simpleNthChildSelector(1, true), nil case "last-of-type": - out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true} + return simpleNthLastChildSelector(1, true), nil case "only-child": - out = onlyChildPseudoClassSelector{ofType: false} + return onlyChildSelector(false), nil case "only-of-type": - out = onlyChildPseudoClassSelector{ofType: true} + return onlyChildSelector(true), nil case "input": - out = inputPseudoClassSelector{} + return inputSelector, nil case "empty": - out = emptyElementPseudoClassSelector{} + return emptyElementSelector, nil case "root": - out = rootPseudoClassSelector{} - case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error": - return nil, name, nil - default: - return out, "", fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name) + return rootSelector, nil } - return + + return nil, fmt.Errorf("unknown pseudoclass :%s", name) } // parseInteger parses a decimal integer. @@ -703,8 +705,8 @@ invalid: // parseSimpleSelectorSequence parses a selector sequence that applies to // a single element. -func (p *parser) parseSimpleSelectorSequence() (Sel, error) { - var selectors []Sel +func (p *parser) parseSimpleSelectorSequence() (Selector, error) { + var result Selector if p.i >= len(p.s) { return nil, errors.New("expected selector, found EOF instead") @@ -721,17 +723,13 @@ func (p *parser) parseSimpleSelectorSequence() (Sel, error) { if err != nil { return nil, err } - selectors = append(selectors, r) + result = r } - var pseudoElement string loop: for p.i < len(p.s) { - var ( - ns Sel - newPseudoElement string - err error - ) + var ns Selector + var err error switch p.s[p.i] { case '#': ns, err = p.parseIDSelector() @@ -740,57 +738,44 @@ loop: case '[': ns, err = p.parseAttributeSelector() case ':': - ns, newPseudoElement, err = p.parsePseudoclassSelector() + ns, err = p.parsePseudoclassSelector() default: break loop } if err != nil { return nil, err } - // From https://drafts.csswg.org/selectors-3/#pseudo-elements : - // "Only one pseudo-element may appear per selector, and if present - // it must appear after the sequence of simple selectors that - // represents the subjects of the selector."" - if ns == nil { // we found a pseudo-element - if pseudoElement != "" { - return nil, fmt.Errorf("only one pseudo-element is accepted per selector, got %s and %s", pseudoElement, newPseudoElement) - } - if !p.acceptPseudoElements { - return nil, fmt.Errorf("pseudo-element %s found, but pseudo-elements support is disabled", newPseudoElement) - } - pseudoElement = newPseudoElement + if result == nil { + result = ns } else { - if pseudoElement != "" { - return nil, fmt.Errorf("pseudo-element %s must be at the end of selector", pseudoElement) - } - selectors = append(selectors, ns) + result = intersectionSelector(result, ns) } - } - if len(selectors) == 1 && pseudoElement == "" { // no need wrap the selectors in compoundSelector - return selectors[0], nil + + if result == nil { + result = func(n *html.Node) bool { + return n.Type == html.ElementNode + } } - return compoundSelector{selectors: selectors, pseudoElement: pseudoElement}, nil + + return result, nil } // parseSelector parses a selector that may include combinators. -func (p *parser) parseSelector() (Sel, error) { +func (p *parser) parseSelector() (result Selector, err error) { p.skipWhitespace() - result, err := p.parseSimpleSelectorSequence() + result, err = p.parseSimpleSelectorSequence() if err != nil { - return nil, err + return } for { - var ( - combinator byte - c Sel - ) + var combinator byte if p.skipWhitespace() { combinator = ' ' } if p.i >= len(p.s) { - return result, nil + return } switch p.s[p.i] { @@ -800,39 +785,51 @@ func (p *parser) parseSelector() (Sel, error) { p.skipWhitespace() case ',', ')': // These characters can't begin a selector, but they can legally occur after one. - return result, nil + return } if combinator == 0 { - return result, nil + return } - c, err = p.parseSimpleSelectorSequence() + c, err := p.parseSimpleSelectorSequence() if err != nil { return nil, err } - result = combinedSelector{first: result, combinator: combinator, second: c} + + switch combinator { + case ' ': + result = descendantSelector(result, c) + case '>': + result = childSelector(result, c) + case '+': + result = siblingSelector(result, c, true) + case '~': + result = siblingSelector(result, c, false) + } } + + panic("unreachable") } // parseSelectorGroup parses a group of selectors, separated by commas. -func (p *parser) parseSelectorGroup() (SelectorGroup, error) { - current, err := p.parseSelector() +func (p *parser) parseSelectorGroup() (result Selector, err error) { + result, err = p.parseSelector() if err != nil { - return nil, err + return } - result := SelectorGroup{current} for p.i < len(p.s) { if p.s[p.i] != ',' { - break + return result, nil } p.i++ c, err := p.parseSelector() if err != nil { return nil, err } - result = append(result, c) + result = unionSelector(result, c) } - return result, nil + + return } diff --git a/vendor/github.com/andybalholm/cascadia/selector.go b/vendor/github.com/andybalholm/cascadia/selector.go index e2a6dc4..9fb05cc 100644 --- a/vendor/github.com/andybalholm/cascadia/selector.go +++ b/vendor/github.com/andybalholm/cascadia/selector.go @@ -9,60 +9,36 @@ import ( "golang.org/x/net/html" ) -// Matcher is the interface for basic selector functionality. -// Match returns whether a selector matches n. -type Matcher interface { - Match(n *html.Node) bool -} - -// Sel is the interface for all the functionality provided by selectors. -type Sel interface { - Matcher - Specificity() Specificity - - // Returns a CSS input compiling to this selector. - String() string +// the Selector type, and functions for creating them - // Returns a pseudo-element, or an empty string. - PseudoElement() string -} - -// Parse parses a selector. Use `ParseWithPseudoElement` -// if you need support for pseudo-elements. -func Parse(sel string) (Sel, error) { - p := &parser{s: sel} - compiled, err := p.parseSelector() - if err != nil { - return nil, err - } +// A Selector is a function which tells whether a node matches or not. +type Selector func(*html.Node) bool - if p.i < len(sel) { - return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) +// hasChildMatch returns whether n has any child that matches a. +func hasChildMatch(n *html.Node, a Selector) bool { + for c := n.FirstChild; c != nil; c = c.NextSibling { + if a(c) { + return true + } } - - return compiled, nil + return false } -// ParseWithPseudoElement parses a single selector, -// with support for pseudo-element. -func ParseWithPseudoElement(sel string) (Sel, error) { - p := &parser{s: sel, acceptPseudoElements: true} - compiled, err := p.parseSelector() - if err != nil { - return nil, err - } - - if p.i < len(sel) { - return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) +// hasDescendantMatch performs a depth-first search of n's descendants, +// testing whether any of them match a. It returns true as soon as a match is +// found, or false if no match is found. +func hasDescendantMatch(n *html.Node, a Selector) bool { + for c := n.FirstChild; c != nil; c = c.NextSibling { + if a(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) { + return true + } } - - return compiled, nil + return false } -// ParseGroup parses a selector, or a group of selectors separated by commas. -// Use `ParseGroupWithPseudoElements` -// if you need support for pseudo-elements. -func ParseGroup(sel string) (SelectorGroup, error) { +// Compile parses a selector and returns, if successful, a Selector object +// that can be used to match against html.Node objects. +func Compile(sel string) (Selector, error) { p := &parser{s: sel} compiled, err := p.parseSelectorGroup() if err != nil { @@ -76,39 +52,6 @@ func ParseGroup(sel string) (SelectorGroup, error) { return compiled, nil } -// ParseGroupWithPseudoElements parses a selector, or a group of selectors separated by commas. -// It supports pseudo-elements. -func ParseGroupWithPseudoElements(sel string) (SelectorGroup, error) { - p := &parser{s: sel, acceptPseudoElements: true} - compiled, err := p.parseSelectorGroup() - if err != nil { - return nil, err - } - - if p.i < len(sel) { - return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) - } - - return compiled, nil -} - -// A Selector is a function which tells whether a node matches or not. -// -// This type is maintained for compatibility; I recommend using the newer and -// more idiomatic interfaces Sel and Matcher. -type Selector func(*html.Node) bool - -// Compile parses a selector and returns, if successful, a Selector object -// that can be used to match against html.Node objects. -func Compile(sel string) (Selector, error) { - compiled, err := ParseGroup(sel) - if err != nil { - return nil, err - } - - return Selector(compiled.Match), nil -} - // MustCompile is like Compile, but panics instead of returning an error. func MustCompile(sel string) Selector { compiled, err := Compile(sel) @@ -136,23 +79,6 @@ func (s Selector) matchAllInto(n *html.Node, storage []*html.Node) []*html.Node return storage } -func queryInto(n *html.Node, m Matcher, storage []*html.Node) []*html.Node { - for child := n.FirstChild; child != nil; child = child.NextSibling { - if m.Match(child) { - storage = append(storage, child) - } - storage = queryInto(child, m, storage) - } - - return storage -} - -// QueryAll returns a slice of all the nodes that match m, from the descendants -// of n. -func QueryAll(n *html.Node, m Matcher) []*html.Node { - return queryInto(n, m, nil) -} - // Match returns true if the node matches the selector. func (s Selector) Match(n *html.Node) bool { return s(n) @@ -173,21 +99,6 @@ func (s Selector) MatchFirst(n *html.Node) *html.Node { return nil } -// Query returns the first node that matches m, from the descendants of n. -// If none matches, it returns nil. -func Query(n *html.Node, m Matcher) *html.Node { - for c := n.FirstChild; c != nil; c = c.NextSibling { - if m.Match(c) { - return c - } - if matched := Query(c, m); matched != nil { - return matched - } - } - - return nil -} - // Filter returns the nodes in nodes that match the selector. func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) { for _, n := range nodes { @@ -198,148 +109,106 @@ func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) { return result } -// Filter returns the nodes that match m. -func Filter(nodes []*html.Node, m Matcher) (result []*html.Node) { - for _, n := range nodes { - if m.Match(n) { - result = append(result, n) - } +// typeSelector returns a Selector that matches elements with a given tag name. +func typeSelector(tag string) Selector { + tag = toLowerASCII(tag) + return func(n *html.Node) bool { + return n.Type == html.ElementNode && n.Data == tag } - return result } -type tagSelector struct { - tag string -} - -// Matches elements with a given tag name. -func (t tagSelector) Match(n *html.Node) bool { - return n.Type == html.ElementNode && n.Data == t.tag -} - -func (c tagSelector) Specificity() Specificity { - return Specificity{0, 0, 1} -} - -func (c tagSelector) PseudoElement() string { - return "" -} - -type classSelector struct { - class string -} - -// Matches elements by class attribute. -func (t classSelector) Match(n *html.Node) bool { - return matchAttribute(n, "class", func(s string) bool { - return matchInclude(t.class, s) - }) -} - -func (c classSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c classSelector) PseudoElement() string { - return "" -} - -type idSelector struct { - id string -} - -// Matches elements by id attribute. -func (t idSelector) Match(n *html.Node) bool { - return matchAttribute(n, "id", func(s string) bool { - return s == t.id - }) -} +// toLowerASCII returns s with all ASCII capital letters lowercased. +func toLowerASCII(s string) string { + var b []byte + for i := 0; i < len(s); i++ { + if c := s[i]; 'A' <= c && c <= 'Z' { + if b == nil { + b = make([]byte, len(s)) + copy(b, s) + } + b[i] = s[i] + ('a' - 'A') + } + } -func (c idSelector) Specificity() Specificity { - return Specificity{1, 0, 0} -} + if b == nil { + return s + } -func (c idSelector) PseudoElement() string { - return "" + return string(b) } -type attrSelector struct { - key, val, operation string - regexp *regexp.Regexp +// attributeSelector returns a Selector that matches elements +// where the attribute named key satisifes the function f. +func attributeSelector(key string, f func(string) bool) Selector { + key = toLowerASCII(key) + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false + } + for _, a := range n.Attr { + if a.Key == key && f(a.Val) { + return true + } + } + return false + } } -// Matches elements by attribute value. -func (t attrSelector) Match(n *html.Node) bool { - switch t.operation { - case "": - return matchAttribute(n, t.key, func(string) bool { return true }) - case "=": - return matchAttribute(n, t.key, func(s string) bool { return s == t.val }) - case "!=": - return attributeNotEqualMatch(t.key, t.val, n) - case "~=": - // matches elements where the attribute named key is a whitespace-separated list that includes val. - return matchAttribute(n, t.key, func(s string) bool { return matchInclude(t.val, s) }) - case "|=": - return attributeDashMatch(t.key, t.val, n) - case "^=": - return attributePrefixMatch(t.key, t.val, n) - case "$=": - return attributeSuffixMatch(t.key, t.val, n) - case "*=": - return attributeSubstringMatch(t.key, t.val, n) - case "#=": - return attributeRegexMatch(t.key, t.regexp, n) - default: - panic(fmt.Sprintf("unsuported operation : %s", t.operation)) - } +// attributeExistsSelector returns a Selector that matches elements that have +// an attribute named key. +func attributeExistsSelector(key string) Selector { + return attributeSelector(key, func(string) bool { return true }) } -// matches elements where the attribute named key satisifes the function f. -func matchAttribute(n *html.Node, key string, f func(string) bool) bool { - if n.Type != html.ElementNode { - return false - } - for _, a := range n.Attr { - if a.Key == key && f(a.Val) { - return true - } - } - return false +// attributeEqualsSelector returns a Selector that matches elements where +// the attribute named key has the value val. +func attributeEqualsSelector(key, val string) Selector { + return attributeSelector(key, + func(s string) bool { + return s == val + }) } -// attributeNotEqualMatch matches elements where +// attributeNotEqualSelector returns a Selector that matches elements where // the attribute named key does not have the value val. -func attributeNotEqualMatch(key, val string, n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } - for _, a := range n.Attr { - if a.Key == key && a.Val == val { +func attributeNotEqualSelector(key, val string) Selector { + key = toLowerASCII(key) + return func(n *html.Node) bool { + if n.Type != html.ElementNode { return false } + for _, a := range n.Attr { + if a.Key == key && a.Val == val { + return false + } + } + return true } - return true } -// returns true if s is a whitespace-separated list that includes val. -func matchInclude(val, s string) bool { - for s != "" { - i := strings.IndexAny(s, " \t\r\n\f") - if i == -1 { - return s == val - } - if s[:i] == val { - return true - } - s = s[i+1:] - } - return false +// attributeIncludesSelector returns a Selector that matches elements where +// the attribute named key is a whitespace-separated list that includes val. +func attributeIncludesSelector(key, val string) Selector { + return attributeSelector(key, + func(s string) bool { + for s != "" { + i := strings.IndexAny(s, " \t\r\n\f") + if i == -1 { + return s == val + } + if s[:i] == val { + return true + } + s = s[i+1:] + } + return false + }) } -// matches elements where the attribute named key equals val or starts with val plus a hyphen. -func attributeDashMatch(key, val string, n *html.Node) bool { - return matchAttribute(n, key, +// attributeDashmatchSelector returns a Selector that matches elements where +// the attribute named key equals val or starts with val plus a hyphen. +func attributeDashmatchSelector(key, val string) Selector { + return attributeSelector(key, func(s string) bool { if s == val { return true @@ -354,10 +223,10 @@ func attributeDashMatch(key, val string, n *html.Node) bool { }) } -// attributePrefixMatch returns a Selector that matches elements where +// attributePrefixSelector returns a Selector that matches elements where // the attribute named key starts with val. -func attributePrefixMatch(key, val string, n *html.Node) bool { - return matchAttribute(n, key, +func attributePrefixSelector(key, val string) Selector { + return attributeSelector(key, func(s string) bool { if strings.TrimSpace(s) == "" { return false @@ -366,10 +235,10 @@ func attributePrefixMatch(key, val string, n *html.Node) bool { }) } -// attributeSuffixMatch matches elements where +// attributeSuffixSelector returns a Selector that matches elements where // the attribute named key ends with val. -func attributeSuffixMatch(key, val string, n *html.Node) bool { - return matchAttribute(n, key, +func attributeSuffixSelector(key, val string) Selector { + return attributeSelector(key, func(s string) bool { if strings.TrimSpace(s) == "" { return false @@ -378,10 +247,10 @@ func attributeSuffixMatch(key, val string, n *html.Node) bool { }) } -// attributeSubstringMatch matches nodes where +// attributeSubstringSelector returns a Selector that matches nodes where // the attribute named key contains val. -func attributeSubstringMatch(key, val string, n *html.Node) bool { - return matchAttribute(n, key, +func attributeSubstringSelector(key, val string) Selector { + return attributeSelector(key, func(s string) bool { if strings.TrimSpace(s) == "" { return false @@ -390,130 +259,39 @@ func attributeSubstringMatch(key, val string, n *html.Node) bool { }) } -// attributeRegexMatch matches nodes where +// attributeRegexSelector returns a Selector that matches nodes where // the attribute named key matches the regular expression rx -func attributeRegexMatch(key string, rx *regexp.Regexp, n *html.Node) bool { - return matchAttribute(n, key, +func attributeRegexSelector(key string, rx *regexp.Regexp) Selector { + return attributeSelector(key, func(s string) bool { return rx.MatchString(s) }) } -func (c attrSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c attrSelector) PseudoElement() string { - return "" -} - -// ---------------- Pseudo class selectors ---------------- -// we use severals concrete types of pseudo-class selectors - -type relativePseudoClassSelector struct { - name string // one of "not", "has", "haschild" - match SelectorGroup -} - -func (s relativePseudoClassSelector) Match(n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } - switch s.name { - case "not": - // matches elements that do not match a. - return !s.match.Match(n) - case "has": - // matches elements with any descendant that matches a. - return hasDescendantMatch(n, s.match) - case "haschild": - // matches elements with a child that matches a. - return hasChildMatch(n, s.match) - default: - panic(fmt.Sprintf("unsupported relative pseudo class selector : %s", s.name)) +// intersectionSelector returns a selector that matches nodes that match +// both a and b. +func intersectionSelector(a, b Selector) Selector { + return func(n *html.Node) bool { + return a(n) && b(n) } } -// hasChildMatch returns whether n has any child that matches a. -func hasChildMatch(n *html.Node, a Matcher) bool { - for c := n.FirstChild; c != nil; c = c.NextSibling { - if a.Match(c) { - return true - } - } - return false -} - -// hasDescendantMatch performs a depth-first search of n's descendants, -// testing whether any of them match a. It returns true as soon as a match is -// found, or false if no match is found. -func hasDescendantMatch(n *html.Node, a Matcher) bool { - for c := n.FirstChild; c != nil; c = c.NextSibling { - if a.Match(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) { - return true - } +// unionSelector returns a selector that matches elements that match +// either a or b. +func unionSelector(a, b Selector) Selector { + return func(n *html.Node) bool { + return a(n) || b(n) } - return false } -// Specificity returns the specificity of the most specific selectors -// in the pseudo-class arguments. -// See https://www.w3.org/TR/selectors/#specificity-rules -func (s relativePseudoClassSelector) Specificity() Specificity { - var max Specificity - for _, sel := range s.match { - newSpe := sel.Specificity() - if max.Less(newSpe) { - max = newSpe +// negatedSelector returns a selector that matches elements that do not match a. +func negatedSelector(a Selector) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false } + return !a(n) } - return max -} - -func (c relativePseudoClassSelector) PseudoElement() string { - return "" -} - -type containsPseudoClassSelector struct { - own bool - value string -} - -func (s containsPseudoClassSelector) Match(n *html.Node) bool { - var text string - if s.own { - // matches nodes that directly contain the given text - text = strings.ToLower(nodeOwnText(n)) - } else { - // matches nodes that contain the given text. - text = strings.ToLower(nodeText(n)) - } - return strings.Contains(text, s.value) -} - -func (s containsPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c containsPseudoClassSelector) PseudoElement() string { - return "" -} - -type regexpPseudoClassSelector struct { - own bool - regexp *regexp.Regexp -} - -func (s regexpPseudoClassSelector) Match(n *html.Node) bool { - var text string - if s.own { - // matches nodes whose text directly matches the specified regular expression - text = nodeOwnText(n) - } else { - // matches nodes whose text matches the specified regular expression - text = nodeText(n) - } - return s.regexp.MatchString(text) } // writeNodeText writes the text contained in n and its descendants to b. @@ -547,214 +325,221 @@ func nodeOwnText(n *html.Node) string { return b.String() } -func (s regexpPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c regexpPseudoClassSelector) PseudoElement() string { - return "" -} - -type nthPseudoClassSelector struct { - a, b int - last, ofType bool -} - -func (s nthPseudoClassSelector) Match(n *html.Node) bool { - if s.a == 0 { - if s.last { - return simpleNthLastChildMatch(s.b, s.ofType, n) - } else { - return simpleNthChildMatch(s.b, s.ofType, n) - } +// textSubstrSelector returns a selector that matches nodes that +// contain the given text. +func textSubstrSelector(val string) Selector { + return func(n *html.Node) bool { + text := strings.ToLower(nodeText(n)) + return strings.Contains(text, val) } - return nthChildMatch(s.a, s.b, s.last, s.ofType, n) } -// nthChildMatch implements :nth-child(an+b). -// If last is true, implements :nth-last-child instead. -// If ofType is true, implements :nth-of-type instead. -func nthChildMatch(a, b int, last, ofType bool, n *html.Node) bool { - if n.Type != html.ElementNode { - return false +// ownTextSubstrSelector returns a selector that matches nodes that +// directly contain the given text +func ownTextSubstrSelector(val string) Selector { + return func(n *html.Node) bool { + text := strings.ToLower(nodeOwnText(n)) + return strings.Contains(text, val) } +} - parent := n.Parent - if parent == nil { - return false +// textRegexSelector returns a selector that matches nodes whose text matches +// the specified regular expression +func textRegexSelector(rx *regexp.Regexp) Selector { + return func(n *html.Node) bool { + return rx.MatchString(nodeText(n)) } +} - if parent.Type == html.DocumentNode { - return false +// ownTextRegexSelector returns a selector that matches nodes whose text +// directly matches the specified regular expression +func ownTextRegexSelector(rx *regexp.Regexp) Selector { + return func(n *html.Node) bool { + return rx.MatchString(nodeOwnText(n)) } +} - i := -1 - count := 0 - for c := parent.FirstChild; c != nil; c = c.NextSibling { - if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) { - continue - } - count++ - if c == n { - i = count - if !last { - break - } +// hasChildSelector returns a selector that matches elements +// with a child that matches a. +func hasChildSelector(a Selector) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false } + return hasChildMatch(n, a) } +} - if i == -1 { - // This shouldn't happen, since n should always be one of its parent's children. - return false - } - - if last { - i = count - i + 1 - } - - i -= b - if a == 0 { - return i == 0 +// hasDescendantSelector returns a selector that matches elements +// with any descendant that matches a. +func hasDescendantSelector(a Selector) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false + } + return hasDescendantMatch(n, a) } - - return i%a == 0 && i/a >= 0 } -// simpleNthChildMatch implements :nth-child(b). +// nthChildSelector returns a selector that implements :nth-child(an+b). +// If last is true, implements :nth-last-child instead. // If ofType is true, implements :nth-of-type instead. -func simpleNthChildMatch(b int, ofType bool, n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } - - parent := n.Parent - if parent == nil { - return false - } +func nthChildSelector(a, b int, last, ofType bool) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false + } - if parent.Type == html.DocumentNode { - return false - } + parent := n.Parent + if parent == nil { + return false + } - count := 0 - for c := parent.FirstChild; c != nil; c = c.NextSibling { - if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { - continue + if parent.Type == html.DocumentNode { + return false } - count++ - if c == n { - return count == b + + i := -1 + count := 0 + for c := parent.FirstChild; c != nil; c = c.NextSibling { + if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) { + continue + } + count++ + if c == n { + i = count + if !last { + break + } + } } - if count >= b { + + if i == -1 { + // This shouldn't happen, since n should always be one of its parent's children. return false } - } - return false -} -// simpleNthLastChildMatch implements :nth-last-child(b). -// If ofType is true, implements :nth-last-of-type instead. -func simpleNthLastChildMatch(b int, ofType bool, n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } + if last { + i = count - i + 1 + } - parent := n.Parent - if parent == nil { - return false - } + i -= b + if a == 0 { + return i == 0 + } - if parent.Type == html.DocumentNode { - return false + return i%a == 0 && i/a >= 0 } +} - count := 0 - for c := parent.LastChild; c != nil; c = c.PrevSibling { - if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { - continue - } - count++ - if c == n { - return count == b +// simpleNthChildSelector returns a selector that implements :nth-child(b). +// If ofType is true, implements :nth-of-type instead. +func simpleNthChildSelector(b int, ofType bool) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false } - if count >= b { + + parent := n.Parent + if parent == nil { return false } - } - return false -} -// Specificity for nth-child pseudo-class. -// Does not support a list of selectors -func (s nthPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} + if parent.Type == html.DocumentNode { + return false + } -func (c nthPseudoClassSelector) PseudoElement() string { - return "" + count := 0 + for c := parent.FirstChild; c != nil; c = c.NextSibling { + if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { + continue + } + count++ + if c == n { + return count == b + } + if count >= b { + return false + } + } + return false + } } -type onlyChildPseudoClassSelector struct { - ofType bool -} +// simpleNthLastChildSelector returns a selector that implements +// :nth-last-child(b). If ofType is true, implements :nth-last-of-type +// instead. +func simpleNthLastChildSelector(b int, ofType bool) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false + } -// Match implements :only-child. -// If `ofType` is true, it implements :only-of-type instead. -func (s onlyChildPseudoClassSelector) Match(n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } + parent := n.Parent + if parent == nil { + return false + } - parent := n.Parent - if parent == nil { - return false - } + if parent.Type == html.DocumentNode { + return false + } - if parent.Type == html.DocumentNode { + count := 0 + for c := parent.LastChild; c != nil; c = c.PrevSibling { + if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { + continue + } + count++ + if c == n { + return count == b + } + if count >= b { + return false + } + } return false } +} - count := 0 - for c := parent.FirstChild; c != nil; c = c.NextSibling { - if (c.Type != html.ElementNode) || (s.ofType && c.Data != n.Data) { - continue +// onlyChildSelector returns a selector that implements :only-child. +// If ofType is true, it implements :only-of-type instead. +func onlyChildSelector(ofType bool) Selector { + return func(n *html.Node) bool { + if n.Type != html.ElementNode { + return false } - count++ - if count > 1 { + + parent := n.Parent + if parent == nil { return false } - } - return count == 1 -} + if parent.Type == html.DocumentNode { + return false + } -func (s onlyChildPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} + count := 0 + for c := parent.FirstChild; c != nil; c = c.NextSibling { + if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) { + continue + } + count++ + if count > 1 { + return false + } + } -func (c onlyChildPseudoClassSelector) PseudoElement() string { - return "" + return count == 1 + } } -type inputPseudoClassSelector struct{} - -// Matches input, select, textarea and button elements. -func (s inputPseudoClassSelector) Match(n *html.Node) bool { +// inputSelector is a Selector that matches input, select, textarea and button elements. +func inputSelector(n *html.Node) bool { return n.Type == html.ElementNode && (n.Data == "input" || n.Data == "select" || n.Data == "textarea" || n.Data == "button") } -func (s inputPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c inputPseudoClassSelector) PseudoElement() string { - return "" -} - -type emptyElementPseudoClassSelector struct{} - -// Matches empty elements. -func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool { +// emptyElementSelector is a Selector that matches empty elements. +func emptyElementSelector(n *html.Node) bool { if n.Type != html.ElementNode { return false } @@ -769,170 +554,69 @@ func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool { return true } -func (s emptyElementPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c emptyElementPseudoClassSelector) PseudoElement() string { - return "" -} - -type rootPseudoClassSelector struct{} - -// Match implements :root -func (s rootPseudoClassSelector) Match(n *html.Node) bool { - if n.Type != html.ElementNode { - return false - } - if n.Parent == nil { - return false - } - return n.Parent.Type == html.DocumentNode -} - -func (s rootPseudoClassSelector) Specificity() Specificity { - return Specificity{0, 1, 0} -} - -func (c rootPseudoClassSelector) PseudoElement() string { - return "" -} - -type compoundSelector struct { - selectors []Sel - pseudoElement string -} - -// Matches elements if each sub-selectors matches. -func (t compoundSelector) Match(n *html.Node) bool { - if len(t.selectors) == 0 { - return n.Type == html.ElementNode - } - - for _, sel := range t.selectors { - if !sel.Match(n) { +// descendantSelector returns a Selector that matches an element if +// it matches d and has an ancestor that matches a. +func descendantSelector(a, d Selector) Selector { + return func(n *html.Node) bool { + if !d(n) { return false } - } - return true -} -func (s compoundSelector) Specificity() Specificity { - var out Specificity - for _, sel := range s.selectors { - out = out.Add(sel.Specificity()) - } - if s.pseudoElement != "" { - // https://drafts.csswg.org/selectors-3/#specificity - out = out.Add(Specificity{0, 0, 1}) - } - return out -} - -func (c compoundSelector) PseudoElement() string { - return c.pseudoElement -} - -type combinedSelector struct { - first Sel - combinator byte - second Sel -} - -func (t combinedSelector) Match(n *html.Node) bool { - if t.first == nil { - return false // maybe we should panic - } - switch t.combinator { - case 0: - return t.first.Match(n) - case ' ': - return descendantMatch(t.first, t.second, n) - case '>': - return childMatch(t.first, t.second, n) - case '+': - return siblingMatch(t.first, t.second, true, n) - case '~': - return siblingMatch(t.first, t.second, false, n) - default: - panic("unknown combinator") - } -} + for p := n.Parent; p != nil; p = p.Parent { + if a(p) { + return true + } + } -// matches an element if it matches d and has an ancestor that matches a. -func descendantMatch(a, d Matcher, n *html.Node) bool { - if !d.Match(n) { return false } - - for p := n.Parent; p != nil; p = p.Parent { - if a.Match(p) { - return true - } - } - - return false } -// matches an element if it matches d and its parent matches a. -func childMatch(a, d Matcher, n *html.Node) bool { - return d.Match(n) && n.Parent != nil && a.Match(n.Parent) +// childSelector returns a Selector that matches an element if +// it matches d and its parent matches a. +func childSelector(a, d Selector) Selector { + return func(n *html.Node) bool { + return d(n) && n.Parent != nil && a(n.Parent) + } } -// matches an element if it matches s2 and is preceded by an element that matches s1. +// siblingSelector returns a Selector that matches an element +// if it matches s2 and in is preceded by an element that matches s1. // If adjacent is true, the sibling must be immediately before the element. -func siblingMatch(s1, s2 Matcher, adjacent bool, n *html.Node) bool { - if !s2.Match(n) { - return false - } +func siblingSelector(s1, s2 Selector, adjacent bool) Selector { + return func(n *html.Node) bool { + if !s2(n) { + return false + } - if adjacent { - for n = n.PrevSibling; n != nil; n = n.PrevSibling { - if n.Type == html.TextNode || n.Type == html.CommentNode { - continue + if adjacent { + for n = n.PrevSibling; n != nil; n = n.PrevSibling { + if n.Type == html.TextNode || n.Type == html.CommentNode { + continue + } + return s1(n) } - return s1.Match(n) + return false } - return false - } - // Walk backwards looking for element that matches s1 - for c := n.PrevSibling; c != nil; c = c.PrevSibling { - if s1.Match(c) { - return true + // Walk backwards looking for element that matches s1 + for c := n.PrevSibling; c != nil; c = c.PrevSibling { + if s1(c) { + return true + } } - } - - return false -} -func (s combinedSelector) Specificity() Specificity { - spec := s.first.Specificity() - if s.second != nil { - spec = spec.Add(s.second.Specificity()) + return false } - return spec } -// on combinedSelector, a pseudo-element only makes sens on the last -// selector, although others increase specificity. -func (c combinedSelector) PseudoElement() string { - if c.second == nil { - return "" +// rootSelector implements :root +func rootSelector(n *html.Node) bool { + if n.Type != html.ElementNode { + return false } - return c.second.PseudoElement() -} - -// A SelectorGroup is a list of selectors, which matches if any of the -// individual selectors matches. -type SelectorGroup []Sel - -// Match returns true if the node matches one of the single selectors. -func (s SelectorGroup) Match(n *html.Node) bool { - for _, sel := range s { - if sel.Match(n) { - return true - } + if n.Parent == nil { + return false } - return false + return n.Parent.Type == html.DocumentNode } diff --git a/vendor/github.com/andybalholm/cascadia/serialize.go b/vendor/github.com/andybalholm/cascadia/serialize.go deleted file mode 100644 index f15b079..0000000 --- a/vendor/github.com/andybalholm/cascadia/serialize.go +++ /dev/null @@ -1,120 +0,0 @@ -package cascadia - -import ( - "fmt" - "strings" -) - -// implements the reverse operation Sel -> string - -func (c tagSelector) String() string { - return c.tag -} - -func (c idSelector) String() string { - return "#" + c.id -} - -func (c classSelector) String() string { - return "." + c.class -} - -func (c attrSelector) String() string { - val := c.val - if c.operation == "#=" { - val = c.regexp.String() - } else if c.operation != "" { - val = fmt.Sprintf(`"%s"`, val) - } - return fmt.Sprintf(`[%s%s%s]`, c.key, c.operation, val) -} - -func (c relativePseudoClassSelector) String() string { - return fmt.Sprintf(":%s(%s)", c.name, c.match.String()) -} -func (c containsPseudoClassSelector) String() string { - s := "contains" - if c.own { - s += "Own" - } - return fmt.Sprintf(`:%s("%s")`, s, c.value) -} -func (c regexpPseudoClassSelector) String() string { - s := "matches" - if c.own { - s += "Own" - } - return fmt.Sprintf(":%s(%s)", s, c.regexp.String()) -} -func (c nthPseudoClassSelector) String() string { - if c.a == 0 && c.b == 1 { // special cases - s := ":first-" - if c.last { - s = ":last-" - } - if c.ofType { - s += "of-type" - } else { - s += "child" - } - return s - } - var name string - switch [2]bool{c.last, c.ofType} { - case [2]bool{true, true}: - name = "nth-last-of-type" - case [2]bool{true, false}: - name = "nth-last-child" - case [2]bool{false, true}: - name = "nth-of-type" - case [2]bool{false, false}: - name = "nth-child" - } - return fmt.Sprintf(":%s(%dn+%d)", name, c.a, c.b) -} -func (c onlyChildPseudoClassSelector) String() string { - if c.ofType { - return ":only-of-type" - } - return ":only-child" -} -func (c inputPseudoClassSelector) String() string { - return ":input" -} -func (c emptyElementPseudoClassSelector) String() string { - return ":empty" -} -func (c rootPseudoClassSelector) String() string { - return ":root" -} - -func (c compoundSelector) String() string { - if len(c.selectors) == 0 && c.pseudoElement == "" { - return "*" - } - chunks := make([]string, len(c.selectors)) - for i, sel := range c.selectors { - chunks[i] = sel.String() - } - s := strings.Join(chunks, "") - if c.pseudoElement != "" { - s += "::" + c.pseudoElement - } - return s -} - -func (c combinedSelector) String() string { - start := c.first.String() - if c.second != nil { - start += fmt.Sprintf(" %s %s", string(c.combinator), c.second.String()) - } - return start -} - -func (c SelectorGroup) String() string { - ck := make([]string, len(c)) - for i, s := range c { - ck[i] = s.String() - } - return strings.Join(ck, ", ") -} diff --git a/vendor/github.com/andybalholm/cascadia/specificity.go b/vendor/github.com/andybalholm/cascadia/specificity.go deleted file mode 100644 index 8db864f..0000000 --- a/vendor/github.com/andybalholm/cascadia/specificity.go +++ /dev/null @@ -1,26 +0,0 @@ -package cascadia - -// Specificity is the CSS specificity as defined in -// https://www.w3.org/TR/selectors/#specificity-rules -// with the convention Specificity = [A,B,C]. -type Specificity [3]int - -// returns `true` if s < other (strictly), false otherwise -func (s Specificity) Less(other Specificity) bool { - for i := range s { - if s[i] < other[i] { - return true - } - if s[i] > other[i] { - return false - } - } - return false -} - -func (s Specificity) Add(other Specificity) Specificity { - for i, sp := range other { - s[i] += sp - } - return s -} diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore new file mode 100644 index 0000000..042091d --- /dev/null +++ b/vendor/github.com/golang/snappy/.gitignore @@ -0,0 +1,16 @@ +cmd/snappytool/snappytool +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS index f10b49b..bcfa195 100644 --- a/vendor/github.com/golang/snappy/AUTHORS +++ b/vendor/github.com/golang/snappy/AUTHORS @@ -11,6 +11,5 @@ Damian Gryski <dgryski@gmail.com> Google Inc. Jan Mercl <0xjnml@gmail.com> -Klaus Post <klauspost@gmail.com> Rodolfo Carvalho <rhcarvalho@gmail.com> Sebastien Binet <seb.binet@gmail.com> diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS index 3bd40cf..931ae31 100644 --- a/vendor/github.com/golang/snappy/CONTRIBUTORS +++ b/vendor/github.com/golang/snappy/CONTRIBUTORS @@ -29,7 +29,6 @@ Damian Gryski <dgryski@gmail.com> Jan Mercl <0xjnml@gmail.com> Kai Backman <kaib@golang.org> -Klaus Post <klauspost@gmail.com> Marc-Antoine Ruel <maruel@chromium.org> Nigel Tao <nigeltao@golang.org> Rob Pike <r@golang.org> diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go index f1e04b1..72efb03 100644 --- a/vendor/github.com/golang/snappy/decode.go +++ b/vendor/github.com/golang/snappy/decode.go @@ -52,8 +52,6 @@ const ( // Otherwise, a newly allocated slice will be returned. // // The dst and src must not overlap. It is valid to pass a nil dst. -// -// Decode handles the Snappy block format, not the Snappy stream format. func Decode(dst, src []byte) ([]byte, error) { dLen, s, err := decodedLen(src) if err != nil { @@ -85,8 +83,6 @@ func NewReader(r io.Reader) *Reader { } // Reader is an io.Reader that can read Snappy-compressed bytes. -// -// Reader handles the Snappy stream format, not the Snappy block format. type Reader struct { r io.Reader err error diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go index b88318e..8c9f204 100644 --- a/vendor/github.com/golang/snappy/decode_other.go +++ b/vendor/github.com/golang/snappy/decode_other.go @@ -85,28 +85,14 @@ func decode(dst, src []byte) int { if offset <= 0 || d < offset || length > len(dst)-d { return decodeErrCodeCorrupt } - // Copy from an earlier sub-slice of dst to a later sub-slice. - // If no overlap, use the built-in copy: - if offset >= length { - copy(dst[d:d+length], dst[d-offset:]) - d += length - continue - } - - // Unlike the built-in copy function, this byte-by-byte copy always runs + // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike + // the built-in copy function, this byte-by-byte copy always runs // forwards, even if the slices overlap. Conceptually, this is: // // d += forwardCopy(dst[d:d+length], dst[d-offset:]) - // - // We align the slices into a and b and show the compiler they are the same size. - // This allows the loop to run without bounds checks. - a := dst[d : d+length] - b := dst[d-offset:] - b = b[:len(a)] - for i := range a { - a[i] = b[i] + for end := d + length; d != end; d++ { + dst[d] = dst[d-offset] } - d += length } if d != len(dst) { return decodeErrCodeCorrupt diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go index 7f23657..8d393e9 100644 --- a/vendor/github.com/golang/snappy/encode.go +++ b/vendor/github.com/golang/snappy/encode.go @@ -15,8 +15,6 @@ import ( // Otherwise, a newly allocated slice will be returned. // // The dst and src must not overlap. It is valid to pass a nil dst. -// -// Encode handles the Snappy block format, not the Snappy stream format. func Encode(dst, src []byte) []byte { if n := MaxEncodedLen(len(src)); n < 0 { panic(ErrTooLarge) @@ -141,8 +139,6 @@ func NewBufferedWriter(w io.Writer) *Writer { } // Writer is an io.Writer that can write Snappy-compressed bytes. -// -// Writer handles the Snappy stream format, not the Snappy block format. type Writer struct { w io.Writer err error diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go index 5f6ed93..59d4ee9 100644 --- a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go +++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go @@ -19,7 +19,5 @@ const supportAllowUnexported = true // The parent struct, v, must be addressable, while f must be a StructField // describing the field to retrieve. func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value { - // See https://github.com/google/go-cmp/issues/167 for discussion of the - // following expression. - return reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() + return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem() } diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index a20390d..7934481 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -226,19 +226,7 @@ func (validator) apply(s *state, vx, vy reflect.Value) { // Unable to Interface implies unexported field without visibility access. if !vx.CanInterface() || !vy.CanInterface() { const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported" - var name string - if t := s.curPath.Index(-2).Type(); t.Name() != "" { - // Named type with unexported fields. - name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType - } else { - // Unnamed type with unexported fields. Derive PkgPath from field. - var pkgPath string - for i := 0; i < t.NumField() && pkgPath == ""; i++ { - pkgPath = t.Field(i).PkgPath - } - name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) - } - panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) + panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help)) } panic("not reachable") diff --git a/vendor/github.com/google/uuid/.travis.yml b/vendor/github.com/google/uuid/.travis.yml new file mode 100644 index 0000000..d8156a6 --- /dev/null +++ b/vendor/github.com/google/uuid/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.4.3 + - 1.5.3 + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go index 9ad1aba..84af91c 100644 --- a/vendor/github.com/google/uuid/version4.go +++ b/vendor/github.com/google/uuid/version4.go @@ -27,12 +27,8 @@ func New() UUID { // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { - return NewRandomFromReader(rander) -} - -func NewRandomFromReader(r io.Reader) (UUID, error) { var uuid UUID - _, err := io.ReadFull(r, uuid[:]) + _, err := io.ReadFull(rander, uuid[:]) if err != nil { return Nil, err } @@ -40,4 +36,3 @@ func NewRandomFromReader(r io.Reader) (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } - diff --git a/vendor/github.com/pborman/uuid/.travis.yml b/vendor/github.com/pborman/uuid/.travis.yml new file mode 100644 index 0000000..3deb4a1 --- /dev/null +++ b/vendor/github.com/pborman/uuid/.travis.yml @@ -0,0 +1,10 @@ +language: go + +go: + - "1.9" + - "1.10" + - "1.11" + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pborman/uuid/time.go b/vendor/github.com/pborman/uuid/time.go index 7286824..5c0960d 100644 --- a/vendor/github.com/pborman/uuid/time.go +++ b/vendor/github.com/pborman/uuid/time.go @@ -29,7 +29,7 @@ func GetTime() (Time, uint16, error) { return guuid.GetTime() } // for func ClockSequence() int { return guuid.ClockSequence() } -// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to // -1 causes a new sequence to be generated. func SetClockSequence(seq int) { guuid.SetClockSequence(seq) } diff --git a/vendor/github.com/pborman/uuid/version4.go b/vendor/github.com/pborman/uuid/version4.go index 767dd0c..b459d46 100644 --- a/vendor/github.com/pborman/uuid/version4.go +++ b/vendor/github.com/pborman/uuid/version4.go @@ -6,7 +6,7 @@ package uuid import guuid "github.com/google/uuid" -// NewRandom returns a Random (Version 4) UUID or panics. +// Random returns a Random (Version 4) UUID or panics. // // The strength of the UUIDs is based on the strength of the crypto/rand // package. |