├── .gitignore ├── vendor ├── github.com │ ├── PuerkitoBio │ │ └── goquery │ │ │ ├── .gitattributes │ │ │ ├── go.mod │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── go.sum │ │ │ ├── LICENSE │ │ │ ├── iteration.go │ │ │ ├── query.go │ │ │ ├── expand.go │ │ │ ├── array.go │ │ │ ├── type.go │ │ │ ├── doc.go │ │ │ ├── utilities.go │ │ │ ├── filter.go │ │ │ ├── property.go │ │ │ ├── README.md │ │ │ └── manipulation.go │ ├── andybalholm │ │ └── cascadia │ │ │ ├── go.mod │ │ │ ├── .travis.yml │ │ │ ├── README.md │ │ │ ├── specificity.go │ │ │ └── LICENSE │ └── docopt │ │ └── docopt.go │ │ ├── test_golang.docopt │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── error.go │ │ ├── LICENSE │ │ ├── doc.go │ │ ├── token.go │ │ ├── README.md │ │ ├── opts.go │ │ ├── pattern.go │ │ ├── testcases.docopt │ │ └── docopt.go ├── golang.org │ └── x │ │ └── net │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── PATENTS │ │ ├── LICENSE │ │ └── html │ │ ├── atom │ │ └── atom.go │ │ ├── const.go │ │ ├── doc.go │ │ ├── doctype.go │ │ ├── node.go │ │ ├── escape.go │ │ ├── foreign.go │ │ └── render.go └── modules.txt ├── internal └── api │ ├── response.go │ ├── item.go │ ├── answer_test.go │ ├── answer.go │ └── search.go ├── go.mod ├── go.sum ├── cmd └── so │ └── main.go ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/.gitattributes: -------------------------------------------------------------------------------- 1 | testdata/* linguist-vendored 2 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/go.mod: -------------------------------------------------------------------------------- 1 | module "github.com/andybalholm/cascadia" 2 | 3 | require "golang.org/x/net" v0.0.0-20180218175443-cbe0f9307d01 4 | -------------------------------------------------------------------------------- /internal/api/response.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Response models a response from the Search API 4 | type Response struct { 5 | Items []Item `json:"items"` 6 | } 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cheat/so 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.5.1 7 | github.com/docopt/docopt.go v0.0.0-20180111231733-ee0de3bc6815 8 | ) 9 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Go Authors for copyright purposes. 2 | # The master list of authors is in the main Go distribution, 3 | # visible at http://tip.golang.org/AUTHORS. 4 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Go contributors. 2 | # The master list of contributors is in the main Go distribution, 3 | # visible at http://tip.golang.org/CONTRIBUTORS. 4 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/test_golang.docopt: -------------------------------------------------------------------------------- 1 | r"""usage: prog [NAME_-2]...""" 2 | $ prog 10 20 3 | {"NAME_-2": ["10", "20"]} 4 | 5 | $ prog 10 6 | {"NAME_-2": ["10"]} 7 | 8 | $ prog 9 | {"NAME_-2": []} 10 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/PuerkitoBio/goquery 2 | 3 | require ( 4 | github.com/andybalholm/cascadia v1.1.0 5 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 6 | ) 7 | 8 | go 1.13 9 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3 5 | - 1.4 6 | 7 | install: 8 | - go get github.com/andybalholm/cascadia 9 | 10 | script: 11 | - go test -v 12 | 13 | notifications: 14 | email: false 15 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/.gitignore: -------------------------------------------------------------------------------- 1 | # editor temporary files 2 | *.sublime-* 3 | .DS_Store 4 | *.swp 5 | #*.*# 6 | tags 7 | 8 | # direnv config 9 | .env* 10 | 11 | # test binaries 12 | *.test 13 | 14 | # coverage and profilte outputs 15 | *.out 16 | 17 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2.x 5 | - 1.3.x 6 | - 1.4.x 7 | - 1.5.x 8 | - 1.6.x 9 | - 1.7.x 10 | - 1.8.x 11 | - 1.9.x 12 | - 1.10.x 13 | - 1.11.x 14 | - 1.12.x 15 | - 1.13.x 16 | - tip 17 | 18 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/PuerkitoBio/goquery v1.5.1 2 | github.com/PuerkitoBio/goquery 3 | # github.com/andybalholm/cascadia v1.1.0 4 | github.com/andybalholm/cascadia 5 | # github.com/docopt/docopt.go v0.0.0-20180111231733-ee0de3bc6815 6 | github.com/docopt/docopt.go 7 | # golang.org/x/net v0.0.0-20200202094626-16171245cfb2 8 | golang.org/x/net/html 9 | golang.org/x/net/html/atom 10 | -------------------------------------------------------------------------------- /internal/api/item.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // Item models Search Response items (questions) 4 | type Item struct { 5 | AnswerCount int `json:"answer_count"` 6 | Answers []Answer `json:"answers"` 7 | Body string `json:"body"` 8 | Link string `json:"link"` 9 | QuestionID int `json:"question_id"` 10 | Score int `json:"score"` 11 | Title string `json:"title"` 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | # coverage droppings 25 | profile.cov 26 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/README.md: -------------------------------------------------------------------------------- 1 | # cascadia 2 | 3 | [![](https://travis-ci.org/andybalholm/cascadia.svg)](https://travis-ci.org/andybalholm/cascadia) 4 | 5 | The Cascadia package implements CSS selectors for use with the parse trees produced by the html package. 6 | 7 | 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. 8 | 9 | [Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia). 10 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/specificity.go: -------------------------------------------------------------------------------- 1 | package cascadia 2 | 3 | // Specificity is the CSS specificity as defined in 4 | // https://www.w3.org/TR/selectors/#specificity-rules 5 | // with the convention Specificity = [A,B,C]. 6 | type Specificity [3]int 7 | 8 | // returns `true` if s < other (strictly), false otherwise 9 | func (s Specificity) Less(other Specificity) bool { 10 | for i := range s { 11 | if s[i] < other[i] { 12 | return true 13 | } 14 | if s[i] > other[i] { 15 | return false 16 | } 17 | } 18 | return false 19 | } 20 | 21 | func (s Specificity) Add(other Specificity) Specificity { 22 | for i, sp := range other { 23 | s[i] += sp 24 | } 25 | return s 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI (http://travis-ci.org/) is a continuous integration 2 | # service for open source projects. This file configures it 3 | # to run unit tests for docopt-go. 4 | 5 | language: go 6 | 7 | go: 8 | - 1.4 9 | - 1.5 10 | - 1.6 11 | - 1.7 12 | - 1.8 13 | - 1.9 14 | - tip 15 | 16 | matrix: 17 | fast_finish: true 18 | 19 | before_install: 20 | - go get golang.org/x/tools/cmd/cover 21 | - go get github.com/mattn/goveralls 22 | 23 | install: 24 | - go get -d -v ./... && go build -v ./... 25 | 26 | script: 27 | - go vet -x ./... 28 | - go test -v ./... 29 | - go test -covermode=count -coverprofile=profile.cov . 30 | 31 | after_script: 32 | - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci 33 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= 2 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 4 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 5 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= 6 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 7 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 8 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 9 | -------------------------------------------------------------------------------- /internal/api/answer_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestExtract asserts that Extract properly extracts code blocks from an 8 | // Answer's body 9 | func TestExtract(t *testing.T) { 10 | 11 | // initialize an answer 12 | // NB: important to note is that we only want to extract the 13 | //
blocks, not the inline blocks. 14 | body := ` 15 |

Try this: 16 | 17 |


18 | 	fmt.Println("hello, world!")
19 | 	
20 | 21 |

It will write "hello, world!" to stdout` 22 | ans := Answer{Body: body} 23 | 24 | // extract the code blocks 25 | got, err := ans.Extract() 26 | if err != nil { 27 | t.Errorf("failed to extract code: %v", err) 28 | } 29 | 30 | // assert that the expected result was returned 31 | want := `fmt.Println("hello, world!")` 32 | if got != want { 33 | t.Errorf("failed to extract code: want: %s, got: %s", want, got) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /internal/api/answer.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/PuerkitoBio/goquery" 8 | ) 9 | 10 | // Answer models an answer to a Question 11 | type Answer struct { 12 | AnswerID int `json:"answer_id"` 13 | Body string `json:"body"` 14 | IsAccepted bool `json:"is_accepted"` 15 | Link string `json:"link"` 16 | Score int `json:"score"` 17 | Code string `json:"code"` 18 | } 19 | 20 | // Extract extracts code blocks from the Answer body 21 | func (a *Answer) Extract() (string, error) { 22 | // parse the body string into a DOM 23 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(a.Body)) 24 | if err != nil { 25 | return "", fmt.Errorf("failed to parse document: %v", err) 26 | } 27 | 28 | // extract and return code blocks 29 | blocks := []string{} 30 | doc.Find("pre code").Each(func(i int, s *goquery.Selection) { 31 | blocks = append(blocks, s.Text()) 32 | }) 33 | 34 | return strings.TrimSpace(strings.Join(blocks, "\n")), nil 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/error.go: -------------------------------------------------------------------------------- 1 | package docopt 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type errorType int 8 | 9 | const ( 10 | errorUser errorType = iota 11 | errorLanguage 12 | ) 13 | 14 | func (e errorType) String() string { 15 | switch e { 16 | case errorUser: 17 | return "errorUser" 18 | case errorLanguage: 19 | return "errorLanguage" 20 | } 21 | return "" 22 | } 23 | 24 | // UserError records an error with program arguments. 25 | type UserError struct { 26 | msg string 27 | Usage string 28 | } 29 | 30 | func (e UserError) Error() string { 31 | return e.msg 32 | } 33 | func newUserError(msg string, f ...interface{}) error { 34 | return &UserError{fmt.Sprintf(msg, f...), ""} 35 | } 36 | 37 | // LanguageError records an error with the doc string. 38 | type LanguageError struct { 39 | msg string 40 | } 41 | 42 | func (e LanguageError) Error() string { 43 | return e.msg 44 | } 45 | func newLanguageError(msg string, f ...interface{}) error { 46 | return &LanguageError{fmt.Sprintf(msg, f...)} 47 | } 48 | 49 | var newError = fmt.Errorf 50 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Keith Batten 4 | Copyright (c) 2016 David Irvine 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= 2 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 3 | github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= 4 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 5 | github.com/docopt/docopt.go v0.0.0-20180111231733-ee0de3bc6815 h1:HMAfwOa33y82IaQEKQDfUCiwNlxtM1iw7HLM9ru0RNc= 6 | github.com/docopt/docopt.go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:l7JNRynTRuqe45tpIyItHNqZWTxywYjp87MWTOnU5cg= 7 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 8 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 9 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= 10 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 13 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Andy Balholm. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2016, Martin Angers & Contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/iteration.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | // Each iterates over a Selection object, executing a function for each 4 | // matched element. It returns the current Selection object. The function 5 | // f is called for each element in the selection with the index of the 6 | // element in that selection starting at 0, and a *Selection that contains 7 | // only that element. 8 | func (s *Selection) Each(f func(int, *Selection)) *Selection { 9 | for i, n := range s.Nodes { 10 | f(i, newSingleSelection(n, s.document)) 11 | } 12 | return s 13 | } 14 | 15 | // EachWithBreak iterates over a Selection object, executing a function for each 16 | // matched element. It is identical to Each except that it is possible to break 17 | // out of the loop by returning false in the callback function. It returns the 18 | // current Selection object. 19 | func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection { 20 | for i, n := range s.Nodes { 21 | if !f(i, newSingleSelection(n, s.document)) { 22 | return s 23 | } 24 | } 25 | return s 26 | } 27 | 28 | // Map passes each element in the current matched set through a function, 29 | // producing a slice of string holding the returned values. The function 30 | // f is called for each element in the selection with the index of the 31 | // element in that selection starting at 0, and a *Selection that contains 32 | // only that element. 33 | func (s *Selection) Map(f func(int, *Selection) string) (result []string) { 34 | for i, n := range s.Nodes { 35 | result = append(result, f(i, newSingleSelection(n, s.document))) 36 | } 37 | 38 | return result 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/query.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Is checks the current matched set of elements against a selector and 6 | // returns true if at least one of these elements matches. 7 | func (s *Selection) Is(selector string) bool { 8 | return s.IsMatcher(compileMatcher(selector)) 9 | } 10 | 11 | // IsMatcher checks the current matched set of elements against a matcher and 12 | // returns true if at least one of these elements matches. 13 | func (s *Selection) IsMatcher(m Matcher) bool { 14 | if len(s.Nodes) > 0 { 15 | if len(s.Nodes) == 1 { 16 | return m.Match(s.Nodes[0]) 17 | } 18 | return len(m.Filter(s.Nodes)) > 0 19 | } 20 | 21 | return false 22 | } 23 | 24 | // IsFunction checks the current matched set of elements against a predicate and 25 | // returns true if at least one of these elements matches. 26 | func (s *Selection) IsFunction(f func(int, *Selection) bool) bool { 27 | return s.FilterFunction(f).Length() > 0 28 | } 29 | 30 | // IsSelection checks the current matched set of elements against a Selection object 31 | // and returns true if at least one of these elements matches. 32 | func (s *Selection) IsSelection(sel *Selection) bool { 33 | return s.FilterSelection(sel).Length() > 0 34 | } 35 | 36 | // IsNodes checks the current matched set of elements against the specified nodes 37 | // and returns true if at least one of these elements matches. 38 | func (s *Selection) IsNodes(nodes ...*html.Node) bool { 39 | return s.FilterNodes(nodes...).Length() > 0 40 | } 41 | 42 | // Contains returns true if the specified Node is within, 43 | // at any depth, one of the nodes in the Selection object. 44 | // It is NOT inclusive, to behave like jQuery's implementation, and 45 | // unlike Javascript's .contains, so if the contained 46 | // node is itself in the selection, it returns false. 47 | func (s *Selection) Contains(n *html.Node) bool { 48 | return sliceContains(s.Nodes, n) 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package docopt parses command-line arguments based on a help message. 3 | 4 | Given a conventional command-line help message, docopt processes the arguments. 5 | See https://github.com/docopt/docopt#help-message-format for a description of 6 | the help message format. 7 | 8 | This package exposes three different APIs, depending on the level of control 9 | required. The first, simplest way to parse your docopt usage is to just call: 10 | 11 | docopt.ParseDoc(usage) 12 | 13 | This will use os.Args[1:] as the argv slice, and use the default parser 14 | options. If you want to provide your own version string and args, then use: 15 | 16 | docopt.ParseArgs(usage, argv, "1.2.3") 17 | 18 | If the last parameter (version) is a non-empty string, it will be printed when 19 | --version is given in the argv slice. Finally, we can instantiate our own 20 | docopt.Parser which gives us control over how things like help messages are 21 | printed and whether to exit after displaying usage messages, etc. 22 | 23 | parser := &docopt.Parser{ 24 | HelpHandler: docopt.PrintHelpOnly, 25 | OptionsFirst: true, 26 | } 27 | opts, err := parser.ParseArgs(usage, argv, "") 28 | 29 | In particular, setting your own custom HelpHandler function makes unit testing 30 | your own docs with example command line invocations much more enjoyable. 31 | 32 | All three of these return a map of option names to the values parsed from argv, 33 | and an error or nil. You can get the values using the helpers, or just treat it 34 | as a regular map: 35 | 36 | flag, _ := opts.Bool("--flag") 37 | secs, _ := opts.Int("") 38 | 39 | Additionally, you can `Bind` these to a struct, assigning option values to the 40 | exported fields of that struct, all at once. 41 | 42 | var config struct { 43 | Command string `docopt:""` 44 | Tries int `docopt:"-n"` 45 | Force bool // Gets the value of --force 46 | } 47 | opts.Bind(&config) 48 | */ 49 | package docopt 50 | -------------------------------------------------------------------------------- /internal/api/search.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "sort" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // Search queries the Stack Exchange API for a question matching the search 14 | // criteria 15 | func Search(search string, site string, tags string) ([]Answer, error) { 16 | 17 | // trim the search string 18 | search = strings.TrimSpace(search) 19 | 20 | // disallow an empty search string 21 | if search == "" { 22 | return []Answer{}, fmt.Errorf(" must not be empty") 23 | } 24 | 25 | // initialize an HTTP client 26 | url := "https://api.stackexchange.com/2.2/search/advanced" 27 | cl := http.Client{ 28 | Timeout: time.Second * 5, 29 | } 30 | 31 | req, err := http.NewRequest(http.MethodGet, url, nil) 32 | if err != nil { 33 | return []Answer{}, fmt.Errorf("failed to init http request: %v", err) 34 | } 35 | 36 | // assemble the query string parameters 37 | q := req.URL.Query() 38 | q.Add("answers", "1") // this is the minimum - not the total 39 | q.Add("filter", "!2uDdBA_UQti85J*)fMajDUwad2zPVKEdobB3yIR1nL") 40 | q.Add("order", "desc") 41 | q.Add("q", search) 42 | q.Add("site", site) 43 | q.Add("sort", "relevance") 44 | if tags != "" { 45 | q.Add("tagged", tags) 46 | } 47 | req.URL.RawQuery = q.Encode() 48 | 49 | // make the HTTP request 50 | res, err := cl.Do(req) 51 | if err != nil { 52 | return []Answer{}, fmt.Errorf("failed to make http request: %v", err) 53 | } 54 | 55 | // unpack the JSON response 56 | body, err := ioutil.ReadAll(res.Body) 57 | if err != nil { 58 | return []Answer{}, fmt.Errorf("failed to read response: %v", err) 59 | } 60 | 61 | response := Response{} 62 | err = json.Unmarshal(body, &response) 63 | if err != nil { 64 | return []Answer{}, fmt.Errorf("failed to unmarshall json: %v", err) 65 | } 66 | 67 | // return early if no answers were returned 68 | if len(response.Items) == 0 { 69 | return []Answer{}, nil 70 | } 71 | 72 | question := response.Items[0] 73 | answers := make([]Answer, 0) 74 | for _, a := range question.Answers { 75 | if a.Score >= 1 { 76 | answers = append(answers, a) 77 | } 78 | } 79 | 80 | // sort the answers by score from highest to lowest 81 | sort.SliceStable(answers, func(i, j int) bool { 82 | return answers[i].Score > answers[j].Score 83 | }) 84 | 85 | return answers, nil 86 | } 87 | -------------------------------------------------------------------------------- /cmd/so/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // TODO: relevance/score filter 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | 11 | "github.com/docopt/docopt.go" 12 | 13 | "github.com/cheat/so/internal/api" 14 | ) 15 | 16 | const version = "1.0.0" 17 | 18 | func main() { 19 | 20 | // specify the docopts 21 | // NB: the --json flag is currently ignored: `so` will currently only output 22 | // JSON. This flag is being left here to make it easier to potentially add 23 | // other output formats in the future. 24 | usage := `Usage: 25 | so [options] 26 | 27 | Options: 28 | -j --json Output JSON 29 | -n --number= Maximum number of answers to return 30 | -t --tag= Filter by ` 31 | 32 | // initialize options 33 | opts, err := docopt.Parse(usage, nil, true, version, false) 34 | if err != nil { 35 | // panic here, because this should never happen 36 | fmt.Fprintf(os.Stderr, "docopt failed to parse: %v\n", err) 37 | os.Exit(1) 38 | } 39 | 40 | // optionally assign a --tag value 41 | tags := "" 42 | if opts["--tag"] != nil { 43 | tags = opts["--tag"].(string) 44 | } 45 | 46 | // query the search API 47 | answers, err := api.Search(opts[""].(string), "stackoverflow", tags) 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "failed to query api: %v\n", err) 50 | os.Exit(1) 51 | } 52 | 53 | // decorate the (raw) answers with `Code` members 54 | for i := range answers { 55 | answers[i].Code, err = answers[i].Extract() 56 | if err != nil { 57 | fmt.Fprintf(os.Stderr, "failed to extract code blocks: %v\n", err) 58 | os.Exit(1) 59 | } 60 | } 61 | 62 | // filter out answers that contain no 63 | var filtered []api.Answer 64 | for _, a := range answers { 65 | if a.Code != "" { 66 | filtered = append(filtered, a) 67 | } 68 | } 69 | answers = filtered 70 | 71 | // optionally constrain the response to --number answers 72 | if opts["--number"] != nil { 73 | 74 | // parse --number into an integer (`limit`) 75 | limit, err := strconv.ParseInt(opts["--number"].(string), 10, 64) 76 | if err != nil { 77 | fmt.Fprintf(os.Stderr, "Failed to parse --number: %v\n", err) 78 | os.Exit(1) 79 | } 80 | 81 | // assert that `answers` <= `limit` 82 | if len(answers) > int(limit) { 83 | answers = answers[0:limit] 84 | } 85 | } 86 | 87 | // output JSON and exit 88 | j, err := json.Marshal(answers) 89 | if err != nil { 90 | fmt.Fprintf(os.Stderr, "Failed to marshal answers: %v\n", err) 91 | os.Exit(1) 92 | } 93 | fmt.Println(string(j)) 94 | os.Exit(0) 95 | } 96 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/atom/atom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package atom provides integer codes (also known as atoms) for a fixed set of 6 | // frequently occurring HTML strings: tag names and attribute keys such as "p" 7 | // and "id". 8 | // 9 | // Sharing an atom's name between all elements with the same tag can result in 10 | // fewer string allocations when tokenizing and parsing HTML. Integer 11 | // comparisons are also generally faster than string comparisons. 12 | // 13 | // The value of an atom's particular code is not guaranteed to stay the same 14 | // between versions of this package. Neither is any ordering guaranteed: 15 | // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to 16 | // be dense. The only guarantees are that e.g. looking up "div" will yield 17 | // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. 18 | package atom // import "golang.org/x/net/html/atom" 19 | 20 | // Atom is an integer code for a string. The zero value maps to "". 21 | type Atom uint32 22 | 23 | // String returns the atom's name. 24 | func (a Atom) String() string { 25 | start := uint32(a >> 8) 26 | n := uint32(a & 0xff) 27 | if start+n > uint32(len(atomText)) { 28 | return "" 29 | } 30 | return atomText[start : start+n] 31 | } 32 | 33 | func (a Atom) string() string { 34 | return atomText[a>>8 : a>>8+a&0xff] 35 | } 36 | 37 | // fnv computes the FNV hash with an arbitrary starting value h. 38 | func fnv(h uint32, s []byte) uint32 { 39 | for i := range s { 40 | h ^= uint32(s[i]) 41 | h *= 16777619 42 | } 43 | return h 44 | } 45 | 46 | func match(s string, t []byte) bool { 47 | for i, c := range t { 48 | if s[i] != c { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | // Lookup returns the atom whose name is s. It returns zero if there is no 56 | // such atom. The lookup is case sensitive. 57 | func Lookup(s []byte) Atom { 58 | if len(s) == 0 || len(s) > maxAtomLen { 59 | return 0 60 | } 61 | h := fnv(hash0, s) 62 | if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 63 | return a 64 | } 65 | if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 66 | return a 67 | } 68 | return 0 69 | } 70 | 71 | // String returns a string whose contents are equal to s. In that sense, it is 72 | // equivalent to string(s) but may be more efficient. 73 | func String(s []byte) string { 74 | if a := Lookup(s); a != 0 { 75 | return a.String() 76 | } 77 | return string(s) 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | so 2 | == 3 | `so` is a companion binary to [vim-so][]. It queries the Stack Overflow API, and makes 4 | code snippets available to `vim-so`. 5 | 6 | 7 | Installing 8 | ---------- 9 | Download the newest release from [Releases][] and manually install it on your 10 | system `PATH`. 11 | 12 | 13 | FAQ 14 | --- 15 | ### Why is this executable necessary? ### 16 | `vim-so` must query the Stack Overflow API for code snippets, yet `vim` 17 | provides no native mechanism for making HTTP requests. This executable bridges 18 | that gap. 19 | 20 | ### Why is this executable not bundled with vim-so? ### 21 | This executable is approximately 6Mb in size. In order to support multiple 22 | platforms (currently six in total), `vim-so` would need to contain a compiled 23 | executable for each, and I do not wish to release a ~40Mb `vim` plugin. 24 | 25 | ### Why was this not written in Python? ### 26 | I have had significant difficulty in the past maintaining Python software that 27 | is distributed among a wide user-base. I'm sidestepping that problem by 28 | distributing an executable. 29 | 30 | ### Can I use this to "manually" search Stack Overflow on the command-line? ### 31 | Not in a convenient way. The executable only outputs JSON. 32 | 33 | ### Why not? ### 34 | Initially, I intended for `so` to support that use-case. However, I backed away 35 | from that goal upon realizing two things: 36 | 37 | 1. It is difficult to display a Stack Overflow thread on the command line with 38 | a user-experience that is equivalent to the browser. The browser provides a 39 | superior experience. 40 | 41 | 2. Signficantly: I've found that search engines do a better job of returning 42 | relevant Stack Overflow threads than Stack Overflow's own API. If the API 43 | doesn't return an adequate response, your preferred search engine will 44 | likely do a better job. 45 | 46 | With that being said, if you're interested in doing this anyway, you may wish 47 | to check out the [how2][] project. 48 | 49 | 50 | ### So is this tool useless? ### 51 | I don't think so. It's imperfect, but seems to get the job done about 50% of 52 | the time. And when it works, it feels a bit magical. 53 | 54 | ### What else should I know? ### 55 | You shouldn't waste your time asking Stack Overflow the same question more than 56 | once. After you've gotten an answer from Stack Overflow - either via `vim-so` 57 | or your web-browser - you should cache it locally using [cheat][] and 58 | [vim-cheat][]. 59 | 60 | 61 | [Releases]: https://github.com/cheat/so/releases 62 | [cheat]: https://github.com/cheat/cheat 63 | [how2]: https://github.com/santinic/how2 64 | [vim-cheat]: https://github.com/cheat/vim-cheat 65 | [vim-so]: https://github.com/cheat/vim-so/releases 66 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/expand.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Add adds the selector string's matching nodes to those in the current 6 | // selection and returns a new Selection object. 7 | // The selector string is run in the context of the document of the current 8 | // Selection object. 9 | func (s *Selection) Add(selector string) *Selection { 10 | return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...) 11 | } 12 | 13 | // AddMatcher adds the matcher's matching nodes to those in the current 14 | // selection and returns a new Selection object. 15 | // The matcher is run in the context of the document of the current 16 | // Selection object. 17 | func (s *Selection) AddMatcher(m Matcher) *Selection { 18 | return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...) 19 | } 20 | 21 | // AddSelection adds the specified Selection object's nodes to those in the 22 | // current selection and returns a new Selection object. 23 | func (s *Selection) AddSelection(sel *Selection) *Selection { 24 | if sel == nil { 25 | return s.AddNodes() 26 | } 27 | return s.AddNodes(sel.Nodes...) 28 | } 29 | 30 | // Union is an alias for AddSelection. 31 | func (s *Selection) Union(sel *Selection) *Selection { 32 | return s.AddSelection(sel) 33 | } 34 | 35 | // AddNodes adds the specified nodes to those in the 36 | // current selection and returns a new Selection object. 37 | func (s *Selection) AddNodes(nodes ...*html.Node) *Selection { 38 | return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil)) 39 | } 40 | 41 | // AndSelf adds the previous set of elements on the stack to the current set. 42 | // It returns a new Selection object containing the current Selection combined 43 | // with the previous one. 44 | // Deprecated: This function has been deprecated and is now an alias for AddBack(). 45 | func (s *Selection) AndSelf() *Selection { 46 | return s.AddBack() 47 | } 48 | 49 | // AddBack adds the previous set of elements on the stack to the current set. 50 | // It returns a new Selection object containing the current Selection combined 51 | // with the previous one. 52 | func (s *Selection) AddBack() *Selection { 53 | return s.AddSelection(s.prevSel) 54 | } 55 | 56 | // AddBackFiltered reduces the previous set of elements on the stack to those that 57 | // match the selector string, and adds them to the current set. 58 | // It returns a new Selection object containing the current Selection combined 59 | // with the filtered previous one 60 | func (s *Selection) AddBackFiltered(selector string) *Selection { 61 | return s.AddSelection(s.prevSel.Filter(selector)) 62 | } 63 | 64 | // AddBackMatcher reduces the previous set of elements on the stack to those that match 65 | // the mateher, and adds them to the curernt set. 66 | // It returns a new Selection object containing the current Selection combined 67 | // with the filtered previous one 68 | func (s *Selection) AddBackMatcher(m Matcher) *Selection { 69 | return s.AddSelection(s.prevSel.FilterMatcher(m)) 70 | } 71 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | // Section 12.2.4.2 of the HTML5 specification says "The following elements 8 | // have varying levels of special parsing rules". 9 | // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements 10 | var isSpecialElementMap = map[string]bool{ 11 | "address": true, 12 | "applet": true, 13 | "area": true, 14 | "article": true, 15 | "aside": true, 16 | "base": true, 17 | "basefont": true, 18 | "bgsound": true, 19 | "blockquote": true, 20 | "body": true, 21 | "br": true, 22 | "button": true, 23 | "caption": true, 24 | "center": true, 25 | "col": true, 26 | "colgroup": true, 27 | "dd": true, 28 | "details": true, 29 | "dir": true, 30 | "div": true, 31 | "dl": true, 32 | "dt": true, 33 | "embed": true, 34 | "fieldset": true, 35 | "figcaption": true, 36 | "figure": true, 37 | "footer": true, 38 | "form": true, 39 | "frame": true, 40 | "frameset": true, 41 | "h1": true, 42 | "h2": true, 43 | "h3": true, 44 | "h4": true, 45 | "h5": true, 46 | "h6": true, 47 | "head": true, 48 | "header": true, 49 | "hgroup": true, 50 | "hr": true, 51 | "html": true, 52 | "iframe": true, 53 | "img": true, 54 | "input": true, 55 | "keygen": true, 56 | "li": true, 57 | "link": true, 58 | "listing": true, 59 | "main": true, 60 | "marquee": true, 61 | "menu": true, 62 | "meta": true, 63 | "nav": true, 64 | "noembed": true, 65 | "noframes": true, 66 | "noscript": true, 67 | "object": true, 68 | "ol": true, 69 | "p": true, 70 | "param": true, 71 | "plaintext": true, 72 | "pre": true, 73 | "script": true, 74 | "section": true, 75 | "select": true, 76 | "source": true, 77 | "style": true, 78 | "summary": true, 79 | "table": true, 80 | "tbody": true, 81 | "td": true, 82 | "template": true, 83 | "textarea": true, 84 | "tfoot": true, 85 | "th": true, 86 | "thead": true, 87 | "title": true, 88 | "tr": true, 89 | "track": true, 90 | "ul": true, 91 | "wbr": true, 92 | "xmp": true, 93 | } 94 | 95 | func isSpecialElement(element *Node) bool { 96 | switch element.Namespace { 97 | case "", "html": 98 | return isSpecialElementMap[element.Data] 99 | case "math": 100 | switch element.Data { 101 | case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": 102 | return true 103 | } 104 | case "svg": 105 | switch element.Data { 106 | case "foreignObject", "desc", "title": 107 | return true 108 | } 109 | } 110 | return false 111 | } 112 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/token.go: -------------------------------------------------------------------------------- 1 | package docopt 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | "unicode" 7 | ) 8 | 9 | type tokenList struct { 10 | tokens []string 11 | errorFunc func(string, ...interface{}) error 12 | err errorType 13 | } 14 | type token string 15 | 16 | func newTokenList(source []string, err errorType) *tokenList { 17 | errorFunc := newError 18 | if err == errorUser { 19 | errorFunc = newUserError 20 | } else if err == errorLanguage { 21 | errorFunc = newLanguageError 22 | } 23 | return &tokenList{source, errorFunc, err} 24 | } 25 | 26 | func tokenListFromString(source string) *tokenList { 27 | return newTokenList(strings.Fields(source), errorUser) 28 | } 29 | 30 | func tokenListFromPattern(source string) *tokenList { 31 | p := regexp.MustCompile(`([\[\]\(\)\|]|\.\.\.)`) 32 | source = p.ReplaceAllString(source, ` $1 `) 33 | p = regexp.MustCompile(`\s+|(\S*<.*?>)`) 34 | split := p.Split(source, -1) 35 | match := p.FindAllStringSubmatch(source, -1) 36 | var result []string 37 | l := len(split) 38 | for i := 0; i < l; i++ { 39 | if len(split[i]) > 0 { 40 | result = append(result, split[i]) 41 | } 42 | if i < l-1 && len(match[i][1]) > 0 { 43 | result = append(result, match[i][1]) 44 | } 45 | } 46 | return newTokenList(result, errorLanguage) 47 | } 48 | 49 | func (t *token) eq(s string) bool { 50 | if t == nil { 51 | return false 52 | } 53 | return string(*t) == s 54 | } 55 | func (t *token) match(matchNil bool, tokenStrings ...string) bool { 56 | if t == nil && matchNil { 57 | return true 58 | } else if t == nil && !matchNil { 59 | return false 60 | } 61 | 62 | for _, tok := range tokenStrings { 63 | if tok == string(*t) { 64 | return true 65 | } 66 | } 67 | return false 68 | } 69 | func (t *token) hasPrefix(prefix string) bool { 70 | if t == nil { 71 | return false 72 | } 73 | return strings.HasPrefix(string(*t), prefix) 74 | } 75 | func (t *token) hasSuffix(suffix string) bool { 76 | if t == nil { 77 | return false 78 | } 79 | return strings.HasSuffix(string(*t), suffix) 80 | } 81 | func (t *token) isUpper() bool { 82 | if t == nil { 83 | return false 84 | } 85 | return isStringUppercase(string(*t)) 86 | } 87 | func (t *token) String() string { 88 | if t == nil { 89 | return "" 90 | } 91 | return string(*t) 92 | } 93 | 94 | func (tl *tokenList) current() *token { 95 | if len(tl.tokens) > 0 { 96 | return (*token)(&(tl.tokens[0])) 97 | } 98 | return nil 99 | } 100 | 101 | func (tl *tokenList) length() int { 102 | return len(tl.tokens) 103 | } 104 | 105 | func (tl *tokenList) move() *token { 106 | if len(tl.tokens) > 0 { 107 | t := tl.tokens[0] 108 | tl.tokens = tl.tokens[1:] 109 | return (*token)(&t) 110 | } 111 | return nil 112 | } 113 | 114 | // returns true if all cased characters in the string are uppercase 115 | // and there are there is at least one cased charcter 116 | func isStringUppercase(s string) bool { 117 | if strings.ToUpper(s) != s { 118 | return false 119 | } 120 | for _, c := range []rune(s) { 121 | if unicode.IsUpper(c) { 122 | return true 123 | } 124 | } 125 | return false 126 | } 127 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package html implements an HTML5-compliant tokenizer and parser. 7 | 8 | Tokenization is done by creating a Tokenizer for an io.Reader r. It is the 9 | caller's responsibility to ensure that r provides UTF-8 encoded HTML. 10 | 11 | z := html.NewTokenizer(r) 12 | 13 | Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), 14 | which parses the next token and returns its type, or an error: 15 | 16 | for { 17 | tt := z.Next() 18 | if tt == html.ErrorToken { 19 | // ... 20 | return ... 21 | } 22 | // Process the current token. 23 | } 24 | 25 | There are two APIs for retrieving the current token. The high-level API is to 26 | call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs 27 | allow optionally calling Raw after Next but before Token, Text, TagName, or 28 | TagAttr. In EBNF notation, the valid call sequence per token is: 29 | 30 | Next {Raw} [ Token | Text | TagName {TagAttr} ] 31 | 32 | Token returns an independent data structure that completely describes a token. 33 | Entities (such as "<") are unescaped, tag names and attribute keys are 34 | lower-cased, and attributes are collected into a []Attribute. For example: 35 | 36 | for { 37 | if z.Next() == html.ErrorToken { 38 | // Returning io.EOF indicates success. 39 | return z.Err() 40 | } 41 | emitToken(z.Token()) 42 | } 43 | 44 | The low-level API performs fewer allocations and copies, but the contents of 45 | the []byte values returned by Text, TagName and TagAttr may change on the next 46 | call to Next. For example, to extract an HTML page's anchor text: 47 | 48 | depth := 0 49 | for { 50 | tt := z.Next() 51 | switch tt { 52 | case html.ErrorToken: 53 | return z.Err() 54 | case html.TextToken: 55 | if depth > 0 { 56 | // emitBytes should copy the []byte it receives, 57 | // if it doesn't process it immediately. 58 | emitBytes(z.Text()) 59 | } 60 | case html.StartTagToken, html.EndTagToken: 61 | tn, _ := z.TagName() 62 | if len(tn) == 1 && tn[0] == 'a' { 63 | if tt == html.StartTagToken { 64 | depth++ 65 | } else { 66 | depth-- 67 | } 68 | } 69 | } 70 | } 71 | 72 | Parsing is done by calling Parse with an io.Reader, which returns the root of 73 | the parse tree (the document element) as a *Node. It is the caller's 74 | responsibility to ensure that the Reader provides UTF-8 encoded HTML. For 75 | example, to process each anchor node in depth-first order: 76 | 77 | doc, err := html.Parse(r) 78 | if err != nil { 79 | // ... 80 | } 81 | var f func(*html.Node) 82 | f = func(n *html.Node) { 83 | if n.Type == html.ElementNode && n.Data == "a" { 84 | // Do something with n... 85 | } 86 | for c := n.FirstChild; c != nil; c = c.NextSibling { 87 | f(c) 88 | } 89 | } 90 | f(doc) 91 | 92 | The relevant specifications include: 93 | https://html.spec.whatwg.org/multipage/syntax.html and 94 | https://html.spec.whatwg.org/multipage/syntax.html#tokenization 95 | */ 96 | package html // import "golang.org/x/net/html" 97 | 98 | // The tokenization algorithm implemented by this package is not a line-by-line 99 | // transliteration of the relatively verbose state-machine in the WHATWG 100 | // specification. A more direct approach is used instead, where the program 101 | // counter implies the state, such as whether it is tokenizing a tag or a text 102 | // node. Specification compliance is verified by checking expected and actual 103 | // outputs over a test suite rather than aiming for algorithmic fidelity. 104 | 105 | // TODO(nigeltao): Does a DOM API belong in this package or a separate one? 106 | // TODO(nigeltao): How does parsing interact with a JavaScript engine? 107 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/README.md: -------------------------------------------------------------------------------- 1 | docopt-go 2 | ========= 3 | 4 | [![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go) 5 | [![Coverage Status](https://coveralls.io/repos/github/docopt/docopt.go/badge.svg)](https://coveralls.io/github/docopt/docopt.go) 6 | [![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.svg)](https://godoc.org/github.com/docopt/docopt.go) 7 | 8 | An implementation of [docopt](http://docopt.org/) in the [Go](http://golang.org/) programming language. 9 | 10 | **docopt** helps you create *beautiful* command-line interfaces easily: 11 | 12 | ```go 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "github.com/docopt/docopt-go" 18 | ) 19 | 20 | func main() { 21 | usage := `Naval Fate. 22 | 23 | Usage: 24 | naval_fate ship new ... 25 | naval_fate ship move [--speed=] 26 | naval_fate ship shoot 27 | naval_fate mine (set|remove) [--moored|--drifting] 28 | naval_fate -h | --help 29 | naval_fate --version 30 | 31 | Options: 32 | -h --help Show this screen. 33 | --version Show version. 34 | --speed= Speed in knots [default: 10]. 35 | --moored Moored (anchored) mine. 36 | --drifting Drifting mine.` 37 | 38 | arguments, _ := docopt.ParseDoc(usage) 39 | fmt.Println(arguments) 40 | } 41 | ``` 42 | 43 | **docopt** parses command-line arguments based on a help message. Don't write parser code: a good help message already has all the necessary information in it. 44 | 45 | ## Installation 46 | 47 | ⚠ Use the alias "docopt-go". To use docopt in your Go code: 48 | 49 | ```go 50 | import "github.com/docopt/docopt-go" 51 | ``` 52 | 53 | To install docopt in your `$GOPATH`: 54 | 55 | ```console 56 | $ go get github.com/docopt/docopt-go 57 | ``` 58 | 59 | ## API 60 | 61 | Given a conventional command-line help message, docopt processes the arguments. See https://github.com/docopt/docopt#help-message-format for a description of the help message format. 62 | 63 | This package exposes three different APIs, depending on the level of control required. The first, simplest way to parse your docopt usage is to just call: 64 | 65 | ```go 66 | docopt.ParseDoc(usage) 67 | ``` 68 | 69 | This will use `os.Args[1:]` as the argv slice, and use the default parser options. If you want to provide your own version string and args, then use: 70 | 71 | ```go 72 | docopt.ParseArgs(usage, argv, "1.2.3") 73 | ``` 74 | 75 | If the last parameter (version) is a non-empty string, it will be printed when `--version` is given in the argv slice. Finally, we can instantiate our own `docopt.Parser` which gives us control over how things like help messages are printed and whether to exit after displaying usage messages, etc. 76 | 77 | ```go 78 | parser := &docopt.Parser{ 79 | HelpHandler: docopt.PrintHelpOnly, 80 | OptionsFirst: true, 81 | } 82 | opts, err := parser.ParseArgs(usage, argv, "") 83 | ``` 84 | 85 | In particular, setting your own custom `HelpHandler` function makes unit testing your own docs with example command line invocations much more enjoyable. 86 | 87 | All three of these return a map of option names to the values parsed from argv, and an error or nil. You can get the values using the helpers, or just treat it as a regular map: 88 | 89 | ```go 90 | flag, _ := opts.Bool("--flag") 91 | secs, _ := opts.Int("") 92 | ``` 93 | 94 | Additionally, you can `Bind` these to a struct, assigning option values to the 95 | exported fields of that struct, all at once. 96 | 97 | ```go 98 | var config struct { 99 | Command string `docopt:""` 100 | Tries int `docopt:"-n"` 101 | Force bool // Gets the value of --force 102 | } 103 | opts.Bind(&config) 104 | ``` 105 | 106 | More documentation is available at [godoc.org](https://godoc.org/github.com/docopt/docopt-go). 107 | 108 | ## Unit Testing 109 | 110 | Unit testing your own usage docs is recommended, so you can be sure that for a given command line invocation, the expected options are set. An example of how to do this is [in the examples folder](examples/unit_test/unit_test.go). 111 | 112 | ## Tests 113 | 114 | All tests from the Python version are implemented and passing at [Travis CI](https://travis-ci.org/docopt/docopt-go). New language-agnostic tests have been added to [test_golang.docopt](test_golang.docopt). 115 | 116 | To run tests for docopt-go, use `go test`. 117 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/array.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "golang.org/x/net/html" 5 | ) 6 | 7 | const ( 8 | maxUint = ^uint(0) 9 | maxInt = int(maxUint >> 1) 10 | 11 | // ToEnd is a special index value that can be used as end index in a call 12 | // to Slice so that all elements are selected until the end of the Selection. 13 | // It is equivalent to passing (*Selection).Length(). 14 | ToEnd = maxInt 15 | ) 16 | 17 | // First reduces the set of matched elements to the first in the set. 18 | // It returns a new Selection object, and an empty Selection object if the 19 | // the selection is empty. 20 | func (s *Selection) First() *Selection { 21 | return s.Eq(0) 22 | } 23 | 24 | // Last reduces the set of matched elements to the last in the set. 25 | // It returns a new Selection object, and an empty Selection object if 26 | // the selection is empty. 27 | func (s *Selection) Last() *Selection { 28 | return s.Eq(-1) 29 | } 30 | 31 | // Eq reduces the set of matched elements to the one at the specified index. 32 | // If a negative index is given, it counts backwards starting at the end of the 33 | // set. It returns a new Selection object, and an empty Selection object if the 34 | // index is invalid. 35 | func (s *Selection) Eq(index int) *Selection { 36 | if index < 0 { 37 | index += len(s.Nodes) 38 | } 39 | 40 | if index >= len(s.Nodes) || index < 0 { 41 | return newEmptySelection(s.document) 42 | } 43 | 44 | return s.Slice(index, index+1) 45 | } 46 | 47 | // Slice reduces the set of matched elements to a subset specified by a range 48 | // of indices. The start index is 0-based and indicates the index of the first 49 | // element to select. The end index is 0-based and indicates the index at which 50 | // the elements stop being selected (the end index is not selected). 51 | // 52 | // The indices may be negative, in which case they represent an offset from the 53 | // end of the selection. 54 | // 55 | // The special value ToEnd may be specified as end index, in which case all elements 56 | // until the end are selected. This works both for a positive and negative start 57 | // index. 58 | func (s *Selection) Slice(start, end int) *Selection { 59 | if start < 0 { 60 | start += len(s.Nodes) 61 | } 62 | if end == ToEnd { 63 | end = len(s.Nodes) 64 | } else if end < 0 { 65 | end += len(s.Nodes) 66 | } 67 | return pushStack(s, s.Nodes[start:end]) 68 | } 69 | 70 | // Get retrieves the underlying node at the specified index. 71 | // Get without parameter is not implemented, since the node array is available 72 | // on the Selection object. 73 | func (s *Selection) Get(index int) *html.Node { 74 | if index < 0 { 75 | index += len(s.Nodes) // Negative index gets from the end 76 | } 77 | return s.Nodes[index] 78 | } 79 | 80 | // Index returns the position of the first element within the Selection object 81 | // relative to its sibling elements. 82 | func (s *Selection) Index() int { 83 | if len(s.Nodes) > 0 { 84 | return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length() 85 | } 86 | return -1 87 | } 88 | 89 | // IndexSelector returns the position of the first element within the 90 | // Selection object relative to the elements matched by the selector, or -1 if 91 | // not found. 92 | func (s *Selection) IndexSelector(selector string) int { 93 | if len(s.Nodes) > 0 { 94 | sel := s.document.Find(selector) 95 | return indexInSlice(sel.Nodes, s.Nodes[0]) 96 | } 97 | return -1 98 | } 99 | 100 | // IndexMatcher returns the position of the first element within the 101 | // Selection object relative to the elements matched by the matcher, or -1 if 102 | // not found. 103 | func (s *Selection) IndexMatcher(m Matcher) int { 104 | if len(s.Nodes) > 0 { 105 | sel := s.document.FindMatcher(m) 106 | return indexInSlice(sel.Nodes, s.Nodes[0]) 107 | } 108 | return -1 109 | } 110 | 111 | // IndexOfNode returns the position of the specified node within the Selection 112 | // object, or -1 if not found. 113 | func (s *Selection) IndexOfNode(node *html.Node) int { 114 | return indexInSlice(s.Nodes, node) 115 | } 116 | 117 | // IndexOfSelection returns the position of the first node in the specified 118 | // Selection object within this Selection object, or -1 if not found. 119 | func (s *Selection) IndexOfSelection(sel *Selection) int { 120 | if sel != nil && len(sel.Nodes) > 0 { 121 | return indexInSlice(s.Nodes, sel.Nodes[0]) 122 | } 123 | return -1 124 | } 125 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # paths 2 | makefile := $(realpath $(lastword $(MAKEFILE_LIST))) 3 | cmd_dir := ./cmd/so 4 | dist_dir := ./dist 5 | 6 | # executables 7 | CAT := cat 8 | COLUMN := column 9 | CTAGS := ctags 10 | GO := go 11 | GREP := grep 12 | GZIP := gzip --best 13 | LINT := revive 14 | MKDIR := mkdir -p 15 | RM := rm 16 | SCC := scc 17 | SED := sed 18 | SORT := sort 19 | ZIP := zip -m 20 | 21 | # build flags 22 | BUILD_FLAGS := -ldflags="-s -w" -mod vendor -trimpath 23 | GOBIN := 24 | TMPDIR := /tmp 25 | 26 | # release binaries 27 | releases := \ 28 | $(dist_dir)/so-darwin-amd64 \ 29 | $(dist_dir)/so-linux-amd64 \ 30 | $(dist_dir)/so-linux-arm5 \ 31 | $(dist_dir)/so-linux-arm6 \ 32 | $(dist_dir)/so-linux-arm7 \ 33 | $(dist_dir)/so-windows-amd64.exe 34 | 35 | ## build: builds an executable for your architecture 36 | .PHONY: build 37 | build: $(dist_dir) clean vendor generate 38 | $(GO) build $(BUILD_FLAGS) -o $(dist_dir)/so $(cmd_dir) 39 | 40 | ## build-release: builds release executables 41 | .PHONY: build-release 42 | build-release: $(releases) 43 | 44 | ## ci: builds a "release" executable for the current architecture (used in ci) 45 | .PHONY: ci 46 | ci: | setup prepare build 47 | 48 | # so-darwin-amd64 49 | $(dist_dir)/so-darwin-amd64: prepare 50 | GOARCH=amd64 GOOS=darwin \ 51 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz 52 | 53 | # so-linux-amd64 54 | $(dist_dir)/so-linux-amd64: prepare 55 | GOARCH=amd64 GOOS=linux \ 56 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz 57 | 58 | # so-linux-arm5 59 | $(dist_dir)/so-linux-arm5: prepare 60 | GOARCH=arm GOOS=linux GOARM=5 \ 61 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz 62 | 63 | # so-linux-arm6 64 | $(dist_dir)/so-linux-arm6: prepare 65 | GOARCH=arm GOOS=linux GOARM=6 \ 66 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz 67 | 68 | # so-linux-arm7 69 | $(dist_dir)/so-linux-arm7: prepare 70 | GOARCH=arm GOOS=linux GOARM=7 \ 71 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(GZIP) $@ && chmod -x $@.gz 72 | 73 | # so-windows-amd64 74 | $(dist_dir)/so-windows-amd64.exe: prepare 75 | GOARCH=amd64 GOOS=windows \ 76 | $(GO) build $(BUILD_FLAGS) -o $@ $(cmd_dir) && $(ZIP) $@.zip $@ 77 | 78 | # ./dist 79 | $(dist_dir): 80 | $(MKDIR) $(dist_dir) 81 | 82 | .PHONY: generate 83 | generate: 84 | $(GO) generate $(cmd_dir) 85 | 86 | ## install: builds and installs so on your PATH 87 | .PHONY: install 88 | install: build 89 | $(GO) install $(BUILD_FLAGS) $(GOBIN) $(cmd_dir) 90 | 91 | ## clean: removes compiled executables 92 | .PHONY: clean 93 | clean: $(dist_dir) 94 | $(RM) -f $(dist_dir)/* 95 | 96 | ## distclean: removes the tags file 97 | .PHONY: distclean 98 | distclean: 99 | $(RM) -f tags 100 | 101 | ## setup: installs revive (linter) and scc (sloc tool) 102 | .PHONY: setup 103 | setup: 104 | GO111MODULE=off $(GO) get -u github.com/boyter/scc github.com/mgechev/revive 105 | 106 | ## sloc: counts "semantic lines of code" 107 | .PHONY: sloc 108 | sloc: 109 | $(SCC) --exclude-dir=vendor 110 | 111 | ## tags: builds a tags file 112 | .PHONY: tags 113 | tags: 114 | $(CTAGS) -R --exclude=vendor --languages=go 115 | 116 | ## vendor: downloads, tidies, and verifies dependencies 117 | .PHONY: vendor 118 | vendor: 119 | $(GO) mod vendor && $(GO) mod tidy && $(GO) mod verify 120 | 121 | ## fmt: runs go fmt 122 | .PHONY: fmt 123 | fmt: 124 | $(GO) fmt ./... 125 | 126 | ## lint: lints go source files 127 | .PHONY: lint 128 | lint: vendor 129 | $(LINT) -exclude vendor/... ./... 130 | 131 | ## vet: vets go source files 132 | .PHONY: vet 133 | vet: 134 | $(GO) vet ./... 135 | 136 | ## test: runs unit-tests 137 | .PHONY: test 138 | test: 139 | $(GO) test ./... 140 | 141 | ## coverage: generates a test coverage report 142 | .PHONY: coverage 143 | coverage: 144 | $(GO) test ./... -coverprofile=$(TMPDIR)/so-coverage.out && \ 145 | $(GO) tool cover -html=$(TMPDIR)/so-coverage.out 146 | 147 | ## check: formats, lints, vets, vendors, and run unit-tests 148 | .PHONY: check 149 | check: | vendor fmt lint vet test 150 | 151 | .PHONY: prepare 152 | prepare: | $(dist_dir) clean generate vendor fmt lint vet test 153 | 154 | ## help: displays this help text 155 | .PHONY: help 156 | help: 157 | @$(CAT) $(makefile) | \ 158 | $(SORT) | \ 159 | $(GREP) "^##" | \ 160 | $(SED) 's/## //g' | \ 161 | $(COLUMN) -t -s ':' 162 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/type.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net/http" 7 | "net/url" 8 | 9 | "github.com/andybalholm/cascadia" 10 | 11 | "golang.org/x/net/html" 12 | ) 13 | 14 | // Document represents an HTML document to be manipulated. Unlike jQuery, which 15 | // is loaded as part of a DOM document, and thus acts upon its containing 16 | // document, GoQuery doesn't know which HTML document to act upon. So it needs 17 | // to be told, and that's what the Document class is for. It holds the root 18 | // document node to manipulate, and can make selections on this document. 19 | type Document struct { 20 | *Selection 21 | Url *url.URL 22 | rootNode *html.Node 23 | } 24 | 25 | // NewDocumentFromNode is a Document constructor that takes a root html Node 26 | // as argument. 27 | func NewDocumentFromNode(root *html.Node) *Document { 28 | return newDocument(root, nil) 29 | } 30 | 31 | // NewDocument is a Document constructor that takes a string URL as argument. 32 | // It loads the specified document, parses it, and stores the root Document 33 | // node, ready to be manipulated. 34 | // 35 | // Deprecated: Use the net/http standard library package to make the request 36 | // and validate the response before calling goquery.NewDocumentFromReader 37 | // with the response's body. 38 | func NewDocument(url string) (*Document, error) { 39 | // Load the URL 40 | res, e := http.Get(url) 41 | if e != nil { 42 | return nil, e 43 | } 44 | return NewDocumentFromResponse(res) 45 | } 46 | 47 | // NewDocumentFromReader returns a Document from an io.Reader. 48 | // It returns an error as second value if the reader's data cannot be parsed 49 | // as html. It does not check if the reader is also an io.Closer, the 50 | // provided reader is never closed by this call. It is the responsibility 51 | // of the caller to close it if required. 52 | func NewDocumentFromReader(r io.Reader) (*Document, error) { 53 | root, e := html.Parse(r) 54 | if e != nil { 55 | return nil, e 56 | } 57 | return newDocument(root, nil), nil 58 | } 59 | 60 | // NewDocumentFromResponse is another Document constructor that takes an http response as argument. 61 | // It loads the specified response's document, parses it, and stores the root Document 62 | // node, ready to be manipulated. The response's body is closed on return. 63 | // 64 | // Deprecated: Use goquery.NewDocumentFromReader with the response's body. 65 | func NewDocumentFromResponse(res *http.Response) (*Document, error) { 66 | if res == nil { 67 | return nil, errors.New("Response is nil") 68 | } 69 | defer res.Body.Close() 70 | if res.Request == nil { 71 | return nil, errors.New("Response.Request is nil") 72 | } 73 | 74 | // Parse the HTML into nodes 75 | root, e := html.Parse(res.Body) 76 | if e != nil { 77 | return nil, e 78 | } 79 | 80 | // Create and fill the document 81 | return newDocument(root, res.Request.URL), nil 82 | } 83 | 84 | // CloneDocument creates a deep-clone of a document. 85 | func CloneDocument(doc *Document) *Document { 86 | return newDocument(cloneNode(doc.rootNode), doc.Url) 87 | } 88 | 89 | // Private constructor, make sure all fields are correctly filled. 90 | func newDocument(root *html.Node, url *url.URL) *Document { 91 | // Create and fill the document 92 | d := &Document{nil, url, root} 93 | d.Selection = newSingleSelection(root, d) 94 | return d 95 | } 96 | 97 | // Selection represents a collection of nodes matching some criteria. The 98 | // initial Selection can be created by using Document.Find, and then 99 | // manipulated using the jQuery-like chainable syntax and methods. 100 | type Selection struct { 101 | Nodes []*html.Node 102 | document *Document 103 | prevSel *Selection 104 | } 105 | 106 | // Helper constructor to create an empty selection 107 | func newEmptySelection(doc *Document) *Selection { 108 | return &Selection{nil, doc, nil} 109 | } 110 | 111 | // Helper constructor to create a selection of only one node 112 | func newSingleSelection(node *html.Node, doc *Document) *Selection { 113 | return &Selection{[]*html.Node{node}, doc, nil} 114 | } 115 | 116 | // Matcher is an interface that defines the methods to match 117 | // HTML nodes against a compiled selector string. Cascadia's 118 | // Selector implements this interface. 119 | type Matcher interface { 120 | Match(*html.Node) bool 121 | MatchAll(*html.Node) []*html.Node 122 | Filter([]*html.Node) []*html.Node 123 | } 124 | 125 | // compileMatcher compiles the selector string s and returns 126 | // the corresponding Matcher. If s is an invalid selector string, 127 | // it returns a Matcher that fails all matches. 128 | func compileMatcher(s string) Matcher { 129 | cs, err := cascadia.Compile(s) 130 | if err != nil { 131 | return invalidMatcher{} 132 | } 133 | return cs 134 | } 135 | 136 | // invalidMatcher is a Matcher that always fails to match. 137 | type invalidMatcher struct{} 138 | 139 | func (invalidMatcher) Match(n *html.Node) bool { return false } 140 | func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil } 141 | func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil } 142 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2016, Martin Angers & Contributors 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, 5 | // are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation and/or 11 | // other materials provided with the distribution. 12 | // * Neither the name of the author nor the names of its contributors may be used to 13 | // endorse or promote products derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 16 | // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 22 | // WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | /* 25 | Package goquery implements features similar to jQuery, including the chainable 26 | syntax, to manipulate and query an HTML document. 27 | 28 | It brings a syntax and a set of features similar to jQuery to the Go language. 29 | It is based on Go's net/html package and the CSS Selector library cascadia. 30 | Since the net/html parser returns nodes, and not a full-featured DOM 31 | tree, jQuery's stateful manipulation functions (like height(), css(), detach()) 32 | have been left off. 33 | 34 | Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is 35 | the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. 36 | See the repository's wiki for various options on how to do this. 37 | 38 | Syntax-wise, it is as close as possible to jQuery, with the same method names when 39 | possible, and that warm and fuzzy chainable interface. jQuery being the 40 | ultra-popular library that it is, writing a similar HTML-manipulating 41 | library was better to follow its API than to start anew (in the same spirit as 42 | Go's fmt package), even though some of its methods are less than intuitive (looking 43 | at you, index()...). 44 | 45 | It is hosted on GitHub, along with additional documentation in the README.md 46 | file: https://github.com/puerkitobio/goquery 47 | 48 | Please note that because of the net/html dependency, goquery requires Go1.1+. 49 | 50 | The various methods are split into files based on the category of behavior. 51 | The three dots (...) indicate that various "overloads" are available. 52 | 53 | * array.go : array-like positional manipulation of the selection. 54 | - Eq() 55 | - First() 56 | - Get() 57 | - Index...() 58 | - Last() 59 | - Slice() 60 | 61 | * expand.go : methods that expand or augment the selection's set. 62 | - Add...() 63 | - AndSelf() 64 | - Union(), which is an alias for AddSelection() 65 | 66 | * filter.go : filtering methods, that reduce the selection's set. 67 | - End() 68 | - Filter...() 69 | - Has...() 70 | - Intersection(), which is an alias of FilterSelection() 71 | - Not...() 72 | 73 | * iteration.go : methods to loop over the selection's nodes. 74 | - Each() 75 | - EachWithBreak() 76 | - Map() 77 | 78 | * manipulation.go : methods for modifying the document 79 | - After...() 80 | - Append...() 81 | - Before...() 82 | - Clone() 83 | - Empty() 84 | - Prepend...() 85 | - Remove...() 86 | - ReplaceWith...() 87 | - Unwrap() 88 | - Wrap...() 89 | - WrapAll...() 90 | - WrapInner...() 91 | 92 | * property.go : methods that inspect and get the node's properties values. 93 | - Attr*(), RemoveAttr(), SetAttr() 94 | - AddClass(), HasClass(), RemoveClass(), ToggleClass() 95 | - Html() 96 | - Length() 97 | - Size(), which is an alias for Length() 98 | - Text() 99 | 100 | * query.go : methods that query, or reflect, a node's identity. 101 | - Contains() 102 | - Is...() 103 | 104 | * traversal.go : methods to traverse the HTML document tree. 105 | - Children...() 106 | - Contents() 107 | - Find...() 108 | - Next...() 109 | - Parent[s]...() 110 | - Prev...() 111 | - Siblings...() 112 | 113 | * type.go : definition of the types exposed by goquery. 114 | - Document 115 | - Selection 116 | - Matcher 117 | 118 | * utilities.go : definition of helper functions (and not methods on a *Selection) 119 | that are not part of jQuery, but are useful to goquery. 120 | - NodeName 121 | - OuterHtml 122 | */ 123 | package goquery 124 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/utilities.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "bytes" 5 | 6 | "golang.org/x/net/html" 7 | ) 8 | 9 | // used to determine if a set (map[*html.Node]bool) should be used 10 | // instead of iterating over a slice. The set uses more memory and 11 | // is slower than slice iteration for small N. 12 | const minNodesForSet = 1000 13 | 14 | var nodeNames = []string{ 15 | html.ErrorNode: "#error", 16 | html.TextNode: "#text", 17 | html.DocumentNode: "#document", 18 | html.CommentNode: "#comment", 19 | } 20 | 21 | // NodeName returns the node name of the first element in the selection. 22 | // It tries to behave in a similar way as the DOM's nodeName property 23 | // (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName). 24 | // 25 | // Go's net/html package defines the following node types, listed with 26 | // the corresponding returned value from this function: 27 | // 28 | // ErrorNode : #error 29 | // TextNode : #text 30 | // DocumentNode : #document 31 | // ElementNode : the element's tag name 32 | // CommentNode : #comment 33 | // DoctypeNode : the name of the document type 34 | // 35 | func NodeName(s *Selection) string { 36 | if s.Length() == 0 { 37 | return "" 38 | } 39 | switch n := s.Get(0); n.Type { 40 | case html.ElementNode, html.DoctypeNode: 41 | return n.Data 42 | default: 43 | if n.Type >= 0 && int(n.Type) < len(nodeNames) { 44 | return nodeNames[n.Type] 45 | } 46 | return "" 47 | } 48 | } 49 | 50 | // OuterHtml returns the outer HTML rendering of the first item in 51 | // the selection - that is, the HTML including the first element's 52 | // tag and attributes. 53 | // 54 | // Unlike InnerHtml, this is a function and not a method on the Selection, 55 | // because this is not a jQuery method (in javascript-land, this is 56 | // a property provided by the DOM). 57 | func OuterHtml(s *Selection) (string, error) { 58 | var buf bytes.Buffer 59 | 60 | if s.Length() == 0 { 61 | return "", nil 62 | } 63 | n := s.Get(0) 64 | if err := html.Render(&buf, n); err != nil { 65 | return "", err 66 | } 67 | return buf.String(), nil 68 | } 69 | 70 | // Loop through all container nodes to search for the target node. 71 | func sliceContains(container []*html.Node, contained *html.Node) bool { 72 | for _, n := range container { 73 | if nodeContains(n, contained) { 74 | return true 75 | } 76 | } 77 | 78 | return false 79 | } 80 | 81 | // Checks if the contained node is within the container node. 82 | func nodeContains(container *html.Node, contained *html.Node) bool { 83 | // Check if the parent of the contained node is the container node, traversing 84 | // upward until the top is reached, or the container is found. 85 | for contained = contained.Parent; contained != nil; contained = contained.Parent { 86 | if container == contained { 87 | return true 88 | } 89 | } 90 | return false 91 | } 92 | 93 | // Checks if the target node is in the slice of nodes. 94 | func isInSlice(slice []*html.Node, node *html.Node) bool { 95 | return indexInSlice(slice, node) > -1 96 | } 97 | 98 | // Returns the index of the target node in the slice, or -1. 99 | func indexInSlice(slice []*html.Node, node *html.Node) int { 100 | if node != nil { 101 | for i, n := range slice { 102 | if n == node { 103 | return i 104 | } 105 | } 106 | } 107 | return -1 108 | } 109 | 110 | // Appends the new nodes to the target slice, making sure no duplicate is added. 111 | // There is no check to the original state of the target slice, so it may still 112 | // contain duplicates. The target slice is returned because append() may create 113 | // a new underlying array. If targetSet is nil, a local set is created with the 114 | // target if len(target) + len(nodes) is greater than minNodesForSet. 115 | func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node { 116 | // if there are not that many nodes, don't use the map, faster to just use nested loops 117 | // (unless a non-nil targetSet is passed, in which case the caller knows better). 118 | if targetSet == nil && len(target)+len(nodes) < minNodesForSet { 119 | for _, n := range nodes { 120 | if !isInSlice(target, n) { 121 | target = append(target, n) 122 | } 123 | } 124 | return target 125 | } 126 | 127 | // if a targetSet is passed, then assume it is reliable, otherwise create one 128 | // and initialize it with the current target contents. 129 | if targetSet == nil { 130 | targetSet = make(map[*html.Node]bool, len(target)) 131 | for _, n := range target { 132 | targetSet[n] = true 133 | } 134 | } 135 | for _, n := range nodes { 136 | if !targetSet[n] { 137 | target = append(target, n) 138 | targetSet[n] = true 139 | } 140 | } 141 | 142 | return target 143 | } 144 | 145 | // Loop through a selection, returning only those nodes that pass the predicate 146 | // function. 147 | func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) { 148 | for i, n := range sel.Nodes { 149 | if predicate(i, newSingleSelection(n, sel.document)) { 150 | result = append(result, n) 151 | } 152 | } 153 | return result 154 | } 155 | 156 | // Creates a new Selection object based on the specified nodes, and keeps the 157 | // source Selection object on the stack (linked list). 158 | func pushStack(fromSel *Selection, nodes []*html.Node) *Selection { 159 | result := &Selection{nodes, fromSel.document, fromSel} 160 | return result 161 | } 162 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/doctype.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // parseDoctype parses the data from a DoctypeToken into a name, 12 | // public identifier, and system identifier. It returns a Node whose Type 13 | // is DoctypeNode, whose Data is the name, and which has attributes 14 | // named "system" and "public" for the two identifiers if they were present. 15 | // quirks is whether the document should be parsed in "quirks mode". 16 | func parseDoctype(s string) (n *Node, quirks bool) { 17 | n = &Node{Type: DoctypeNode} 18 | 19 | // Find the name. 20 | space := strings.IndexAny(s, whitespace) 21 | if space == -1 { 22 | space = len(s) 23 | } 24 | n.Data = s[:space] 25 | // The comparison to "html" is case-sensitive. 26 | if n.Data != "html" { 27 | quirks = true 28 | } 29 | n.Data = strings.ToLower(n.Data) 30 | s = strings.TrimLeft(s[space:], whitespace) 31 | 32 | if len(s) < 6 { 33 | // It can't start with "PUBLIC" or "SYSTEM". 34 | // Ignore the rest of the string. 35 | return n, quirks || s != "" 36 | } 37 | 38 | key := strings.ToLower(s[:6]) 39 | s = s[6:] 40 | for key == "public" || key == "system" { 41 | s = strings.TrimLeft(s, whitespace) 42 | if s == "" { 43 | break 44 | } 45 | quote := s[0] 46 | if quote != '"' && quote != '\'' { 47 | break 48 | } 49 | s = s[1:] 50 | q := strings.IndexRune(s, rune(quote)) 51 | var id string 52 | if q == -1 { 53 | id = s 54 | s = "" 55 | } else { 56 | id = s[:q] 57 | s = s[q+1:] 58 | } 59 | n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) 60 | if key == "public" { 61 | key = "system" 62 | } else { 63 | key = "" 64 | } 65 | } 66 | 67 | if key != "" || s != "" { 68 | quirks = true 69 | } else if len(n.Attr) > 0 { 70 | if n.Attr[0].Key == "public" { 71 | public := strings.ToLower(n.Attr[0].Val) 72 | switch public { 73 | case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": 74 | quirks = true 75 | default: 76 | for _, q := range quirkyIDs { 77 | if strings.HasPrefix(public, q) { 78 | quirks = true 79 | break 80 | } 81 | } 82 | } 83 | // The following two public IDs only cause quirks mode if there is no system ID. 84 | if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || 85 | strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { 86 | quirks = true 87 | } 88 | } 89 | if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && 90 | strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { 91 | quirks = true 92 | } 93 | } 94 | 95 | return n, quirks 96 | } 97 | 98 | // quirkyIDs is a list of public doctype identifiers that cause a document 99 | // to be interpreted in quirks mode. The identifiers should be in lower case. 100 | var quirkyIDs = []string{ 101 | "+//silmaril//dtd html pro v0r11 19970101//", 102 | "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", 103 | "-//as//dtd html 3.0 aswedit + extensions//", 104 | "-//ietf//dtd html 2.0 level 1//", 105 | "-//ietf//dtd html 2.0 level 2//", 106 | "-//ietf//dtd html 2.0 strict level 1//", 107 | "-//ietf//dtd html 2.0 strict level 2//", 108 | "-//ietf//dtd html 2.0 strict//", 109 | "-//ietf//dtd html 2.0//", 110 | "-//ietf//dtd html 2.1e//", 111 | "-//ietf//dtd html 3.0//", 112 | "-//ietf//dtd html 3.2 final//", 113 | "-//ietf//dtd html 3.2//", 114 | "-//ietf//dtd html 3//", 115 | "-//ietf//dtd html level 0//", 116 | "-//ietf//dtd html level 1//", 117 | "-//ietf//dtd html level 2//", 118 | "-//ietf//dtd html level 3//", 119 | "-//ietf//dtd html strict level 0//", 120 | "-//ietf//dtd html strict level 1//", 121 | "-//ietf//dtd html strict level 2//", 122 | "-//ietf//dtd html strict level 3//", 123 | "-//ietf//dtd html strict//", 124 | "-//ietf//dtd html//", 125 | "-//metrius//dtd metrius presentational//", 126 | "-//microsoft//dtd internet explorer 2.0 html strict//", 127 | "-//microsoft//dtd internet explorer 2.0 html//", 128 | "-//microsoft//dtd internet explorer 2.0 tables//", 129 | "-//microsoft//dtd internet explorer 3.0 html strict//", 130 | "-//microsoft//dtd internet explorer 3.0 html//", 131 | "-//microsoft//dtd internet explorer 3.0 tables//", 132 | "-//netscape comm. corp.//dtd html//", 133 | "-//netscape comm. corp.//dtd strict html//", 134 | "-//o'reilly and associates//dtd html 2.0//", 135 | "-//o'reilly and associates//dtd html extended 1.0//", 136 | "-//o'reilly and associates//dtd html extended relaxed 1.0//", 137 | "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", 138 | "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", 139 | "-//spyglass//dtd html 2.0 extended//", 140 | "-//sq//dtd html 2.0 hotmetal + extensions//", 141 | "-//sun microsystems corp.//dtd hotjava html//", 142 | "-//sun microsystems corp.//dtd hotjava strict html//", 143 | "-//w3c//dtd html 3 1995-03-24//", 144 | "-//w3c//dtd html 3.2 draft//", 145 | "-//w3c//dtd html 3.2 final//", 146 | "-//w3c//dtd html 3.2//", 147 | "-//w3c//dtd html 3.2s draft//", 148 | "-//w3c//dtd html 4.0 frameset//", 149 | "-//w3c//dtd html 4.0 transitional//", 150 | "-//w3c//dtd html experimental 19960712//", 151 | "-//w3c//dtd html experimental 970421//", 152 | "-//w3c//dtd w3 html//", 153 | "-//w3o//dtd w3 html 3.0//", 154 | "-//webtechs//dtd mozilla html 2.0//", 155 | "-//webtechs//dtd mozilla html//", 156 | } 157 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/filter.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Filter reduces the set of matched elements to those that match the selector string. 6 | // It returns a new Selection object for this subset of matching elements. 7 | func (s *Selection) Filter(selector string) *Selection { 8 | return s.FilterMatcher(compileMatcher(selector)) 9 | } 10 | 11 | // FilterMatcher reduces the set of matched elements to those that match 12 | // the given matcher. It returns a new Selection object for this subset 13 | // of matching elements. 14 | func (s *Selection) FilterMatcher(m Matcher) *Selection { 15 | return pushStack(s, winnow(s, m, true)) 16 | } 17 | 18 | // Not removes elements from the Selection that match the selector string. 19 | // It returns a new Selection object with the matching elements removed. 20 | func (s *Selection) Not(selector string) *Selection { 21 | return s.NotMatcher(compileMatcher(selector)) 22 | } 23 | 24 | // NotMatcher removes elements from the Selection that match the given matcher. 25 | // It returns a new Selection object with the matching elements removed. 26 | func (s *Selection) NotMatcher(m Matcher) *Selection { 27 | return pushStack(s, winnow(s, m, false)) 28 | } 29 | 30 | // FilterFunction reduces the set of matched elements to those that pass the function's test. 31 | // It returns a new Selection object for this subset of elements. 32 | func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection { 33 | return pushStack(s, winnowFunction(s, f, true)) 34 | } 35 | 36 | // NotFunction removes elements from the Selection that pass the function's test. 37 | // It returns a new Selection object with the matching elements removed. 38 | func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection { 39 | return pushStack(s, winnowFunction(s, f, false)) 40 | } 41 | 42 | // FilterNodes reduces the set of matched elements to those that match the specified nodes. 43 | // It returns a new Selection object for this subset of elements. 44 | func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection { 45 | return pushStack(s, winnowNodes(s, nodes, true)) 46 | } 47 | 48 | // NotNodes removes elements from the Selection that match the specified nodes. 49 | // It returns a new Selection object with the matching elements removed. 50 | func (s *Selection) NotNodes(nodes ...*html.Node) *Selection { 51 | return pushStack(s, winnowNodes(s, nodes, false)) 52 | } 53 | 54 | // FilterSelection reduces the set of matched elements to those that match a 55 | // node in the specified Selection object. 56 | // It returns a new Selection object for this subset of elements. 57 | func (s *Selection) FilterSelection(sel *Selection) *Selection { 58 | if sel == nil { 59 | return pushStack(s, winnowNodes(s, nil, true)) 60 | } 61 | return pushStack(s, winnowNodes(s, sel.Nodes, true)) 62 | } 63 | 64 | // NotSelection removes elements from the Selection that match a node in the specified 65 | // Selection object. It returns a new Selection object with the matching elements removed. 66 | func (s *Selection) NotSelection(sel *Selection) *Selection { 67 | if sel == nil { 68 | return pushStack(s, winnowNodes(s, nil, false)) 69 | } 70 | return pushStack(s, winnowNodes(s, sel.Nodes, false)) 71 | } 72 | 73 | // Intersection is an alias for FilterSelection. 74 | func (s *Selection) Intersection(sel *Selection) *Selection { 75 | return s.FilterSelection(sel) 76 | } 77 | 78 | // Has reduces the set of matched elements to those that have a descendant 79 | // that matches the selector. 80 | // It returns a new Selection object with the matching elements. 81 | func (s *Selection) Has(selector string) *Selection { 82 | return s.HasSelection(s.document.Find(selector)) 83 | } 84 | 85 | // HasMatcher reduces the set of matched elements to those that have a descendant 86 | // that matches the matcher. 87 | // It returns a new Selection object with the matching elements. 88 | func (s *Selection) HasMatcher(m Matcher) *Selection { 89 | return s.HasSelection(s.document.FindMatcher(m)) 90 | } 91 | 92 | // HasNodes reduces the set of matched elements to those that have a 93 | // descendant that matches one of the nodes. 94 | // It returns a new Selection object with the matching elements. 95 | func (s *Selection) HasNodes(nodes ...*html.Node) *Selection { 96 | return s.FilterFunction(func(_ int, sel *Selection) bool { 97 | // Add all nodes that contain one of the specified nodes 98 | for _, n := range nodes { 99 | if sel.Contains(n) { 100 | return true 101 | } 102 | } 103 | return false 104 | }) 105 | } 106 | 107 | // HasSelection reduces the set of matched elements to those that have a 108 | // descendant that matches one of the nodes of the specified Selection object. 109 | // It returns a new Selection object with the matching elements. 110 | func (s *Selection) HasSelection(sel *Selection) *Selection { 111 | if sel == nil { 112 | return s.HasNodes() 113 | } 114 | return s.HasNodes(sel.Nodes...) 115 | } 116 | 117 | // End ends the most recent filtering operation in the current chain and 118 | // returns the set of matched elements to its previous state. 119 | func (s *Selection) End() *Selection { 120 | if s.prevSel != nil { 121 | return s.prevSel 122 | } 123 | return newEmptySelection(s.document) 124 | } 125 | 126 | // Filter based on the matcher, and the indicator to keep (Filter) or 127 | // to get rid of (Not) the matching elements. 128 | func winnow(sel *Selection, m Matcher, keep bool) []*html.Node { 129 | // Optimize if keep is requested 130 | if keep { 131 | return m.Filter(sel.Nodes) 132 | } 133 | // Use grep 134 | return grep(sel, func(i int, s *Selection) bool { 135 | return !m.Match(s.Get(0)) 136 | }) 137 | } 138 | 139 | // Filter based on an array of nodes, and the indicator to keep (Filter) or 140 | // to get rid of (Not) the matching elements. 141 | func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node { 142 | if len(nodes)+len(sel.Nodes) < minNodesForSet { 143 | return grep(sel, func(i int, s *Selection) bool { 144 | return isInSlice(nodes, s.Get(0)) == keep 145 | }) 146 | } 147 | 148 | set := make(map[*html.Node]bool) 149 | for _, n := range nodes { 150 | set[n] = true 151 | } 152 | return grep(sel, func(i int, s *Selection) bool { 153 | return set[s.Get(0)] == keep 154 | }) 155 | } 156 | 157 | // Filter based on a function test, and the indicator to keep (Filter) or 158 | // to get rid of (Not) the matching elements. 159 | func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node { 160 | return grep(sel, func(i int, s *Selection) bool { 161 | return f(i, s) == keep 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "golang.org/x/net/html/atom" 9 | ) 10 | 11 | // A NodeType is the type of a Node. 12 | type NodeType uint32 13 | 14 | const ( 15 | ErrorNode NodeType = iota 16 | TextNode 17 | DocumentNode 18 | ElementNode 19 | CommentNode 20 | DoctypeNode 21 | // RawNode nodes are not returned by the parser, but can be part of the 22 | // Node tree passed to func Render to insert raw HTML (without escaping). 23 | // If so, this package makes no guarantee that the rendered HTML is secure 24 | // (from e.g. Cross Site Scripting attacks) or well-formed. 25 | RawNode 26 | scopeMarkerNode 27 | ) 28 | 29 | // Section 12.2.4.3 says "The markers are inserted when entering applet, 30 | // object, marquee, template, td, th, and caption elements, and are used 31 | // to prevent formatting from "leaking" into applet, object, marquee, 32 | // template, td, th, and caption elements". 33 | var scopeMarker = Node{Type: scopeMarkerNode} 34 | 35 | // A Node consists of a NodeType and some Data (tag name for element nodes, 36 | // content for text) and are part of a tree of Nodes. Element nodes may also 37 | // have a Namespace and contain a slice of Attributes. Data is unescaped, so 38 | // that it looks like "a 0 { 166 | return (*s)[i-1] 167 | } 168 | return nil 169 | } 170 | 171 | // index returns the index of the top-most occurrence of n in the stack, or -1 172 | // if n is not present. 173 | func (s *nodeStack) index(n *Node) int { 174 | for i := len(*s) - 1; i >= 0; i-- { 175 | if (*s)[i] == n { 176 | return i 177 | } 178 | } 179 | return -1 180 | } 181 | 182 | // contains returns whether a is within s. 183 | func (s *nodeStack) contains(a atom.Atom) bool { 184 | for _, n := range *s { 185 | if n.DataAtom == a && n.Namespace == "" { 186 | return true 187 | } 188 | } 189 | return false 190 | } 191 | 192 | // insert inserts a node at the given index. 193 | func (s *nodeStack) insert(i int, n *Node) { 194 | (*s) = append(*s, nil) 195 | copy((*s)[i+1:], (*s)[i:]) 196 | (*s)[i] = n 197 | } 198 | 199 | // remove removes a node from the stack. It is a no-op if n is not present. 200 | func (s *nodeStack) remove(n *Node) { 201 | i := s.index(n) 202 | if i == -1 { 203 | return 204 | } 205 | copy((*s)[i:], (*s)[i+1:]) 206 | j := len(*s) - 1 207 | (*s)[j] = nil 208 | *s = (*s)[:j] 209 | } 210 | 211 | type insertionModeStack []insertionMode 212 | 213 | func (s *insertionModeStack) pop() (im insertionMode) { 214 | i := len(*s) 215 | im = (*s)[i-1] 216 | *s = (*s)[:i-1] 217 | return im 218 | } 219 | 220 | func (s *insertionModeStack) top() insertionMode { 221 | if i := len(*s); i > 0 { 222 | return (*s)[i-1] 223 | } 224 | return nil 225 | } 226 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/escape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "unicode/utf8" 11 | ) 12 | 13 | // These replacements permit compatibility with old numeric entities that 14 | // assumed Windows-1252 encoding. 15 | // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference 16 | var replacementTable = [...]rune{ 17 | '\u20AC', // First entry is what 0x80 should be replaced with. 18 | '\u0081', 19 | '\u201A', 20 | '\u0192', 21 | '\u201E', 22 | '\u2026', 23 | '\u2020', 24 | '\u2021', 25 | '\u02C6', 26 | '\u2030', 27 | '\u0160', 28 | '\u2039', 29 | '\u0152', 30 | '\u008D', 31 | '\u017D', 32 | '\u008F', 33 | '\u0090', 34 | '\u2018', 35 | '\u2019', 36 | '\u201C', 37 | '\u201D', 38 | '\u2022', 39 | '\u2013', 40 | '\u2014', 41 | '\u02DC', 42 | '\u2122', 43 | '\u0161', 44 | '\u203A', 45 | '\u0153', 46 | '\u009D', 47 | '\u017E', 48 | '\u0178', // Last entry is 0x9F. 49 | // 0x00->'\uFFFD' is handled programmatically. 50 | // 0x0D->'\u000D' is a no-op. 51 | } 52 | 53 | // unescapeEntity reads an entity like "<" from b[src:] and writes the 54 | // corresponding "<" to b[dst:], returning the incremented dst and src cursors. 55 | // Precondition: b[src] == '&' && dst <= src. 56 | // attribute should be true if parsing an attribute value. 57 | func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { 58 | // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference 59 | 60 | // i starts at 1 because we already know that s[0] == '&'. 61 | i, s := 1, b[src:] 62 | 63 | if len(s) <= 1 { 64 | b[dst] = b[src] 65 | return dst + 1, src + 1 66 | } 67 | 68 | if s[i] == '#' { 69 | if len(s) <= 3 { // We need to have at least "&#.". 70 | b[dst] = b[src] 71 | return dst + 1, src + 1 72 | } 73 | i++ 74 | c := s[i] 75 | hex := false 76 | if c == 'x' || c == 'X' { 77 | hex = true 78 | i++ 79 | } 80 | 81 | x := '\x00' 82 | for i < len(s) { 83 | c = s[i] 84 | i++ 85 | if hex { 86 | if '0' <= c && c <= '9' { 87 | x = 16*x + rune(c) - '0' 88 | continue 89 | } else if 'a' <= c && c <= 'f' { 90 | x = 16*x + rune(c) - 'a' + 10 91 | continue 92 | } else if 'A' <= c && c <= 'F' { 93 | x = 16*x + rune(c) - 'A' + 10 94 | continue 95 | } 96 | } else if '0' <= c && c <= '9' { 97 | x = 10*x + rune(c) - '0' 98 | continue 99 | } 100 | if c != ';' { 101 | i-- 102 | } 103 | break 104 | } 105 | 106 | if i <= 3 { // No characters matched. 107 | b[dst] = b[src] 108 | return dst + 1, src + 1 109 | } 110 | 111 | if 0x80 <= x && x <= 0x9F { 112 | // Replace characters from Windows-1252 with UTF-8 equivalents. 113 | x = replacementTable[x-0x80] 114 | } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { 115 | // Replace invalid characters with the replacement character. 116 | x = '\uFFFD' 117 | } 118 | 119 | return dst + utf8.EncodeRune(b[dst:], x), src + i 120 | } 121 | 122 | // Consume the maximum number of characters possible, with the 123 | // consumed characters matching one of the named references. 124 | 125 | for i < len(s) { 126 | c := s[i] 127 | i++ 128 | // Lower-cased characters are more common in entities, so we check for them first. 129 | if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { 130 | continue 131 | } 132 | if c != ';' { 133 | i-- 134 | } 135 | break 136 | } 137 | 138 | entityName := string(s[1:i]) 139 | if entityName == "" { 140 | // No-op. 141 | } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { 142 | // No-op. 143 | } else if x := entity[entityName]; x != 0 { 144 | return dst + utf8.EncodeRune(b[dst:], x), src + i 145 | } else if x := entity2[entityName]; x[0] != 0 { 146 | dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) 147 | return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i 148 | } else if !attribute { 149 | maxLen := len(entityName) - 1 150 | if maxLen > longestEntityWithoutSemicolon { 151 | maxLen = longestEntityWithoutSemicolon 152 | } 153 | for j := maxLen; j > 1; j-- { 154 | if x := entity[entityName[:j]]; x != 0 { 155 | return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 156 | } 157 | } 158 | } 159 | 160 | dst1, src1 = dst+i, src+i 161 | copy(b[dst:dst1], b[src:src1]) 162 | return dst1, src1 163 | } 164 | 165 | // unescape unescapes b's entities in-place, so that "a<b" becomes "a': 214 | esc = ">" 215 | case '"': 216 | // """ is shorter than """. 217 | esc = """ 218 | case '\r': 219 | esc = " " 220 | default: 221 | panic("unrecognized escape character") 222 | } 223 | s = s[i+1:] 224 | if _, err := w.WriteString(esc); err != nil { 225 | return err 226 | } 227 | i = strings.IndexAny(s, escapedChars) 228 | } 229 | _, err := w.WriteString(s) 230 | return err 231 | } 232 | 233 | // EscapeString escapes special characters like "<" to become "<". It 234 | // escapes only five such characters: <, >, &, ' and ". 235 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 236 | // always true. 237 | func EscapeString(s string) string { 238 | if strings.IndexAny(s, escapedChars) == -1 { 239 | return s 240 | } 241 | var buf bytes.Buffer 242 | escape(&buf, s) 243 | return buf.String() 244 | } 245 | 246 | // UnescapeString unescapes entities like "<" to become "<". It unescapes a 247 | // larger range of entities than EscapeString escapes. For example, "á" 248 | // unescapes to "á", as does "á" and "&xE1;". 249 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 250 | // always true. 251 | func UnescapeString(s string) string { 252 | for _, c := range s { 253 | if c == '&' { 254 | return string(unescape([]byte(s), false)) 255 | } 256 | } 257 | return s 258 | } 259 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/property.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "strings" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var rxClassTrim = regexp.MustCompile("[\t\r\n]") 12 | 13 | // Attr gets the specified attribute's value for the first element in the 14 | // Selection. To get the value for each element individually, use a looping 15 | // construct such as Each or Map method. 16 | func (s *Selection) Attr(attrName string) (val string, exists bool) { 17 | if len(s.Nodes) == 0 { 18 | return 19 | } 20 | return getAttributeValue(attrName, s.Nodes[0]) 21 | } 22 | 23 | // AttrOr works like Attr but returns default value if attribute is not present. 24 | func (s *Selection) AttrOr(attrName, defaultValue string) string { 25 | if len(s.Nodes) == 0 { 26 | return defaultValue 27 | } 28 | 29 | val, exists := getAttributeValue(attrName, s.Nodes[0]) 30 | if !exists { 31 | return defaultValue 32 | } 33 | 34 | return val 35 | } 36 | 37 | // RemoveAttr removes the named attribute from each element in the set of matched elements. 38 | func (s *Selection) RemoveAttr(attrName string) *Selection { 39 | for _, n := range s.Nodes { 40 | removeAttr(n, attrName) 41 | } 42 | 43 | return s 44 | } 45 | 46 | // SetAttr sets the given attribute on each element in the set of matched elements. 47 | func (s *Selection) SetAttr(attrName, val string) *Selection { 48 | for _, n := range s.Nodes { 49 | attr := getAttributePtr(attrName, n) 50 | if attr == nil { 51 | n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val}) 52 | } else { 53 | attr.Val = val 54 | } 55 | } 56 | 57 | return s 58 | } 59 | 60 | // Text gets the combined text contents of each element in the set of matched 61 | // elements, including their descendants. 62 | func (s *Selection) Text() string { 63 | var buf bytes.Buffer 64 | 65 | // Slightly optimized vs calling Each: no single selection object created 66 | var f func(*html.Node) 67 | f = func(n *html.Node) { 68 | if n.Type == html.TextNode { 69 | // Keep newlines and spaces, like jQuery 70 | buf.WriteString(n.Data) 71 | } 72 | if n.FirstChild != nil { 73 | for c := n.FirstChild; c != nil; c = c.NextSibling { 74 | f(c) 75 | } 76 | } 77 | } 78 | for _, n := range s.Nodes { 79 | f(n) 80 | } 81 | 82 | return buf.String() 83 | } 84 | 85 | // Size is an alias for Length. 86 | func (s *Selection) Size() int { 87 | return s.Length() 88 | } 89 | 90 | // Length returns the number of elements in the Selection object. 91 | func (s *Selection) Length() int { 92 | return len(s.Nodes) 93 | } 94 | 95 | // Html gets the HTML contents of the first element in the set of matched 96 | // elements. It includes text and comment nodes. 97 | func (s *Selection) Html() (ret string, e error) { 98 | // Since there is no .innerHtml, the HTML content must be re-created from 99 | // the nodes using html.Render. 100 | var buf bytes.Buffer 101 | 102 | if len(s.Nodes) > 0 { 103 | for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling { 104 | e = html.Render(&buf, c) 105 | if e != nil { 106 | return 107 | } 108 | } 109 | ret = buf.String() 110 | } 111 | 112 | return 113 | } 114 | 115 | // AddClass adds the given class(es) to each element in the set of matched elements. 116 | // Multiple class names can be specified, separated by a space or via multiple arguments. 117 | func (s *Selection) AddClass(class ...string) *Selection { 118 | classStr := strings.TrimSpace(strings.Join(class, " ")) 119 | 120 | if classStr == "" { 121 | return s 122 | } 123 | 124 | tcls := getClassesSlice(classStr) 125 | for _, n := range s.Nodes { 126 | curClasses, attr := getClassesAndAttr(n, true) 127 | for _, newClass := range tcls { 128 | if !strings.Contains(curClasses, " "+newClass+" ") { 129 | curClasses += newClass + " " 130 | } 131 | } 132 | 133 | setClasses(n, attr, curClasses) 134 | } 135 | 136 | return s 137 | } 138 | 139 | // HasClass determines whether any of the matched elements are assigned the 140 | // given class. 141 | func (s *Selection) HasClass(class string) bool { 142 | class = " " + class + " " 143 | for _, n := range s.Nodes { 144 | classes, _ := getClassesAndAttr(n, false) 145 | if strings.Contains(classes, class) { 146 | return true 147 | } 148 | } 149 | return false 150 | } 151 | 152 | // RemoveClass removes the given class(es) from each element in the set of matched elements. 153 | // Multiple class names can be specified, separated by a space or via multiple arguments. 154 | // If no class name is provided, all classes are removed. 155 | func (s *Selection) RemoveClass(class ...string) *Selection { 156 | var rclasses []string 157 | 158 | classStr := strings.TrimSpace(strings.Join(class, " ")) 159 | remove := classStr == "" 160 | 161 | if !remove { 162 | rclasses = getClassesSlice(classStr) 163 | } 164 | 165 | for _, n := range s.Nodes { 166 | if remove { 167 | removeAttr(n, "class") 168 | } else { 169 | classes, attr := getClassesAndAttr(n, true) 170 | for _, rcl := range rclasses { 171 | classes = strings.Replace(classes, " "+rcl+" ", " ", -1) 172 | } 173 | 174 | setClasses(n, attr, classes) 175 | } 176 | } 177 | 178 | return s 179 | } 180 | 181 | // ToggleClass adds or removes the given class(es) for each element in the set of matched elements. 182 | // Multiple class names can be specified, separated by a space or via multiple arguments. 183 | func (s *Selection) ToggleClass(class ...string) *Selection { 184 | classStr := strings.TrimSpace(strings.Join(class, " ")) 185 | 186 | if classStr == "" { 187 | return s 188 | } 189 | 190 | tcls := getClassesSlice(classStr) 191 | 192 | for _, n := range s.Nodes { 193 | classes, attr := getClassesAndAttr(n, true) 194 | for _, tcl := range tcls { 195 | if strings.Contains(classes, " "+tcl+" ") { 196 | classes = strings.Replace(classes, " "+tcl+" ", " ", -1) 197 | } else { 198 | classes += tcl + " " 199 | } 200 | } 201 | 202 | setClasses(n, attr, classes) 203 | } 204 | 205 | return s 206 | } 207 | 208 | func getAttributePtr(attrName string, n *html.Node) *html.Attribute { 209 | if n == nil { 210 | return nil 211 | } 212 | 213 | for i, a := range n.Attr { 214 | if a.Key == attrName { 215 | return &n.Attr[i] 216 | } 217 | } 218 | return nil 219 | } 220 | 221 | // Private function to get the specified attribute's value from a node. 222 | func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) { 223 | if a := getAttributePtr(attrName, n); a != nil { 224 | val = a.Val 225 | exists = true 226 | } 227 | return 228 | } 229 | 230 | // Get and normalize the "class" attribute from the node. 231 | func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) { 232 | // Applies only to element nodes 233 | if n.Type == html.ElementNode { 234 | attr = getAttributePtr("class", n) 235 | if attr == nil && create { 236 | n.Attr = append(n.Attr, html.Attribute{ 237 | Key: "class", 238 | Val: "", 239 | }) 240 | attr = &n.Attr[len(n.Attr)-1] 241 | } 242 | } 243 | 244 | if attr == nil { 245 | classes = " " 246 | } else { 247 | classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ") 248 | } 249 | 250 | return 251 | } 252 | 253 | func getClassesSlice(classes string) []string { 254 | return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ") 255 | } 256 | 257 | func removeAttr(n *html.Node, attrName string) { 258 | for i, a := range n.Attr { 259 | if a.Key == attrName { 260 | n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr = 261 | n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1] 262 | return 263 | } 264 | } 265 | } 266 | 267 | func setClasses(n *html.Node, attr *html.Attribute, classes string) { 268 | classes = strings.TrimSpace(classes) 269 | if classes == "" { 270 | removeAttr(n, "class") 271 | return 272 | } 273 | 274 | attr.Val = classes 275 | } 276 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/foreign.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { 12 | for i := range aa { 13 | if newName, ok := nameMap[aa[i].Key]; ok { 14 | aa[i].Key = newName 15 | } 16 | } 17 | } 18 | 19 | func adjustForeignAttributes(aa []Attribute) { 20 | for i, a := range aa { 21 | if a.Key == "" || a.Key[0] != 'x' { 22 | continue 23 | } 24 | switch a.Key { 25 | case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", 26 | "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": 27 | j := strings.Index(a.Key, ":") 28 | aa[i].Namespace = a.Key[:j] 29 | aa[i].Key = a.Key[j+1:] 30 | } 31 | } 32 | } 33 | 34 | func htmlIntegrationPoint(n *Node) bool { 35 | if n.Type != ElementNode { 36 | return false 37 | } 38 | switch n.Namespace { 39 | case "math": 40 | if n.Data == "annotation-xml" { 41 | for _, a := range n.Attr { 42 | if a.Key == "encoding" { 43 | val := strings.ToLower(a.Val) 44 | if val == "text/html" || val == "application/xhtml+xml" { 45 | return true 46 | } 47 | } 48 | } 49 | } 50 | case "svg": 51 | switch n.Data { 52 | case "desc", "foreignObject", "title": 53 | return true 54 | } 55 | } 56 | return false 57 | } 58 | 59 | func mathMLTextIntegrationPoint(n *Node) bool { 60 | if n.Namespace != "math" { 61 | return false 62 | } 63 | switch n.Data { 64 | case "mi", "mo", "mn", "ms", "mtext": 65 | return true 66 | } 67 | return false 68 | } 69 | 70 | // Section 12.2.6.5. 71 | var breakout = map[string]bool{ 72 | "b": true, 73 | "big": true, 74 | "blockquote": true, 75 | "body": true, 76 | "br": true, 77 | "center": true, 78 | "code": true, 79 | "dd": true, 80 | "div": true, 81 | "dl": true, 82 | "dt": true, 83 | "em": true, 84 | "embed": true, 85 | "h1": true, 86 | "h2": true, 87 | "h3": true, 88 | "h4": true, 89 | "h5": true, 90 | "h6": true, 91 | "head": true, 92 | "hr": true, 93 | "i": true, 94 | "img": true, 95 | "li": true, 96 | "listing": true, 97 | "menu": true, 98 | "meta": true, 99 | "nobr": true, 100 | "ol": true, 101 | "p": true, 102 | "pre": true, 103 | "ruby": true, 104 | "s": true, 105 | "small": true, 106 | "span": true, 107 | "strong": true, 108 | "strike": true, 109 | "sub": true, 110 | "sup": true, 111 | "table": true, 112 | "tt": true, 113 | "u": true, 114 | "ul": true, 115 | "var": true, 116 | } 117 | 118 | // Section 12.2.6.5. 119 | var svgTagNameAdjustments = map[string]string{ 120 | "altglyph": "altGlyph", 121 | "altglyphdef": "altGlyphDef", 122 | "altglyphitem": "altGlyphItem", 123 | "animatecolor": "animateColor", 124 | "animatemotion": "animateMotion", 125 | "animatetransform": "animateTransform", 126 | "clippath": "clipPath", 127 | "feblend": "feBlend", 128 | "fecolormatrix": "feColorMatrix", 129 | "fecomponenttransfer": "feComponentTransfer", 130 | "fecomposite": "feComposite", 131 | "feconvolvematrix": "feConvolveMatrix", 132 | "fediffuselighting": "feDiffuseLighting", 133 | "fedisplacementmap": "feDisplacementMap", 134 | "fedistantlight": "feDistantLight", 135 | "feflood": "feFlood", 136 | "fefunca": "feFuncA", 137 | "fefuncb": "feFuncB", 138 | "fefuncg": "feFuncG", 139 | "fefuncr": "feFuncR", 140 | "fegaussianblur": "feGaussianBlur", 141 | "feimage": "feImage", 142 | "femerge": "feMerge", 143 | "femergenode": "feMergeNode", 144 | "femorphology": "feMorphology", 145 | "feoffset": "feOffset", 146 | "fepointlight": "fePointLight", 147 | "fespecularlighting": "feSpecularLighting", 148 | "fespotlight": "feSpotLight", 149 | "fetile": "feTile", 150 | "feturbulence": "feTurbulence", 151 | "foreignobject": "foreignObject", 152 | "glyphref": "glyphRef", 153 | "lineargradient": "linearGradient", 154 | "radialgradient": "radialGradient", 155 | "textpath": "textPath", 156 | } 157 | 158 | // Section 12.2.6.1 159 | var mathMLAttributeAdjustments = map[string]string{ 160 | "definitionurl": "definitionURL", 161 | } 162 | 163 | var svgAttributeAdjustments = map[string]string{ 164 | "attributename": "attributeName", 165 | "attributetype": "attributeType", 166 | "basefrequency": "baseFrequency", 167 | "baseprofile": "baseProfile", 168 | "calcmode": "calcMode", 169 | "clippathunits": "clipPathUnits", 170 | "contentscripttype": "contentScriptType", 171 | "contentstyletype": "contentStyleType", 172 | "diffuseconstant": "diffuseConstant", 173 | "edgemode": "edgeMode", 174 | "externalresourcesrequired": "externalResourcesRequired", 175 | "filterunits": "filterUnits", 176 | "glyphref": "glyphRef", 177 | "gradienttransform": "gradientTransform", 178 | "gradientunits": "gradientUnits", 179 | "kernelmatrix": "kernelMatrix", 180 | "kernelunitlength": "kernelUnitLength", 181 | "keypoints": "keyPoints", 182 | "keysplines": "keySplines", 183 | "keytimes": "keyTimes", 184 | "lengthadjust": "lengthAdjust", 185 | "limitingconeangle": "limitingConeAngle", 186 | "markerheight": "markerHeight", 187 | "markerunits": "markerUnits", 188 | "markerwidth": "markerWidth", 189 | "maskcontentunits": "maskContentUnits", 190 | "maskunits": "maskUnits", 191 | "numoctaves": "numOctaves", 192 | "pathlength": "pathLength", 193 | "patterncontentunits": "patternContentUnits", 194 | "patterntransform": "patternTransform", 195 | "patternunits": "patternUnits", 196 | "pointsatx": "pointsAtX", 197 | "pointsaty": "pointsAtY", 198 | "pointsatz": "pointsAtZ", 199 | "preservealpha": "preserveAlpha", 200 | "preserveaspectratio": "preserveAspectRatio", 201 | "primitiveunits": "primitiveUnits", 202 | "refx": "refX", 203 | "refy": "refY", 204 | "repeatcount": "repeatCount", 205 | "repeatdur": "repeatDur", 206 | "requiredextensions": "requiredExtensions", 207 | "requiredfeatures": "requiredFeatures", 208 | "specularconstant": "specularConstant", 209 | "specularexponent": "specularExponent", 210 | "spreadmethod": "spreadMethod", 211 | "startoffset": "startOffset", 212 | "stddeviation": "stdDeviation", 213 | "stitchtiles": "stitchTiles", 214 | "surfacescale": "surfaceScale", 215 | "systemlanguage": "systemLanguage", 216 | "tablevalues": "tableValues", 217 | "targetx": "targetX", 218 | "targety": "targetY", 219 | "textlength": "textLength", 220 | "viewbox": "viewBox", 221 | "viewtarget": "viewTarget", 222 | "xchannelselector": "xChannelSelector", 223 | "ychannelselector": "yChannelSelector", 224 | "zoomandpan": "zoomAndPan", 225 | } 226 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/render.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "strings" 13 | ) 14 | 15 | type writer interface { 16 | io.Writer 17 | io.ByteWriter 18 | WriteString(string) (int, error) 19 | } 20 | 21 | // Render renders the parse tree n to the given writer. 22 | // 23 | // Rendering is done on a 'best effort' basis: calling Parse on the output of 24 | // Render will always result in something similar to the original tree, but it 25 | // is not necessarily an exact clone unless the original tree was 'well-formed'. 26 | // 'Well-formed' is not easily specified; the HTML5 specification is 27 | // complicated. 28 | // 29 | // Calling Parse on arbitrary input typically results in a 'well-formed' parse 30 | // tree. However, it is possible for Parse to yield a 'badly-formed' parse tree. 31 | // For example, in a 'well-formed' parse tree, no element is a child of 32 | // another element: parsing "" results in two sibling elements. 33 | // Similarly, in a 'well-formed' parse tree, no element is a child of a 34 | // element: parsing "

" results in a

with two sibling 35 | // children; the is reparented to the

's parent. However, calling 36 | // Parse on "
" does not return an error, but the result has an 37 | // element with an child, and is therefore not 'well-formed'. 38 | // 39 | // Programmatically constructed trees are typically also 'well-formed', but it 40 | // is possible to construct a tree that looks innocuous but, when rendered and 41 | // re-parsed, results in a different tree. A simple example is that a solitary 42 | // text node would become a tree containing , and elements. 43 | // Another example is that the programmatic equivalent of "abc" 44 | // becomes "abc". 45 | func Render(w io.Writer, n *Node) error { 46 | if x, ok := w.(writer); ok { 47 | return render(x, n) 48 | } 49 | buf := bufio.NewWriter(w) 50 | if err := render(buf, n); err != nil { 51 | return err 52 | } 53 | return buf.Flush() 54 | } 55 | 56 | // plaintextAbort is returned from render1 when a element 57 | // has been rendered. No more end tags should be rendered after that. 58 | var plaintextAbort = errors.New("html: internal error (plaintext abort)") 59 | 60 | func render(w writer, n *Node) error { 61 | err := render1(w, n) 62 | if err == plaintextAbort { 63 | err = nil 64 | } 65 | return err 66 | } 67 | 68 | func render1(w writer, n *Node) error { 69 | // Render non-element nodes; these are the easy cases. 70 | switch n.Type { 71 | case ErrorNode: 72 | return errors.New("html: cannot render an ErrorNode node") 73 | case TextNode: 74 | return escape(w, n.Data) 75 | case DocumentNode: 76 | for c := n.FirstChild; c != nil; c = c.NextSibling { 77 | if err := render1(w, c); err != nil { 78 | return err 79 | } 80 | } 81 | return nil 82 | case ElementNode: 83 | // No-op. 84 | case CommentNode: 85 | if _, err := w.WriteString("<!--"); err != nil { 86 | return err 87 | } 88 | if _, err := w.WriteString(n.Data); err != nil { 89 | return err 90 | } 91 | if _, err := w.WriteString("-->"); err != nil { 92 | return err 93 | } 94 | return nil 95 | case DoctypeNode: 96 | if _, err := w.WriteString("<!DOCTYPE "); err != nil { 97 | return err 98 | } 99 | if _, err := w.WriteString(n.Data); err != nil { 100 | return err 101 | } 102 | if n.Attr != nil { 103 | var p, s string 104 | for _, a := range n.Attr { 105 | switch a.Key { 106 | case "public": 107 | p = a.Val 108 | case "system": 109 | s = a.Val 110 | } 111 | } 112 | if p != "" { 113 | if _, err := w.WriteString(" PUBLIC "); err != nil { 114 | return err 115 | } 116 | if err := writeQuoted(w, p); err != nil { 117 | return err 118 | } 119 | if s != "" { 120 | if err := w.WriteByte(' '); err != nil { 121 | return err 122 | } 123 | if err := writeQuoted(w, s); err != nil { 124 | return err 125 | } 126 | } 127 | } else if s != "" { 128 | if _, err := w.WriteString(" SYSTEM "); err != nil { 129 | return err 130 | } 131 | if err := writeQuoted(w, s); err != nil { 132 | return err 133 | } 134 | } 135 | } 136 | return w.WriteByte('>') 137 | case RawNode: 138 | _, err := w.WriteString(n.Data) 139 | return err 140 | default: 141 | return errors.New("html: unknown node type") 142 | } 143 | 144 | // Render the <xxx> opening tag. 145 | if err := w.WriteByte('<'); err != nil { 146 | return err 147 | } 148 | if _, err := w.WriteString(n.Data); err != nil { 149 | return err 150 | } 151 | for _, a := range n.Attr { 152 | if err := w.WriteByte(' '); err != nil { 153 | return err 154 | } 155 | if a.Namespace != "" { 156 | if _, err := w.WriteString(a.Namespace); err != nil { 157 | return err 158 | } 159 | if err := w.WriteByte(':'); err != nil { 160 | return err 161 | } 162 | } 163 | if _, err := w.WriteString(a.Key); err != nil { 164 | return err 165 | } 166 | if _, err := w.WriteString(`="`); err != nil { 167 | return err 168 | } 169 | if err := escape(w, a.Val); err != nil { 170 | return err 171 | } 172 | if err := w.WriteByte('"'); err != nil { 173 | return err 174 | } 175 | } 176 | if voidElements[n.Data] { 177 | if n.FirstChild != nil { 178 | return fmt.Errorf("html: void element <%s> has child nodes", n.Data) 179 | } 180 | _, err := w.WriteString("/>") 181 | return err 182 | } 183 | if err := w.WriteByte('>'); err != nil { 184 | return err 185 | } 186 | 187 | // Add initial newline where there is danger of a newline beging ignored. 188 | if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { 189 | switch n.Data { 190 | case "pre", "listing", "textarea": 191 | if err := w.WriteByte('\n'); err != nil { 192 | return err 193 | } 194 | } 195 | } 196 | 197 | // Render any child nodes. 198 | switch n.Data { 199 | case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": 200 | for c := n.FirstChild; c != nil; c = c.NextSibling { 201 | if c.Type == TextNode { 202 | if _, err := w.WriteString(c.Data); err != nil { 203 | return err 204 | } 205 | } else { 206 | if err := render1(w, c); err != nil { 207 | return err 208 | } 209 | } 210 | } 211 | if n.Data == "plaintext" { 212 | // Don't render anything else. <plaintext> must be the 213 | // last element in the file, with no closing tag. 214 | return plaintextAbort 215 | } 216 | default: 217 | for c := n.FirstChild; c != nil; c = c.NextSibling { 218 | if err := render1(w, c); err != nil { 219 | return err 220 | } 221 | } 222 | } 223 | 224 | // Render the </xxx> closing tag. 225 | if _, err := w.WriteString("</"); err != nil { 226 | return err 227 | } 228 | if _, err := w.WriteString(n.Data); err != nil { 229 | return err 230 | } 231 | return w.WriteByte('>') 232 | } 233 | 234 | // writeQuoted writes s to w surrounded by quotes. Normally it will use double 235 | // quotes, but if s contains a double quote, it will use single quotes. 236 | // It is used for writing the identifiers in a doctype declaration. 237 | // In valid HTML, they can't contain both types of quotes. 238 | func writeQuoted(w writer, s string) error { 239 | var q byte = '"' 240 | if strings.Contains(s, `"`) { 241 | q = '\'' 242 | } 243 | if err := w.WriteByte(q); err != nil { 244 | return err 245 | } 246 | if _, err := w.WriteString(s); err != nil { 247 | return err 248 | } 249 | if err := w.WriteByte(q); err != nil { 250 | return err 251 | } 252 | return nil 253 | } 254 | 255 | // Section 12.1.2, "Elements", gives this list of void elements. Void elements 256 | // are those that can't have any contents. 257 | var voidElements = map[string]bool{ 258 | "area": true, 259 | "base": true, 260 | "br": true, 261 | "col": true, 262 | "embed": true, 263 | "hr": true, 264 | "img": true, 265 | "input": true, 266 | "keygen": true, 267 | "link": true, 268 | "meta": true, 269 | "param": true, 270 | "source": true, 271 | "track": true, 272 | "wbr": true, 273 | } 274 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/opts.go: -------------------------------------------------------------------------------- 1 | package docopt 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | "strings" 8 | "unicode" 9 | ) 10 | 11 | func errKey(key string) error { 12 | return fmt.Errorf("no such key: %q", key) 13 | } 14 | func errType(key string) error { 15 | return fmt.Errorf("key: %q failed type conversion", key) 16 | } 17 | func errStrconv(key string, convErr error) error { 18 | return fmt.Errorf("key: %q failed type conversion: %s", key, convErr) 19 | } 20 | 21 | // Opts is a map of command line options to their values, with some convenience 22 | // methods for value type conversion (bool, float64, int, string). For example, 23 | // to get an option value as an int: 24 | // 25 | // opts, _ := docopt.ParseDoc("Usage: sleep <seconds>") 26 | // secs, _ := opts.Int("<seconds>") 27 | // 28 | // Additionally, Opts.Bind allows you easily populate a struct's fields with the 29 | // values of each option value. See below for examples. 30 | // 31 | // Lastly, you can still treat Opts as a regular map, and do any type checking 32 | // and conversion that you want to yourself. For example: 33 | // 34 | // if s, ok := opts["<binary>"].(string); ok { 35 | // if val, err := strconv.ParseUint(s, 2, 64); err != nil { ... } 36 | // } 37 | // 38 | // Note that any non-boolean option / flag will have a string value in the 39 | // underlying map. 40 | type Opts map[string]interface{} 41 | 42 | func (o Opts) String(key string) (s string, err error) { 43 | v, ok := o[key] 44 | if !ok { 45 | err = errKey(key) 46 | return 47 | } 48 | s, ok = v.(string) 49 | if !ok { 50 | err = errType(key) 51 | } 52 | return 53 | } 54 | 55 | func (o Opts) Bool(key string) (b bool, err error) { 56 | v, ok := o[key] 57 | if !ok { 58 | err = errKey(key) 59 | return 60 | } 61 | b, ok = v.(bool) 62 | if !ok { 63 | err = errType(key) 64 | } 65 | return 66 | } 67 | 68 | func (o Opts) Int(key string) (i int, err error) { 69 | s, err := o.String(key) 70 | if err != nil { 71 | return 72 | } 73 | i, err = strconv.Atoi(s) 74 | if err != nil { 75 | err = errStrconv(key, err) 76 | } 77 | return 78 | } 79 | 80 | func (o Opts) Float64(key string) (f float64, err error) { 81 | s, err := o.String(key) 82 | if err != nil { 83 | return 84 | } 85 | f, err = strconv.ParseFloat(s, 64) 86 | if err != nil { 87 | err = errStrconv(key, err) 88 | } 89 | return 90 | } 91 | 92 | // Bind populates the fields of a given struct with matching option values. 93 | // Each key in Opts will be mapped to an exported field of the struct pointed 94 | // to by `v`, as follows: 95 | // 96 | // abc int // Unexported field, ignored 97 | // Abc string // Mapped from `--abc`, `<abc>`, or `abc` 98 | // // (case insensitive) 99 | // A string // Mapped from `-a`, `<a>` or `a` 100 | // // (case insensitive) 101 | // Abc int `docopt:"XYZ"` // Mapped from `XYZ` 102 | // Abc bool `docopt:"-"` // Mapped from `-` 103 | // Abc bool `docopt:"-x,--xyz"` // Mapped from `-x` or `--xyz` 104 | // // (first non-zero value found) 105 | // 106 | // Tagged (annotated) fields will always be mapped first. If no field is tagged 107 | // with an option's key, Bind will try to map the option to an appropriately 108 | // named field (as above). 109 | // 110 | // Bind also handles conversion to bool, float, int or string types. 111 | func (o Opts) Bind(v interface{}) error { 112 | structVal := reflect.ValueOf(v) 113 | if structVal.Kind() != reflect.Ptr { 114 | return newError("'v' argument is not pointer to struct type") 115 | } 116 | for structVal.Kind() == reflect.Ptr { 117 | structVal = structVal.Elem() 118 | } 119 | if structVal.Kind() != reflect.Struct { 120 | return newError("'v' argument is not pointer to struct type") 121 | } 122 | structType := structVal.Type() 123 | 124 | tagged := make(map[string]int) // Tagged field tags 125 | untagged := make(map[string]int) // Untagged field names 126 | 127 | for i := 0; i < structType.NumField(); i++ { 128 | field := structType.Field(i) 129 | if isUnexportedField(field) || field.Anonymous { 130 | continue 131 | } 132 | tag := field.Tag.Get("docopt") 133 | if tag == "" { 134 | untagged[field.Name] = i 135 | continue 136 | } 137 | for _, t := range strings.Split(tag, ",") { 138 | tagged[t] = i 139 | } 140 | } 141 | 142 | // Get the index of the struct field to use, based on the option key. 143 | // Second argument is true/false on whether something was matched. 144 | getFieldIndex := func(key string) (int, bool) { 145 | if i, ok := tagged[key]; ok { 146 | return i, true 147 | } 148 | if i, ok := untagged[guessUntaggedField(key)]; ok { 149 | return i, true 150 | } 151 | return -1, false 152 | } 153 | 154 | indexMap := make(map[string]int) // Option keys to field index 155 | 156 | // Pre-check that option keys are mapped to fields and fields are zero valued, before populating them. 157 | for k := range o { 158 | i, ok := getFieldIndex(k) 159 | if !ok { 160 | if k == "--help" || k == "--version" { // Don't require these to be mapped. 161 | continue 162 | } 163 | return newError("mapping of %q is not found in given struct, or is an unexported field", k) 164 | } 165 | fieldVal := structVal.Field(i) 166 | zeroVal := reflect.Zero(fieldVal.Type()) 167 | if !reflect.DeepEqual(fieldVal.Interface(), zeroVal.Interface()) { 168 | return newError("%q field is non-zero, will be overwritten by value of %q", structType.Field(i).Name, k) 169 | } 170 | indexMap[k] = i 171 | } 172 | 173 | // Populate fields with option values. 174 | for k, v := range o { 175 | i, ok := indexMap[k] 176 | if !ok { 177 | continue // Not mapped. 178 | } 179 | field := structVal.Field(i) 180 | if !reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()) { 181 | // The struct's field is already non-zero (by our doing), so don't change it. 182 | // This happens with comma separated tags, e.g. `docopt:"-h,--help"` which is a 183 | // convenient way of checking if one of multiple boolean flags are set. 184 | continue 185 | } 186 | optVal := reflect.ValueOf(v) 187 | // Option value is the zero Value, so we can't get its .Type(). No need to assign anyway, so move along. 188 | if !optVal.IsValid() { 189 | continue 190 | } 191 | if !field.CanSet() { 192 | return newError("%q field cannot be set", structType.Field(i).Name) 193 | } 194 | // Try to assign now if able. bool and string values should be assignable already. 195 | if optVal.Type().AssignableTo(field.Type()) { 196 | field.Set(optVal) 197 | continue 198 | } 199 | // Try to convert the value and assign if able. 200 | switch field.Kind() { 201 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 202 | if x, err := o.Int(k); err == nil { 203 | field.SetInt(int64(x)) 204 | continue 205 | } 206 | case reflect.Float32, reflect.Float64: 207 | if x, err := o.Float64(k); err == nil { 208 | field.SetFloat(x) 209 | continue 210 | } 211 | } 212 | // TODO: Something clever (recursive?) with non-string slices. 213 | // case reflect.Slice: 214 | // if optVal.Kind() == reflect.Slice { 215 | // for i := 0; i < optVal.Len(); i++ { 216 | // sliceVal := optVal.Index(i) 217 | // fmt.Printf("%v", sliceVal) 218 | // } 219 | // fmt.Printf("\n") 220 | // } 221 | return newError("value of %q is not assignable to %q field", k, structType.Field(i).Name) 222 | } 223 | 224 | return nil 225 | } 226 | 227 | // isUnexportedField returns whether the field is unexported. 228 | // isUnexportedField is to avoid the bug in versions older than Go1.3. 229 | // See following links: 230 | // https://code.google.com/p/go/issues/detail?id=7247 231 | // http://golang.org/ref/spec#Exported_identifiers 232 | func isUnexportedField(field reflect.StructField) bool { 233 | return !(field.PkgPath == "" && unicode.IsUpper(rune(field.Name[0]))) 234 | } 235 | 236 | // Convert a string like "--my-special-flag" to "MySpecialFlag". 237 | func titleCaseDashes(key string) string { 238 | nextToUpper := true 239 | mapFn := func(r rune) rune { 240 | if r == '-' { 241 | nextToUpper = true 242 | return -1 243 | } 244 | if nextToUpper { 245 | nextToUpper = false 246 | return unicode.ToUpper(r) 247 | } 248 | return r 249 | } 250 | return strings.Map(mapFn, key) 251 | } 252 | 253 | // Best guess which field.Name in a struct to assign for an option key. 254 | func guessUntaggedField(key string) string { 255 | switch { 256 | case strings.HasPrefix(key, "--") && len(key[2:]) > 1: 257 | return titleCaseDashes(key[2:]) 258 | case strings.HasPrefix(key, "-") && len(key[1:]) == 1: 259 | return titleCaseDashes(key[1:]) 260 | case strings.HasPrefix(key, "<") && strings.HasSuffix(key, ">"): 261 | key = key[1 : len(key)-1] 262 | } 263 | return strings.Title(strings.ToLower(key)) 264 | } 265 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/README.md: -------------------------------------------------------------------------------- 1 | # goquery - a little like that j-thing, only in Go 2 | [![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.svg?branch=master)](http://travis-ci.org/PuerkitoBio/goquery) [![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge) 3 | 4 | goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off. 5 | 6 | Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this. 7 | 8 | Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...). 9 | 10 | ## Table of Contents 11 | 12 | * [Installation](#installation) 13 | * [Changelog](#changelog) 14 | * [API](#api) 15 | * [Examples](#examples) 16 | * [Related Projects](#related-projects) 17 | * [Support](#support) 18 | * [License](#license) 19 | 20 | ## Installation 21 | 22 | Please note that because of the net/html dependency, goquery requires Go1.1+. 23 | 24 | $ go get github.com/PuerkitoBio/goquery 25 | 26 | (optional) To run unit tests: 27 | 28 | $ cd $GOPATH/src/github.com/PuerkitoBio/goquery 29 | $ go test 30 | 31 | (optional) To run benchmarks (warning: it runs for a few minutes): 32 | 33 | $ cd $GOPATH/src/github.com/PuerkitoBio/goquery 34 | $ go test -bench=".*" 35 | 36 | ## Changelog 37 | 38 | **Note that goquery's API is now stable, and will not break.** 39 | 40 | * **2020-02-04 (v1.5.1)** : Update module dependencies. 41 | * **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505). 42 | * **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples. 43 | * **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`. 44 | * **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue). 45 | * **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins). 46 | * **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv). 47 | * **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb). 48 | * **2016-08-28 (v1.0.1)** : Optimize performance for large documents. 49 | * **2016-07-27 (v1.0.0)** : Tag version 1.0.0. 50 | * **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object. 51 | * **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see godoc for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`). 52 | * **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr]. 53 | * **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone]. 54 | * **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone]. 55 | * **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used. 56 | * **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s. 57 | * **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader. 58 | * **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response. 59 | * **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility. 60 | * **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out). 61 | * **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method. 62 | * **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases). 63 | * **v0.1.0** : Initial release. 64 | 65 | ## API 66 | 67 | goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate. 68 | 69 | jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention: 70 | 71 | * When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`) 72 | * When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`) 73 | * The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`) 74 | * The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`) 75 | * The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`) 76 | * The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`) 77 | 78 | Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour). 79 | 80 | The complete [godoc reference documentation can be found here][doc]. 81 | 82 | Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string): 83 | 84 | * `Find("~")` returns an empty selection because the selector string doesn't match anything. 85 | * `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything). 86 | * `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything. 87 | * `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element. 88 | 89 | ## Examples 90 | 91 | See some tips and tricks in the [wiki][]. 92 | 93 | Adapted from example_test.go: 94 | 95 | ```Go 96 | package main 97 | 98 | import ( 99 | "fmt" 100 | "log" 101 | "net/http" 102 | 103 | "github.com/PuerkitoBio/goquery" 104 | ) 105 | 106 | func ExampleScrape() { 107 | // Request the HTML page. 108 | res, err := http.Get("http://metalsucks.net") 109 | if err != nil { 110 | log.Fatal(err) 111 | } 112 | defer res.Body.Close() 113 | if res.StatusCode != 200 { 114 | log.Fatalf("status code error: %d %s", res.StatusCode, res.Status) 115 | } 116 | 117 | // Load the HTML document 118 | doc, err := goquery.NewDocumentFromReader(res.Body) 119 | if err != nil { 120 | log.Fatal(err) 121 | } 122 | 123 | // Find the review items 124 | doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) { 125 | // For each item found, get the band and title 126 | band := s.Find("a").Text() 127 | title := s.Find("i").Text() 128 | fmt.Printf("Review %d: %s - %s\n", i, band, title) 129 | }) 130 | } 131 | 132 | func main() { 133 | ExampleScrape() 134 | } 135 | ``` 136 | 137 | ## Related Projects 138 | 139 | - [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags. 140 | - [andybalholm/cascadia][cascadia], the CSS selector library used by goquery. 141 | - [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors. 142 | - [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework 143 | - [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets. 144 | - [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping. 145 | - [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins 146 | - [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers. 147 | - [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering. 148 | 149 | ## Support 150 | 151 | There are a number of ways you can support the project: 152 | 153 | * Use it, star it, build something with it, spread the word! 154 | - If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section! 155 | * Raise issues to improve the project (note: doc typos and clarifications are issues too!) 156 | - Please search existing issues before opening a new one - it may have already been adressed. 157 | * Pull requests: please discuss new code in an issue first, unless the fix is really trivial. 158 | - Make sure new code is tested. 159 | - Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue. 160 | 161 | If you desperately want to send money my way, I have a BuyMeACoffee.com page: 162 | 163 | <a href="https://www.buymeacoffee.com/mna" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a> 164 | 165 | ## License 166 | 167 | The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic]. 168 | 169 | [jquery]: http://jquery.com/ 170 | [go]: http://golang.org/ 171 | [cascadia]: https://github.com/andybalholm/cascadia 172 | [cascadiacli]: https://github.com/suntong/cascadia 173 | [bsd]: http://opensource.org/licenses/BSD-3-Clause 174 | [golic]: http://golang.org/LICENSE 175 | [caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE 176 | [doc]: http://godoc.org/github.com/PuerkitoBio/goquery 177 | [index]: http://api.jquery.com/index/ 178 | [gonet]: https://github.com/golang/net/ 179 | [html]: http://godoc.org/golang.org/x/net/html 180 | [wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks 181 | [thatguystone]: https://github.com/thatguystone 182 | [piotr]: https://github.com/piotrkowalczuk 183 | [goq]: https://github.com/andrewstuart/goq 184 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/pattern.go: -------------------------------------------------------------------------------- 1 | package docopt 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | type patternType uint 10 | 11 | const ( 12 | // leaf 13 | patternArgument patternType = 1 << iota 14 | patternCommand 15 | patternOption 16 | 17 | // branch 18 | patternRequired 19 | patternOptionAL 20 | patternOptionSSHORTCUT // Marker/placeholder for [options] shortcut. 21 | patternOneOrMore 22 | patternEither 23 | 24 | patternLeaf = patternArgument + 25 | patternCommand + 26 | patternOption 27 | patternBranch = patternRequired + 28 | patternOptionAL + 29 | patternOptionSSHORTCUT + 30 | patternOneOrMore + 31 | patternEither 32 | patternAll = patternLeaf + patternBranch 33 | patternDefault = 0 34 | ) 35 | 36 | func (pt patternType) String() string { 37 | switch pt { 38 | case patternArgument: 39 | return "argument" 40 | case patternCommand: 41 | return "command" 42 | case patternOption: 43 | return "option" 44 | case patternRequired: 45 | return "required" 46 | case patternOptionAL: 47 | return "optional" 48 | case patternOptionSSHORTCUT: 49 | return "optionsshortcut" 50 | case patternOneOrMore: 51 | return "oneormore" 52 | case patternEither: 53 | return "either" 54 | case patternLeaf: 55 | return "leaf" 56 | case patternBranch: 57 | return "branch" 58 | case patternAll: 59 | return "all" 60 | case patternDefault: 61 | return "default" 62 | } 63 | return "" 64 | } 65 | 66 | type pattern struct { 67 | t patternType 68 | 69 | children patternList 70 | 71 | name string 72 | value interface{} 73 | 74 | short string 75 | long string 76 | argcount int 77 | } 78 | 79 | type patternList []*pattern 80 | 81 | func newBranchPattern(t patternType, pl ...*pattern) *pattern { 82 | var p pattern 83 | p.t = t 84 | p.children = make(patternList, len(pl)) 85 | copy(p.children, pl) 86 | return &p 87 | } 88 | 89 | func newRequired(pl ...*pattern) *pattern { 90 | return newBranchPattern(patternRequired, pl...) 91 | } 92 | 93 | func newEither(pl ...*pattern) *pattern { 94 | return newBranchPattern(patternEither, pl...) 95 | } 96 | 97 | func newOneOrMore(pl ...*pattern) *pattern { 98 | return newBranchPattern(patternOneOrMore, pl...) 99 | } 100 | 101 | func newOptional(pl ...*pattern) *pattern { 102 | return newBranchPattern(patternOptionAL, pl...) 103 | } 104 | 105 | func newOptionsShortcut() *pattern { 106 | var p pattern 107 | p.t = patternOptionSSHORTCUT 108 | return &p 109 | } 110 | 111 | func newLeafPattern(t patternType, name string, value interface{}) *pattern { 112 | // default: value=nil 113 | var p pattern 114 | p.t = t 115 | p.name = name 116 | p.value = value 117 | return &p 118 | } 119 | 120 | func newArgument(name string, value interface{}) *pattern { 121 | // default: value=nil 122 | return newLeafPattern(patternArgument, name, value) 123 | } 124 | 125 | func newCommand(name string, value interface{}) *pattern { 126 | // default: value=false 127 | var p pattern 128 | p.t = patternCommand 129 | p.name = name 130 | p.value = value 131 | return &p 132 | } 133 | 134 | func newOption(short, long string, argcount int, value interface{}) *pattern { 135 | // default: "", "", 0, false 136 | var p pattern 137 | p.t = patternOption 138 | p.short = short 139 | p.long = long 140 | if long != "" { 141 | p.name = long 142 | } else { 143 | p.name = short 144 | } 145 | p.argcount = argcount 146 | if value == false && argcount > 0 { 147 | p.value = nil 148 | } else { 149 | p.value = value 150 | } 151 | return &p 152 | } 153 | 154 | func (p *pattern) flat(types patternType) (patternList, error) { 155 | if p.t&patternLeaf != 0 { 156 | if types == patternDefault { 157 | types = patternAll 158 | } 159 | if p.t&types != 0 { 160 | return patternList{p}, nil 161 | } 162 | return patternList{}, nil 163 | } 164 | 165 | if p.t&patternBranch != 0 { 166 | if p.t&types != 0 { 167 | return patternList{p}, nil 168 | } 169 | result := patternList{} 170 | for _, child := range p.children { 171 | childFlat, err := child.flat(types) 172 | if err != nil { 173 | return nil, err 174 | } 175 | result = append(result, childFlat...) 176 | } 177 | return result, nil 178 | } 179 | return nil, newError("unknown pattern type: %d, %d", p.t, types) 180 | } 181 | 182 | func (p *pattern) fix() error { 183 | err := p.fixIdentities(nil) 184 | if err != nil { 185 | return err 186 | } 187 | p.fixRepeatingArguments() 188 | return nil 189 | } 190 | 191 | func (p *pattern) fixIdentities(uniq patternList) error { 192 | // Make pattern-tree tips point to same object if they are equal. 193 | if p.t&patternBranch == 0 { 194 | return nil 195 | } 196 | if uniq == nil { 197 | pFlat, err := p.flat(patternDefault) 198 | if err != nil { 199 | return err 200 | } 201 | uniq = pFlat.unique() 202 | } 203 | for i, child := range p.children { 204 | if child.t&patternBranch == 0 { 205 | ind, err := uniq.index(child) 206 | if err != nil { 207 | return err 208 | } 209 | p.children[i] = uniq[ind] 210 | } else { 211 | err := child.fixIdentities(uniq) 212 | if err != nil { 213 | return err 214 | } 215 | } 216 | } 217 | return nil 218 | } 219 | 220 | func (p *pattern) fixRepeatingArguments() { 221 | // Fix elements that should accumulate/increment values. 222 | var either []patternList 223 | 224 | for _, child := range p.transform().children { 225 | either = append(either, child.children) 226 | } 227 | for _, cas := range either { 228 | casMultiple := patternList{} 229 | for _, e := range cas { 230 | if cas.count(e) > 1 { 231 | casMultiple = append(casMultiple, e) 232 | } 233 | } 234 | for _, e := range casMultiple { 235 | if e.t == patternArgument || e.t == patternOption && e.argcount > 0 { 236 | switch e.value.(type) { 237 | case string: 238 | e.value = strings.Fields(e.value.(string)) 239 | case []string: 240 | default: 241 | e.value = []string{} 242 | } 243 | } 244 | if e.t == patternCommand || e.t == patternOption && e.argcount == 0 { 245 | e.value = 0 246 | } 247 | } 248 | } 249 | } 250 | 251 | func (p *pattern) match(left *patternList, collected *patternList) (bool, *patternList, *patternList) { 252 | if collected == nil { 253 | collected = &patternList{} 254 | } 255 | if p.t&patternRequired != 0 { 256 | l := left 257 | c := collected 258 | for _, p := range p.children { 259 | var matched bool 260 | matched, l, c = p.match(l, c) 261 | if !matched { 262 | return false, left, collected 263 | } 264 | } 265 | return true, l, c 266 | } else if p.t&patternOptionAL != 0 || p.t&patternOptionSSHORTCUT != 0 { 267 | for _, p := range p.children { 268 | _, left, collected = p.match(left, collected) 269 | } 270 | return true, left, collected 271 | } else if p.t&patternOneOrMore != 0 { 272 | if len(p.children) != 1 { 273 | panic("OneOrMore.match(): assert len(p.children) == 1") 274 | } 275 | l := left 276 | c := collected 277 | var lAlt *patternList 278 | matched := true 279 | times := 0 280 | for matched { 281 | // could it be that something didn't match but changed l or c? 282 | matched, l, c = p.children[0].match(l, c) 283 | if matched { 284 | times++ 285 | } 286 | if lAlt == l { 287 | break 288 | } 289 | lAlt = l 290 | } 291 | if times >= 1 { 292 | return true, l, c 293 | } 294 | return false, left, collected 295 | } else if p.t&patternEither != 0 { 296 | type outcomeStruct struct { 297 | matched bool 298 | left *patternList 299 | collected *patternList 300 | length int 301 | } 302 | outcomes := []outcomeStruct{} 303 | for _, p := range p.children { 304 | matched, l, c := p.match(left, collected) 305 | outcome := outcomeStruct{matched, l, c, len(*l)} 306 | if matched { 307 | outcomes = append(outcomes, outcome) 308 | } 309 | } 310 | if len(outcomes) > 0 { 311 | minLen := outcomes[0].length 312 | minIndex := 0 313 | for i, v := range outcomes { 314 | if v.length < minLen { 315 | minIndex = i 316 | } 317 | } 318 | return outcomes[minIndex].matched, outcomes[minIndex].left, outcomes[minIndex].collected 319 | } 320 | return false, left, collected 321 | } else if p.t&patternLeaf != 0 { 322 | pos, match := p.singleMatch(left) 323 | var increment interface{} 324 | if match == nil { 325 | return false, left, collected 326 | } 327 | leftAlt := make(patternList, len((*left)[:pos]), len((*left)[:pos])+len((*left)[pos+1:])) 328 | copy(leftAlt, (*left)[:pos]) 329 | leftAlt = append(leftAlt, (*left)[pos+1:]...) 330 | sameName := patternList{} 331 | for _, a := range *collected { 332 | if a.name == p.name { 333 | sameName = append(sameName, a) 334 | } 335 | } 336 | 337 | switch p.value.(type) { 338 | case int, []string: 339 | switch p.value.(type) { 340 | case int: 341 | increment = 1 342 | case []string: 343 | switch match.value.(type) { 344 | case string: 345 | increment = []string{match.value.(string)} 346 | default: 347 | increment = match.value 348 | } 349 | } 350 | if len(sameName) == 0 { 351 | match.value = increment 352 | collectedMatch := make(patternList, len(*collected), len(*collected)+1) 353 | copy(collectedMatch, *collected) 354 | collectedMatch = append(collectedMatch, match) 355 | return true, &leftAlt, &collectedMatch 356 | } 357 | switch sameName[0].value.(type) { 358 | case int: 359 | sameName[0].value = sameName[0].value.(int) + increment.(int) 360 | case []string: 361 | sameName[0].value = append(sameName[0].value.([]string), increment.([]string)...) 362 | } 363 | return true, &leftAlt, collected 364 | } 365 | collectedMatch := make(patternList, len(*collected), len(*collected)+1) 366 | copy(collectedMatch, *collected) 367 | collectedMatch = append(collectedMatch, match) 368 | return true, &leftAlt, &collectedMatch 369 | } 370 | panic("unmatched type") 371 | } 372 | 373 | func (p *pattern) singleMatch(left *patternList) (int, *pattern) { 374 | if p.t&patternArgument != 0 { 375 | for n, pat := range *left { 376 | if pat.t&patternArgument != 0 { 377 | return n, newArgument(p.name, pat.value) 378 | } 379 | } 380 | return -1, nil 381 | } else if p.t&patternCommand != 0 { 382 | for n, pat := range *left { 383 | if pat.t&patternArgument != 0 { 384 | if pat.value == p.name { 385 | return n, newCommand(p.name, true) 386 | } 387 | break 388 | } 389 | } 390 | return -1, nil 391 | } else if p.t&patternOption != 0 { 392 | for n, pat := range *left { 393 | if p.name == pat.name { 394 | return n, pat 395 | } 396 | } 397 | return -1, nil 398 | } 399 | panic("unmatched type") 400 | } 401 | 402 | func (p *pattern) String() string { 403 | if p.t&patternOption != 0 { 404 | return fmt.Sprintf("%s(%s, %s, %d, %+v)", p.t, p.short, p.long, p.argcount, p.value) 405 | } else if p.t&patternLeaf != 0 { 406 | return fmt.Sprintf("%s(%s, %+v)", p.t, p.name, p.value) 407 | } else if p.t&patternBranch != 0 { 408 | result := "" 409 | for i, child := range p.children { 410 | if i > 0 { 411 | result += ", " 412 | } 413 | result += child.String() 414 | } 415 | return fmt.Sprintf("%s(%s)", p.t, result) 416 | } 417 | panic("unmatched type") 418 | } 419 | 420 | func (p *pattern) transform() *pattern { 421 | /* 422 | Expand pattern into an (almost) equivalent one, but with single Either. 423 | 424 | Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) 425 | Quirks: [-a] => (-a), (-a...) => (-a -a) 426 | */ 427 | result := []patternList{} 428 | groups := []patternList{patternList{p}} 429 | parents := patternRequired + 430 | patternOptionAL + 431 | patternOptionSSHORTCUT + 432 | patternEither + 433 | patternOneOrMore 434 | for len(groups) > 0 { 435 | children := groups[0] 436 | groups = groups[1:] 437 | var child *pattern 438 | for _, c := range children { 439 | if c.t&parents != 0 { 440 | child = c 441 | break 442 | } 443 | } 444 | if child != nil { 445 | children.remove(child) 446 | if child.t&patternEither != 0 { 447 | for _, c := range child.children { 448 | r := patternList{} 449 | r = append(r, c) 450 | r = append(r, children...) 451 | groups = append(groups, r) 452 | } 453 | } else if child.t&patternOneOrMore != 0 { 454 | r := patternList{} 455 | r = append(r, child.children.double()...) 456 | r = append(r, children...) 457 | groups = append(groups, r) 458 | } else { 459 | r := patternList{} 460 | r = append(r, child.children...) 461 | r = append(r, children...) 462 | groups = append(groups, r) 463 | } 464 | } else { 465 | result = append(result, children) 466 | } 467 | } 468 | either := patternList{} 469 | for _, e := range result { 470 | either = append(either, newRequired(e...)) 471 | } 472 | return newEither(either...) 473 | } 474 | 475 | func (p *pattern) eq(other *pattern) bool { 476 | return reflect.DeepEqual(p, other) 477 | } 478 | 479 | func (pl patternList) unique() patternList { 480 | table := make(map[string]bool) 481 | result := patternList{} 482 | for _, v := range pl { 483 | if !table[v.String()] { 484 | table[v.String()] = true 485 | result = append(result, v) 486 | } 487 | } 488 | return result 489 | } 490 | 491 | func (pl patternList) index(p *pattern) (int, error) { 492 | for i, c := range pl { 493 | if c.eq(p) { 494 | return i, nil 495 | } 496 | } 497 | return -1, newError("%s not in list", p) 498 | } 499 | 500 | func (pl patternList) count(p *pattern) int { 501 | count := 0 502 | for _, c := range pl { 503 | if c.eq(p) { 504 | count++ 505 | } 506 | } 507 | return count 508 | } 509 | 510 | func (pl patternList) diff(l patternList) patternList { 511 | lAlt := make(patternList, len(l)) 512 | copy(lAlt, l) 513 | result := make(patternList, 0, len(pl)) 514 | for _, v := range pl { 515 | if v != nil { 516 | match := false 517 | for i, w := range lAlt { 518 | if w.eq(v) { 519 | match = true 520 | lAlt[i] = nil 521 | break 522 | } 523 | } 524 | if match == false { 525 | result = append(result, v) 526 | } 527 | } 528 | } 529 | return result 530 | } 531 | 532 | func (pl patternList) double() patternList { 533 | l := len(pl) 534 | result := make(patternList, l*2) 535 | copy(result, pl) 536 | copy(result[l:2*l], pl) 537 | return result 538 | } 539 | 540 | func (pl *patternList) remove(p *pattern) { 541 | (*pl) = pl.diff(patternList{p}) 542 | } 543 | 544 | func (pl patternList) dictionary() map[string]interface{} { 545 | dict := make(map[string]interface{}) 546 | for _, a := range pl { 547 | dict[a.name] = a.value 548 | } 549 | return dict 550 | } 551 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/testcases.docopt: -------------------------------------------------------------------------------- 1 | r"""Usage: prog 2 | 3 | """ 4 | $ prog 5 | {} 6 | 7 | $ prog --xxx 8 | "user-error" 9 | 10 | 11 | r"""Usage: prog [options] 12 | 13 | Options: -a All. 14 | 15 | """ 16 | $ prog 17 | {"-a": false} 18 | 19 | $ prog -a 20 | {"-a": true} 21 | 22 | $ prog -x 23 | "user-error" 24 | 25 | 26 | r"""Usage: prog [options] 27 | 28 | Options: --all All. 29 | 30 | """ 31 | $ prog 32 | {"--all": false} 33 | 34 | $ prog --all 35 | {"--all": true} 36 | 37 | $ prog --xxx 38 | "user-error" 39 | 40 | 41 | r"""Usage: prog [options] 42 | 43 | Options: -v, --verbose Verbose. 44 | 45 | """ 46 | $ prog --verbose 47 | {"--verbose": true} 48 | 49 | $ prog --ver 50 | {"--verbose": true} 51 | 52 | $ prog -v 53 | {"--verbose": true} 54 | 55 | 56 | r"""Usage: prog [options] 57 | 58 | Options: -p PATH 59 | 60 | """ 61 | $ prog -p home/ 62 | {"-p": "home/"} 63 | 64 | $ prog -phome/ 65 | {"-p": "home/"} 66 | 67 | $ prog -p 68 | "user-error" 69 | 70 | 71 | r"""Usage: prog [options] 72 | 73 | Options: --path <path> 74 | 75 | """ 76 | $ prog --path home/ 77 | {"--path": "home/"} 78 | 79 | $ prog --path=home/ 80 | {"--path": "home/"} 81 | 82 | $ prog --pa home/ 83 | {"--path": "home/"} 84 | 85 | $ prog --pa=home/ 86 | {"--path": "home/"} 87 | 88 | $ prog --path 89 | "user-error" 90 | 91 | 92 | r"""Usage: prog [options] 93 | 94 | Options: -p PATH, --path=<path> Path to files. 95 | 96 | """ 97 | $ prog -proot 98 | {"--path": "root"} 99 | 100 | 101 | r"""Usage: prog [options] 102 | 103 | Options: -p --path PATH Path to files. 104 | 105 | """ 106 | $ prog -p root 107 | {"--path": "root"} 108 | 109 | $ prog --path root 110 | {"--path": "root"} 111 | 112 | 113 | r"""Usage: prog [options] 114 | 115 | Options: 116 | -p PATH Path to files [default: ./] 117 | 118 | """ 119 | $ prog 120 | {"-p": "./"} 121 | 122 | $ prog -phome 123 | {"-p": "home"} 124 | 125 | 126 | r"""UsAgE: prog [options] 127 | 128 | OpTiOnS: --path=<files> Path to files 129 | [dEfAuLt: /root] 130 | 131 | """ 132 | $ prog 133 | {"--path": "/root"} 134 | 135 | $ prog --path=home 136 | {"--path": "home"} 137 | 138 | 139 | r"""usage: prog [options] 140 | 141 | options: 142 | -a Add 143 | -r Remote 144 | -m <msg> Message 145 | 146 | """ 147 | $ prog -a -r -m Hello 148 | {"-a": true, 149 | "-r": true, 150 | "-m": "Hello"} 151 | 152 | $ prog -armyourass 153 | {"-a": true, 154 | "-r": true, 155 | "-m": "yourass"} 156 | 157 | $ prog -a -r 158 | {"-a": true, 159 | "-r": true, 160 | "-m": null} 161 | 162 | 163 | r"""Usage: prog [options] 164 | 165 | Options: --version 166 | --verbose 167 | 168 | """ 169 | $ prog --version 170 | {"--version": true, 171 | "--verbose": false} 172 | 173 | $ prog --verbose 174 | {"--version": false, 175 | "--verbose": true} 176 | 177 | $ prog --ver 178 | "user-error" 179 | 180 | $ prog --verb 181 | {"--version": false, 182 | "--verbose": true} 183 | 184 | 185 | r"""usage: prog [-a -r -m <msg>] 186 | 187 | options: 188 | -a Add 189 | -r Remote 190 | -m <msg> Message 191 | 192 | """ 193 | $ prog -armyourass 194 | {"-a": true, 195 | "-r": true, 196 | "-m": "yourass"} 197 | 198 | 199 | r"""usage: prog [-armmsg] 200 | 201 | options: -a Add 202 | -r Remote 203 | -m <msg> Message 204 | 205 | """ 206 | $ prog -a -r -m Hello 207 | {"-a": true, 208 | "-r": true, 209 | "-m": "Hello"} 210 | 211 | 212 | r"""usage: prog -a -b 213 | 214 | options: 215 | -a 216 | -b 217 | 218 | """ 219 | $ prog -a -b 220 | {"-a": true, "-b": true} 221 | 222 | $ prog -b -a 223 | {"-a": true, "-b": true} 224 | 225 | $ prog -a 226 | "user-error" 227 | 228 | $ prog 229 | "user-error" 230 | 231 | 232 | r"""usage: prog (-a -b) 233 | 234 | options: -a 235 | -b 236 | 237 | """ 238 | $ prog -a -b 239 | {"-a": true, "-b": true} 240 | 241 | $ prog -b -a 242 | {"-a": true, "-b": true} 243 | 244 | $ prog -a 245 | "user-error" 246 | 247 | $ prog 248 | "user-error" 249 | 250 | 251 | r"""usage: prog [-a] -b 252 | 253 | options: -a 254 | -b 255 | 256 | """ 257 | $ prog -a -b 258 | {"-a": true, "-b": true} 259 | 260 | $ prog -b -a 261 | {"-a": true, "-b": true} 262 | 263 | $ prog -a 264 | "user-error" 265 | 266 | $ prog -b 267 | {"-a": false, "-b": true} 268 | 269 | $ prog 270 | "user-error" 271 | 272 | 273 | r"""usage: prog [(-a -b)] 274 | 275 | options: -a 276 | -b 277 | 278 | """ 279 | $ prog -a -b 280 | {"-a": true, "-b": true} 281 | 282 | $ prog -b -a 283 | {"-a": true, "-b": true} 284 | 285 | $ prog -a 286 | "user-error" 287 | 288 | $ prog -b 289 | "user-error" 290 | 291 | $ prog 292 | {"-a": false, "-b": false} 293 | 294 | 295 | r"""usage: prog (-a|-b) 296 | 297 | options: -a 298 | -b 299 | 300 | """ 301 | $ prog -a -b 302 | "user-error" 303 | 304 | $ prog 305 | "user-error" 306 | 307 | $ prog -a 308 | {"-a": true, "-b": false} 309 | 310 | $ prog -b 311 | {"-a": false, "-b": true} 312 | 313 | 314 | r"""usage: prog [ -a | -b ] 315 | 316 | options: -a 317 | -b 318 | 319 | """ 320 | $ prog -a -b 321 | "user-error" 322 | 323 | $ prog 324 | {"-a": false, "-b": false} 325 | 326 | $ prog -a 327 | {"-a": true, "-b": false} 328 | 329 | $ prog -b 330 | {"-a": false, "-b": true} 331 | 332 | 333 | r"""usage: prog <arg>""" 334 | $ prog 10 335 | {"<arg>": "10"} 336 | 337 | $ prog 10 20 338 | "user-error" 339 | 340 | $ prog 341 | "user-error" 342 | 343 | 344 | r"""usage: prog [<arg>]""" 345 | $ prog 10 346 | {"<arg>": "10"} 347 | 348 | $ prog 10 20 349 | "user-error" 350 | 351 | $ prog 352 | {"<arg>": null} 353 | 354 | 355 | r"""usage: prog <kind> <name> <type>""" 356 | $ prog 10 20 40 357 | {"<kind>": "10", "<name>": "20", "<type>": "40"} 358 | 359 | $ prog 10 20 360 | "user-error" 361 | 362 | $ prog 363 | "user-error" 364 | 365 | 366 | r"""usage: prog <kind> [<name> <type>]""" 367 | $ prog 10 20 40 368 | {"<kind>": "10", "<name>": "20", "<type>": "40"} 369 | 370 | $ prog 10 20 371 | {"<kind>": "10", "<name>": "20", "<type>": null} 372 | 373 | $ prog 374 | "user-error" 375 | 376 | 377 | r"""usage: prog [<kind> | <name> <type>]""" 378 | $ prog 10 20 40 379 | "user-error" 380 | 381 | $ prog 20 40 382 | {"<kind>": null, "<name>": "20", "<type>": "40"} 383 | 384 | $ prog 385 | {"<kind>": null, "<name>": null, "<type>": null} 386 | 387 | 388 | r"""usage: prog (<kind> --all | <name>) 389 | 390 | options: 391 | --all 392 | 393 | """ 394 | $ prog 10 --all 395 | {"<kind>": "10", "--all": true, "<name>": null} 396 | 397 | $ prog 10 398 | {"<kind>": null, "--all": false, "<name>": "10"} 399 | 400 | $ prog 401 | "user-error" 402 | 403 | 404 | r"""usage: prog [<name> <name>]""" 405 | $ prog 10 20 406 | {"<name>": ["10", "20"]} 407 | 408 | $ prog 10 409 | {"<name>": ["10"]} 410 | 411 | $ prog 412 | {"<name>": []} 413 | 414 | 415 | r"""usage: prog [(<name> <name>)]""" 416 | $ prog 10 20 417 | {"<name>": ["10", "20"]} 418 | 419 | $ prog 10 420 | "user-error" 421 | 422 | $ prog 423 | {"<name>": []} 424 | 425 | 426 | r"""usage: prog NAME...""" 427 | $ prog 10 20 428 | {"NAME": ["10", "20"]} 429 | 430 | $ prog 10 431 | {"NAME": ["10"]} 432 | 433 | $ prog 434 | "user-error" 435 | 436 | 437 | r"""usage: prog [NAME]...""" 438 | $ prog 10 20 439 | {"NAME": ["10", "20"]} 440 | 441 | $ prog 10 442 | {"NAME": ["10"]} 443 | 444 | $ prog 445 | {"NAME": []} 446 | 447 | 448 | r"""usage: prog [NAME...]""" 449 | $ prog 10 20 450 | {"NAME": ["10", "20"]} 451 | 452 | $ prog 10 453 | {"NAME": ["10"]} 454 | 455 | $ prog 456 | {"NAME": []} 457 | 458 | 459 | r"""usage: prog [NAME [NAME ...]]""" 460 | $ prog 10 20 461 | {"NAME": ["10", "20"]} 462 | 463 | $ prog 10 464 | {"NAME": ["10"]} 465 | 466 | $ prog 467 | {"NAME": []} 468 | 469 | 470 | r"""usage: prog (NAME | --foo NAME) 471 | 472 | options: --foo 473 | 474 | """ 475 | $ prog 10 476 | {"NAME": "10", "--foo": false} 477 | 478 | $ prog --foo 10 479 | {"NAME": "10", "--foo": true} 480 | 481 | $ prog --foo=10 482 | "user-error" 483 | 484 | 485 | r"""usage: prog (NAME | --foo) [--bar | NAME] 486 | 487 | options: --foo 488 | options: --bar 489 | 490 | """ 491 | $ prog 10 492 | {"NAME": ["10"], "--foo": false, "--bar": false} 493 | 494 | $ prog 10 20 495 | {"NAME": ["10", "20"], "--foo": false, "--bar": false} 496 | 497 | $ prog --foo --bar 498 | {"NAME": [], "--foo": true, "--bar": true} 499 | 500 | 501 | r"""Naval Fate. 502 | 503 | Usage: 504 | prog ship new <name>... 505 | prog ship [<name>] move <x> <y> [--speed=<kn>] 506 | prog ship shoot <x> <y> 507 | prog mine (set|remove) <x> <y> [--moored|--drifting] 508 | prog -h | --help 509 | prog --version 510 | 511 | Options: 512 | -h --help Show this screen. 513 | --version Show version. 514 | --speed=<kn> Speed in knots [default: 10]. 515 | --moored Mored (anchored) mine. 516 | --drifting Drifting mine. 517 | 518 | """ 519 | $ prog ship Guardian move 150 300 --speed=20 520 | {"--drifting": false, 521 | "--help": false, 522 | "--moored": false, 523 | "--speed": "20", 524 | "--version": false, 525 | "<name>": ["Guardian"], 526 | "<x>": "150", 527 | "<y>": "300", 528 | "mine": false, 529 | "move": true, 530 | "new": false, 531 | "remove": false, 532 | "set": false, 533 | "ship": true, 534 | "shoot": false} 535 | 536 | 537 | r"""usage: prog --hello""" 538 | $ prog --hello 539 | {"--hello": true} 540 | 541 | 542 | r"""usage: prog [--hello=<world>]""" 543 | $ prog 544 | {"--hello": null} 545 | 546 | $ prog --hello wrld 547 | {"--hello": "wrld"} 548 | 549 | 550 | r"""usage: prog [-o]""" 551 | $ prog 552 | {"-o": false} 553 | 554 | $ prog -o 555 | {"-o": true} 556 | 557 | 558 | r"""usage: prog [-opr]""" 559 | $ prog -op 560 | {"-o": true, "-p": true, "-r": false} 561 | 562 | 563 | r"""usage: prog --aabb | --aa""" 564 | $ prog --aa 565 | {"--aabb": false, "--aa": true} 566 | 567 | $ prog --a 568 | "user-error" # not a unique prefix 569 | 570 | # 571 | # Counting number of flags 572 | # 573 | 574 | r"""Usage: prog -v""" 575 | $ prog -v 576 | {"-v": true} 577 | 578 | 579 | r"""Usage: prog [-v -v]""" 580 | $ prog 581 | {"-v": 0} 582 | 583 | $ prog -v 584 | {"-v": 1} 585 | 586 | $ prog -vv 587 | {"-v": 2} 588 | 589 | 590 | r"""Usage: prog -v ...""" 591 | $ prog 592 | "user-error" 593 | 594 | $ prog -v 595 | {"-v": 1} 596 | 597 | $ prog -vv 598 | {"-v": 2} 599 | 600 | $ prog -vvvvvv 601 | {"-v": 6} 602 | 603 | 604 | r"""Usage: prog [-v | -vv | -vvv] 605 | 606 | This one is probably most readable user-friednly variant. 607 | 608 | """ 609 | $ prog 610 | {"-v": 0} 611 | 612 | $ prog -v 613 | {"-v": 1} 614 | 615 | $ prog -vv 616 | {"-v": 2} 617 | 618 | $ prog -vvvv 619 | "user-error" 620 | 621 | 622 | r"""usage: prog [--ver --ver]""" 623 | $ prog --ver --ver 624 | {"--ver": 2} 625 | 626 | 627 | # 628 | # Counting commands 629 | # 630 | 631 | r"""usage: prog [go]""" 632 | $ prog go 633 | {"go": true} 634 | 635 | 636 | r"""usage: prog [go go]""" 637 | $ prog 638 | {"go": 0} 639 | 640 | $ prog go 641 | {"go": 1} 642 | 643 | $ prog go go 644 | {"go": 2} 645 | 646 | $ prog go go go 647 | "user-error" 648 | 649 | r"""usage: prog go...""" 650 | $ prog go go go go go 651 | {"go": 5} 652 | 653 | # 654 | # [options] does not include options from usage-pattern 655 | # 656 | r"""usage: prog [options] [-a] 657 | 658 | options: -a 659 | -b 660 | """ 661 | $ prog -a 662 | {"-a": true, "-b": false} 663 | 664 | $ prog -aa 665 | "user-error" 666 | 667 | # 668 | # Test [options] shourtcut 669 | # 670 | 671 | r"""Usage: prog [options] A 672 | Options: 673 | -q Be quiet 674 | -v Be verbose. 675 | 676 | """ 677 | $ prog arg 678 | {"A": "arg", "-v": false, "-q": false} 679 | 680 | $ prog -v arg 681 | {"A": "arg", "-v": true, "-q": false} 682 | 683 | $ prog -q arg 684 | {"A": "arg", "-v": false, "-q": true} 685 | 686 | # 687 | # Test single dash 688 | # 689 | 690 | r"""usage: prog [-]""" 691 | 692 | $ prog - 693 | {"-": true} 694 | 695 | $ prog 696 | {"-": false} 697 | 698 | # 699 | # If argument is repeated, its value should always be a list 700 | # 701 | 702 | r"""usage: prog [NAME [NAME ...]]""" 703 | 704 | $ prog a b 705 | {"NAME": ["a", "b"]} 706 | 707 | $ prog 708 | {"NAME": []} 709 | 710 | # 711 | # Option's argument defaults to null/None 712 | # 713 | 714 | r"""usage: prog [options] 715 | options: 716 | -a Add 717 | -m <msg> Message 718 | 719 | """ 720 | $ prog -a 721 | {"-m": null, "-a": true} 722 | 723 | # 724 | # Test options without description 725 | # 726 | 727 | r"""usage: prog --hello""" 728 | $ prog --hello 729 | {"--hello": true} 730 | 731 | r"""usage: prog [--hello=<world>]""" 732 | $ prog 733 | {"--hello": null} 734 | 735 | $ prog --hello wrld 736 | {"--hello": "wrld"} 737 | 738 | r"""usage: prog [-o]""" 739 | $ prog 740 | {"-o": false} 741 | 742 | $ prog -o 743 | {"-o": true} 744 | 745 | r"""usage: prog [-opr]""" 746 | $ prog -op 747 | {"-o": true, "-p": true, "-r": false} 748 | 749 | r"""usage: git [-v | --verbose]""" 750 | $ prog -v 751 | {"-v": true, "--verbose": false} 752 | 753 | r"""usage: git remote [-v | --verbose]""" 754 | $ prog remote -v 755 | {"remote": true, "-v": true, "--verbose": false} 756 | 757 | # 758 | # Test empty usage pattern 759 | # 760 | 761 | r"""usage: prog""" 762 | $ prog 763 | {} 764 | 765 | r"""usage: prog 766 | prog <a> <b> 767 | """ 768 | $ prog 1 2 769 | {"<a>": "1", "<b>": "2"} 770 | 771 | $ prog 772 | {"<a>": null, "<b>": null} 773 | 774 | r"""usage: prog <a> <b> 775 | prog 776 | """ 777 | $ prog 778 | {"<a>": null, "<b>": null} 779 | 780 | # 781 | # Option's argument should not capture default value from usage pattern 782 | # 783 | 784 | r"""usage: prog [--file=<f>]""" 785 | $ prog 786 | {"--file": null} 787 | 788 | r"""usage: prog [--file=<f>] 789 | 790 | options: --file <a> 791 | 792 | """ 793 | $ prog 794 | {"--file": null} 795 | 796 | r"""Usage: prog [-a <host:port>] 797 | 798 | Options: -a, --address <host:port> TCP address [default: localhost:6283]. 799 | 800 | """ 801 | $ prog 802 | {"--address": "localhost:6283"} 803 | 804 | # 805 | # If option with argument could be repeated, 806 | # its arguments should be accumulated into a list 807 | # 808 | 809 | r"""usage: prog --long=<arg> ...""" 810 | 811 | $ prog --long one 812 | {"--long": ["one"]} 813 | 814 | $ prog --long one --long two 815 | {"--long": ["one", "two"]} 816 | 817 | # 818 | # Test multiple elements repeated at once 819 | # 820 | 821 | r"""usage: prog (go <direction> --speed=<km/h>)...""" 822 | $ prog go left --speed=5 go right --speed=9 823 | {"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]} 824 | 825 | # 826 | # Required options should work with option shortcut 827 | # 828 | 829 | r"""usage: prog [options] -a 830 | 831 | options: -a 832 | 833 | """ 834 | $ prog -a 835 | {"-a": true} 836 | 837 | # 838 | # If option could be repeated its defaults should be split into a list 839 | # 840 | 841 | r"""usage: prog [-o <o>]... 842 | 843 | options: -o <o> [default: x] 844 | 845 | """ 846 | $ prog -o this -o that 847 | {"-o": ["this", "that"]} 848 | 849 | $ prog 850 | {"-o": ["x"]} 851 | 852 | r"""usage: prog [-o <o>]... 853 | 854 | options: -o <o> [default: x y] 855 | 856 | """ 857 | $ prog -o this 858 | {"-o": ["this"]} 859 | 860 | $ prog 861 | {"-o": ["x", "y"]} 862 | 863 | # 864 | # Test stacked option's argument 865 | # 866 | 867 | r"""usage: prog -pPATH 868 | 869 | options: -p PATH 870 | 871 | """ 872 | $ prog -pHOME 873 | {"-p": "HOME"} 874 | 875 | # 876 | # Issue 56: Repeated mutually exclusive args give nested lists sometimes 877 | # 878 | 879 | r"""Usage: foo (--xx=x|--yy=y)...""" 880 | $ prog --xx=1 --yy=2 881 | {"--xx": ["1"], "--yy": ["2"]} 882 | 883 | # 884 | # POSIXly correct tokenization 885 | # 886 | 887 | r"""usage: prog [<input file>]""" 888 | $ prog f.txt 889 | {"<input file>": "f.txt"} 890 | 891 | r"""usage: prog [--input=<file name>]...""" 892 | $ prog --input a.txt --input=b.txt 893 | {"--input": ["a.txt", "b.txt"]} 894 | 895 | # 896 | # Issue 85: `[options]` shourtcut with multiple subcommands 897 | # 898 | 899 | r"""usage: prog good [options] 900 | prog fail [options] 901 | 902 | options: --loglevel=N 903 | 904 | """ 905 | $ prog fail --loglevel 5 906 | {"--loglevel": "5", "fail": true, "good": false} 907 | 908 | # 909 | # Usage-section syntax 910 | # 911 | 912 | r"""usage:prog --foo""" 913 | $ prog --foo 914 | {"--foo": true} 915 | 916 | r"""PROGRAM USAGE: prog --foo""" 917 | $ prog --foo 918 | {"--foo": true} 919 | 920 | r"""Usage: prog --foo 921 | prog --bar 922 | NOT PART OF SECTION""" 923 | $ prog --foo 924 | {"--foo": true, "--bar": false} 925 | 926 | r"""Usage: 927 | prog --foo 928 | prog --bar 929 | 930 | NOT PART OF SECTION""" 931 | $ prog --foo 932 | {"--foo": true, "--bar": false} 933 | 934 | r"""Usage: 935 | prog --foo 936 | prog --bar 937 | NOT PART OF SECTION""" 938 | $ prog --foo 939 | {"--foo": true, "--bar": false} 940 | 941 | # 942 | # Options-section syntax 943 | # 944 | 945 | r"""Usage: prog [options] 946 | 947 | global options: --foo 948 | local options: --baz 949 | --bar 950 | other options: 951 | --egg 952 | --spam 953 | -not-an-option- 954 | 955 | """ 956 | $ prog --baz --egg 957 | {"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false} 958 | -------------------------------------------------------------------------------- /vendor/github.com/docopt/docopt.go/docopt.go: -------------------------------------------------------------------------------- 1 | // Licensed under terms of MIT license (see LICENSE-MIT) 2 | // Copyright (c) 2013 Keith Batten, kbatten@gmail.com 3 | // Copyright (c) 2016 David Irvine 4 | 5 | package docopt 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "regexp" 11 | "strings" 12 | ) 13 | 14 | type Parser struct { 15 | // HelpHandler is called when we encounter bad user input, or when the user 16 | // asks for help. 17 | // By default, this calls os.Exit(0) if it handled a built-in option such 18 | // as -h, --help or --version. If the user errored with a wrong command or 19 | // options, we exit with a return code of 1. 20 | HelpHandler func(err error, usage string) 21 | // OptionsFirst requires that option flags always come before positional 22 | // arguments; otherwise they can overlap. 23 | OptionsFirst bool 24 | // SkipHelpFlags tells the parser not to look for -h and --help flags and 25 | // call the HelpHandler. 26 | SkipHelpFlags bool 27 | } 28 | 29 | var PrintHelpAndExit = func(err error, usage string) { 30 | if err != nil { 31 | fmt.Fprintln(os.Stderr, usage) 32 | os.Exit(1) 33 | } else { 34 | fmt.Println(usage) 35 | os.Exit(0) 36 | } 37 | } 38 | 39 | var PrintHelpOnly = func(err error, usage string) { 40 | if err != nil { 41 | fmt.Fprintln(os.Stderr, usage) 42 | } else { 43 | fmt.Println(usage) 44 | } 45 | } 46 | 47 | var NoHelpHandler = func(err error, usage string) {} 48 | 49 | var DefaultParser = &Parser{ 50 | HelpHandler: PrintHelpAndExit, 51 | OptionsFirst: false, 52 | SkipHelpFlags: false, 53 | } 54 | 55 | // ParseDoc parses os.Args[1:] based on the interface described in doc, using the default parser options. 56 | func ParseDoc(doc string) (Opts, error) { 57 | return ParseArgs(doc, nil, "") 58 | } 59 | 60 | // ParseArgs parses custom arguments based on the interface described in doc. If you provide a non-empty version 61 | // string, then this will be displayed when the --version flag is found. This method uses the default parser options. 62 | func ParseArgs(doc string, argv []string, version string) (Opts, error) { 63 | return DefaultParser.ParseArgs(doc, argv, version) 64 | } 65 | 66 | // ParseArgs parses custom arguments based on the interface described in doc. If you provide a non-empty version 67 | // string, then this will be displayed when the --version flag is found. 68 | func (p *Parser) ParseArgs(doc string, argv []string, version string) (Opts, error) { 69 | return p.parse(doc, argv, version) 70 | } 71 | 72 | // Deprecated: Parse is provided for backward compatibility with the original docopt.go package. 73 | // Please rather make use of ParseDoc, ParseArgs, or use your own custom Parser. 74 | func Parse(doc string, argv []string, help bool, version string, optionsFirst bool, exit ...bool) (map[string]interface{}, error) { 75 | exitOk := true 76 | if len(exit) > 0 { 77 | exitOk = exit[0] 78 | } 79 | p := &Parser{ 80 | OptionsFirst: optionsFirst, 81 | SkipHelpFlags: !help, 82 | } 83 | if exitOk { 84 | p.HelpHandler = PrintHelpAndExit 85 | } else { 86 | p.HelpHandler = PrintHelpOnly 87 | } 88 | return p.parse(doc, argv, version) 89 | } 90 | 91 | func (p *Parser) parse(doc string, argv []string, version string) (map[string]interface{}, error) { 92 | if argv == nil { 93 | argv = os.Args[1:] 94 | } 95 | if p.HelpHandler == nil { 96 | p.HelpHandler = DefaultParser.HelpHandler 97 | } 98 | args, output, err := parse(doc, argv, !p.SkipHelpFlags, version, p.OptionsFirst) 99 | if _, ok := err.(*UserError); ok { 100 | // the user gave us bad input 101 | p.HelpHandler(err, output) 102 | } else if len(output) > 0 && err == nil { 103 | // the user asked for help or --version 104 | p.HelpHandler(err, output) 105 | } 106 | return args, err 107 | } 108 | 109 | // ----------------------------------------------------------------------------- 110 | 111 | // parse and return a map of args, output and all errors 112 | func parse(doc string, argv []string, help bool, version string, optionsFirst bool) (args map[string]interface{}, output string, err error) { 113 | if argv == nil && len(os.Args) > 1 { 114 | argv = os.Args[1:] 115 | } 116 | 117 | usageSections := parseSection("usage:", doc) 118 | 119 | if len(usageSections) == 0 { 120 | err = newLanguageError("\"usage:\" (case-insensitive) not found.") 121 | return 122 | } 123 | if len(usageSections) > 1 { 124 | err = newLanguageError("More than one \"usage:\" (case-insensitive).") 125 | return 126 | } 127 | usage := usageSections[0] 128 | 129 | options := parseDefaults(doc) 130 | formal, err := formalUsage(usage) 131 | if err != nil { 132 | output = handleError(err, usage) 133 | return 134 | } 135 | 136 | pat, err := parsePattern(formal, &options) 137 | if err != nil { 138 | output = handleError(err, usage) 139 | return 140 | } 141 | 142 | patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst) 143 | if err != nil { 144 | output = handleError(err, usage) 145 | return 146 | } 147 | patFlat, err := pat.flat(patternOption) 148 | if err != nil { 149 | output = handleError(err, usage) 150 | return 151 | } 152 | patternOptions := patFlat.unique() 153 | 154 | patFlat, err = pat.flat(patternOptionSSHORTCUT) 155 | if err != nil { 156 | output = handleError(err, usage) 157 | return 158 | } 159 | for _, optionsShortcut := range patFlat { 160 | docOptions := parseDefaults(doc) 161 | optionsShortcut.children = docOptions.unique().diff(patternOptions) 162 | } 163 | 164 | if output = extras(help, version, patternArgv, doc); len(output) > 0 { 165 | return 166 | } 167 | 168 | err = pat.fix() 169 | if err != nil { 170 | output = handleError(err, usage) 171 | return 172 | } 173 | matched, left, collected := pat.match(&patternArgv, nil) 174 | if matched && len(*left) == 0 { 175 | patFlat, err = pat.flat(patternDefault) 176 | if err != nil { 177 | output = handleError(err, usage) 178 | return 179 | } 180 | args = append(patFlat, *collected...).dictionary() 181 | return 182 | } 183 | 184 | err = newUserError("") 185 | output = handleError(err, usage) 186 | return 187 | } 188 | 189 | func handleError(err error, usage string) string { 190 | if _, ok := err.(*UserError); ok { 191 | return strings.TrimSpace(fmt.Sprintf("%s\n%s", err, usage)) 192 | } 193 | return "" 194 | } 195 | 196 | func parseSection(name, source string) []string { 197 | p := regexp.MustCompile(`(?im)^([^\n]*` + name + `[^\n]*\n?(?:[ \t].*?(?:\n|$))*)`) 198 | s := p.FindAllString(source, -1) 199 | if s == nil { 200 | s = []string{} 201 | } 202 | for i, v := range s { 203 | s[i] = strings.TrimSpace(v) 204 | } 205 | return s 206 | } 207 | 208 | func parseDefaults(doc string) patternList { 209 | defaults := patternList{} 210 | p := regexp.MustCompile(`\n[ \t]*(-\S+?)`) 211 | for _, s := range parseSection("options:", doc) { 212 | // FIXME corner case "bla: options: --foo" 213 | _, _, s = stringPartition(s, ":") // get rid of "options:" 214 | split := p.Split("\n"+s, -1)[1:] 215 | match := p.FindAllStringSubmatch("\n"+s, -1) 216 | for i := range split { 217 | optionDescription := match[i][1] + split[i] 218 | if strings.HasPrefix(optionDescription, "-") { 219 | defaults = append(defaults, parseOption(optionDescription)) 220 | } 221 | } 222 | } 223 | return defaults 224 | } 225 | 226 | func parsePattern(source string, options *patternList) (*pattern, error) { 227 | tokens := tokenListFromPattern(source) 228 | result, err := parseExpr(tokens, options) 229 | if err != nil { 230 | return nil, err 231 | } 232 | if tokens.current() != nil { 233 | return nil, tokens.errorFunc("unexpected ending: %s" + strings.Join(tokens.tokens, " ")) 234 | } 235 | return newRequired(result...), nil 236 | } 237 | 238 | func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) { 239 | /* 240 | Parse command-line argument vector. 241 | 242 | If options_first: 243 | argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; 244 | else: 245 | argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; 246 | */ 247 | parsed := patternList{} 248 | for tokens.current() != nil { 249 | if tokens.current().eq("--") { 250 | for _, v := range tokens.tokens { 251 | parsed = append(parsed, newArgument("", v)) 252 | } 253 | return parsed, nil 254 | } else if tokens.current().hasPrefix("--") { 255 | pl, err := parseLong(tokens, options) 256 | if err != nil { 257 | return nil, err 258 | } 259 | parsed = append(parsed, pl...) 260 | } else if tokens.current().hasPrefix("-") && !tokens.current().eq("-") { 261 | ps, err := parseShorts(tokens, options) 262 | if err != nil { 263 | return nil, err 264 | } 265 | parsed = append(parsed, ps...) 266 | } else if optionsFirst { 267 | for _, v := range tokens.tokens { 268 | parsed = append(parsed, newArgument("", v)) 269 | } 270 | return parsed, nil 271 | } else { 272 | parsed = append(parsed, newArgument("", tokens.move().String())) 273 | } 274 | } 275 | return parsed, nil 276 | } 277 | 278 | func parseOption(optionDescription string) *pattern { 279 | optionDescription = strings.TrimSpace(optionDescription) 280 | options, _, description := stringPartition(optionDescription, " ") 281 | options = strings.Replace(options, ",", " ", -1) 282 | options = strings.Replace(options, "=", " ", -1) 283 | 284 | short := "" 285 | long := "" 286 | argcount := 0 287 | var value interface{} 288 | value = false 289 | 290 | reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`) 291 | for _, s := range strings.Fields(options) { 292 | if strings.HasPrefix(s, "--") { 293 | long = s 294 | } else if strings.HasPrefix(s, "-") { 295 | short = s 296 | } else { 297 | argcount = 1 298 | } 299 | if argcount > 0 { 300 | matched := reDefault.FindAllStringSubmatch(description, -1) 301 | if len(matched) > 0 { 302 | value = matched[0][1] 303 | } else { 304 | value = nil 305 | } 306 | } 307 | } 308 | return newOption(short, long, argcount, value) 309 | } 310 | 311 | func parseExpr(tokens *tokenList, options *patternList) (patternList, error) { 312 | // expr ::= seq ( '|' seq )* ; 313 | seq, err := parseSeq(tokens, options) 314 | if err != nil { 315 | return nil, err 316 | } 317 | if !tokens.current().eq("|") { 318 | return seq, nil 319 | } 320 | var result patternList 321 | if len(seq) > 1 { 322 | result = patternList{newRequired(seq...)} 323 | } else { 324 | result = seq 325 | } 326 | for tokens.current().eq("|") { 327 | tokens.move() 328 | seq, err = parseSeq(tokens, options) 329 | if err != nil { 330 | return nil, err 331 | } 332 | if len(seq) > 1 { 333 | result = append(result, newRequired(seq...)) 334 | } else { 335 | result = append(result, seq...) 336 | } 337 | } 338 | if len(result) > 1 { 339 | return patternList{newEither(result...)}, nil 340 | } 341 | return result, nil 342 | } 343 | 344 | func parseSeq(tokens *tokenList, options *patternList) (patternList, error) { 345 | // seq ::= ( atom [ '...' ] )* ; 346 | result := patternList{} 347 | for !tokens.current().match(true, "]", ")", "|") { 348 | atom, err := parseAtom(tokens, options) 349 | if err != nil { 350 | return nil, err 351 | } 352 | if tokens.current().eq("...") { 353 | atom = patternList{newOneOrMore(atom...)} 354 | tokens.move() 355 | } 356 | result = append(result, atom...) 357 | } 358 | return result, nil 359 | } 360 | 361 | func parseAtom(tokens *tokenList, options *patternList) (patternList, error) { 362 | // atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ; 363 | tok := tokens.current() 364 | result := patternList{} 365 | if tokens.current().match(false, "(", "[") { 366 | tokens.move() 367 | var matching string 368 | pl, err := parseExpr(tokens, options) 369 | if err != nil { 370 | return nil, err 371 | } 372 | if tok.eq("(") { 373 | matching = ")" 374 | result = patternList{newRequired(pl...)} 375 | } else if tok.eq("[") { 376 | matching = "]" 377 | result = patternList{newOptional(pl...)} 378 | } 379 | moved := tokens.move() 380 | if !moved.eq(matching) { 381 | return nil, tokens.errorFunc("unmatched '%s', expected: '%s' got: '%s'", tok, matching, moved) 382 | } 383 | return result, nil 384 | } else if tok.eq("options") { 385 | tokens.move() 386 | return patternList{newOptionsShortcut()}, nil 387 | } else if tok.hasPrefix("--") && !tok.eq("--") { 388 | return parseLong(tokens, options) 389 | } else if tok.hasPrefix("-") && !tok.eq("-") && !tok.eq("--") { 390 | return parseShorts(tokens, options) 391 | } else if tok.hasPrefix("<") && tok.hasSuffix(">") || tok.isUpper() { 392 | return patternList{newArgument(tokens.move().String(), nil)}, nil 393 | } 394 | return patternList{newCommand(tokens.move().String(), false)}, nil 395 | } 396 | 397 | func parseLong(tokens *tokenList, options *patternList) (patternList, error) { 398 | // long ::= '--' chars [ ( ' ' | '=' ) chars ] ; 399 | long, eq, v := stringPartition(tokens.move().String(), "=") 400 | var value interface{} 401 | var opt *pattern 402 | if eq == "" && v == "" { 403 | value = nil 404 | } else { 405 | value = v 406 | } 407 | 408 | if !strings.HasPrefix(long, "--") { 409 | return nil, newError("long option '%s' doesn't start with --", long) 410 | } 411 | similar := patternList{} 412 | for _, o := range *options { 413 | if o.long == long { 414 | similar = append(similar, o) 415 | } 416 | } 417 | if tokens.err == errorUser && len(similar) == 0 { // if no exact match 418 | similar = patternList{} 419 | for _, o := range *options { 420 | if strings.HasPrefix(o.long, long) { 421 | similar = append(similar, o) 422 | } 423 | } 424 | } 425 | if len(similar) > 1 { // might be simply specified ambiguously 2+ times? 426 | similarLong := make([]string, len(similar)) 427 | for i, s := range similar { 428 | similarLong[i] = s.long 429 | } 430 | return nil, tokens.errorFunc("%s is not a unique prefix: %s?", long, strings.Join(similarLong, ", ")) 431 | } else if len(similar) < 1 { 432 | argcount := 0 433 | if eq == "=" { 434 | argcount = 1 435 | } 436 | opt = newOption("", long, argcount, false) 437 | *options = append(*options, opt) 438 | if tokens.err == errorUser { 439 | var val interface{} 440 | if argcount > 0 { 441 | val = value 442 | } else { 443 | val = true 444 | } 445 | opt = newOption("", long, argcount, val) 446 | } 447 | } else { 448 | opt = newOption(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) 449 | if opt.argcount == 0 { 450 | if value != nil { 451 | return nil, tokens.errorFunc("%s must not have an argument", opt.long) 452 | } 453 | } else { 454 | if value == nil { 455 | if tokens.current().match(true, "--") { 456 | return nil, tokens.errorFunc("%s requires argument", opt.long) 457 | } 458 | moved := tokens.move() 459 | if moved != nil { 460 | value = moved.String() // only set as string if not nil 461 | } 462 | } 463 | } 464 | if tokens.err == errorUser { 465 | if value != nil { 466 | opt.value = value 467 | } else { 468 | opt.value = true 469 | } 470 | } 471 | } 472 | 473 | return patternList{opt}, nil 474 | } 475 | 476 | func parseShorts(tokens *tokenList, options *patternList) (patternList, error) { 477 | // shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ; 478 | tok := tokens.move() 479 | if !tok.hasPrefix("-") || tok.hasPrefix("--") { 480 | return nil, newError("short option '%s' doesn't start with -", tok) 481 | } 482 | left := strings.TrimLeft(tok.String(), "-") 483 | parsed := patternList{} 484 | for left != "" { 485 | var opt *pattern 486 | short := "-" + left[0:1] 487 | left = left[1:] 488 | similar := patternList{} 489 | for _, o := range *options { 490 | if o.short == short { 491 | similar = append(similar, o) 492 | } 493 | } 494 | if len(similar) > 1 { 495 | return nil, tokens.errorFunc("%s is specified ambiguously %d times", short, len(similar)) 496 | } else if len(similar) < 1 { 497 | opt = newOption(short, "", 0, false) 498 | *options = append(*options, opt) 499 | if tokens.err == errorUser { 500 | opt = newOption(short, "", 0, true) 501 | } 502 | } else { // why copying is necessary here? 503 | opt = newOption(short, similar[0].long, similar[0].argcount, similar[0].value) 504 | var value interface{} 505 | if opt.argcount > 0 { 506 | if left == "" { 507 | if tokens.current().match(true, "--") { 508 | return nil, tokens.errorFunc("%s requires argument", short) 509 | } 510 | value = tokens.move().String() 511 | } else { 512 | value = left 513 | left = "" 514 | } 515 | } 516 | if tokens.err == errorUser { 517 | if value != nil { 518 | opt.value = value 519 | } else { 520 | opt.value = true 521 | } 522 | } 523 | } 524 | parsed = append(parsed, opt) 525 | } 526 | return parsed, nil 527 | } 528 | 529 | func formalUsage(section string) (string, error) { 530 | _, _, section = stringPartition(section, ":") // drop "usage:" 531 | pu := strings.Fields(section) 532 | 533 | if len(pu) == 0 { 534 | return "", newLanguageError("no fields found in usage (perhaps a spacing error).") 535 | } 536 | 537 | result := "( " 538 | for _, s := range pu[1:] { 539 | if s == pu[0] { 540 | result += ") | ( " 541 | } else { 542 | result += s + " " 543 | } 544 | } 545 | result += ")" 546 | 547 | return result, nil 548 | } 549 | 550 | func extras(help bool, version string, options patternList, doc string) string { 551 | if help { 552 | for _, o := range options { 553 | if (o.name == "-h" || o.name == "--help") && o.value == true { 554 | return strings.Trim(doc, "\n") 555 | } 556 | } 557 | } 558 | if version != "" { 559 | for _, o := range options { 560 | if (o.name == "--version") && o.value == true { 561 | return version 562 | } 563 | } 564 | } 565 | return "" 566 | } 567 | 568 | func stringPartition(s, sep string) (string, string, string) { 569 | sepPos := strings.Index(s, sep) 570 | if sepPos == -1 { // no seperator found 571 | return s, "", "" 572 | } 573 | split := strings.SplitN(s, sep, 2) 574 | return split[0], sep, split[1] 575 | } 576 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/manipulation.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/net/html" 7 | ) 8 | 9 | // After applies the selector from the root document and inserts the matched elements 10 | // after the elements in the set of matched elements. 11 | // 12 | // If one of the matched elements in the selection is not currently in the 13 | // document, it's impossible to insert nodes after it, so it will be ignored. 14 | // 15 | // This follows the same rules as Selection.Append. 16 | func (s *Selection) After(selector string) *Selection { 17 | return s.AfterMatcher(compileMatcher(selector)) 18 | } 19 | 20 | // AfterMatcher applies the matcher from the root document and inserts the matched elements 21 | // after the elements in the set of matched elements. 22 | // 23 | // If one of the matched elements in the selection is not currently in the 24 | // document, it's impossible to insert nodes after it, so it will be ignored. 25 | // 26 | // This follows the same rules as Selection.Append. 27 | func (s *Selection) AfterMatcher(m Matcher) *Selection { 28 | return s.AfterNodes(m.MatchAll(s.document.rootNode)...) 29 | } 30 | 31 | // AfterSelection inserts the elements in the selection after each element in the set of matched 32 | // elements. 33 | // 34 | // This follows the same rules as Selection.Append. 35 | func (s *Selection) AfterSelection(sel *Selection) *Selection { 36 | return s.AfterNodes(sel.Nodes...) 37 | } 38 | 39 | // AfterHtml parses the html and inserts it after the set of matched elements. 40 | // 41 | // This follows the same rules as Selection.Append. 42 | func (s *Selection) AfterHtml(html string) *Selection { 43 | return s.AfterNodes(parseHtml(html)...) 44 | } 45 | 46 | // AfterNodes inserts the nodes after each element in the set of matched elements. 47 | // 48 | // This follows the same rules as Selection.Append. 49 | func (s *Selection) AfterNodes(ns ...*html.Node) *Selection { 50 | return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) { 51 | if sn.Parent != nil { 52 | sn.Parent.InsertBefore(n, sn.NextSibling) 53 | } 54 | }) 55 | } 56 | 57 | // Append appends the elements specified by the selector to the end of each element 58 | // in the set of matched elements, following those rules: 59 | // 60 | // 1) The selector is applied to the root document. 61 | // 62 | // 2) Elements that are part of the document will be moved to the new location. 63 | // 64 | // 3) If there are multiple locations to append to, cloned nodes will be 65 | // appended to all target locations except the last one, which will be moved 66 | // as noted in (2). 67 | func (s *Selection) Append(selector string) *Selection { 68 | return s.AppendMatcher(compileMatcher(selector)) 69 | } 70 | 71 | // AppendMatcher appends the elements specified by the matcher to the end of each element 72 | // in the set of matched elements. 73 | // 74 | // This follows the same rules as Selection.Append. 75 | func (s *Selection) AppendMatcher(m Matcher) *Selection { 76 | return s.AppendNodes(m.MatchAll(s.document.rootNode)...) 77 | } 78 | 79 | // AppendSelection appends the elements in the selection to the end of each element 80 | // in the set of matched elements. 81 | // 82 | // This follows the same rules as Selection.Append. 83 | func (s *Selection) AppendSelection(sel *Selection) *Selection { 84 | return s.AppendNodes(sel.Nodes...) 85 | } 86 | 87 | // AppendHtml parses the html and appends it to the set of matched elements. 88 | func (s *Selection) AppendHtml(html string) *Selection { 89 | return s.AppendNodes(parseHtml(html)...) 90 | } 91 | 92 | // AppendNodes appends the specified nodes to each node in the set of matched elements. 93 | // 94 | // This follows the same rules as Selection.Append. 95 | func (s *Selection) AppendNodes(ns ...*html.Node) *Selection { 96 | return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) { 97 | sn.AppendChild(n) 98 | }) 99 | } 100 | 101 | // Before inserts the matched elements before each element in the set of matched elements. 102 | // 103 | // This follows the same rules as Selection.Append. 104 | func (s *Selection) Before(selector string) *Selection { 105 | return s.BeforeMatcher(compileMatcher(selector)) 106 | } 107 | 108 | // BeforeMatcher inserts the matched elements before each element in the set of matched elements. 109 | // 110 | // This follows the same rules as Selection.Append. 111 | func (s *Selection) BeforeMatcher(m Matcher) *Selection { 112 | return s.BeforeNodes(m.MatchAll(s.document.rootNode)...) 113 | } 114 | 115 | // BeforeSelection inserts the elements in the selection before each element in the set of matched 116 | // elements. 117 | // 118 | // This follows the same rules as Selection.Append. 119 | func (s *Selection) BeforeSelection(sel *Selection) *Selection { 120 | return s.BeforeNodes(sel.Nodes...) 121 | } 122 | 123 | // BeforeHtml parses the html and inserts it before the set of matched elements. 124 | // 125 | // This follows the same rules as Selection.Append. 126 | func (s *Selection) BeforeHtml(html string) *Selection { 127 | return s.BeforeNodes(parseHtml(html)...) 128 | } 129 | 130 | // BeforeNodes inserts the nodes before each element in the set of matched elements. 131 | // 132 | // This follows the same rules as Selection.Append. 133 | func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection { 134 | return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) { 135 | if sn.Parent != nil { 136 | sn.Parent.InsertBefore(n, sn) 137 | } 138 | }) 139 | } 140 | 141 | // Clone creates a deep copy of the set of matched nodes. The new nodes will not be 142 | // attached to the document. 143 | func (s *Selection) Clone() *Selection { 144 | ns := newEmptySelection(s.document) 145 | ns.Nodes = cloneNodes(s.Nodes) 146 | return ns 147 | } 148 | 149 | // Empty removes all children nodes from the set of matched elements. 150 | // It returns the children nodes in a new Selection. 151 | func (s *Selection) Empty() *Selection { 152 | var nodes []*html.Node 153 | 154 | for _, n := range s.Nodes { 155 | for c := n.FirstChild; c != nil; c = n.FirstChild { 156 | n.RemoveChild(c) 157 | nodes = append(nodes, c) 158 | } 159 | } 160 | 161 | return pushStack(s, nodes) 162 | } 163 | 164 | // Prepend prepends the elements specified by the selector to each element in 165 | // the set of matched elements, following the same rules as Append. 166 | func (s *Selection) Prepend(selector string) *Selection { 167 | return s.PrependMatcher(compileMatcher(selector)) 168 | } 169 | 170 | // PrependMatcher prepends the elements specified by the matcher to each 171 | // element in the set of matched elements. 172 | // 173 | // This follows the same rules as Selection.Append. 174 | func (s *Selection) PrependMatcher(m Matcher) *Selection { 175 | return s.PrependNodes(m.MatchAll(s.document.rootNode)...) 176 | } 177 | 178 | // PrependSelection prepends the elements in the selection to each element in 179 | // the set of matched elements. 180 | // 181 | // This follows the same rules as Selection.Append. 182 | func (s *Selection) PrependSelection(sel *Selection) *Selection { 183 | return s.PrependNodes(sel.Nodes...) 184 | } 185 | 186 | // PrependHtml parses the html and prepends it to the set of matched elements. 187 | func (s *Selection) PrependHtml(html string) *Selection { 188 | return s.PrependNodes(parseHtml(html)...) 189 | } 190 | 191 | // PrependNodes prepends the specified nodes to each node in the set of 192 | // matched elements. 193 | // 194 | // This follows the same rules as Selection.Append. 195 | func (s *Selection) PrependNodes(ns ...*html.Node) *Selection { 196 | return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) { 197 | // sn.FirstChild may be nil, in which case this functions like 198 | // sn.AppendChild() 199 | sn.InsertBefore(n, sn.FirstChild) 200 | }) 201 | } 202 | 203 | // Remove removes the set of matched elements from the document. 204 | // It returns the same selection, now consisting of nodes not in the document. 205 | func (s *Selection) Remove() *Selection { 206 | for _, n := range s.Nodes { 207 | if n.Parent != nil { 208 | n.Parent.RemoveChild(n) 209 | } 210 | } 211 | 212 | return s 213 | } 214 | 215 | // RemoveFiltered removes the set of matched elements by selector. 216 | // It returns the Selection of removed nodes. 217 | func (s *Selection) RemoveFiltered(selector string) *Selection { 218 | return s.RemoveMatcher(compileMatcher(selector)) 219 | } 220 | 221 | // RemoveMatcher removes the set of matched elements. 222 | // It returns the Selection of removed nodes. 223 | func (s *Selection) RemoveMatcher(m Matcher) *Selection { 224 | return s.FilterMatcher(m).Remove() 225 | } 226 | 227 | // ReplaceWith replaces each element in the set of matched elements with the 228 | // nodes matched by the given selector. 229 | // It returns the removed elements. 230 | // 231 | // This follows the same rules as Selection.Append. 232 | func (s *Selection) ReplaceWith(selector string) *Selection { 233 | return s.ReplaceWithMatcher(compileMatcher(selector)) 234 | } 235 | 236 | // ReplaceWithMatcher replaces each element in the set of matched elements with 237 | // the nodes matched by the given Matcher. 238 | // It returns the removed elements. 239 | // 240 | // This follows the same rules as Selection.Append. 241 | func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection { 242 | return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...) 243 | } 244 | 245 | // ReplaceWithSelection replaces each element in the set of matched elements with 246 | // the nodes from the given Selection. 247 | // It returns the removed elements. 248 | // 249 | // This follows the same rules as Selection.Append. 250 | func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection { 251 | return s.ReplaceWithNodes(sel.Nodes...) 252 | } 253 | 254 | // ReplaceWithHtml replaces each element in the set of matched elements with 255 | // the parsed HTML. 256 | // It returns the removed elements. 257 | // 258 | // This follows the same rules as Selection.Append. 259 | func (s *Selection) ReplaceWithHtml(html string) *Selection { 260 | return s.ReplaceWithNodes(parseHtml(html)...) 261 | } 262 | 263 | // ReplaceWithNodes replaces each element in the set of matched elements with 264 | // the given nodes. 265 | // It returns the removed elements. 266 | // 267 | // This follows the same rules as Selection.Append. 268 | func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection { 269 | s.AfterNodes(ns...) 270 | return s.Remove() 271 | } 272 | 273 | // SetHtml sets the html content of each element in the selection to 274 | // specified html string. 275 | func (s *Selection) SetHtml(html string) *Selection { 276 | return setHtmlNodes(s, parseHtml(html)...) 277 | } 278 | 279 | // SetText sets the content of each element in the selection to specified content. 280 | // The provided text string is escaped. 281 | func (s *Selection) SetText(text string) *Selection { 282 | return s.SetHtml(html.EscapeString(text)) 283 | } 284 | 285 | // Unwrap removes the parents of the set of matched elements, leaving the matched 286 | // elements (and their siblings, if any) in their place. 287 | // It returns the original selection. 288 | func (s *Selection) Unwrap() *Selection { 289 | s.Parent().Each(func(i int, ss *Selection) { 290 | // For some reason, jquery allows unwrap to remove the <head> element, so 291 | // allowing it here too. Same for <html>. Why it allows those elements to 292 | // be unwrapped while not allowing body is a mystery to me. 293 | if ss.Nodes[0].Data != "body" { 294 | ss.ReplaceWithSelection(ss.Contents()) 295 | } 296 | }) 297 | 298 | return s 299 | } 300 | 301 | // Wrap wraps each element in the set of matched elements inside the first 302 | // element matched by the given selector. The matched child is cloned before 303 | // being inserted into the document. 304 | // 305 | // It returns the original set of elements. 306 | func (s *Selection) Wrap(selector string) *Selection { 307 | return s.WrapMatcher(compileMatcher(selector)) 308 | } 309 | 310 | // WrapMatcher wraps each element in the set of matched elements inside the 311 | // first element matched by the given matcher. The matched child is cloned 312 | // before being inserted into the document. 313 | // 314 | // It returns the original set of elements. 315 | func (s *Selection) WrapMatcher(m Matcher) *Selection { 316 | return s.wrapNodes(m.MatchAll(s.document.rootNode)...) 317 | } 318 | 319 | // WrapSelection wraps each element in the set of matched elements inside the 320 | // first element in the given Selection. The element is cloned before being 321 | // inserted into the document. 322 | // 323 | // It returns the original set of elements. 324 | func (s *Selection) WrapSelection(sel *Selection) *Selection { 325 | return s.wrapNodes(sel.Nodes...) 326 | } 327 | 328 | // WrapHtml wraps each element in the set of matched elements inside the inner- 329 | // most child of the given HTML. 330 | // 331 | // It returns the original set of elements. 332 | func (s *Selection) WrapHtml(html string) *Selection { 333 | return s.wrapNodes(parseHtml(html)...) 334 | } 335 | 336 | // WrapNode wraps each element in the set of matched elements inside the inner- 337 | // most child of the given node. The given node is copied before being inserted 338 | // into the document. 339 | // 340 | // It returns the original set of elements. 341 | func (s *Selection) WrapNode(n *html.Node) *Selection { 342 | return s.wrapNodes(n) 343 | } 344 | 345 | func (s *Selection) wrapNodes(ns ...*html.Node) *Selection { 346 | s.Each(func(i int, ss *Selection) { 347 | ss.wrapAllNodes(ns...) 348 | }) 349 | 350 | return s 351 | } 352 | 353 | // WrapAll wraps a single HTML structure, matched by the given selector, around 354 | // all elements in the set of matched elements. The matched child is cloned 355 | // before being inserted into the document. 356 | // 357 | // It returns the original set of elements. 358 | func (s *Selection) WrapAll(selector string) *Selection { 359 | return s.WrapAllMatcher(compileMatcher(selector)) 360 | } 361 | 362 | // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher, 363 | // around all elements in the set of matched elements. The matched child is 364 | // cloned before being inserted into the document. 365 | // 366 | // It returns the original set of elements. 367 | func (s *Selection) WrapAllMatcher(m Matcher) *Selection { 368 | return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...) 369 | } 370 | 371 | // WrapAllSelection wraps a single HTML structure, the first node of the given 372 | // Selection, around all elements in the set of matched elements. The matched 373 | // child is cloned before being inserted into the document. 374 | // 375 | // It returns the original set of elements. 376 | func (s *Selection) WrapAllSelection(sel *Selection) *Selection { 377 | return s.wrapAllNodes(sel.Nodes...) 378 | } 379 | 380 | // WrapAllHtml wraps the given HTML structure around all elements in the set of 381 | // matched elements. The matched child is cloned before being inserted into the 382 | // document. 383 | // 384 | // It returns the original set of elements. 385 | func (s *Selection) WrapAllHtml(html string) *Selection { 386 | return s.wrapAllNodes(parseHtml(html)...) 387 | } 388 | 389 | func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection { 390 | if len(ns) > 0 { 391 | return s.WrapAllNode(ns[0]) 392 | } 393 | return s 394 | } 395 | 396 | // WrapAllNode wraps the given node around the first element in the Selection, 397 | // making all other nodes in the Selection children of the given node. The node 398 | // is cloned before being inserted into the document. 399 | // 400 | // It returns the original set of elements. 401 | func (s *Selection) WrapAllNode(n *html.Node) *Selection { 402 | if s.Size() == 0 { 403 | return s 404 | } 405 | 406 | wrap := cloneNode(n) 407 | 408 | first := s.Nodes[0] 409 | if first.Parent != nil { 410 | first.Parent.InsertBefore(wrap, first) 411 | first.Parent.RemoveChild(first) 412 | } 413 | 414 | for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) { 415 | wrap = c 416 | } 417 | 418 | newSingleSelection(wrap, s.document).AppendSelection(s) 419 | 420 | return s 421 | } 422 | 423 | // WrapInner wraps an HTML structure, matched by the given selector, around the 424 | // content of element in the set of matched elements. The matched child is 425 | // cloned before being inserted into the document. 426 | // 427 | // It returns the original set of elements. 428 | func (s *Selection) WrapInner(selector string) *Selection { 429 | return s.WrapInnerMatcher(compileMatcher(selector)) 430 | } 431 | 432 | // WrapInnerMatcher wraps an HTML structure, matched by the given selector, 433 | // around the content of element in the set of matched elements. The matched 434 | // child is cloned before being inserted into the document. 435 | // 436 | // It returns the original set of elements. 437 | func (s *Selection) WrapInnerMatcher(m Matcher) *Selection { 438 | return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...) 439 | } 440 | 441 | // WrapInnerSelection wraps an HTML structure, matched by the given selector, 442 | // around the content of element in the set of matched elements. The matched 443 | // child is cloned before being inserted into the document. 444 | // 445 | // It returns the original set of elements. 446 | func (s *Selection) WrapInnerSelection(sel *Selection) *Selection { 447 | return s.wrapInnerNodes(sel.Nodes...) 448 | } 449 | 450 | // WrapInnerHtml wraps an HTML structure, matched by the given selector, around 451 | // the content of element in the set of matched elements. The matched child is 452 | // cloned before being inserted into the document. 453 | // 454 | // It returns the original set of elements. 455 | func (s *Selection) WrapInnerHtml(html string) *Selection { 456 | return s.wrapInnerNodes(parseHtml(html)...) 457 | } 458 | 459 | // WrapInnerNode wraps an HTML structure, matched by the given selector, around 460 | // the content of element in the set of matched elements. The matched child is 461 | // cloned before being inserted into the document. 462 | // 463 | // It returns the original set of elements. 464 | func (s *Selection) WrapInnerNode(n *html.Node) *Selection { 465 | return s.wrapInnerNodes(n) 466 | } 467 | 468 | func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection { 469 | if len(ns) == 0 { 470 | return s 471 | } 472 | 473 | s.Each(func(i int, s *Selection) { 474 | contents := s.Contents() 475 | 476 | if contents.Size() > 0 { 477 | contents.wrapAllNodes(ns...) 478 | } else { 479 | s.AppendNodes(cloneNode(ns[0])) 480 | } 481 | }) 482 | 483 | return s 484 | } 485 | 486 | func parseHtml(h string) []*html.Node { 487 | // Errors are only returned when the io.Reader returns any error besides 488 | // EOF, but strings.Reader never will 489 | nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode}) 490 | if err != nil { 491 | panic("goquery: failed to parse HTML: " + err.Error()) 492 | } 493 | return nodes 494 | } 495 | 496 | func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection { 497 | for _, n := range s.Nodes { 498 | for c := n.FirstChild; c != nil; c = n.FirstChild { 499 | n.RemoveChild(c) 500 | } 501 | for _, c := range ns { 502 | n.AppendChild(cloneNode(c)) 503 | } 504 | } 505 | return s 506 | } 507 | 508 | // Get the first child that is an ElementNode 509 | func getFirstChildEl(n *html.Node) *html.Node { 510 | c := n.FirstChild 511 | for c != nil && c.Type != html.ElementNode { 512 | c = c.NextSibling 513 | } 514 | return c 515 | } 516 | 517 | // Deep copy a slice of nodes. 518 | func cloneNodes(ns []*html.Node) []*html.Node { 519 | cns := make([]*html.Node, 0, len(ns)) 520 | 521 | for _, n := range ns { 522 | cns = append(cns, cloneNode(n)) 523 | } 524 | 525 | return cns 526 | } 527 | 528 | // Deep copy a node. The new node has clones of all the original node's 529 | // children but none of its parents or siblings. 530 | func cloneNode(n *html.Node) *html.Node { 531 | nn := &html.Node{ 532 | Type: n.Type, 533 | DataAtom: n.DataAtom, 534 | Data: n.Data, 535 | Attr: make([]html.Attribute, len(n.Attr)), 536 | } 537 | 538 | copy(nn.Attr, n.Attr) 539 | for c := n.FirstChild; c != nil; c = c.NextSibling { 540 | nn.AppendChild(cloneNode(c)) 541 | } 542 | 543 | return nn 544 | } 545 | 546 | func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool, 547 | f func(sn *html.Node, n *html.Node)) *Selection { 548 | 549 | lasti := s.Size() - 1 550 | 551 | // net.Html doesn't provide document fragments for insertion, so to get 552 | // things in the correct order with After() and Prepend(), the callback 553 | // needs to be called on the reverse of the nodes. 554 | if reverse { 555 | for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 { 556 | ns[i], ns[j] = ns[j], ns[i] 557 | } 558 | } 559 | 560 | for i, sn := range s.Nodes { 561 | for _, n := range ns { 562 | if i != lasti { 563 | f(sn, cloneNode(n)) 564 | } else { 565 | if n.Parent != nil { 566 | n.Parent.RemoveChild(n) 567 | } 568 | f(sn, n) 569 | } 570 | } 571 | } 572 | 573 | return s 574 | } 575 | --------------------------------------------------------------------------------