fancl hai 1 ano
achega
67accdedf4
Modificáronse 100 ficheiros con 11968 adicións e 0 borrados
  1. 46 0
      .gitignore
  2. 24 0
      go.mod
  3. 50 0
      go.sum
  4. 133 0
      pgenr.go
  5. 64 0
      render.go
  6. 24 0
      render_test.go
  7. 213 0
      theme/default.go
  8. 5 0
      theme/theme.go
  9. 18 0
      vendor/github.com/Masterminds/goutils/.travis.yml
  10. 8 0
      vendor/github.com/Masterminds/goutils/CHANGELOG.md
  11. 202 0
      vendor/github.com/Masterminds/goutils/LICENSE.txt
  12. 70 0
      vendor/github.com/Masterminds/goutils/README.md
  13. 21 0
      vendor/github.com/Masterminds/goutils/appveyor.yml
  14. 230 0
      vendor/github.com/Masterminds/goutils/cryptorandomstringutils.go
  15. 248 0
      vendor/github.com/Masterminds/goutils/randomstringutils.go
  16. 240 0
      vendor/github.com/Masterminds/goutils/stringutils.go
  17. 357 0
      vendor/github.com/Masterminds/goutils/wordutils.go
  18. 29 0
      vendor/github.com/Masterminds/semver/.travis.yml
  19. 109 0
      vendor/github.com/Masterminds/semver/CHANGELOG.md
  20. 19 0
      vendor/github.com/Masterminds/semver/LICENSE.txt
  21. 36 0
      vendor/github.com/Masterminds/semver/Makefile
  22. 194 0
      vendor/github.com/Masterminds/semver/README.md
  23. 44 0
      vendor/github.com/Masterminds/semver/appveyor.yml
  24. 24 0
      vendor/github.com/Masterminds/semver/collection.go
  25. 423 0
      vendor/github.com/Masterminds/semver/constraints.go
  26. 115 0
      vendor/github.com/Masterminds/semver/doc.go
  27. 425 0
      vendor/github.com/Masterminds/semver/version.go
  28. 10 0
      vendor/github.com/Masterminds/semver/version_fuzz.go
  29. 2 0
      vendor/github.com/Masterminds/sprig/.gitignore
  30. 26 0
      vendor/github.com/Masterminds/sprig/.travis.yml
  31. 282 0
      vendor/github.com/Masterminds/sprig/CHANGELOG.md
  32. 20 0
      vendor/github.com/Masterminds/sprig/LICENSE.txt
  33. 13 0
      vendor/github.com/Masterminds/sprig/Makefile
  34. 78 0
      vendor/github.com/Masterminds/sprig/README.md
  35. 26 0
      vendor/github.com/Masterminds/sprig/appveyor.yml
  36. 502 0
      vendor/github.com/Masterminds/sprig/crypto.go
  37. 83 0
      vendor/github.com/Masterminds/sprig/date.go
  38. 83 0
      vendor/github.com/Masterminds/sprig/defaults.go
  39. 119 0
      vendor/github.com/Masterminds/sprig/dict.go
  40. 19 0
      vendor/github.com/Masterminds/sprig/doc.go
  41. 306 0
      vendor/github.com/Masterminds/sprig/functions.go
  42. 19 0
      vendor/github.com/Masterminds/sprig/glide.yaml
  43. 311 0
      vendor/github.com/Masterminds/sprig/list.go
  44. 12 0
      vendor/github.com/Masterminds/sprig/network.go
  45. 169 0
      vendor/github.com/Masterminds/sprig/numeric.go
  46. 28 0
      vendor/github.com/Masterminds/sprig/reflect.go
  47. 35 0
      vendor/github.com/Masterminds/sprig/regex.go
  48. 23 0
      vendor/github.com/Masterminds/sprig/semver.go
  49. 233 0
      vendor/github.com/Masterminds/sprig/strings.go
  50. 66 0
      vendor/github.com/Masterminds/sprig/url.go
  51. 1 0
      vendor/github.com/PuerkitoBio/goquery/.gitattributes
  52. 16 0
      vendor/github.com/PuerkitoBio/goquery/.gitignore
  53. 17 0
      vendor/github.com/PuerkitoBio/goquery/.travis.yml
  54. 12 0
      vendor/github.com/PuerkitoBio/goquery/LICENSE
  55. 183 0
      vendor/github.com/PuerkitoBio/goquery/README.md
  56. 124 0
      vendor/github.com/PuerkitoBio/goquery/array.go
  57. 123 0
      vendor/github.com/PuerkitoBio/goquery/doc.go
  58. 70 0
      vendor/github.com/PuerkitoBio/goquery/expand.go
  59. 163 0
      vendor/github.com/PuerkitoBio/goquery/filter.go
  60. 39 0
      vendor/github.com/PuerkitoBio/goquery/iteration.go
  61. 574 0
      vendor/github.com/PuerkitoBio/goquery/manipulation.go
  62. 275 0
      vendor/github.com/PuerkitoBio/goquery/property.go
  63. 49 0
      vendor/github.com/PuerkitoBio/goquery/query.go
  64. 698 0
      vendor/github.com/PuerkitoBio/goquery/traversal.go
  65. 141 0
      vendor/github.com/PuerkitoBio/goquery/type.go
  66. 161 0
      vendor/github.com/PuerkitoBio/goquery/utilities.go
  67. 14 0
      vendor/github.com/andybalholm/cascadia/.travis.yml
  68. 24 0
      vendor/github.com/andybalholm/cascadia/LICENSE
  69. 9 0
      vendor/github.com/andybalholm/cascadia/README.md
  70. 801 0
      vendor/github.com/andybalholm/cascadia/parser.go
  71. 833 0
      vendor/github.com/andybalholm/cascadia/selector.go
  72. 26 0
      vendor/github.com/andybalholm/cascadia/specificity.go
  73. 9 0
      vendor/github.com/google/uuid/.travis.yml
  74. 10 0
      vendor/github.com/google/uuid/CONTRIBUTING.md
  75. 9 0
      vendor/github.com/google/uuid/CONTRIBUTORS
  76. 27 0
      vendor/github.com/google/uuid/LICENSE
  77. 19 0
      vendor/github.com/google/uuid/README.md
  78. 80 0
      vendor/github.com/google/uuid/dce.go
  79. 12 0
      vendor/github.com/google/uuid/doc.go
  80. 53 0
      vendor/github.com/google/uuid/hash.go
  81. 38 0
      vendor/github.com/google/uuid/marshal.go
  82. 90 0
      vendor/github.com/google/uuid/node.go
  83. 12 0
      vendor/github.com/google/uuid/node_js.go
  84. 33 0
      vendor/github.com/google/uuid/node_net.go
  85. 118 0
      vendor/github.com/google/uuid/null.go
  86. 59 0
      vendor/github.com/google/uuid/sql.go
  87. 123 0
      vendor/github.com/google/uuid/time.go
  88. 43 0
      vendor/github.com/google/uuid/util.go
  89. 294 0
      vendor/github.com/google/uuid/uuid.go
  90. 44 0
      vendor/github.com/google/uuid/version1.go
  91. 76 0
      vendor/github.com/google/uuid/version4.go
  92. 27 0
      vendor/github.com/gorilla/css/LICENSE
  93. 33 0
      vendor/github.com/gorilla/css/scanner/doc.go
  94. 356 0
      vendor/github.com/gorilla/css/scanner/scanner.go
  95. 24 0
      vendor/github.com/huandu/xstrings/.gitignore
  96. 7 0
      vendor/github.com/huandu/xstrings/.travis.yml
  97. 23 0
      vendor/github.com/huandu/xstrings/CONTRIBUTING.md
  98. 22 0
      vendor/github.com/huandu/xstrings/LICENSE
  99. 117 0
      vendor/github.com/huandu/xstrings/README.md
  100. 21 0
      vendor/github.com/huandu/xstrings/common.go

+ 46 - 0
.gitignore

@@ -0,0 +1,46 @@
+bin/
+
+.svn/
+.godeps
+./build
+.cover/
+dist
+_site
+_posts
+*.dat
+.vscode
+cti.yaml
+cmd/mock
+cmd/report
+
+# Go.gitignore
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+storage
+.idea
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+profile
+
+# vim stuff
+*.sw[op]

+ 24 - 0
go.mod

@@ -0,0 +1,24 @@
+module git.nspix.com/golang/pgenr
+
+go 1.17
+
+require (
+	github.com/Masterminds/sprig v2.22.0+incompatible
+	github.com/vanng822/go-premailer v1.20.1
+)
+
+require (
+	github.com/Masterminds/goutils v1.1.1 // indirect
+	github.com/Masterminds/semver v1.5.0 // indirect
+	github.com/PuerkitoBio/goquery v1.5.1 // indirect
+	github.com/andybalholm/cascadia v1.1.0 // indirect
+	github.com/google/uuid v1.3.0 // indirect
+	github.com/gorilla/css v1.0.0 // indirect
+	github.com/huandu/xstrings v1.3.2 // indirect
+	github.com/imdario/mergo v0.3.13 // indirect
+	github.com/mitchellh/copystructure v1.2.0 // indirect
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/vanng822/css v1.0.1 // indirect
+	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
+)

+ 50 - 0
go.sum

@@ -0,0 +1,50 @@
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
+github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
+github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
+github.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8=
+github.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=
+github.com/vanng822/go-premailer v1.20.1 h1:2LTSIULXxNV5IOB5BSD3dlfOG95cq8qqExtRZMImTGA=
+github.com/vanng822/go-premailer v1.20.1/go.mod h1:RAxbRFp6M/B171gsKu8dsyq+Y5NGsUUvYfg+WQWusbE=
+github.com/vanng822/r2router v0.0.0-20150523112421-1023140a4f30/go.mod h1:1BVq8p2jVr55Ost2PkZWDrG86PiJ/0lxqcXoAcGxvWU=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
+golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 133 - 0
pgenr.go

@@ -0,0 +1,133 @@
+package pgenr
+
+const (
+	TextThemeSuccess = "success"
+	TextThemeDanger  = "danger"
+)
+
+type (
+	TextOption func(t *Text)
+
+	ButtonOption func(btn *Button)
+
+	Text struct {
+		Content string
+		Theme   string
+		Color   string
+	}
+
+	Button struct {
+		Url   string
+		Text  string
+		Color string
+	}
+
+	Entry struct {
+		Title string
+		Items map[string]*Text
+	}
+
+	Action struct {
+		Instructions string
+		Button       *Button
+		InviteCode   string
+	}
+
+	Page struct {
+		Title     string
+		Head      string
+		Intros    []string
+		Entries   []*Entry
+		Actions   []Action
+		Outros    []string
+		Copyright string
+	}
+)
+
+func (e *Entry) AddItem(label string, txt *Text) *Entry {
+	if e.Items == nil {
+		e.Items = make(map[string]*Text)
+	}
+	e.Items[label] = txt
+	return e
+}
+
+func (page *Page) SetHead(s string) *Page {
+	page.Head = s
+	return page
+}
+
+func (page *Page) SetCopyright(s string) *Page {
+	page.Copyright = s
+	return page
+}
+
+func (page *Page) AddIntro(s string) *Page {
+	page.Intros = append(page.Intros, s)
+	return page
+}
+
+func (page *Page) AddEntry(e *Entry) *Page {
+	page.Entries = append(page.Entries, e)
+	return page
+}
+
+func (page *Page) AddButtonAction(s string, btn *Button) *Page {
+	page.Actions = append(page.Actions, Action{Instructions: s, Button: btn})
+	return page
+}
+
+func (page *Page) AddInviteCodeAction(s string, code string) *Page {
+	page.Actions = append(page.Actions, Action{Instructions: s, InviteCode: code})
+	return page
+}
+
+func (page *Page) AddOutro(s string) *Page {
+	page.Outros = append(page.Outros, s)
+	return page
+}
+
+func NewPage(title string) *Page {
+	return &Page{
+		Title:   title,
+		Intros:  make([]string, 0),
+		Entries: make([]*Entry, 0),
+		Actions: make([]Action, 0),
+		Outros:  make([]string, 0),
+	}
+}
+
+func NewEntry(title string) *Entry {
+	return &Entry{
+		Title: title,
+		Items: make(map[string]*Text),
+	}
+}
+
+func WithTextTheme(s string) TextOption {
+	return func(t *Text) {
+		t.Theme = s
+	}
+}
+
+func WithTextColor(color string) TextOption {
+	return func(t *Text) {
+		t.Color = color
+	}
+}
+
+func NewButton(label, link string, opts ...ButtonOption) *Button {
+	btn := &Button{Text: label, Url: link}
+	for _, cb := range opts {
+		cb(btn)
+	}
+	return btn
+}
+
+func NewText(s string, opts ...TextOption) *Text {
+	txt := &Text{Content: s}
+	for _, cb := range opts {
+		cb(txt)
+	}
+	return txt
+}

+ 64 - 0
render.go

@@ -0,0 +1,64 @@
+package pgenr
+
+import (
+	"bytes"
+	"git.nspix.com/golang/pgenr/theme"
+	"github.com/Masterminds/sprig"
+	"github.com/vanng822/go-premailer/premailer"
+	"html/template"
+)
+
+const (
+	Name = "pgenr"
+)
+
+type (
+	RenderOption func(o *RenderOptions)
+
+	Template struct {
+		Page *Page
+	}
+
+	RenderOptions struct {
+		Theme     theme.Theme
+		InlineCss bool
+	}
+)
+
+var templateFunc = template.FuncMap{
+	"url": func(s string) template.URL {
+		return template.URL(s)
+	},
+}
+
+func Render(page *Page, cbs ...RenderOption) (str string, err error) {
+	var (
+		tpl     *template.Template
+		buffer  bytes.Buffer
+		cssProc premailer.Premailer
+	)
+	opts := &RenderOptions{
+		Theme:     &theme.Default{},
+		InlineCss: true,
+	}
+	for _, cb := range cbs {
+		cb(opts)
+	}
+	if tpl, err = template.New(Name).Funcs(sprig.FuncMap()).Funcs(templateFunc).Funcs(template.FuncMap{
+		"safe": func(s string) template.HTML { return template.HTML(s) }, // Used for keeping comments in generated template
+	}).Parse(opts.Theme.Template()); err != nil {
+		return
+	}
+	if err = tpl.Execute(&buffer, Template{page}); err != nil {
+		return
+	}
+	if !opts.InlineCss {
+		str = buffer.String()
+		return
+	}
+	if cssProc, err = premailer.NewPremailerFromBytes(buffer.Bytes(), premailer.NewOptions()); err != nil {
+		return
+	}
+	str, err = cssProc.Transform()
+	return
+}

+ 24 - 0
render_test.go

@@ -0,0 +1,24 @@
+package pgenr
+
+import (
+	"io/ioutil"
+	"testing"
+)
+
+func TestRender(t *testing.T) {
+	page := NewPage("Hi Jon Snow,")
+	page.SetHead("Hi Jon Snow,").
+		SetCopyright("Copyright © 2017 Hermes. All rights reserved")
+	page.AddIntro("Welcome to Hermes! We're very excited to have you on board.")
+	//page.AddEntry(NewEntry("Welcome to Hermes").AddItem("Stock1", NewText("14.58", WithTextTheme(TextThemeSuccess))).AddItem("asddas", NewText("15.8", WithTextTheme(TextThemeDanger))))
+	page.AddOutro("Need help, or have questions? Just reply to this email, we'd love to help.")
+	page.AddOutro("Yours truly,")
+	page.AddOutro("Hermes - http://google.com")
+	//page.AddButtonAction("To get started with Hermes, please click here:", NewButton("Confirm your account", "https://example-hermes.com/"))
+	page.AddInviteCodeAction("To get started with Hermes, please click here:", "950038")
+	if str, err := Render(page); err == nil {
+		ioutil.WriteFile("d:\\aaa.html", []byte(str), 0644)
+	} else {
+		t.Error(err)
+	}
+}

+ 213 - 0
theme/default.go

@@ -0,0 +1,213 @@
+package theme
+
+type Default struct {
+}
+
+func (theme *Default) Template() string {
+	return `
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>{{ .Page.Title }}</title>
+  <style>
+    html {
+      height: 100%;
+    }
+
+    body {
+      width: 100% !important;
+      height: 100%;
+      margin: 0;
+      font-size: .96rem;
+      line-height: 1.4;
+      background-color: #F2F4F6;
+      color: #64687E;
+      -webkit-text-size-adjust: none;
+      font-family: Helvetica Neue, Tahoma, Arial, PingFangSC-Regular, Hiragino Sans GB, Microsoft Yahei, sans-serif;
+      -webkit-font-smoothing: antialiased;
+      touch-action: manipulation;
+      text-size-adjust: none;
+    }
+
+    .container {
+      max-width: 560px;
+      height: 100%;
+      margin: 0 auto;
+      background-color: white;
+    }
+
+    .container-inner {
+      padding: 1rem;
+    }
+
+	.action-block{
+		margin: 30px 0 20px 0;
+		text-align: center;
+	}
+
+    .text-success {
+      color: #20c55c;
+    }
+
+    .text-danger {
+      color: #ff0476;
+    }
+
+    .text-center {
+      text-align: center;
+    }
+
+    article {
+      line-height: 1.6rem
+    }
+
+    article h1,
+    article h2,
+    article h3,
+    article h4 {
+      color: #232323;
+      margin-bottom: 1.28rem;
+    }
+
+ 	a {
+      font-style: none;
+      text-decoration: none;
+    }
+
+    article p {
+      margin-bottom: 1rem;
+    }
+
+    .preview-label {
+      min-width: 4em;
+      text-align: justify;
+      text-align-last: justify;
+    }
+
+    .preview-value {
+      text-align: right;
+      display: block;
+      overflow: hidden;
+      word-break: normal;
+      word-wrap: break-word;
+      color: rgba(0, 0, 0, 0.9);
+      flex: 1 1 auto !important;
+    }
+
+    .grid {
+      padding: 1rem 0;
+    }
+	
+    .grid h4 {
+      margin-top: 0;
+      margin-bottom: .68rem;
+    }
+
+    .row {
+      display: flex;
+      justify-content: flex-start;
+      line-height: 2;
+    }
+
+    .button {
+      padding: .8rem 2rem;
+      color: white;
+      background-color: #727cf5;
+      border-radius: .25rem;
+    }
+
+	.invite-code {
+		font-size: 32px;
+    	letter-spacing: .6rem;
+		color: #454742;
+	}
+	
+	.copyright {
+		text-align: center;
+		font-size: .9rem;
+		color: #8c8c86;
+		margin: 50px 0 10px 0;
+	}
+	
+    .cell {
+      flex: 1 1 auto !important;
+    }
+  </style>
+</head>
+
+<body>
+  <div class="container">
+    <div class="container-inner">
+      <article>
+		{{ if .Page.Head }}<h1>{{ .Page.Head }}</h1>{{ end }}
+		{{ with .Page.Intros }}
+			{{ if gt (len .) 0 }}
+			  {{ range $line := . }}
+				<p>{{ $line }}</p>
+			  {{ end }}
+			{{ end }}
+		{{ end }}
+		{{ with .Page.Actions }}
+			{{ if gt (len .) 0 }}
+				{{ range $action := . }}
+					{{ if $action.Instructions }} <p> {{ $action.Instructions }} </p> {{ end }}
+					{{ if $action.InviteCode }}
+						<div class="action-block"><span class="invite-code">{{ $action.InviteCode }}</span></div>
+					{{ else if $action.Button }}
+						<div class="action-block">
+							{{ if $action.Button.Color }}
+							<a class="button" href="{{ $action.Button.Url }}" style="background-color:{{ $action.Button.Color }}">{{ $action.Button.Text }}</a>
+							{{ else }}
+							<a class="button" href="{{ $action.Button.Url }}" >{{ $action.Button.Text }}</a>
+							{{ end }}
+						</div>
+					{{ end }}
+				{{ end }}
+			{{ end }}
+		{{ end }}
+		{{ with .Page.Entries }}
+			{{ if gt (len .) 0 }}
+				{{ range $entry := . }}
+				<div class="grid">
+					{{if $entry.Title }}<h4>{{ $entry.Title }}</h4>{{ end }}
+					{{ with $entry.Items }}
+						{{ if gt (len .) 0 }}
+						  {{ range $label,$value := . }}
+							<div class="row">
+							  <div class="preview-label">{{ $label }}</div>
+							  <div class="preview-value">
+							  {{ if $value.Theme }}
+							    <span class="text-{{ $value.Theme }}">{{ $value.Content }}</span>
+							  {{ else if $value.Color }}
+								<span style="color:{{ $value.Theme }}">{{ $value.Content }}</span>
+							  {{ else }}
+								{{ $value.Content }}
+							  {{ end }}
+							  </div>
+						    </div>
+						  {{ end }}
+						{{ end }}
+					{{ end }}
+				</div>
+				{{ end }}
+			{{ end }}
+		{{ end }}
+		{{ with .Page.Outros }}
+			{{ if gt (len .) 0 }}
+			  {{ range $line := . }}
+				<p>{{ $line }}</p>
+			  {{ end }}
+			{{ end }}
+		{{ end }}
+		{{ if .Page.Copyright }}<div class="copyright">{{ .Page.Copyright }}</div>{{ end }}
+      </article>
+    </div>
+  </div>
+</body>
+</html>
+`
+}

+ 5 - 0
theme/theme.go

@@ -0,0 +1,5 @@
+package theme
+
+type Theme interface {
+	Template() string
+}

+ 18 - 0
vendor/github.com/Masterminds/goutils/.travis.yml

@@ -0,0 +1,18 @@
+language: go
+
+go:
+  - 1.6
+  - 1.7
+  - 1.8
+  - tip
+
+script:
+  - go test -v
+
+notifications:
+  webhooks:
+    urls:
+      - https://webhooks.gitter.im/e/06e3328629952dabe3e0
+    on_success: change  # options: [always|never|change] default: always
+    on_failure: always  # options: [always|never|change] default: always
+    on_start: never     # options: [always|never|change] default: always

+ 8 - 0
vendor/github.com/Masterminds/goutils/CHANGELOG.md

@@ -0,0 +1,8 @@
+# 1.0.1 (2017-05-31)
+
+## Fixed
+- #21: Fix generation of alphanumeric strings (thanks @dbarranco)
+
+# 1.0.0 (2014-04-30)
+
+- Initial release.

+ 202 - 0
vendor/github.com/Masterminds/goutils/LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 70 - 0
vendor/github.com/Masterminds/goutils/README.md

@@ -0,0 +1,70 @@
+GoUtils
+===========
+[![Stability: Maintenance](https://masterminds.github.io/stability/maintenance.svg)](https://masterminds.github.io/stability/maintenance.html)
+[![GoDoc](https://godoc.org/github.com/Masterminds/goutils?status.png)](https://godoc.org/github.com/Masterminds/goutils) [![Build Status](https://travis-ci.org/Masterminds/goutils.svg?branch=master)](https://travis-ci.org/Masterminds/goutils) [![Build status](https://ci.appveyor.com/api/projects/status/sc2b1ew0m7f0aiju?svg=true)](https://ci.appveyor.com/project/mattfarina/goutils)
+
+
+GoUtils provides users with utility functions to manipulate strings in various ways. It is a Go implementation of some
+string manipulation libraries of Java Apache Commons. GoUtils includes the following Java Apache Commons classes:
+* WordUtils    
+* RandomStringUtils  
+* StringUtils (partial implementation)
+
+## Installation
+If you have Go set up on your system, from the GOPATH directory within the command line/terminal, enter this:
+
+	go get github.com/Masterminds/goutils
+
+If you do not have Go set up on your system, please follow the [Go installation directions from the documenation](http://golang.org/doc/install), and then follow the instructions above to install GoUtils.
+
+
+## Documentation
+GoUtils doc is available here: [![GoDoc](https://godoc.org/github.com/Masterminds/goutils?status.png)](https://godoc.org/github.com/Masterminds/goutils)
+
+
+## Usage
+The code snippets below show examples of how to use GoUtils. Some functions return errors while others do not. The first instance below, which does not return an error, is the `Initials` function (located within the `wordutils.go` file).
+
+    package main
+
+    import (
+        "fmt"
+    	"github.com/Masterminds/goutils"
+    )
+
+    func main() {
+
+    	// EXAMPLE 1: A goutils function which returns no errors
+        fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
+
+    }
+Some functions return errors mainly due to illegal arguements used as parameters. The code example below illustrates how to deal with function that returns an error. In this instance, the function is the `Random` function (located within the `randomstringutils.go` file).
+
+    package main
+
+    import (
+        "fmt"
+        "github.com/Masterminds/goutils"
+    )
+
+    func main() {
+
+        // EXAMPLE 2: A goutils function which returns an error
+        rand1, err1 := goutils.Random (-1, 0, 0, true, true)  
+
+        if err1 != nil {
+			fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
+		} else {
+			fmt.Println(rand1)
+		}
+
+    }
+
+## License
+GoUtils is licensed under the Apache License, Version 2.0. Please check the LICENSE.txt file or visit http://www.apache.org/licenses/LICENSE-2.0 for a copy of the license.
+
+## Issue Reporting
+Make suggestions or report issues using the Git issue tracker: https://github.com/Masterminds/goutils/issues
+
+## Website
+* [GoUtils webpage](http://Masterminds.github.io/goutils/)

+ 21 - 0
vendor/github.com/Masterminds/goutils/appveyor.yml

@@ -0,0 +1,21 @@
+version: build-{build}.{branch}
+
+clone_folder: C:\gopath\src\github.com\Masterminds\goutils
+shallow_clone: true
+
+environment:
+  GOPATH: C:\gopath
+
+platform:
+  - x64
+
+build: off
+
+install:
+  - go version
+  - go env
+
+test_script:
+  - go test -v
+
+deploy: off

+ 230 - 0
vendor/github.com/Masterminds/goutils/cryptorandomstringutils.go

@@ -0,0 +1,230 @@
+/*
+Copyright 2014 Alexander Okoli
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package goutils
+
+import (
+	"crypto/rand"
+	"fmt"
+	"math"
+	"math/big"
+	"unicode"
+)
+
+/*
+CryptoRandomNonAlphaNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)).
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomNonAlphaNumeric(count int) (string, error) {
+	return CryptoRandomAlphaNumericCustom(count, false, false)
+}
+
+/*
+CryptoRandomAscii creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive).
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomAscii(count int) (string, error) {
+	return CryptoRandom(count, 32, 127, false, false)
+}
+
+/*
+CryptoRandomNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of numeric characters.
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomNumeric(count int) (string, error) {
+	return CryptoRandom(count, 0, 0, false, true)
+}
+
+/*
+CryptoRandomAlphabetic creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
+
+Parameters:
+	count - the length of random string to create
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomAlphabetic(count int) (string, error) {
+	return CryptoRandom(count, 0, 0, true, false)
+}
+
+/*
+CryptoRandomAlphaNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alpha-numeric characters.
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomAlphaNumeric(count int) (string, error) {
+	return CryptoRandom(count, 0, 0, true, true)
+}
+
+/*
+CryptoRandomAlphaNumericCustom creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
+
+Parameters:
+	count - the length of random string to create
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
+*/
+func CryptoRandomAlphaNumericCustom(count int, letters bool, numbers bool) (string, error) {
+	return CryptoRandom(count, 0, 0, letters, numbers)
+}
+
+/*
+CryptoRandom creates a random string based on a variety of options, using using golang's crypto/rand source of randomness.
+If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used,
+unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively.
+If chars is not nil, characters stored in chars that are between start and end are chosen.
+
+Parameters:
+	count - the length of random string to create
+	start - the position in set of chars (ASCII/Unicode int) to start at
+	end - the position in set of chars (ASCII/Unicode int) to end before
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+	chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
+
+Returns:
+	string - the random string
+	error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars)
+*/
+func CryptoRandom(count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) {
+	if count == 0 {
+		return "", nil
+	} else if count < 0 {
+		err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...")
+		return "", err
+	}
+	if chars != nil && len(chars) == 0 {
+		err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty")
+		return "", err
+	}
+
+	if start == 0 && end == 0 {
+		if chars != nil {
+			end = len(chars)
+		} else {
+			if !letters && !numbers {
+				end = math.MaxInt32
+			} else {
+				end = 'z' + 1
+				start = ' '
+			}
+		}
+	} else {
+		if end <= start {
+			err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start)
+			return "", err
+		}
+
+		if chars != nil && end > len(chars) {
+			err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars))
+			return "", err
+		}
+	}
+
+	buffer := make([]rune, count)
+	gap := end - start
+
+	// high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319
+	//  low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343
+
+	for count != 0 {
+		count--
+		var ch rune
+		if chars == nil {
+			ch = rune(getCryptoRandomInt(gap) + int64(start))
+		} else {
+			ch = chars[getCryptoRandomInt(gap)+int64(start)]
+		}
+
+		if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
+			if ch >= 56320 && ch <= 57343 { // low surrogate range
+				if count == 0 {
+					count++
+				} else {
+					// Insert low surrogate
+					buffer[count] = ch
+					count--
+					// Insert high surrogate
+					buffer[count] = rune(55296 + getCryptoRandomInt(128))
+				}
+			} else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial)
+				if count == 0 {
+					count++
+				} else {
+					// Insert low surrogate
+					buffer[count] = rune(56320 + getCryptoRandomInt(128))
+					count--
+					// Insert high surrogate
+					buffer[count] = ch
+				}
+			} else if ch >= 56192 && ch <= 56319 {
+				// private high surrogate, skip it
+				count++
+			} else {
+				// not one of the surrogates*
+				buffer[count] = ch
+			}
+		} else {
+			count++
+		}
+	}
+	return string(buffer), nil
+}
+
+func getCryptoRandomInt(count int) int64 {
+	nBig, err := rand.Int(rand.Reader, big.NewInt(int64(count)))
+	if err != nil {
+		panic(err)
+	}
+	return nBig.Int64()
+}

+ 248 - 0
vendor/github.com/Masterminds/goutils/randomstringutils.go

@@ -0,0 +1,248 @@
+/*
+Copyright 2014 Alexander Okoli
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package goutils
+
+import (
+	"fmt"
+	"math"
+	"math/rand"
+	"time"
+	"unicode"
+)
+
+// RANDOM provides the time-based seed used to generate random numbers
+var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+/*
+RandomNonAlphaNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)).
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomNonAlphaNumeric(count int) (string, error) {
+	return RandomAlphaNumericCustom(count, false, false)
+}
+
+/*
+RandomAscii creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive).
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomAscii(count int) (string, error) {
+	return Random(count, 32, 127, false, false)
+}
+
+/*
+RandomNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of numeric characters.
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomNumeric(count int) (string, error) {
+	return Random(count, 0, 0, false, true)
+}
+
+/*
+RandomAlphabetic creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alphabetic characters.
+
+Parameters:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomAlphabetic(count int) (string, error) {
+	return Random(count, 0, 0, true, false)
+}
+
+/*
+RandomAlphaNumeric creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alpha-numeric characters.
+
+Parameter:
+	count - the length of random string to create
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomAlphaNumeric(count int) (string, error) {
+	return Random(count, 0, 0, true, true)
+}
+
+/*
+RandomAlphaNumericCustom creates a random string whose length is the number of characters specified.
+Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
+
+Parameters:
+	count - the length of random string to create
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func RandomAlphaNumericCustom(count int, letters bool, numbers bool) (string, error) {
+	return Random(count, 0, 0, letters, numbers)
+}
+
+/*
+Random creates a random string based on a variety of options, using default source of randomness.
+This method has exactly the same semantics as RandomSeed(int, int, int, bool, bool, []char, *rand.Rand), but
+instead of using an externally supplied source of randomness, it uses the internal *rand.Rand instance.
+
+Parameters:
+	count - the length of random string to create
+	start - the position in set of chars (ASCII/Unicode int) to start at
+	end - the position in set of chars (ASCII/Unicode int) to end before
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+	chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
+
+Returns:
+	string - the random string
+	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
+*/
+func Random(count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) {
+	return RandomSeed(count, start, end, letters, numbers, chars, RANDOM)
+}
+
+/*
+RandomSeed creates a random string based on a variety of options, using supplied source of randomness.
+If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used,
+unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively.
+If chars is not nil, characters stored in chars that are between start and end are chosen.
+This method accepts a user-supplied *rand.Rand instance to use as a source of randomness. By seeding a single *rand.Rand instance
+with a fixed seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably.
+
+Parameters:
+	count - the length of random string to create
+	start - the position in set of chars (ASCII/Unicode decimals) to start at
+	end - the position in set of chars (ASCII/Unicode decimals) to end before
+	letters - if true, generated string may include alphabetic characters
+	numbers - if true, generated string may include numeric characters
+	chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
+	random - a source of randomness.
+
+Returns:
+	string - the random string
+	error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars)
+*/
+func RandomSeed(count int, start int, end int, letters bool, numbers bool, chars []rune, random *rand.Rand) (string, error) {
+
+	if count == 0 {
+		return "", nil
+	} else if count < 0 {
+		err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...")
+		return "", err
+	}
+	if chars != nil && len(chars) == 0 {
+		err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty")
+		return "", err
+	}
+
+	if start == 0 && end == 0 {
+		if chars != nil {
+			end = len(chars)
+		} else {
+			if !letters && !numbers {
+				end = math.MaxInt32
+			} else {
+				end = 'z' + 1
+				start = ' '
+			}
+		}
+	} else {
+		if end <= start {
+			err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start)
+			return "", err
+		}
+
+		if chars != nil && end > len(chars) {
+			err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars))
+			return "", err
+		}
+	}
+
+	buffer := make([]rune, count)
+	gap := end - start
+
+	// high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319
+	//  low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343
+
+	for count != 0 {
+		count--
+		var ch rune
+		if chars == nil {
+			ch = rune(random.Intn(gap) + start)
+		} else {
+			ch = chars[random.Intn(gap)+start]
+		}
+
+		if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
+			if ch >= 56320 && ch <= 57343 { // low surrogate range
+				if count == 0 {
+					count++
+				} else {
+					// Insert low surrogate
+					buffer[count] = ch
+					count--
+					// Insert high surrogate
+					buffer[count] = rune(55296 + random.Intn(128))
+				}
+			} else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial)
+				if count == 0 {
+					count++
+				} else {
+					// Insert low surrogate
+					buffer[count] = rune(56320 + random.Intn(128))
+					count--
+					// Insert high surrogate
+					buffer[count] = ch
+				}
+			} else if ch >= 56192 && ch <= 56319 {
+				// private high surrogate, skip it
+				count++
+			} else {
+				// not one of the surrogates*
+				buffer[count] = ch
+			}
+		} else {
+			count++
+		}
+	}
+	return string(buffer), nil
+}

+ 240 - 0
vendor/github.com/Masterminds/goutils/stringutils.go

@@ -0,0 +1,240 @@
+/*
+Copyright 2014 Alexander Okoli
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package goutils
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"unicode"
+)
+
+// Typically returned by functions where a searched item cannot be found
+const INDEX_NOT_FOUND = -1
+
+/*
+Abbreviate abbreviates a string using ellipses. This will turn  the string "Now is the time for all good men" into "Now is the time for..."
+
+Specifically, the algorithm is as follows:
+
+    - If str is less than maxWidth characters long, return it.
+    - Else abbreviate it to (str[0:maxWidth - 3] + "...").
+    - If maxWidth is less than 4, return an illegal argument error.
+    - In no case will it return a string of length greater than maxWidth.
+
+Parameters:
+    str -  the string to check
+    maxWidth - maximum length of result string, must be at least 4
+
+Returns:
+    string - abbreviated string
+    error - if the width is too small
+*/
+func Abbreviate(str string, maxWidth int) (string, error) {
+	return AbbreviateFull(str, 0, maxWidth)
+}
+
+/*
+AbbreviateFull abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "...is the time for..."
+This function works like Abbreviate(string, int), but allows you to specify a "left edge" offset. Note that this left edge is not
+necessarily going to be the leftmost character in the result, or the first character following the ellipses, but it will appear
+somewhere in the result.
+In no case will it return a string of length greater than maxWidth.
+
+Parameters:
+    str - the string to check
+    offset - left edge of source string
+    maxWidth - maximum length of result string, must be at least 4
+
+Returns:
+    string - abbreviated string
+    error - if the width is too small
+*/
+func AbbreviateFull(str string, offset int, maxWidth int) (string, error) {
+	if str == "" {
+		return "", nil
+	}
+	if maxWidth < 4 {
+		err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width is 4")
+		return "", err
+	}
+	if len(str) <= maxWidth {
+		return str, nil
+	}
+	if offset > len(str) {
+		offset = len(str)
+	}
+	if len(str)-offset < (maxWidth - 3) { // 15 - 5 < 10 - 3 =  10 < 7
+		offset = len(str) - (maxWidth - 3)
+	}
+	abrevMarker := "..."
+	if offset <= 4 {
+		return str[0:maxWidth-3] + abrevMarker, nil // str.substring(0, maxWidth - 3) + abrevMarker;
+	}
+	if maxWidth < 7 {
+		err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width with offset is 7")
+		return "", err
+	}
+	if (offset + maxWidth - 3) < len(str) { // 5 + (10-3) < 15 = 12 < 15
+		abrevStr, _ := Abbreviate(str[offset:len(str)], (maxWidth - 3))
+		return abrevMarker + abrevStr, nil // abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
+	}
+	return abrevMarker + str[(len(str)-(maxWidth-3)):len(str)], nil // abrevMarker + str.substring(str.length() - (maxWidth - 3));
+}
+
+/*
+DeleteWhiteSpace deletes all whitespaces from a string as defined by unicode.IsSpace(rune).
+It returns the string without whitespaces.
+
+Parameter:
+    str - the string to delete whitespace from, may be nil
+
+Returns:
+    the string without whitespaces
+*/
+func DeleteWhiteSpace(str string) string {
+	if str == "" {
+		return str
+	}
+	sz := len(str)
+	var chs bytes.Buffer
+	count := 0
+	for i := 0; i < sz; i++ {
+		ch := rune(str[i])
+		if !unicode.IsSpace(ch) {
+			chs.WriteRune(ch)
+			count++
+		}
+	}
+	if count == sz {
+		return str
+	}
+	return chs.String()
+}
+
+/*
+IndexOfDifference compares two strings, and returns the index at which the strings begin to differ.
+
+Parameters:
+    str1 - the first string
+    str2 - the second string
+
+Returns:
+    the index where str1 and str2 begin to differ; -1 if they are equal
+*/
+func IndexOfDifference(str1 string, str2 string) int {
+	if str1 == str2 {
+		return INDEX_NOT_FOUND
+	}
+	if IsEmpty(str1) || IsEmpty(str2) {
+		return 0
+	}
+	var i int
+	for i = 0; i < len(str1) && i < len(str2); i++ {
+		if rune(str1[i]) != rune(str2[i]) {
+			break
+		}
+	}
+	if i < len(str2) || i < len(str1) {
+		return i
+	}
+	return INDEX_NOT_FOUND
+}
+
+/*
+IsBlank checks if a string is whitespace or empty (""). Observe the following behavior:
+
+    goutils.IsBlank("")        = true
+    goutils.IsBlank(" ")       = true
+    goutils.IsBlank("bob")     = false
+    goutils.IsBlank("  bob  ") = false
+
+Parameter:
+    str - the string to check
+
+Returns:
+    true - if the string is whitespace or empty ("")
+*/
+func IsBlank(str string) bool {
+	strLen := len(str)
+	if str == "" || strLen == 0 {
+		return true
+	}
+	for i := 0; i < strLen; i++ {
+		if unicode.IsSpace(rune(str[i])) == false {
+			return false
+		}
+	}
+	return true
+}
+
+/*
+IndexOf returns the index of the first instance of sub in str, with the search beginning from the
+index start point specified. -1 is returned if sub is not present in str.
+
+An empty string ("") will return -1 (INDEX_NOT_FOUND). A negative start position is treated as zero.
+A start position greater than the string length returns -1.
+
+Parameters:
+    str - the string to check
+    sub - the substring to find
+    start - the start position; negative treated as zero
+
+Returns:
+    the first index where the sub string was found  (always >= start)
+*/
+func IndexOf(str string, sub string, start int) int {
+
+	if start < 0 {
+		start = 0
+	}
+
+	if len(str) < start {
+		return INDEX_NOT_FOUND
+	}
+
+	if IsEmpty(str) || IsEmpty(sub) {
+		return INDEX_NOT_FOUND
+	}
+
+	partialIndex := strings.Index(str[start:len(str)], sub)
+	if partialIndex == -1 {
+		return INDEX_NOT_FOUND
+	}
+	return partialIndex + start
+}
+
+// IsEmpty checks if a string is empty (""). Returns true if empty, and false otherwise.
+func IsEmpty(str string) bool {
+	return len(str) == 0
+}
+
+// Returns either the passed in string, or if the string is empty, the value of defaultStr.
+func DefaultString(str string, defaultStr string) string {
+	if IsEmpty(str) {
+		return defaultStr
+	}
+	return str
+}
+
+// Returns either the passed in string, or if the string is whitespace, empty (""), the value of defaultStr.
+func DefaultIfBlank(str string, defaultStr string) string {
+	if IsBlank(str) {
+		return defaultStr
+	}
+	return str
+}

+ 357 - 0
vendor/github.com/Masterminds/goutils/wordutils.go

@@ -0,0 +1,357 @@
+/*
+Copyright 2014 Alexander Okoli
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+Package goutils provides utility functions to manipulate strings in various ways.
+The code snippets below show examples of how to use goutils. Some functions return
+errors while others do not, so usage would vary as a result.
+
+Example:
+
+    package main
+
+    import (
+        "fmt"
+        "github.com/aokoli/goutils"
+    )
+
+    func main() {
+
+        // EXAMPLE 1: A goutils function which returns no errors
+        fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
+
+
+
+        // EXAMPLE 2: A goutils function which returns an error
+        rand1, err1 := goutils.Random (-1, 0, 0, true, true)
+
+        if err1 != nil {
+            fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
+        } else {
+            fmt.Println(rand1)
+        }
+    }
+*/
+package goutils
+
+import (
+	"bytes"
+	"strings"
+	"unicode"
+)
+
+// VERSION indicates the current version of goutils
+const VERSION = "1.0.0"
+
+/*
+Wrap wraps a single line of text, identifying words by ' '.
+New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped.
+Leading spaces on a new line are stripped. Trailing spaces are not stripped.
+
+Parameters:
+    str - the string to be word wrapped
+    wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
+
+Returns:
+    a line with newlines inserted
+*/
+func Wrap(str string, wrapLength int) string {
+	return WrapCustom(str, wrapLength, "", false)
+}
+
+/*
+WrapCustom wraps a single line of text, identifying words by ' '.
+Leading spaces on a new line are stripped. Trailing spaces are not stripped.
+
+Parameters:
+    str - the string to be word wrapped
+    wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
+    newLineStr - the string to insert for a new line, "" uses '\n'
+    wrapLongWords - true if long words (such as URLs) should be wrapped
+
+Returns:
+    a line with newlines inserted
+*/
+func WrapCustom(str string, wrapLength int, newLineStr string, wrapLongWords bool) string {
+
+	if str == "" {
+		return ""
+	}
+	if newLineStr == "" {
+		newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons
+	}
+	if wrapLength < 1 {
+		wrapLength = 1
+	}
+
+	inputLineLength := len(str)
+	offset := 0
+
+	var wrappedLine bytes.Buffer
+
+	for inputLineLength-offset > wrapLength {
+
+		if rune(str[offset]) == ' ' {
+			offset++
+			continue
+		}
+
+		end := wrapLength + offset + 1
+		spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset
+
+		if spaceToWrapAt >= offset {
+			// normal word (not longer than wrapLength)
+			wrappedLine.WriteString(str[offset:spaceToWrapAt])
+			wrappedLine.WriteString(newLineStr)
+			offset = spaceToWrapAt + 1
+
+		} else {
+			// long word or URL
+			if wrapLongWords {
+				end := wrapLength + offset
+				// long words are wrapped one line at a time
+				wrappedLine.WriteString(str[offset:end])
+				wrappedLine.WriteString(newLineStr)
+				offset += wrapLength
+			} else {
+				// long words aren't wrapped, just extended beyond limit
+				end := wrapLength + offset
+				index := strings.IndexRune(str[end:len(str)], ' ')
+				if index == -1 {
+					wrappedLine.WriteString(str[offset:len(str)])
+					offset = inputLineLength
+				} else {
+					spaceToWrapAt = index + end
+					wrappedLine.WriteString(str[offset:spaceToWrapAt])
+					wrappedLine.WriteString(newLineStr)
+					offset = spaceToWrapAt + 1
+				}
+			}
+		}
+	}
+
+	wrappedLine.WriteString(str[offset:len(str)])
+
+	return wrappedLine.String()
+
+}
+
+/*
+Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed.
+To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune).
+The delimiters represent a set of characters understood to separate words. The first string character
+and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "".
+Capitalization uses the Unicode title case, normally equivalent to upper case.
+
+Parameters:
+    str - the string to capitalize
+    delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
+
+Returns:
+    capitalized string
+*/
+func Capitalize(str string, delimiters ...rune) string {
+
+	var delimLen int
+
+	if delimiters == nil {
+		delimLen = -1
+	} else {
+		delimLen = len(delimiters)
+	}
+
+	if str == "" || delimLen == 0 {
+		return str
+	}
+
+	buffer := []rune(str)
+	capitalizeNext := true
+	for i := 0; i < len(buffer); i++ {
+		ch := buffer[i]
+		if isDelimiter(ch, delimiters...) {
+			capitalizeNext = true
+		} else if capitalizeNext {
+			buffer[i] = unicode.ToTitle(ch)
+			capitalizeNext = false
+		}
+	}
+	return string(buffer)
+
+}
+
+/*
+CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a
+titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood
+to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized.
+Capitalization uses the Unicode title case, normally equivalent to upper case.
+
+Parameters:
+    str - the string to capitalize fully
+    delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
+
+Returns:
+    capitalized string
+*/
+func CapitalizeFully(str string, delimiters ...rune) string {
+
+	var delimLen int
+
+	if delimiters == nil {
+		delimLen = -1
+	} else {
+		delimLen = len(delimiters)
+	}
+
+	if str == "" || delimLen == 0 {
+		return str
+	}
+	str = strings.ToLower(str)
+	return Capitalize(str, delimiters...)
+}
+
+/*
+Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed.
+The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter
+character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char).
+
+Parameters:
+    str - the string to uncapitalize fully
+    delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
+
+Returns:
+    uncapitalized string
+*/
+func Uncapitalize(str string, delimiters ...rune) string {
+
+	var delimLen int
+
+	if delimiters == nil {
+		delimLen = -1
+	} else {
+		delimLen = len(delimiters)
+	}
+
+	if str == "" || delimLen == 0 {
+		return str
+	}
+
+	buffer := []rune(str)
+	uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char.
+	for i := 0; i < len(buffer); i++ {
+		ch := buffer[i]
+		if isDelimiter(ch, delimiters...) {
+			uncapitalizeNext = true
+		} else if uncapitalizeNext {
+			buffer[i] = unicode.ToLower(ch)
+			uncapitalizeNext = false
+		}
+	}
+	return string(buffer)
+}
+
+/*
+SwapCase swaps the case of a string using a word based algorithm.
+
+Conversion algorithm:
+
+    Upper case character converts to Lower case
+    Title case character converts to Lower case
+    Lower case character after Whitespace or at start converts to Title case
+    Other Lower case character converts to Upper case
+    Whitespace is defined by unicode.IsSpace(char).
+
+Parameters:
+    str - the string to swap case
+
+Returns:
+    the changed string
+*/
+func SwapCase(str string) string {
+	if str == "" {
+		return str
+	}
+	buffer := []rune(str)
+
+	whitespace := true
+
+	for i := 0; i < len(buffer); i++ {
+		ch := buffer[i]
+		if unicode.IsUpper(ch) {
+			buffer[i] = unicode.ToLower(ch)
+			whitespace = false
+		} else if unicode.IsTitle(ch) {
+			buffer[i] = unicode.ToLower(ch)
+			whitespace = false
+		} else if unicode.IsLower(ch) {
+			if whitespace {
+				buffer[i] = unicode.ToTitle(ch)
+				whitespace = false
+			} else {
+				buffer[i] = unicode.ToUpper(ch)
+			}
+		} else {
+			whitespace = unicode.IsSpace(ch)
+		}
+	}
+	return string(buffer)
+}
+
+/*
+Initials extracts the initial letters from each word in the string. The first letter of the string and all first
+letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters
+parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string.
+
+Parameters:
+    str - the string to get initials from
+    delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter
+Returns:
+    string of initial letters
+*/
+func Initials(str string, delimiters ...rune) string {
+	if str == "" {
+		return str
+	}
+	if delimiters != nil && len(delimiters) == 0 {
+		return ""
+	}
+	strLen := len(str)
+	var buf bytes.Buffer
+	lastWasGap := true
+	for i := 0; i < strLen; i++ {
+		ch := rune(str[i])
+
+		if isDelimiter(ch, delimiters...) {
+			lastWasGap = true
+		} else if lastWasGap {
+			buf.WriteRune(ch)
+			lastWasGap = false
+		}
+	}
+	return buf.String()
+}
+
+// private function (lower case func name)
+func isDelimiter(ch rune, delimiters ...rune) bool {
+	if delimiters == nil {
+		return unicode.IsSpace(ch)
+	}
+	for _, delimiter := range delimiters {
+		if ch == delimiter {
+			return true
+		}
+	}
+	return false
+}

+ 29 - 0
vendor/github.com/Masterminds/semver/.travis.yml

@@ -0,0 +1,29 @@
+language: go
+
+go:
+  - 1.6.x
+  - 1.7.x
+  - 1.8.x
+  - 1.9.x
+  - 1.10.x
+  - 1.11.x
+  - 1.12.x
+  - tip
+
+# Setting sudo access to false will let Travis CI use containers rather than
+# VMs to run the tests. For more details see:
+# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
+# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
+sudo: false
+
+script:
+  - make setup
+  - make test
+
+notifications:
+  webhooks:
+    urls:
+      - https://webhooks.gitter.im/e/06e3328629952dabe3e0
+    on_success: change  # options: [always|never|change] default: always
+    on_failure: always  # options: [always|never|change] default: always
+    on_start: never     # options: [always|never|change] default: always

+ 109 - 0
vendor/github.com/Masterminds/semver/CHANGELOG.md

@@ -0,0 +1,109 @@
+# 1.5.0 (2019-09-11)
+
+## Added
+
+- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c)
+
+## Changed
+
+- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil)
+- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil)
+- #72: Adding docs comment pointing to vert for a cli
+- #71: Update the docs on pre-release comparator handling
+- #89: Test with new go versions (thanks @thedevsaddam)
+- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll)
+
+## Fixed
+
+- #78: Fix unchecked error in example code (thanks @ravron)
+- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
+- #97: Fixed copyright file for proper display on GitHub
+- #107: Fix handling prerelease when sorting alphanum and num 
+- #109: Fixed where Validate sometimes returns wrong message on error
+
+# 1.4.2 (2018-04-10)
+
+## Changed
+- #72: Updated the docs to point to vert for a console appliaction
+- #71: Update the docs on pre-release comparator handling
+
+## Fixed
+- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
+
+# 1.4.1 (2018-04-02)
+
+## Fixed
+- Fixed #64: Fix pre-release precedence issue (thanks @uudashr)
+
+# 1.4.0 (2017-10-04)
+
+## Changed
+- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill)
+
+# 1.3.1 (2017-07-10)
+
+## Fixed
+- Fixed #57: number comparisons in prerelease sometimes inaccurate
+
+# 1.3.0 (2017-05-02)
+
+## Added
+- #45: Added json (un)marshaling support (thanks @mh-cbon)
+- Stability marker. See https://masterminds.github.io/stability/
+
+## Fixed
+- #51: Fix handling of single digit tilde constraint (thanks @dgodd)
+
+## Changed
+- #55: The godoc icon moved from png to svg
+
+# 1.2.3 (2017-04-03)
+
+## Fixed
+- #46: Fixed 0.x.x and 0.0.x in constraints being treated as *
+
+# Release 1.2.2 (2016-12-13)
+
+## Fixed
+- #34: Fixed issue where hyphen range was not working with pre-release parsing.
+
+# Release 1.2.1 (2016-11-28)
+
+## Fixed
+- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha"
+  properly.
+
+# Release 1.2.0 (2016-11-04)
+
+## Added
+- #20: Added MustParse function for versions (thanks @adamreese)
+- #15: Added increment methods on versions (thanks @mh-cbon)
+
+## Fixed
+- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and
+  might not satisfy the intended compatibility. The change here ignores pre-releases
+  on constraint checks (e.g., ~ or ^) when a pre-release is not part of the
+  constraint. For example, `^1.2.3` will ignore pre-releases while
+  `^1.2.3-alpha` will include them.
+
+# Release 1.1.1 (2016-06-30)
+
+## Changed
+- Issue #9: Speed up version comparison performance (thanks @sdboyer)
+- Issue #8: Added benchmarks (thanks @sdboyer)
+- Updated Go Report Card URL to new location
+- Updated Readme to add code snippet formatting (thanks @mh-cbon)
+- Updating tagging to v[SemVer] structure for compatibility with other tools.
+
+# Release 1.1.0 (2016-03-11)
+
+- Issue #2: Implemented validation to provide reasons a versions failed a
+  constraint.
+
+# Release 1.0.1 (2015-12-31)
+
+- Fixed #1: * constraint failing on valid versions.
+
+# Release 1.0.0 (2015-10-20)
+
+- Initial release

+ 19 - 0
vendor/github.com/Masterminds/semver/LICENSE.txt

@@ -0,0 +1,19 @@
+Copyright (C) 2014-2019, Matt Butcher and Matt Farina
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 36 - 0
vendor/github.com/Masterminds/semver/Makefile

@@ -0,0 +1,36 @@
+.PHONY: setup
+setup:
+	go get -u gopkg.in/alecthomas/gometalinter.v1
+	gometalinter.v1 --install
+
+.PHONY: test
+test: validate lint
+	@echo "==> Running tests"
+	go test -v
+
+.PHONY: validate
+validate:
+	@echo "==> Running static validations"
+	@gometalinter.v1 \
+	  --disable-all \
+	  --enable deadcode \
+	  --severity deadcode:error \
+	  --enable gofmt \
+	  --enable gosimple \
+	  --enable ineffassign \
+	  --enable misspell \
+	  --enable vet \
+	  --tests \
+	  --vendor \
+	  --deadline 60s \
+	  ./... || exit_code=1
+
+.PHONY: lint
+lint:
+	@echo "==> Running linters"
+	@gometalinter.v1 \
+	  --disable-all \
+	  --enable golint \
+	  --vendor \
+	  --deadline 60s \
+	  ./... || :

+ 194 - 0
vendor/github.com/Masterminds/semver/README.md

@@ -0,0 +1,194 @@
+# SemVer
+
+The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
+
+* Parse semantic versions
+* Sort semantic versions
+* Check if a semantic version fits within a set of constraints
+* Optionally work with a `v` prefix
+
+[![Stability:
+Active](https://masterminds.github.io/stability/active.svg)](https://masterminds.github.io/stability/active.html)
+[![Build Status](https://travis-ci.org/Masterminds/semver.svg)](https://travis-ci.org/Masterminds/semver) [![Build status](https://ci.appveyor.com/api/projects/status/jfk66lib7hb985k8/branch/master?svg=true&passingText=windows%20build%20passing&failingText=windows%20build%20failing)](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [![GoDoc](https://godoc.org/github.com/Masterminds/semver?status.svg)](https://godoc.org/github.com/Masterminds/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver)
+
+If you are looking for a command line tool for version comparisons please see
+[vert](https://github.com/Masterminds/vert) which uses this library.
+
+## Parsing Semantic Versions
+
+To parse a semantic version use the `NewVersion` function. For example,
+
+```go
+    v, err := semver.NewVersion("1.2.3-beta.1+build345")
+```
+
+If there is an error the version wasn't parseable. The version object has methods
+to get the parts of the version, compare it to other versions, convert the
+version back into a string, and get the original string. For more details
+please see the [documentation](https://godoc.org/github.com/Masterminds/semver).
+
+## Sorting Semantic Versions
+
+A set of versions can be sorted using the [`sort`](https://golang.org/pkg/sort/)
+package from the standard library. For example,
+
+```go
+    raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
+    vs := make([]*semver.Version, len(raw))
+	for i, r := range raw {
+		v, err := semver.NewVersion(r)
+		if err != nil {
+			t.Errorf("Error parsing version: %s", err)
+		}
+
+		vs[i] = v
+	}
+
+	sort.Sort(semver.Collection(vs))
+```
+
+## Checking Version Constraints
+
+Checking a version against version constraints is one of the most featureful
+parts of the package.
+
+```go
+    c, err := semver.NewConstraint(">= 1.2.3")
+    if err != nil {
+        // Handle constraint not being parseable.
+    }
+
+    v, _ := semver.NewVersion("1.3")
+    if err != nil {
+        // Handle version not being parseable.
+    }
+    // Check if the version meets the constraints. The a variable will be true.
+    a := c.Check(v)
+```
+
+## Basic Comparisons
+
+There are two elements to the comparisons. First, a comparison string is a list
+of comma separated and comparisons. These are then separated by || separated or
+comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
+comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
+greater than or equal to 4.2.3.
+
+The basic comparisons are:
+
+* `=`: equal (aliased to no operator)
+* `!=`: not equal
+* `>`: greater than
+* `<`: less than
+* `>=`: greater than or equal to
+* `<=`: less than or equal to
+
+## Working With Pre-release Versions
+
+Pre-releases, for those not familiar with them, are used for software releases
+prior to stable or generally available releases. Examples of pre-releases include
+development, alpha, beta, and release candidate releases. A pre-release may be
+a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
+order of precidence, pre-releases come before their associated releases. In this
+example `1.2.3-beta.1 < 1.2.3`.
+
+According to the Semantic Version specification pre-releases may not be
+API compliant with their release counterpart. It says,
+
+> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
+
+SemVer comparisons without a pre-release comparator will skip pre-release versions.
+For example, `>=1.2.3` will skip pre-releases when looking at a list of releases
+while `>=1.2.3-0` will evaluate and find pre-releases.
+
+The reason for the `0` as a pre-release version in the example comparison is
+because pre-releases can only contain ASCII alphanumerics and hyphens (along with
+`.` separators), per the spec. Sorting happens in ASCII sort order, again per the spec. The lowest character is a `0` in ASCII sort order (see an [ASCII Table](http://www.asciitable.com/))
+
+Understanding ASCII sort ordering is important because A-Z comes before a-z. That
+means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
+sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
+the spec specifies.
+
+## Hyphen Range Comparisons
+
+There are multiple methods to handle ranges and the first is hyphens ranges.
+These look like:
+
+* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
+* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
+
+## Wildcards In Comparisons
+
+The `x`, `X`, and `*` characters can be used as a wildcard character. This works
+for all comparison operators. When used on the `=` operator it falls
+back to the pack level comparison (see tilde below). For example,
+
+* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
+* `>= 1.2.x` is equivalent to `>= 1.2.0`
+* `<= 2.x` is equivalent to `< 3`
+* `*` is equivalent to `>= 0.0.0`
+
+## Tilde Range Comparisons (Patch)
+
+The tilde (`~`) comparison operator is for patch level ranges when a minor
+version is specified and major level changes when the minor number is missing.
+For example,
+
+* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
+* `~1` is equivalent to `>= 1, < 2`
+* `~2.3` is equivalent to `>= 2.3, < 2.4`
+* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
+* `~1.x` is equivalent to `>= 1, < 2`
+
+## Caret Range Comparisons (Major)
+
+The caret (`^`) comparison operator is for major level changes. This is useful
+when comparisons of API versions as a major change is API breaking. For example,
+
+* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
+* `^0.0.1` is equivalent to `>= 0.0.1, < 1.0.0`
+* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
+* `^2.3` is equivalent to `>= 2.3, < 3`
+* `^2.x` is equivalent to `>= 2.0.0, < 3`
+
+# Validation
+
+In addition to testing a version against a constraint, a version can be validated
+against a constraint. When validation fails a slice of errors containing why a
+version didn't meet the constraint is returned. For example,
+
+```go
+    c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
+    if err != nil {
+        // Handle constraint not being parseable.
+    }
+
+    v, _ := semver.NewVersion("1.3")
+    if err != nil {
+        // Handle version not being parseable.
+    }
+
+    // Validate a version against a constraint.
+    a, msgs := c.Validate(v)
+    // a is false
+    for _, m := range msgs {
+        fmt.Println(m)
+
+        // Loops over the errors which would read
+        // "1.3 is greater than 1.2.3"
+        // "1.3 is less than 1.4"
+    }
+```
+
+# Fuzzing
+
+ [dvyukov/go-fuzz](https://github.com/dvyukov/go-fuzz) is used for fuzzing.
+
+1. `go-fuzz-build`
+2. `go-fuzz -workdir=fuzz`
+
+# Contribute
+
+If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
+or [create a pull request](https://github.com/Masterminds/semver/pulls).

+ 44 - 0
vendor/github.com/Masterminds/semver/appveyor.yml

@@ -0,0 +1,44 @@
+version: build-{build}.{branch}
+
+clone_folder: C:\gopath\src\github.com\Masterminds\semver
+shallow_clone: true
+
+environment:
+  GOPATH: C:\gopath
+
+platform:
+  - x64
+
+install:
+  - go version
+  - go env
+  - go get -u gopkg.in/alecthomas/gometalinter.v1
+  - set PATH=%PATH%;%GOPATH%\bin
+  - gometalinter.v1.exe --install
+
+build_script:
+  - go install -v ./...
+
+test_script:
+  - "gometalinter.v1 \
+    --disable-all \
+    --enable deadcode \
+    --severity deadcode:error \
+    --enable gofmt \
+    --enable gosimple \
+    --enable ineffassign \
+    --enable misspell \
+    --enable vet \
+    --tests \
+    --vendor \
+    --deadline 60s \
+    ./... || exit_code=1"
+  - "gometalinter.v1 \
+    --disable-all \
+    --enable golint \
+    --vendor \
+    --deadline 60s \
+    ./... || :"
+  - go test -v
+
+deploy: off

+ 24 - 0
vendor/github.com/Masterminds/semver/collection.go

@@ -0,0 +1,24 @@
+package semver
+
+// Collection is a collection of Version instances and implements the sort
+// interface. See the sort package for more details.
+// https://golang.org/pkg/sort/
+type Collection []*Version
+
+// Len returns the length of a collection. The number of Version instances
+// on the slice.
+func (c Collection) Len() int {
+	return len(c)
+}
+
+// Less is needed for the sort interface to compare two Version objects on the
+// slice. If checks if one is less than the other.
+func (c Collection) Less(i, j int) bool {
+	return c[i].LessThan(c[j])
+}
+
+// Swap is needed for the sort interface to replace the Version objects
+// at two different positions in the slice.
+func (c Collection) Swap(i, j int) {
+	c[i], c[j] = c[j], c[i]
+}

+ 423 - 0
vendor/github.com/Masterminds/semver/constraints.go

@@ -0,0 +1,423 @@
+package semver
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+// Constraints is one or more constraint that a semantic version can be
+// checked against.
+type Constraints struct {
+	constraints [][]*constraint
+}
+
+// NewConstraint returns a Constraints instance that a Version instance can
+// be checked against. If there is a parse error it will be returned.
+func NewConstraint(c string) (*Constraints, error) {
+
+	// Rewrite - ranges into a comparison operation.
+	c = rewriteRange(c)
+
+	ors := strings.Split(c, "||")
+	or := make([][]*constraint, len(ors))
+	for k, v := range ors {
+		cs := strings.Split(v, ",")
+		result := make([]*constraint, len(cs))
+		for i, s := range cs {
+			pc, err := parseConstraint(s)
+			if err != nil {
+				return nil, err
+			}
+
+			result[i] = pc
+		}
+		or[k] = result
+	}
+
+	o := &Constraints{constraints: or}
+	return o, nil
+}
+
+// Check tests if a version satisfies the constraints.
+func (cs Constraints) Check(v *Version) bool {
+	// loop over the ORs and check the inner ANDs
+	for _, o := range cs.constraints {
+		joy := true
+		for _, c := range o {
+			if !c.check(v) {
+				joy = false
+				break
+			}
+		}
+
+		if joy {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Validate checks if a version satisfies a constraint. If not a slice of
+// reasons for the failure are returned in addition to a bool.
+func (cs Constraints) Validate(v *Version) (bool, []error) {
+	// loop over the ORs and check the inner ANDs
+	var e []error
+
+	// Capture the prerelease message only once. When it happens the first time
+	// this var is marked
+	var prerelesase bool
+	for _, o := range cs.constraints {
+		joy := true
+		for _, c := range o {
+			// Before running the check handle the case there the version is
+			// a prerelease and the check is not searching for prereleases.
+			if c.con.pre == "" && v.pre != "" {
+				if !prerelesase {
+					em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
+					e = append(e, em)
+					prerelesase = true
+				}
+				joy = false
+
+			} else {
+
+				if !c.check(v) {
+					em := fmt.Errorf(c.msg, v, c.orig)
+					e = append(e, em)
+					joy = false
+				}
+			}
+		}
+
+		if joy {
+			return true, []error{}
+		}
+	}
+
+	return false, e
+}
+
+var constraintOps map[string]cfunc
+var constraintMsg map[string]string
+var constraintRegex *regexp.Regexp
+
+func init() {
+	constraintOps = map[string]cfunc{
+		"":   constraintTildeOrEqual,
+		"=":  constraintTildeOrEqual,
+		"!=": constraintNotEqual,
+		">":  constraintGreaterThan,
+		"<":  constraintLessThan,
+		">=": constraintGreaterThanEqual,
+		"=>": constraintGreaterThanEqual,
+		"<=": constraintLessThanEqual,
+		"=<": constraintLessThanEqual,
+		"~":  constraintTilde,
+		"~>": constraintTilde,
+		"^":  constraintCaret,
+	}
+
+	constraintMsg = map[string]string{
+		"":   "%s is not equal to %s",
+		"=":  "%s is not equal to %s",
+		"!=": "%s is equal to %s",
+		">":  "%s is less than or equal to %s",
+		"<":  "%s is greater than or equal to %s",
+		">=": "%s is less than %s",
+		"=>": "%s is less than %s",
+		"<=": "%s is greater than %s",
+		"=<": "%s is greater than %s",
+		"~":  "%s does not have same major and minor version as %s",
+		"~>": "%s does not have same major and minor version as %s",
+		"^":  "%s does not have same major version as %s",
+	}
+
+	ops := make([]string, 0, len(constraintOps))
+	for k := range constraintOps {
+		ops = append(ops, regexp.QuoteMeta(k))
+	}
+
+	constraintRegex = regexp.MustCompile(fmt.Sprintf(
+		`^\s*(%s)\s*(%s)\s*$`,
+		strings.Join(ops, "|"),
+		cvRegex))
+
+	constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
+		`\s*(%s)\s+-\s+(%s)\s*`,
+		cvRegex, cvRegex))
+}
+
+// An individual constraint
+type constraint struct {
+	// The callback function for the restraint. It performs the logic for
+	// the constraint.
+	function cfunc
+
+	msg string
+
+	// The version used in the constraint check. For example, if a constraint
+	// is '<= 2.0.0' the con a version instance representing 2.0.0.
+	con *Version
+
+	// The original parsed version (e.g., 4.x from != 4.x)
+	orig string
+
+	// When an x is used as part of the version (e.g., 1.x)
+	minorDirty bool
+	dirty      bool
+	patchDirty bool
+}
+
+// Check if a version meets the constraint
+func (c *constraint) check(v *Version) bool {
+	return c.function(v, c)
+}
+
+type cfunc func(v *Version, c *constraint) bool
+
+func parseConstraint(c string) (*constraint, error) {
+	m := constraintRegex.FindStringSubmatch(c)
+	if m == nil {
+		return nil, fmt.Errorf("improper constraint: %s", c)
+	}
+
+	ver := m[2]
+	orig := ver
+	minorDirty := false
+	patchDirty := false
+	dirty := false
+	if isX(m[3]) {
+		ver = "0.0.0"
+		dirty = true
+	} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
+		minorDirty = true
+		dirty = true
+		ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
+	} else if isX(strings.TrimPrefix(m[5], ".")) {
+		dirty = true
+		patchDirty = true
+		ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
+	}
+
+	con, err := NewVersion(ver)
+	if err != nil {
+
+		// The constraintRegex should catch any regex parsing errors. So,
+		// we should never get here.
+		return nil, errors.New("constraint Parser Error")
+	}
+
+	cs := &constraint{
+		function:   constraintOps[m[1]],
+		msg:        constraintMsg[m[1]],
+		con:        con,
+		orig:       orig,
+		minorDirty: minorDirty,
+		patchDirty: patchDirty,
+		dirty:      dirty,
+	}
+	return cs, nil
+}
+
+// Constraint functions
+func constraintNotEqual(v *Version, c *constraint) bool {
+	if c.dirty {
+
+		// If there is a pre-release on the version but the constraint isn't looking
+		// for them assume that pre-releases are not compatible. See issue 21 for
+		// more details.
+		if v.Prerelease() != "" && c.con.Prerelease() == "" {
+			return false
+		}
+
+		if c.con.Major() != v.Major() {
+			return true
+		}
+		if c.con.Minor() != v.Minor() && !c.minorDirty {
+			return true
+		} else if c.minorDirty {
+			return false
+		}
+
+		return false
+	}
+
+	return !v.Equal(c.con)
+}
+
+func constraintGreaterThan(v *Version, c *constraint) bool {
+
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	return v.Compare(c.con) == 1
+}
+
+func constraintLessThan(v *Version, c *constraint) bool {
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	if !c.dirty {
+		return v.Compare(c.con) < 0
+	}
+
+	if v.Major() > c.con.Major() {
+		return false
+	} else if v.Minor() > c.con.Minor() && !c.minorDirty {
+		return false
+	}
+
+	return true
+}
+
+func constraintGreaterThanEqual(v *Version, c *constraint) bool {
+
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	return v.Compare(c.con) >= 0
+}
+
+func constraintLessThanEqual(v *Version, c *constraint) bool {
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	if !c.dirty {
+		return v.Compare(c.con) <= 0
+	}
+
+	if v.Major() > c.con.Major() {
+		return false
+	} else if v.Minor() > c.con.Minor() && !c.minorDirty {
+		return false
+	}
+
+	return true
+}
+
+// ~*, ~>* --> >= 0.0.0 (any)
+// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
+// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
+// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
+// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
+// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
+func constraintTilde(v *Version, c *constraint) bool {
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	if v.LessThan(c.con) {
+		return false
+	}
+
+	// ~0.0.0 is a special case where all constraints are accepted. It's
+	// equivalent to >= 0.0.0.
+	if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 &&
+		!c.minorDirty && !c.patchDirty {
+		return true
+	}
+
+	if v.Major() != c.con.Major() {
+		return false
+	}
+
+	if v.Minor() != c.con.Minor() && !c.minorDirty {
+		return false
+	}
+
+	return true
+}
+
+// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
+// it's a straight =
+func constraintTildeOrEqual(v *Version, c *constraint) bool {
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	if c.dirty {
+		c.msg = constraintMsg["~"]
+		return constraintTilde(v, c)
+	}
+
+	return v.Equal(c.con)
+}
+
+// ^* --> (any)
+// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
+// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
+// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
+// ^1.2.3 --> >=1.2.3, <2.0.0
+// ^1.2.0 --> >=1.2.0, <2.0.0
+func constraintCaret(v *Version, c *constraint) bool {
+	// If there is a pre-release on the version but the constraint isn't looking
+	// for them assume that pre-releases are not compatible. See issue 21 for
+	// more details.
+	if v.Prerelease() != "" && c.con.Prerelease() == "" {
+		return false
+	}
+
+	if v.LessThan(c.con) {
+		return false
+	}
+
+	if v.Major() != c.con.Major() {
+		return false
+	}
+
+	return true
+}
+
+var constraintRangeRegex *regexp.Regexp
+
+const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
+	`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
+	`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
+
+func isX(x string) bool {
+	switch x {
+	case "x", "*", "X":
+		return true
+	default:
+		return false
+	}
+}
+
+func rewriteRange(i string) string {
+	m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
+	if m == nil {
+		return i
+	}
+	o := i
+	for _, v := range m {
+		t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
+		o = strings.Replace(o, v[0], t, 1)
+	}
+
+	return o
+}

+ 115 - 0
vendor/github.com/Masterminds/semver/doc.go

@@ -0,0 +1,115 @@
+/*
+Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
+
+Specifically it provides the ability to:
+
+    * Parse semantic versions
+    * Sort semantic versions
+    * Check if a semantic version fits within a set of constraints
+    * Optionally work with a `v` prefix
+
+Parsing Semantic Versions
+
+To parse a semantic version use the `NewVersion` function. For example,
+
+    v, err := semver.NewVersion("1.2.3-beta.1+build345")
+
+If there is an error the version wasn't parseable. The version object has methods
+to get the parts of the version, compare it to other versions, convert the
+version back into a string, and get the original string. For more details
+please see the documentation at https://godoc.org/github.com/Masterminds/semver.
+
+Sorting Semantic Versions
+
+A set of versions can be sorted using the `sort` package from the standard library.
+For example,
+
+    raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
+    vs := make([]*semver.Version, len(raw))
+	for i, r := range raw {
+		v, err := semver.NewVersion(r)
+		if err != nil {
+			t.Errorf("Error parsing version: %s", err)
+		}
+
+		vs[i] = v
+	}
+
+	sort.Sort(semver.Collection(vs))
+
+Checking Version Constraints
+
+Checking a version against version constraints is one of the most featureful
+parts of the package.
+
+    c, err := semver.NewConstraint(">= 1.2.3")
+    if err != nil {
+        // Handle constraint not being parseable.
+    }
+
+    v, err := semver.NewVersion("1.3")
+    if err != nil {
+        // Handle version not being parseable.
+    }
+    // Check if the version meets the constraints. The a variable will be true.
+    a := c.Check(v)
+
+Basic Comparisons
+
+There are two elements to the comparisons. First, a comparison string is a list
+of comma separated and comparisons. These are then separated by || separated or
+comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
+comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
+greater than or equal to 4.2.3.
+
+The basic comparisons are:
+
+    * `=`: equal (aliased to no operator)
+    * `!=`: not equal
+    * `>`: greater than
+    * `<`: less than
+    * `>=`: greater than or equal to
+    * `<=`: less than or equal to
+
+Hyphen Range Comparisons
+
+There are multiple methods to handle ranges and the first is hyphens ranges.
+These look like:
+
+    * `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
+    * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
+
+Wildcards In Comparisons
+
+The `x`, `X`, and `*` characters can be used as a wildcard character. This works
+for all comparison operators. When used on the `=` operator it falls
+back to the pack level comparison (see tilde below). For example,
+
+    * `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
+    * `>= 1.2.x` is equivalent to `>= 1.2.0`
+    * `<= 2.x` is equivalent to `<= 3`
+    * `*` is equivalent to `>= 0.0.0`
+
+Tilde Range Comparisons (Patch)
+
+The tilde (`~`) comparison operator is for patch level ranges when a minor
+version is specified and major level changes when the minor number is missing.
+For example,
+
+    * `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
+    * `~1` is equivalent to `>= 1, < 2`
+    * `~2.3` is equivalent to `>= 2.3, < 2.4`
+    * `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
+    * `~1.x` is equivalent to `>= 1, < 2`
+
+Caret Range Comparisons (Major)
+
+The caret (`^`) comparison operator is for major level changes. This is useful
+when comparisons of API versions as a major change is API breaking. For example,
+
+    * `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
+    * `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
+    * `^2.3` is equivalent to `>= 2.3, < 3`
+    * `^2.x` is equivalent to `>= 2.0.0, < 3`
+*/
+package semver

+ 425 - 0
vendor/github.com/Masterminds/semver/version.go

@@ -0,0 +1,425 @@
+package semver
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// The compiled version of the regex created at init() is cached here so it
+// only needs to be created once.
+var versionRegex *regexp.Regexp
+var validPrereleaseRegex *regexp.Regexp
+
+var (
+	// ErrInvalidSemVer is returned a version is found to be invalid when
+	// being parsed.
+	ErrInvalidSemVer = errors.New("Invalid Semantic Version")
+
+	// ErrInvalidMetadata is returned when the metadata is an invalid format
+	ErrInvalidMetadata = errors.New("Invalid Metadata string")
+
+	// ErrInvalidPrerelease is returned when the pre-release is an invalid format
+	ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
+)
+
+// SemVerRegex is the regular expression used to parse a semantic version.
+const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
+	`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
+	`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
+
+// ValidPrerelease is the regular expression which validates
+// both prerelease and metadata values.
+const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)$`
+
+// Version represents a single semantic version.
+type Version struct {
+	major, minor, patch int64
+	pre                 string
+	metadata            string
+	original            string
+}
+
+func init() {
+	versionRegex = regexp.MustCompile("^" + SemVerRegex + "$")
+	validPrereleaseRegex = regexp.MustCompile(ValidPrerelease)
+}
+
+// NewVersion parses a given version and returns an instance of Version or
+// an error if unable to parse the version.
+func NewVersion(v string) (*Version, error) {
+	m := versionRegex.FindStringSubmatch(v)
+	if m == nil {
+		return nil, ErrInvalidSemVer
+	}
+
+	sv := &Version{
+		metadata: m[8],
+		pre:      m[5],
+		original: v,
+	}
+
+	var temp int64
+	temp, err := strconv.ParseInt(m[1], 10, 64)
+	if err != nil {
+		return nil, fmt.Errorf("Error parsing version segment: %s", err)
+	}
+	sv.major = temp
+
+	if m[2] != "" {
+		temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("Error parsing version segment: %s", err)
+		}
+		sv.minor = temp
+	} else {
+		sv.minor = 0
+	}
+
+	if m[3] != "" {
+		temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("Error parsing version segment: %s", err)
+		}
+		sv.patch = temp
+	} else {
+		sv.patch = 0
+	}
+
+	return sv, nil
+}
+
+// MustParse parses a given version and panics on error.
+func MustParse(v string) *Version {
+	sv, err := NewVersion(v)
+	if err != nil {
+		panic(err)
+	}
+	return sv
+}
+
+// String converts a Version object to a string.
+// Note, if the original version contained a leading v this version will not.
+// See the Original() method to retrieve the original value. Semantic Versions
+// don't contain a leading v per the spec. Instead it's optional on
+// implementation.
+func (v *Version) String() string {
+	var buf bytes.Buffer
+
+	fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
+	if v.pre != "" {
+		fmt.Fprintf(&buf, "-%s", v.pre)
+	}
+	if v.metadata != "" {
+		fmt.Fprintf(&buf, "+%s", v.metadata)
+	}
+
+	return buf.String()
+}
+
+// Original returns the original value passed in to be parsed.
+func (v *Version) Original() string {
+	return v.original
+}
+
+// Major returns the major version.
+func (v *Version) Major() int64 {
+	return v.major
+}
+
+// Minor returns the minor version.
+func (v *Version) Minor() int64 {
+	return v.minor
+}
+
+// Patch returns the patch version.
+func (v *Version) Patch() int64 {
+	return v.patch
+}
+
+// Prerelease returns the pre-release version.
+func (v *Version) Prerelease() string {
+	return v.pre
+}
+
+// Metadata returns the metadata on the version.
+func (v *Version) Metadata() string {
+	return v.metadata
+}
+
+// originalVPrefix returns the original 'v' prefix if any.
+func (v *Version) originalVPrefix() string {
+
+	// Note, only lowercase v is supported as a prefix by the parser.
+	if v.original != "" && v.original[:1] == "v" {
+		return v.original[:1]
+	}
+	return ""
+}
+
+// IncPatch produces the next patch version.
+// If the current version does not have prerelease/metadata information,
+// it unsets metadata and prerelease values, increments patch number.
+// If the current version has any of prerelease or metadata information,
+// it unsets both values and keeps curent patch value
+func (v Version) IncPatch() Version {
+	vNext := v
+	// according to http://semver.org/#spec-item-9
+	// Pre-release versions have a lower precedence than the associated normal version.
+	// according to http://semver.org/#spec-item-10
+	// Build metadata SHOULD be ignored when determining version precedence.
+	if v.pre != "" {
+		vNext.metadata = ""
+		vNext.pre = ""
+	} else {
+		vNext.metadata = ""
+		vNext.pre = ""
+		vNext.patch = v.patch + 1
+	}
+	vNext.original = v.originalVPrefix() + "" + vNext.String()
+	return vNext
+}
+
+// IncMinor produces the next minor version.
+// Sets patch to 0.
+// Increments minor number.
+// Unsets metadata.
+// Unsets prerelease status.
+func (v Version) IncMinor() Version {
+	vNext := v
+	vNext.metadata = ""
+	vNext.pre = ""
+	vNext.patch = 0
+	vNext.minor = v.minor + 1
+	vNext.original = v.originalVPrefix() + "" + vNext.String()
+	return vNext
+}
+
+// IncMajor produces the next major version.
+// Sets patch to 0.
+// Sets minor to 0.
+// Increments major number.
+// Unsets metadata.
+// Unsets prerelease status.
+func (v Version) IncMajor() Version {
+	vNext := v
+	vNext.metadata = ""
+	vNext.pre = ""
+	vNext.patch = 0
+	vNext.minor = 0
+	vNext.major = v.major + 1
+	vNext.original = v.originalVPrefix() + "" + vNext.String()
+	return vNext
+}
+
+// SetPrerelease defines the prerelease value.
+// Value must not include the required 'hypen' prefix.
+func (v Version) SetPrerelease(prerelease string) (Version, error) {
+	vNext := v
+	if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) {
+		return vNext, ErrInvalidPrerelease
+	}
+	vNext.pre = prerelease
+	vNext.original = v.originalVPrefix() + "" + vNext.String()
+	return vNext, nil
+}
+
+// SetMetadata defines metadata value.
+// Value must not include the required 'plus' prefix.
+func (v Version) SetMetadata(metadata string) (Version, error) {
+	vNext := v
+	if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) {
+		return vNext, ErrInvalidMetadata
+	}
+	vNext.metadata = metadata
+	vNext.original = v.originalVPrefix() + "" + vNext.String()
+	return vNext, nil
+}
+
+// LessThan tests if one version is less than another one.
+func (v *Version) LessThan(o *Version) bool {
+	return v.Compare(o) < 0
+}
+
+// GreaterThan tests if one version is greater than another one.
+func (v *Version) GreaterThan(o *Version) bool {
+	return v.Compare(o) > 0
+}
+
+// Equal tests if two versions are equal to each other.
+// Note, versions can be equal with different metadata since metadata
+// is not considered part of the comparable version.
+func (v *Version) Equal(o *Version) bool {
+	return v.Compare(o) == 0
+}
+
+// Compare compares this version to another one. It returns -1, 0, or 1 if
+// the version smaller, equal, or larger than the other version.
+//
+// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
+// lower than the version without a prerelease.
+func (v *Version) Compare(o *Version) int {
+	// Compare the major, minor, and patch version for differences. If a
+	// difference is found return the comparison.
+	if d := compareSegment(v.Major(), o.Major()); d != 0 {
+		return d
+	}
+	if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
+		return d
+	}
+	if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
+		return d
+	}
+
+	// At this point the major, minor, and patch versions are the same.
+	ps := v.pre
+	po := o.Prerelease()
+
+	if ps == "" && po == "" {
+		return 0
+	}
+	if ps == "" {
+		return 1
+	}
+	if po == "" {
+		return -1
+	}
+
+	return comparePrerelease(ps, po)
+}
+
+// UnmarshalJSON implements JSON.Unmarshaler interface.
+func (v *Version) UnmarshalJSON(b []byte) error {
+	var s string
+	if err := json.Unmarshal(b, &s); err != nil {
+		return err
+	}
+	temp, err := NewVersion(s)
+	if err != nil {
+		return err
+	}
+	v.major = temp.major
+	v.minor = temp.minor
+	v.patch = temp.patch
+	v.pre = temp.pre
+	v.metadata = temp.metadata
+	v.original = temp.original
+	temp = nil
+	return nil
+}
+
+// MarshalJSON implements JSON.Marshaler interface.
+func (v *Version) MarshalJSON() ([]byte, error) {
+	return json.Marshal(v.String())
+}
+
+func compareSegment(v, o int64) int {
+	if v < o {
+		return -1
+	}
+	if v > o {
+		return 1
+	}
+
+	return 0
+}
+
+func comparePrerelease(v, o string) int {
+
+	// split the prelease versions by their part. The separator, per the spec,
+	// is a .
+	sparts := strings.Split(v, ".")
+	oparts := strings.Split(o, ".")
+
+	// Find the longer length of the parts to know how many loop iterations to
+	// go through.
+	slen := len(sparts)
+	olen := len(oparts)
+
+	l := slen
+	if olen > slen {
+		l = olen
+	}
+
+	// Iterate over each part of the prereleases to compare the differences.
+	for i := 0; i < l; i++ {
+		// Since the lentgh of the parts can be different we need to create
+		// a placeholder. This is to avoid out of bounds issues.
+		stemp := ""
+		if i < slen {
+			stemp = sparts[i]
+		}
+
+		otemp := ""
+		if i < olen {
+			otemp = oparts[i]
+		}
+
+		d := comparePrePart(stemp, otemp)
+		if d != 0 {
+			return d
+		}
+	}
+
+	// Reaching here means two versions are of equal value but have different
+	// metadata (the part following a +). They are not identical in string form
+	// but the version comparison finds them to be equal.
+	return 0
+}
+
+func comparePrePart(s, o string) int {
+	// Fastpath if they are equal
+	if s == o {
+		return 0
+	}
+
+	// When s or o are empty we can use the other in an attempt to determine
+	// the response.
+	if s == "" {
+		if o != "" {
+			return -1
+		}
+		return 1
+	}
+
+	if o == "" {
+		if s != "" {
+			return 1
+		}
+		return -1
+	}
+
+	// When comparing strings "99" is greater than "103". To handle
+	// cases like this we need to detect numbers and compare them. According
+	// to the semver spec, numbers are always positive. If there is a - at the
+	// start like -99 this is to be evaluated as an alphanum. numbers always
+	// have precedence over alphanum. Parsing as Uints because negative numbers
+	// are ignored.
+
+	oi, n1 := strconv.ParseUint(o, 10, 64)
+	si, n2 := strconv.ParseUint(s, 10, 64)
+
+	// The case where both are strings compare the strings
+	if n1 != nil && n2 != nil {
+		if s > o {
+			return 1
+		}
+		return -1
+	} else if n1 != nil {
+		// o is a string and s is a number
+		return -1
+	} else if n2 != nil {
+		// s is a string and o is a number
+		return 1
+	}
+	// Both are numbers
+	if si > oi {
+		return 1
+	}
+	return -1
+
+}

+ 10 - 0
vendor/github.com/Masterminds/semver/version_fuzz.go

@@ -0,0 +1,10 @@
+// +build gofuzz
+
+package semver
+
+func Fuzz(data []byte) int {
+	if _, err := NewVersion(string(data)); err != nil {
+		return 0
+	}
+	return 1
+}

+ 2 - 0
vendor/github.com/Masterminds/sprig/.gitignore

@@ -0,0 +1,2 @@
+vendor/
+/.glide

+ 26 - 0
vendor/github.com/Masterminds/sprig/.travis.yml

@@ -0,0 +1,26 @@
+language: go
+
+go:
+  - 1.9.x
+  - 1.10.x
+  - 1.11.x
+  - 1.12.x
+  - 1.13.x
+  - tip
+
+# Setting sudo access to false will let Travis CI use containers rather than
+# VMs to run the tests. For more details see:
+# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
+# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
+sudo: false
+
+script:
+  - make setup test
+
+notifications:
+  webhooks:
+    urls:
+      - https://webhooks.gitter.im/e/06e3328629952dabe3e0
+    on_success: change  # options: [always|never|change] default: always
+    on_failure: always  # options: [always|never|change] default: always
+    on_start: never     # options: [always|never|change] default: always

+ 282 - 0
vendor/github.com/Masterminds/sprig/CHANGELOG.md

@@ -0,0 +1,282 @@
+# Changelog
+
+## Release 2.22.0 (2019-10-02)
+
+### Added
+
+- #173: Added getHostByName function to resolve dns names to ips (thanks @fcgravalos)
+- #195: Added deepCopy function for use with dicts
+
+### Changed
+
+- Updated merge and mergeOverwrite documentation to explain copying and how to
+  use deepCopy with it
+
+## Release 2.21.0 (2019-09-18)
+
+### Added
+
+- #122: Added encryptAES/decryptAES functions (thanks @n0madic)
+- #128: Added toDecimal support (thanks @Dean-Coakley)
+- #169: Added list contcat (thanks @astorath)
+- #174: Added deepEqual function (thanks @bonifaido)
+- #170: Added url parse and join functions (thanks @astorath)
+
+### Changed
+
+- #171: Updated glide config for Google UUID to v1 and to add ranges to semver and testify
+
+### Fixed
+
+- #172: Fix semver wildcard example (thanks @piepmatz)
+- #175: Fix dateInZone doc example (thanks @s3than)
+
+## Release 2.20.0 (2019-06-18)
+
+### Added
+
+- #164: Adding function to get unix epoch for a time (@mattfarina)
+- #166: Adding tests for date_in_zone (@mattfarina)
+
+### Changed
+
+- #144: Fix function comments based on best practices from Effective Go (@CodeLingoTeam)
+- #150: Handles pointer type for time.Time in "htmlDate" (@mapreal19)
+- #161, #157, #160,  #153, #158, #156,  #155,  #159, #152 documentation updates (@badeadan)
+
+### Fixed
+
+## Release 2.19.0 (2019-03-02)
+
+IMPORTANT: This release reverts a change from 2.18.0
+
+In the previous release (2.18), we prematurely merged a partial change to the crypto functions that led to creating two sets of crypto functions (I blame @technosophos -- since that's me). This release rolls back that change, and does what was originally intended: It alters the existing crypto functions to use secure random.
+
+We debated whether this classifies as a change worthy of major revision, but given the proximity to the last release, we have decided that treating 2.18 as a faulty release is the correct course of action. We apologize for any inconvenience.
+
+### Changed
+
+- Fix substr panic 35fb796 (Alexey igrychev)
+- Remove extra period 1eb7729 (Matthew Lorimor)
+- Make random string functions use crypto by default 6ceff26 (Matthew Lorimor)
+- README edits/fixes/suggestions 08fe136 (Lauri Apple)
+
+
+## Release 2.18.0 (2019-02-12)
+
+### Added
+
+- Added mergeOverwrite function
+- cryptographic functions that use secure random (see fe1de12)
+
+### Changed
+
+- Improve documentation of regexMatch function, resolves #139 90b89ce (Jan Tagscherer)
+- Handle has for nil list 9c10885 (Daniel Cohen)
+- Document behaviour of mergeOverwrite fe0dbe9 (Lukas Rieder)
+- doc: adds missing documentation. 4b871e6 (Fernandez Ludovic)
+- Replace outdated goutils imports 01893d2 (Matthew Lorimor)
+- Surface crypto secure random strings from goutils fe1de12 (Matthew Lorimor)
+- Handle untyped nil values as paramters to string functions 2b2ec8f (Morten Torkildsen)
+
+### Fixed
+
+- Fix dict merge issue and provide mergeOverwrite .dst .src1 to overwrite from src -> dst 4c59c12 (Lukas Rieder)
+- Fix substr var names and comments d581f80 (Dean Coakley)
+- Fix substr documentation 2737203 (Dean Coakley)
+
+## Release 2.17.1 (2019-01-03)
+
+### Fixed
+
+The 2.17.0 release did not have a version pinned for xstrings, which caused compilation failures when xstrings < 1.2 was used. This adds the correct version string to glide.yaml.
+
+## Release 2.17.0 (2019-01-03)
+
+### Added
+
+- adds alder32sum function and test 6908fc2 (marshallford)
+- Added kebabcase function ca331a1 (Ilyes512)
+
+### Changed
+
+- Update goutils to 1.1.0 4e1125d (Matt Butcher)
+
+### Fixed
+
+- Fix 'has' documentation e3f2a85 (dean-coakley)
+- docs(dict): fix typo in pick example dc424f9 (Dustin Specker)
+- fixes spelling errors... not sure how that happened 4cf188a (marshallford)
+
+## Release 2.16.0 (2018-08-13)
+
+### Added
+
+- add splitn function fccb0b0 (Helgi Þorbjörnsson)
+- Add slice func df28ca7 (gongdo)
+- Generate serial number a3bdffd (Cody Coons)
+- Extract values of dict with values function df39312 (Lawrence Jones)
+
+### Changed
+
+- Modify panic message for list.slice ae38335 (gongdo)
+- Minor improvement in code quality - Removed an unreachable piece of code at defaults.go#L26:6 - Resolve formatting issues. 5834241 (Abhishek Kashyap)
+- Remove duplicated documentation 1d97af1 (Matthew Fisher)
+- Test on go 1.11 49df809 (Helgi Þormar Þorbjörnsson)
+
+### Fixed
+
+- Fix file permissions c5f40b5 (gongdo)
+- Fix example for buildCustomCert 7779e0d (Tin Lam)
+
+## Release 2.15.0 (2018-04-02)
+
+### Added
+
+- #68 and #69: Add json helpers to docs (thanks @arunvelsriram)
+- #66: Add ternary function (thanks @binoculars)
+- #67: Allow keys function to take multiple dicts (thanks @binoculars)
+- #89: Added sha1sum to crypto function (thanks @benkeil)
+- #81: Allow customizing Root CA that used by genSignedCert (thanks @chenzhiwei)
+- #92: Add travis testing for go 1.10
+- #93: Adding appveyor config for windows testing
+
+### Changed
+
+- #90: Updating to more recent dependencies
+- #73: replace satori/go.uuid with google/uuid (thanks @petterw)
+
+### Fixed
+
+- #76: Fixed documentation typos (thanks @Thiht)
+- Fixed rounding issue on the `ago` function. Note, the removes support for Go 1.8 and older
+
+## Release 2.14.1 (2017-12-01)
+
+### Fixed
+
+- #60: Fix typo in function name documentation (thanks @neil-ca-moore)
+- #61: Removing line with {{ due to blocking github pages genertion
+- #64: Update the list functions to handle int, string, and other slices for compatibility
+
+## Release 2.14.0 (2017-10-06)
+
+This new version of Sprig adds a set of functions for generating and working with SSL certificates.
+
+- `genCA` generates an SSL Certificate Authority
+- `genSelfSignedCert` generates an SSL self-signed certificate
+- `genSignedCert` generates an SSL certificate and key based on a given CA
+
+## Release 2.13.0 (2017-09-18)
+
+This release adds new functions, including:
+
+- `regexMatch`, `regexFindAll`, `regexFind`, `regexReplaceAll`, `regexReplaceAllLiteral`, and `regexSplit` to work with regular expressions
+- `floor`, `ceil`, and `round` math functions
+- `toDate` converts a string to a date
+- `nindent` is just like `indent` but also prepends a new line
+- `ago` returns the time from `time.Now`
+
+### Added
+
+- #40: Added basic regex functionality (thanks @alanquillin)
+- #41: Added ceil floor and round functions (thanks @alanquillin)
+- #48: Added toDate function (thanks @andreynering)
+- #50: Added nindent function (thanks @binoculars)
+- #46: Added ago function (thanks @slayer)
+
+### Changed
+
+- #51: Updated godocs to include new string functions (thanks @curtisallen)
+- #49: Added ability to merge multiple dicts (thanks @binoculars)
+
+## Release 2.12.0 (2017-05-17)
+
+- `snakecase`, `camelcase`, and `shuffle` are three new string functions
+- `fail` allows you to bail out of a template render when conditions are not met
+
+## Release 2.11.0 (2017-05-02)
+
+- Added `toJson` and `toPrettyJson`
+- Added `merge`
+- Refactored documentation
+
+## Release 2.10.0 (2017-03-15)
+
+- Added `semver` and `semverCompare` for Semantic Versions
+- `list` replaces `tuple`
+- Fixed issue with `join`
+- Added `first`, `last`, `intial`, `rest`, `prepend`, `append`, `toString`, `toStrings`, `sortAlpha`, `reverse`, `coalesce`, `pluck`, `pick`, `compact`, `keys`, `omit`, `uniq`, `has`, `without`
+
+## Release 2.9.0 (2017-02-23)
+
+- Added `splitList` to split a list
+- Added crypto functions of `genPrivateKey` and `derivePassword`
+
+## Release 2.8.0 (2016-12-21)
+
+- Added access to several path functions (`base`, `dir`, `clean`, `ext`, and `abs`)
+- Added functions for _mutating_ dictionaries (`set`, `unset`, `hasKey`)
+
+## Release 2.7.0 (2016-12-01)
+
+- Added `sha256sum` to generate a hash of an input
+- Added functions to convert a numeric or string to `int`, `int64`, `float64`
+
+## Release 2.6.0 (2016-10-03)
+
+- Added a `uuidv4` template function for generating UUIDs inside of a template.
+
+## Release 2.5.0 (2016-08-19)
+
+- New `trimSuffix`, `trimPrefix`, `hasSuffix`, and `hasPrefix` functions
+- New aliases have been added for a few functions that didn't follow the naming conventions (`trimAll` and `abbrevBoth`)
+- `trimall` and `abbrevboth` (notice the case) are deprecated and will be removed in 3.0.0
+
+## Release 2.4.0 (2016-08-16)
+
+- Adds two functions: `until` and `untilStep`
+
+## Release 2.3.0 (2016-06-21)
+
+- cat: Concatenate strings with whitespace separators.
+- replace: Replace parts of a string: `replace " " "-" "Me First"` renders "Me-First"
+- plural: Format plurals: `len "foo" | plural "one foo" "many foos"` renders "many foos"
+- indent: Indent blocks of text in a way that is sensitive to "\n" characters.
+
+## Release 2.2.0 (2016-04-21)
+
+- Added a `genPrivateKey` function (Thanks @bacongobbler)
+
+## Release 2.1.0 (2016-03-30)
+
+- `default` now prints the default value when it does not receive a value down the pipeline. It is much safer now to do `{{.Foo | default "bar"}}`.
+- Added accessors for "hermetic" functions. These return only functions that, when given the same input, produce the same output.
+
+## Release 2.0.0 (2016-03-29)
+
+Because we switched from `int` to `int64` as the return value for all integer math functions, the library's major version number has been incremented.
+
+- `min` complements `max` (formerly `biggest`)
+- `empty` indicates that a value is the empty value for its type
+- `tuple` creates a tuple inside of a template: `{{$t := tuple "a", "b" "c"}}`
+- `dict` creates a dictionary inside of a template `{{$d := dict "key1" "val1" "key2" "val2"}}` 
+- Date formatters have been added for HTML dates (as used in `date` input fields)
+- Integer math functions can convert from a number of types, including `string` (via `strconv.ParseInt`).
+
+## Release 1.2.0 (2016-02-01)
+
+- Added quote and squote
+- Added b32enc and b32dec
+- add now takes varargs
+- biggest now takes varargs
+
+## Release 1.1.0 (2015-12-29)
+
+- Added #4: Added contains function. strings.Contains, but with the arguments
+  switched to simplify common pipelines. (thanks krancour)
+- Added Travis-CI testing support
+
+## Release 1.0.0 (2015-12-23)
+
+- Initial release

+ 20 - 0
vendor/github.com/Masterminds/sprig/LICENSE.txt

@@ -0,0 +1,20 @@
+Sprig
+Copyright (C) 2013 Masterminds
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 13 - 0
vendor/github.com/Masterminds/sprig/Makefile

@@ -0,0 +1,13 @@
+
+HAS_GLIDE := $(shell command -v glide;)
+
+.PHONY: test
+test:
+	go test -v .
+
+.PHONY: setup
+setup:
+ifndef HAS_GLIDE
+	go get -u github.com/Masterminds/glide
+endif
+	glide install

+ 78 - 0
vendor/github.com/Masterminds/sprig/README.md

@@ -0,0 +1,78 @@
+# Sprig: Template functions for Go templates
+[![Stability: Sustained](https://masterminds.github.io/stability/sustained.svg)](https://masterminds.github.io/stability/sustained.html)
+[![Build Status](https://travis-ci.org/Masterminds/sprig.svg?branch=master)](https://travis-ci.org/Masterminds/sprig)
+
+The Go language comes with a [built-in template
+language](http://golang.org/pkg/text/template/), but not
+very many template functions. Sprig is a library that provides more than 100 commonly
+used template functions.
+
+It is inspired by the template functions found in
+[Twig](http://twig.sensiolabs.org/documentation) and in various
+JavaScript libraries, such as [underscore.js](http://underscorejs.org/).
+
+## Usage
+
+**Template developers**: Please use Sprig's [function documentation](http://masterminds.github.io/sprig/) for
+detailed instructions and code snippets for the >100 template functions available.
+
+**Go developers**: If you'd like to include Sprig as a library in your program,
+our API documentation is available [at GoDoc.org](http://godoc.org/github.com/Masterminds/sprig).
+
+For standard usage, read on.
+
+### Load the Sprig library
+
+To load the Sprig `FuncMap`:
+
+```go
+
+import (
+  "github.com/Masterminds/sprig"
+  "html/template"
+)
+
+// This example illustrates that the FuncMap *must* be set before the
+// templates themselves are loaded.
+tpl := template.Must(
+  template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html")
+)
+
+
+```
+
+### Calling the functions inside of templates
+
+By convention, all functions are lowercase. This seems to follow the Go
+idiom for template functions (as opposed to template methods, which are
+TitleCase). For example, this:
+
+```
+{{ "hello!" | upper | repeat 5 }}
+```
+
+produces this:
+
+```
+HELLO!HELLO!HELLO!HELLO!HELLO!
+```
+
+## Principles Driving Our Function Selection
+
+We followed these principles to decide which functions to add and how to implement them:
+
+- Use template functions to build layout. The following
+  types of operations are within the domain of template functions:
+  - Formatting
+  - Layout
+  - Simple type conversions
+  - Utilities that assist in handling common formatting and layout needs (e.g. arithmetic)
+- Template functions should not return errors unless there is no way to print
+  a sensible value. For example, converting a string to an integer should not
+  produce an error if conversion fails. Instead, it should display a default
+  value.
+- Simple math is necessary for grid layouts, pagers, and so on. Complex math
+  (anything other than arithmetic) should be done outside of templates.
+- Template functions only deal with the data passed into them. They never retrieve
+  data from a source.
+- Finally, do not override core Go template functions.

+ 26 - 0
vendor/github.com/Masterminds/sprig/appveyor.yml

@@ -0,0 +1,26 @@
+
+version: build-{build}.{branch}
+
+clone_folder: C:\gopath\src\github.com\Masterminds\sprig
+shallow_clone: true
+
+environment:
+  GOPATH: C:\gopath
+
+platform:
+  - x64
+
+install:
+  - go get -u github.com/Masterminds/glide
+  - set PATH=%GOPATH%\bin;%PATH%
+  - go version
+  - go env
+
+build_script:
+  - glide install
+  - go install ./...
+
+test_script:
+  - go test -v
+
+deploy: off

+ 502 - 0
vendor/github.com/Masterminds/sprig/crypto.go

@@ -0,0 +1,502 @@
+package sprig
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/dsa"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/base64"
+	"encoding/binary"
+	"encoding/hex"
+	"encoding/pem"
+	"errors"
+	"fmt"
+	"io"
+	"hash/adler32"
+	"math/big"
+	"net"
+	"time"
+
+	"github.com/google/uuid"
+	"golang.org/x/crypto/scrypt"
+)
+
+func sha256sum(input string) string {
+	hash := sha256.Sum256([]byte(input))
+	return hex.EncodeToString(hash[:])
+}
+
+func sha1sum(input string) string {
+	hash := sha1.Sum([]byte(input))
+	return hex.EncodeToString(hash[:])
+}
+
+func adler32sum(input string) string {
+	hash := adler32.Checksum([]byte(input))
+	return fmt.Sprintf("%d", hash)
+}
+
+// uuidv4 provides a safe and secure UUID v4 implementation
+func uuidv4() string {
+	return fmt.Sprintf("%s", uuid.New())
+}
+
+var master_password_seed = "com.lyndir.masterpassword"
+
+var password_type_templates = map[string][][]byte{
+	"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
+	"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
+		[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
+		[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
+		[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
+		[]byte("CvccCvcvCvccno")},
+	"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
+	"short":  {[]byte("Cvcn")},
+	"basic":  {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
+	"pin":    {[]byte("nnnn")},
+}
+
+var template_characters = map[byte]string{
+	'V': "AEIOU",
+	'C': "BCDFGHJKLMNPQRSTVWXYZ",
+	'v': "aeiou",
+	'c': "bcdfghjklmnpqrstvwxyz",
+	'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
+	'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
+	'n': "0123456789",
+	'o': "@&%?,=[]_:-+*$#!'^~;()/.",
+	'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
+}
+
+func derivePassword(counter uint32, password_type, password, user, site string) string {
+	var templates = password_type_templates[password_type]
+	if templates == nil {
+		return fmt.Sprintf("cannot find password template %s", password_type)
+	}
+
+	var buffer bytes.Buffer
+	buffer.WriteString(master_password_seed)
+	binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
+	buffer.WriteString(user)
+
+	salt := buffer.Bytes()
+	key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
+	if err != nil {
+		return fmt.Sprintf("failed to derive password: %s", err)
+	}
+
+	buffer.Truncate(len(master_password_seed))
+	binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
+	buffer.WriteString(site)
+	binary.Write(&buffer, binary.BigEndian, counter)
+
+	var hmacv = hmac.New(sha256.New, key)
+	hmacv.Write(buffer.Bytes())
+	var seed = hmacv.Sum(nil)
+	var temp = templates[int(seed[0])%len(templates)]
+
+	buffer.Truncate(0)
+	for i, element := range temp {
+		pass_chars := template_characters[element]
+		pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
+		buffer.WriteByte(pass_char)
+	}
+
+	return buffer.String()
+}
+
+func generatePrivateKey(typ string) string {
+	var priv interface{}
+	var err error
+	switch typ {
+	case "", "rsa":
+		// good enough for government work
+		priv, err = rsa.GenerateKey(rand.Reader, 4096)
+	case "dsa":
+		key := new(dsa.PrivateKey)
+		// again, good enough for government work
+		if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
+			return fmt.Sprintf("failed to generate dsa params: %s", err)
+		}
+		err = dsa.GenerateKey(key, rand.Reader)
+		priv = key
+	case "ecdsa":
+		// again, good enough for government work
+		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	default:
+		return "Unknown type " + typ
+	}
+	if err != nil {
+		return fmt.Sprintf("failed to generate private key: %s", err)
+	}
+
+	return string(pem.EncodeToMemory(pemBlockForKey(priv)))
+}
+
+type DSAKeyFormat struct {
+	Version       int
+	P, Q, G, Y, X *big.Int
+}
+
+func pemBlockForKey(priv interface{}) *pem.Block {
+	switch k := priv.(type) {
+	case *rsa.PrivateKey:
+		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
+	case *dsa.PrivateKey:
+		val := DSAKeyFormat{
+			P: k.P, Q: k.Q, G: k.G,
+			Y: k.Y, X: k.X,
+		}
+		bytes, _ := asn1.Marshal(val)
+		return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
+	case *ecdsa.PrivateKey:
+		b, _ := x509.MarshalECPrivateKey(k)
+		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
+	default:
+		return nil
+	}
+}
+
+type certificate struct {
+	Cert string
+	Key  string
+}
+
+func buildCustomCertificate(b64cert string, b64key string) (certificate, error) {
+	crt := certificate{}
+
+	cert, err := base64.StdEncoding.DecodeString(b64cert)
+	if err != nil {
+		return crt, errors.New("unable to decode base64 certificate")
+	}
+
+	key, err := base64.StdEncoding.DecodeString(b64key)
+	if err != nil {
+		return crt, errors.New("unable to decode base64 private key")
+	}
+
+	decodedCert, _ := pem.Decode(cert)
+	if decodedCert == nil {
+		return crt, errors.New("unable to decode certificate")
+	}
+	_, err = x509.ParseCertificate(decodedCert.Bytes)
+	if err != nil {
+		return crt, fmt.Errorf(
+			"error parsing certificate: decodedCert.Bytes: %s",
+			err,
+		)
+	}
+
+	decodedKey, _ := pem.Decode(key)
+	if decodedKey == nil {
+		return crt, errors.New("unable to decode key")
+	}
+	_, err = x509.ParsePKCS1PrivateKey(decodedKey.Bytes)
+	if err != nil {
+		return crt, fmt.Errorf(
+			"error parsing prive key: decodedKey.Bytes: %s",
+			err,
+		)
+	}
+
+	crt.Cert = string(cert)
+	crt.Key = string(key)
+
+	return crt, nil
+}
+
+func generateCertificateAuthority(
+	cn string,
+	daysValid int,
+) (certificate, error) {
+	ca := certificate{}
+
+	template, err := getBaseCertTemplate(cn, nil, nil, daysValid)
+	if err != nil {
+		return ca, err
+	}
+	// Override KeyUsage and IsCA
+	template.KeyUsage = x509.KeyUsageKeyEncipherment |
+		x509.KeyUsageDigitalSignature |
+		x509.KeyUsageCertSign
+	template.IsCA = true
+
+	priv, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return ca, fmt.Errorf("error generating rsa key: %s", err)
+	}
+
+	ca.Cert, ca.Key, err = getCertAndKey(template, priv, template, priv)
+	if err != nil {
+		return ca, err
+	}
+
+	return ca, nil
+}
+
+func generateSelfSignedCertificate(
+	cn string,
+	ips []interface{},
+	alternateDNS []interface{},
+	daysValid int,
+) (certificate, error) {
+	cert := certificate{}
+
+	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
+	if err != nil {
+		return cert, err
+	}
+
+	priv, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return cert, fmt.Errorf("error generating rsa key: %s", err)
+	}
+
+	cert.Cert, cert.Key, err = getCertAndKey(template, priv, template, priv)
+	if err != nil {
+		return cert, err
+	}
+
+	return cert, nil
+}
+
+func generateSignedCertificate(
+	cn string,
+	ips []interface{},
+	alternateDNS []interface{},
+	daysValid int,
+	ca certificate,
+) (certificate, error) {
+	cert := certificate{}
+
+	decodedSignerCert, _ := pem.Decode([]byte(ca.Cert))
+	if decodedSignerCert == nil {
+		return cert, errors.New("unable to decode certificate")
+	}
+	signerCert, err := x509.ParseCertificate(decodedSignerCert.Bytes)
+	if err != nil {
+		return cert, fmt.Errorf(
+			"error parsing certificate: decodedSignerCert.Bytes: %s",
+			err,
+		)
+	}
+	decodedSignerKey, _ := pem.Decode([]byte(ca.Key))
+	if decodedSignerKey == nil {
+		return cert, errors.New("unable to decode key")
+	}
+	signerKey, err := x509.ParsePKCS1PrivateKey(decodedSignerKey.Bytes)
+	if err != nil {
+		return cert, fmt.Errorf(
+			"error parsing prive key: decodedSignerKey.Bytes: %s",
+			err,
+		)
+	}
+
+	template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid)
+	if err != nil {
+		return cert, err
+	}
+
+	priv, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return cert, fmt.Errorf("error generating rsa key: %s", err)
+	}
+
+	cert.Cert, cert.Key, err = getCertAndKey(
+		template,
+		priv,
+		signerCert,
+		signerKey,
+	)
+	if err != nil {
+		return cert, err
+	}
+
+	return cert, nil
+}
+
+func getCertAndKey(
+	template *x509.Certificate,
+	signeeKey *rsa.PrivateKey,
+	parent *x509.Certificate,
+	signingKey *rsa.PrivateKey,
+) (string, string, error) {
+	derBytes, err := x509.CreateCertificate(
+		rand.Reader,
+		template,
+		parent,
+		&signeeKey.PublicKey,
+		signingKey,
+	)
+	if err != nil {
+		return "", "", fmt.Errorf("error creating certificate: %s", err)
+	}
+
+	certBuffer := bytes.Buffer{}
+	if err := pem.Encode(
+		&certBuffer,
+		&pem.Block{Type: "CERTIFICATE", Bytes: derBytes},
+	); err != nil {
+		return "", "", fmt.Errorf("error pem-encoding certificate: %s", err)
+	}
+
+	keyBuffer := bytes.Buffer{}
+	if err := pem.Encode(
+		&keyBuffer,
+		&pem.Block{
+			Type:  "RSA PRIVATE KEY",
+			Bytes: x509.MarshalPKCS1PrivateKey(signeeKey),
+		},
+	); err != nil {
+		return "", "", fmt.Errorf("error pem-encoding key: %s", err)
+	}
+
+	return string(certBuffer.Bytes()), string(keyBuffer.Bytes()), nil
+}
+
+func getBaseCertTemplate(
+	cn string,
+	ips []interface{},
+	alternateDNS []interface{},
+	daysValid int,
+) (*x509.Certificate, error) {
+	ipAddresses, err := getNetIPs(ips)
+	if err != nil {
+		return nil, err
+	}
+	dnsNames, err := getAlternateDNSStrs(alternateDNS)
+	if err != nil {
+		return nil, err
+	}
+	serialNumberUpperBound := new(big.Int).Lsh(big.NewInt(1), 128)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberUpperBound)
+	if err != nil {
+		return nil, err
+	}
+	return &x509.Certificate{
+		SerialNumber: serialNumber,
+		Subject: pkix.Name{
+			CommonName: cn,
+		},
+		IPAddresses: ipAddresses,
+		DNSNames:    dnsNames,
+		NotBefore:   time.Now(),
+		NotAfter:    time.Now().Add(time.Hour * 24 * time.Duration(daysValid)),
+		KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+		ExtKeyUsage: []x509.ExtKeyUsage{
+			x509.ExtKeyUsageServerAuth,
+			x509.ExtKeyUsageClientAuth,
+		},
+		BasicConstraintsValid: true,
+	}, nil
+}
+
+func getNetIPs(ips []interface{}) ([]net.IP, error) {
+	if ips == nil {
+		return []net.IP{}, nil
+	}
+	var ipStr string
+	var ok bool
+	var netIP net.IP
+	netIPs := make([]net.IP, len(ips))
+	for i, ip := range ips {
+		ipStr, ok = ip.(string)
+		if !ok {
+			return nil, fmt.Errorf("error parsing ip: %v is not a string", ip)
+		}
+		netIP = net.ParseIP(ipStr)
+		if netIP == nil {
+			return nil, fmt.Errorf("error parsing ip: %s", ipStr)
+		}
+		netIPs[i] = netIP
+	}
+	return netIPs, nil
+}
+
+func getAlternateDNSStrs(alternateDNS []interface{}) ([]string, error) {
+	if alternateDNS == nil {
+		return []string{}, nil
+	}
+	var dnsStr string
+	var ok bool
+	alternateDNSStrs := make([]string, len(alternateDNS))
+	for i, dns := range alternateDNS {
+		dnsStr, ok = dns.(string)
+		if !ok {
+			return nil, fmt.Errorf(
+				"error processing alternate dns name: %v is not a string",
+				dns,
+			)
+		}
+		alternateDNSStrs[i] = dnsStr
+	}
+	return alternateDNSStrs, nil
+}
+
+func encryptAES(password string, plaintext string) (string, error) {
+	if plaintext == "" {
+		return "", nil
+	}
+
+	key := make([]byte, 32)
+	copy(key, []byte(password))
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	content := []byte(plaintext)
+	blockSize := block.BlockSize()
+	padding := blockSize - len(content)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	content = append(content, padtext...)
+
+	ciphertext := make([]byte, aes.BlockSize+len(content))
+
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		return "", err
+	}
+
+	mode := cipher.NewCBCEncrypter(block, iv)
+	mode.CryptBlocks(ciphertext[aes.BlockSize:], content)
+
+	return base64.StdEncoding.EncodeToString(ciphertext), nil
+}
+
+func decryptAES(password string, crypt64 string) (string, error) {
+	if crypt64 == "" {
+		return "", nil
+	}
+
+	key := make([]byte, 32)
+	copy(key, []byte(password))
+
+	crypt, err := base64.StdEncoding.DecodeString(crypt64)
+	if err != nil {
+		return "", err
+	}
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	iv := crypt[:aes.BlockSize]
+	crypt = crypt[aes.BlockSize:]
+	decrypted := make([]byte, len(crypt))
+	mode := cipher.NewCBCDecrypter(block, iv)
+	mode.CryptBlocks(decrypted, crypt)
+
+	return string(decrypted[:len(decrypted)-int(decrypted[len(decrypted)-1])]), nil
+}

+ 83 - 0
vendor/github.com/Masterminds/sprig/date.go

@@ -0,0 +1,83 @@
+package sprig
+
+import (
+	"strconv"
+	"time"
+)
+
+// Given a format and a date, format the date string.
+//
+// Date can be a `time.Time` or an `int, int32, int64`.
+// In the later case, it is treated as seconds since UNIX
+// epoch.
+func date(fmt string, date interface{}) string {
+	return dateInZone(fmt, date, "Local")
+}
+
+func htmlDate(date interface{}) string {
+	return dateInZone("2006-01-02", date, "Local")
+}
+
+func htmlDateInZone(date interface{}, zone string) string {
+	return dateInZone("2006-01-02", date, zone)
+}
+
+func dateInZone(fmt string, date interface{}, zone string) string {
+	var t time.Time
+	switch date := date.(type) {
+	default:
+		t = time.Now()
+	case time.Time:
+		t = date
+	case *time.Time:
+		t = *date
+	case int64:
+		t = time.Unix(date, 0)
+	case int:
+		t = time.Unix(int64(date), 0)
+	case int32:
+		t = time.Unix(int64(date), 0)
+	}
+
+	loc, err := time.LoadLocation(zone)
+	if err != nil {
+		loc, _ = time.LoadLocation("UTC")
+	}
+
+	return t.In(loc).Format(fmt)
+}
+
+func dateModify(fmt string, date time.Time) time.Time {
+	d, err := time.ParseDuration(fmt)
+	if err != nil {
+		return date
+	}
+	return date.Add(d)
+}
+
+func dateAgo(date interface{}) string {
+	var t time.Time
+
+	switch date := date.(type) {
+	default:
+		t = time.Now()
+	case time.Time:
+		t = date
+	case int64:
+		t = time.Unix(date, 0)
+	case int:
+		t = time.Unix(int64(date), 0)
+	}
+	// Drop resolution to seconds
+	duration := time.Since(t).Round(time.Second)
+	return duration.String()
+}
+
+func toDate(fmt, str string) time.Time {
+	t, _ := time.ParseInLocation(fmt, str, time.Local)
+	return t
+}
+
+func unixEpoch(date time.Time) string {
+	return strconv.FormatInt(date.Unix(), 10)
+}

+ 83 - 0
vendor/github.com/Masterminds/sprig/defaults.go

@@ -0,0 +1,83 @@
+package sprig
+
+import (
+	"encoding/json"
+	"reflect"
+)
+
+// dfault checks whether `given` is set, and returns default if not set.
+//
+// This returns `d` if `given` appears not to be set, and `given` otherwise.
+//
+// For numeric types 0 is unset.
+// For strings, maps, arrays, and slices, len() = 0 is considered unset.
+// For bool, false is unset.
+// Structs are never considered unset.
+//
+// For everything else, including pointers, a nil value is unset.
+func dfault(d interface{}, given ...interface{}) interface{} {
+
+	if empty(given) || empty(given[0]) {
+		return d
+	}
+	return given[0]
+}
+
+// empty returns true if the given value has the zero value for its type.
+func empty(given interface{}) bool {
+	g := reflect.ValueOf(given)
+	if !g.IsValid() {
+		return true
+	}
+
+	// Basically adapted from text/template.isTrue
+	switch g.Kind() {
+	default:
+		return g.IsNil()
+	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+		return g.Len() == 0
+	case reflect.Bool:
+		return g.Bool() == false
+	case reflect.Complex64, reflect.Complex128:
+		return g.Complex() == 0
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return g.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return g.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return g.Float() == 0
+	case reflect.Struct:
+		return false
+	}
+}
+
+// coalesce returns the first non-empty value.
+func coalesce(v ...interface{}) interface{} {
+	for _, val := range v {
+		if !empty(val) {
+			return val
+		}
+	}
+	return nil
+}
+
+// toJson encodes an item into a JSON string
+func toJson(v interface{}) string {
+	output, _ := json.Marshal(v)
+	return string(output)
+}
+
+// toPrettyJson encodes an item into a pretty (indented) JSON string
+func toPrettyJson(v interface{}) string {
+	output, _ := json.MarshalIndent(v, "", "  ")
+	return string(output)
+}
+
+// ternary returns the first value if the last value is true, otherwise returns the second value.
+func ternary(vt interface{}, vf interface{}, v bool) interface{} {
+	if v {
+		return vt
+	}
+
+	return vf
+}

+ 119 - 0
vendor/github.com/Masterminds/sprig/dict.go

@@ -0,0 +1,119 @@
+package sprig
+
+import (
+	"github.com/imdario/mergo"
+	"github.com/mitchellh/copystructure"
+)
+
+func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
+	d[key] = value
+	return d
+}
+
+func unset(d map[string]interface{}, key string) map[string]interface{} {
+	delete(d, key)
+	return d
+}
+
+func hasKey(d map[string]interface{}, key string) bool {
+	_, ok := d[key]
+	return ok
+}
+
+func pluck(key string, d ...map[string]interface{}) []interface{} {
+	res := []interface{}{}
+	for _, dict := range d {
+		if val, ok := dict[key]; ok {
+			res = append(res, val)
+		}
+	}
+	return res
+}
+
+func keys(dicts ...map[string]interface{}) []string {
+	k := []string{}
+	for _, dict := range dicts {
+		for key := range dict {
+			k = append(k, key)
+		}
+	}
+	return k
+}
+
+func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
+	res := map[string]interface{}{}
+	for _, k := range keys {
+		if v, ok := dict[k]; ok {
+			res[k] = v
+		}
+	}
+	return res
+}
+
+func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
+	res := map[string]interface{}{}
+
+	omit := make(map[string]bool, len(keys))
+	for _, k := range keys {
+		omit[k] = true
+	}
+
+	for k, v := range dict {
+		if _, ok := omit[k]; !ok {
+			res[k] = v
+		}
+	}
+	return res
+}
+
+func dict(v ...interface{}) map[string]interface{} {
+	dict := map[string]interface{}{}
+	lenv := len(v)
+	for i := 0; i < lenv; i += 2 {
+		key := strval(v[i])
+		if i+1 >= lenv {
+			dict[key] = ""
+			continue
+		}
+		dict[key] = v[i+1]
+	}
+	return dict
+}
+
+func merge(dst map[string]interface{}, srcs ...map[string]interface{}) interface{} {
+	for _, src := range srcs {
+		if err := mergo.Merge(&dst, src); err != nil {
+			// Swallow errors inside of a template.
+			return ""
+		}
+	}
+	return dst
+}
+
+func mergeOverwrite(dst map[string]interface{}, srcs ...map[string]interface{}) interface{} {
+	for _, src := range srcs {
+		if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
+			// Swallow errors inside of a template.
+			return ""
+		}
+	}
+	return dst
+}
+
+func values(dict map[string]interface{}) []interface{} {
+	values := []interface{}{}
+	for _, value := range dict {
+		values = append(values, value)
+	}
+
+	return values
+}
+
+func deepCopy(i interface{}) interface{} {
+	c, err := copystructure.Copy(i)
+	if err != nil {
+		panic("deepCopy error: " + err.Error())
+	}
+
+	return c
+}

+ 19 - 0
vendor/github.com/Masterminds/sprig/doc.go

@@ -0,0 +1,19 @@
+/*
+Sprig: Template functions for Go.
+
+This package contains a number of utility functions for working with data
+inside of Go `html/template` and `text/template` files.
+
+To add these functions, use the `template.Funcs()` method:
+
+	t := templates.New("foo").Funcs(sprig.FuncMap())
+
+Note that you should add the function map before you parse any template files.
+
+	In several cases, Sprig reverses the order of arguments from the way they
+	appear in the standard library. This is to make it easier to pipe
+	arguments into functions.
+
+See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions.
+*/
+package sprig

+ 306 - 0
vendor/github.com/Masterminds/sprig/functions.go

@@ -0,0 +1,306 @@
+package sprig
+
+import (
+	"errors"
+	"html/template"
+	"os"
+	"path"
+	"reflect"
+	"strconv"
+	"strings"
+	ttemplate "text/template"
+	"time"
+
+	util "github.com/Masterminds/goutils"
+	"github.com/huandu/xstrings"
+)
+
+// Produce the function map.
+//
+// Use this to pass the functions into the template engine:
+//
+// 	tpl := template.New("foo").Funcs(sprig.FuncMap()))
+//
+func FuncMap() template.FuncMap {
+	return HtmlFuncMap()
+}
+
+// HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
+func HermeticTxtFuncMap() ttemplate.FuncMap {
+	r := TxtFuncMap()
+	for _, name := range nonhermeticFunctions {
+		delete(r, name)
+	}
+	return r
+}
+
+// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
+func HermeticHtmlFuncMap() template.FuncMap {
+	r := HtmlFuncMap()
+	for _, name := range nonhermeticFunctions {
+		delete(r, name)
+	}
+	return r
+}
+
+// TxtFuncMap returns a 'text/template'.FuncMap
+func TxtFuncMap() ttemplate.FuncMap {
+	return ttemplate.FuncMap(GenericFuncMap())
+}
+
+// HtmlFuncMap returns an 'html/template'.Funcmap
+func HtmlFuncMap() template.FuncMap {
+	return template.FuncMap(GenericFuncMap())
+}
+
+// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
+func GenericFuncMap() map[string]interface{} {
+	gfm := make(map[string]interface{}, len(genericMap))
+	for k, v := range genericMap {
+		gfm[k] = v
+	}
+	return gfm
+}
+
+// These functions are not guaranteed to evaluate to the same result for given input, because they
+// refer to the environemnt or global state.
+var nonhermeticFunctions = []string{
+	// Date functions
+	"date",
+	"date_in_zone",
+	"date_modify",
+	"now",
+	"htmlDate",
+	"htmlDateInZone",
+	"dateInZone",
+	"dateModify",
+
+	// Strings
+	"randAlphaNum",
+	"randAlpha",
+	"randAscii",
+	"randNumeric",
+	"uuidv4",
+
+	// OS
+	"env",
+	"expandenv",
+
+	// Network
+	"getHostByName",
+}
+
+var genericMap = map[string]interface{}{
+	"hello": func() string { return "Hello!" },
+
+	// Date functions
+	"date":           date,
+	"date_in_zone":   dateInZone,
+	"date_modify":    dateModify,
+	"now":            func() time.Time { return time.Now() },
+	"htmlDate":       htmlDate,
+	"htmlDateInZone": htmlDateInZone,
+	"dateInZone":     dateInZone,
+	"dateModify":     dateModify,
+	"ago":            dateAgo,
+	"toDate":         toDate,
+	"unixEpoch":      unixEpoch,
+
+	// Strings
+	"abbrev":     abbrev,
+	"abbrevboth": abbrevboth,
+	"trunc":      trunc,
+	"trim":       strings.TrimSpace,
+	"upper":      strings.ToUpper,
+	"lower":      strings.ToLower,
+	"title":      strings.Title,
+	"untitle":    untitle,
+	"substr":     substring,
+	// Switch order so that "foo" | repeat 5
+	"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
+	// Deprecated: Use trimAll.
+	"trimall": func(a, b string) string { return strings.Trim(b, a) },
+	// Switch order so that "$foo" | trimall "$"
+	"trimAll":      func(a, b string) string { return strings.Trim(b, a) },
+	"trimSuffix":   func(a, b string) string { return strings.TrimSuffix(b, a) },
+	"trimPrefix":   func(a, b string) string { return strings.TrimPrefix(b, a) },
+	"nospace":      util.DeleteWhiteSpace,
+	"initials":     initials,
+	"randAlphaNum": randAlphaNumeric,
+	"randAlpha":    randAlpha,
+	"randAscii":    randAscii,
+	"randNumeric":  randNumeric,
+	"swapcase":     util.SwapCase,
+	"shuffle":      xstrings.Shuffle,
+	"snakecase":    xstrings.ToSnakeCase,
+	"camelcase":    xstrings.ToCamelCase,
+	"kebabcase":    xstrings.ToKebabCase,
+	"wrap":         func(l int, s string) string { return util.Wrap(s, l) },
+	"wrapWith":     func(l int, sep, str string) string { return util.WrapCustom(str, l, sep, true) },
+	// Switch order so that "foobar" | contains "foo"
+	"contains":   func(substr string, str string) bool { return strings.Contains(str, substr) },
+	"hasPrefix":  func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
+	"hasSuffix":  func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
+	"quote":      quote,
+	"squote":     squote,
+	"cat":        cat,
+	"indent":     indent,
+	"nindent":    nindent,
+	"replace":    replace,
+	"plural":     plural,
+	"sha1sum":    sha1sum,
+	"sha256sum":  sha256sum,
+	"adler32sum": adler32sum,
+	"toString":   strval,
+
+	// Wrap Atoi to stop errors.
+	"atoi":      func(a string) int { i, _ := strconv.Atoi(a); return i },
+	"int64":     toInt64,
+	"int":       toInt,
+	"float64":   toFloat64,
+	"toDecimal": toDecimal,
+
+	//"gt": func(a, b int) bool {return a > b},
+	//"gte": func(a, b int) bool {return a >= b},
+	//"lt": func(a, b int) bool {return a < b},
+	//"lte": func(a, b int) bool {return a <= b},
+
+	// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
+	"split":     split,
+	"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
+	// splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
+	"splitn":    splitn,
+	"toStrings": strslice,
+
+	"until":     until,
+	"untilStep": untilStep,
+
+	// VERY basic arithmetic.
+	"add1": func(i interface{}) int64 { return toInt64(i) + 1 },
+	"add": func(i ...interface{}) int64 {
+		var a int64 = 0
+		for _, b := range i {
+			a += toInt64(b)
+		}
+		return a
+	},
+	"sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
+	"div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
+	"mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
+	"mul": func(a interface{}, v ...interface{}) int64 {
+		val := toInt64(a)
+		for _, b := range v {
+			val = val * toInt64(b)
+		}
+		return val
+	},
+	"biggest": max,
+	"max":     max,
+	"min":     min,
+	"ceil":    ceil,
+	"floor":   floor,
+	"round":   round,
+
+	// string slices. Note that we reverse the order b/c that's better
+	// for template processing.
+	"join":      join,
+	"sortAlpha": sortAlpha,
+
+	// Defaults
+	"default":      dfault,
+	"empty":        empty,
+	"coalesce":     coalesce,
+	"compact":      compact,
+	"deepCopy":     deepCopy,
+	"toJson":       toJson,
+	"toPrettyJson": toPrettyJson,
+	"ternary":      ternary,
+
+	// Reflection
+	"typeOf":     typeOf,
+	"typeIs":     typeIs,
+	"typeIsLike": typeIsLike,
+	"kindOf":     kindOf,
+	"kindIs":     kindIs,
+	"deepEqual":  reflect.DeepEqual,
+
+	// OS:
+	"env":       func(s string) string { return os.Getenv(s) },
+	"expandenv": func(s string) string { return os.ExpandEnv(s) },
+
+	// Network:
+	"getHostByName": getHostByName,
+
+	// File Paths:
+	"base":  path.Base,
+	"dir":   path.Dir,
+	"clean": path.Clean,
+	"ext":   path.Ext,
+	"isAbs": path.IsAbs,
+
+	// Encoding:
+	"b64enc": base64encode,
+	"b64dec": base64decode,
+	"b32enc": base32encode,
+	"b32dec": base32decode,
+
+	// Data Structures:
+	"tuple":          list, // FIXME: with the addition of append/prepend these are no longer immutable.
+	"list":           list,
+	"dict":           dict,
+	"set":            set,
+	"unset":          unset,
+	"hasKey":         hasKey,
+	"pluck":          pluck,
+	"keys":           keys,
+	"pick":           pick,
+	"omit":           omit,
+	"merge":          merge,
+	"mergeOverwrite": mergeOverwrite,
+	"values":         values,
+
+	"append": push, "push": push,
+	"prepend": prepend,
+	"first":   first,
+	"rest":    rest,
+	"last":    last,
+	"initial": initial,
+	"reverse": reverse,
+	"uniq":    uniq,
+	"without": without,
+	"has":     has,
+	"slice":   slice,
+	"concat":  concat,
+
+	// Crypto:
+	"genPrivateKey":     generatePrivateKey,
+	"derivePassword":    derivePassword,
+	"buildCustomCert":   buildCustomCertificate,
+	"genCA":             generateCertificateAuthority,
+	"genSelfSignedCert": generateSelfSignedCertificate,
+	"genSignedCert":     generateSignedCertificate,
+	"encryptAES":        encryptAES,
+	"decryptAES":        decryptAES,
+
+	// UUIDs:
+	"uuidv4": uuidv4,
+
+	// SemVer:
+	"semver":        semver,
+	"semverCompare": semverCompare,
+
+	// Flow Control:
+	"fail": func(msg string) (string, error) { return "", errors.New(msg) },
+
+	// Regex
+	"regexMatch":             regexMatch,
+	"regexFindAll":           regexFindAll,
+	"regexFind":              regexFind,
+	"regexReplaceAll":        regexReplaceAll,
+	"regexReplaceAllLiteral": regexReplaceAllLiteral,
+	"regexSplit":             regexSplit,
+
+	// URLs:
+	"urlParse": urlParse,
+	"urlJoin":  urlJoin,
+}

+ 19 - 0
vendor/github.com/Masterminds/sprig/glide.yaml

@@ -0,0 +1,19 @@
+package: github.com/Masterminds/sprig
+import:
+- package: github.com/Masterminds/goutils
+  version: ^1.0.0
+- package: github.com/google/uuid
+  version: ^1.0.0
+- package: golang.org/x/crypto
+  subpackages:
+  - scrypt
+- package: github.com/Masterminds/semver
+  version: ^v1.2.2
+- package: github.com/stretchr/testify
+  version: ^v1.2.2
+- package: github.com/imdario/mergo
+  version: ~0.3.7
+- package: github.com/huandu/xstrings
+  version: ^1.2
+- package: github.com/mitchellh/copystructure
+  version: ^1.0.0

+ 311 - 0
vendor/github.com/Masterminds/sprig/list.go

@@ -0,0 +1,311 @@
+package sprig
+
+import (
+	"fmt"
+	"reflect"
+	"sort"
+)
+
+// Reflection is used in these functions so that slices and arrays of strings,
+// ints, and other types not implementing []interface{} can be worked with.
+// For example, this is useful if you need to work on the output of regexs.
+
+func list(v ...interface{}) []interface{} {
+	return v
+}
+
+func push(list interface{}, v interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		nl := make([]interface{}, l)
+		for i := 0; i < l; i++ {
+			nl[i] = l2.Index(i).Interface()
+		}
+
+		return append(nl, v)
+
+	default:
+		panic(fmt.Sprintf("Cannot push on type %s", tp))
+	}
+}
+
+func prepend(list interface{}, v interface{}) []interface{} {
+	//return append([]interface{}{v}, list...)
+
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		nl := make([]interface{}, l)
+		for i := 0; i < l; i++ {
+			nl[i] = l2.Index(i).Interface()
+		}
+
+		return append([]interface{}{v}, nl...)
+
+	default:
+		panic(fmt.Sprintf("Cannot prepend on type %s", tp))
+	}
+}
+
+func last(list interface{}) interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		if l == 0 {
+			return nil
+		}
+
+		return l2.Index(l - 1).Interface()
+	default:
+		panic(fmt.Sprintf("Cannot find last on type %s", tp))
+	}
+}
+
+func first(list interface{}) interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		if l == 0 {
+			return nil
+		}
+
+		return l2.Index(0).Interface()
+	default:
+		panic(fmt.Sprintf("Cannot find first on type %s", tp))
+	}
+}
+
+func rest(list interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		if l == 0 {
+			return nil
+		}
+
+		nl := make([]interface{}, l-1)
+		for i := 1; i < l; i++ {
+			nl[i-1] = l2.Index(i).Interface()
+		}
+
+		return nl
+	default:
+		panic(fmt.Sprintf("Cannot find rest on type %s", tp))
+	}
+}
+
+func initial(list interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		if l == 0 {
+			return nil
+		}
+
+		nl := make([]interface{}, l-1)
+		for i := 0; i < l-1; i++ {
+			nl[i] = l2.Index(i).Interface()
+		}
+
+		return nl
+	default:
+		panic(fmt.Sprintf("Cannot find initial on type %s", tp))
+	}
+}
+
+func sortAlpha(list interface{}) []string {
+	k := reflect.Indirect(reflect.ValueOf(list)).Kind()
+	switch k {
+	case reflect.Slice, reflect.Array:
+		a := strslice(list)
+		s := sort.StringSlice(a)
+		s.Sort()
+		return s
+	}
+	return []string{strval(list)}
+}
+
+func reverse(v interface{}) []interface{} {
+	tp := reflect.TypeOf(v).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(v)
+
+		l := l2.Len()
+		// We do not sort in place because the incoming array should not be altered.
+		nl := make([]interface{}, l)
+		for i := 0; i < l; i++ {
+			nl[l-i-1] = l2.Index(i).Interface()
+		}
+
+		return nl
+	default:
+		panic(fmt.Sprintf("Cannot find reverse on type %s", tp))
+	}
+}
+
+func compact(list interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		nl := []interface{}{}
+		var item interface{}
+		for i := 0; i < l; i++ {
+			item = l2.Index(i).Interface()
+			if !empty(item) {
+				nl = append(nl, item)
+			}
+		}
+
+		return nl
+	default:
+		panic(fmt.Sprintf("Cannot compact on type %s", tp))
+	}
+}
+
+func uniq(list interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		dest := []interface{}{}
+		var item interface{}
+		for i := 0; i < l; i++ {
+			item = l2.Index(i).Interface()
+			if !inList(dest, item) {
+				dest = append(dest, item)
+			}
+		}
+
+		return dest
+	default:
+		panic(fmt.Sprintf("Cannot find uniq on type %s", tp))
+	}
+}
+
+func inList(haystack []interface{}, needle interface{}) bool {
+	for _, h := range haystack {
+		if reflect.DeepEqual(needle, h) {
+			return true
+		}
+	}
+	return false
+}
+
+func without(list interface{}, omit ...interface{}) []interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		res := []interface{}{}
+		var item interface{}
+		for i := 0; i < l; i++ {
+			item = l2.Index(i).Interface()
+			if !inList(omit, item) {
+				res = append(res, item)
+			}
+		}
+
+		return res
+	default:
+		panic(fmt.Sprintf("Cannot find without on type %s", tp))
+	}
+}
+
+func has(needle interface{}, haystack interface{}) bool {
+	if haystack == nil {
+		return false
+	}
+	tp := reflect.TypeOf(haystack).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(haystack)
+		var item interface{}
+		l := l2.Len()
+		for i := 0; i < l; i++ {
+			item = l2.Index(i).Interface()
+			if reflect.DeepEqual(needle, item) {
+				return true
+			}
+		}
+
+		return false
+	default:
+		panic(fmt.Sprintf("Cannot find has on type %s", tp))
+	}
+}
+
+// $list := [1, 2, 3, 4, 5]
+// slice $list     -> list[0:5] = list[:]
+// slice $list 0 3 -> list[0:3] = list[:3]
+// slice $list 3 5 -> list[3:5]
+// slice $list 3   -> list[3:5] = list[3:]
+func slice(list interface{}, indices ...interface{}) interface{} {
+	tp := reflect.TypeOf(list).Kind()
+	switch tp {
+	case reflect.Slice, reflect.Array:
+		l2 := reflect.ValueOf(list)
+
+		l := l2.Len()
+		if l == 0 {
+			return nil
+		}
+
+		var start, end int
+		if len(indices) > 0 {
+			start = toInt(indices[0])
+		}
+		if len(indices) < 2 {
+			end = l
+		} else {
+			end = toInt(indices[1])
+		}
+
+		return l2.Slice(start, end).Interface()
+	default:
+		panic(fmt.Sprintf("list should be type of slice or array but %s", tp))
+	}
+}
+
+func concat(lists ...interface{}) interface{} {
+	var res []interface{}
+	for _, list := range lists {
+		tp := reflect.TypeOf(list).Kind()
+		switch tp {
+		case reflect.Slice, reflect.Array:
+			l2 := reflect.ValueOf(list)
+			for i := 0; i < l2.Len(); i++ {
+				res = append(res, l2.Index(i).Interface())
+			}
+		default:
+			panic(fmt.Sprintf("Cannot concat type %s as list", tp))
+		}
+	}
+	return res
+}

+ 12 - 0
vendor/github.com/Masterminds/sprig/network.go

@@ -0,0 +1,12 @@
+package sprig
+
+import (
+	"math/rand"
+	"net"
+)
+
+func getHostByName(name string) string {
+	addrs, _ := net.LookupHost(name)
+	//TODO: add error handing when release v3 cames out
+	return addrs[rand.Intn(len(addrs))]
+}

+ 169 - 0
vendor/github.com/Masterminds/sprig/numeric.go

@@ -0,0 +1,169 @@
+package sprig
+
+import (
+	"fmt"
+	"math"
+	"reflect"
+	"strconv"
+)
+
+// toFloat64 converts 64-bit floats
+func toFloat64(v interface{}) float64 {
+	if str, ok := v.(string); ok {
+		iv, err := strconv.ParseFloat(str, 64)
+		if err != nil {
+			return 0
+		}
+		return iv
+	}
+
+	val := reflect.Indirect(reflect.ValueOf(v))
+	switch val.Kind() {
+	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+		return float64(val.Int())
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
+		return float64(val.Uint())
+	case reflect.Uint, reflect.Uint64:
+		return float64(val.Uint())
+	case reflect.Float32, reflect.Float64:
+		return val.Float()
+	case reflect.Bool:
+		if val.Bool() == true {
+			return 1
+		}
+		return 0
+	default:
+		return 0
+	}
+}
+
+func toInt(v interface{}) int {
+	//It's not optimal. Bud I don't want duplicate toInt64 code.
+	return int(toInt64(v))
+}
+
+// toInt64 converts integer types to 64-bit integers
+func toInt64(v interface{}) int64 {
+	if str, ok := v.(string); ok {
+		iv, err := strconv.ParseInt(str, 10, 64)
+		if err != nil {
+			return 0
+		}
+		return iv
+	}
+
+	val := reflect.Indirect(reflect.ValueOf(v))
+	switch val.Kind() {
+	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+		return val.Int()
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32:
+		return int64(val.Uint())
+	case reflect.Uint, reflect.Uint64:
+		tv := val.Uint()
+		if tv <= math.MaxInt64 {
+			return int64(tv)
+		}
+		// TODO: What is the sensible thing to do here?
+		return math.MaxInt64
+	case reflect.Float32, reflect.Float64:
+		return int64(val.Float())
+	case reflect.Bool:
+		if val.Bool() == true {
+			return 1
+		}
+		return 0
+	default:
+		return 0
+	}
+}
+
+func max(a interface{}, i ...interface{}) int64 {
+	aa := toInt64(a)
+	for _, b := range i {
+		bb := toInt64(b)
+		if bb > aa {
+			aa = bb
+		}
+	}
+	return aa
+}
+
+func min(a interface{}, i ...interface{}) int64 {
+	aa := toInt64(a)
+	for _, b := range i {
+		bb := toInt64(b)
+		if bb < aa {
+			aa = bb
+		}
+	}
+	return aa
+}
+
+func until(count int) []int {
+	step := 1
+	if count < 0 {
+		step = -1
+	}
+	return untilStep(0, count, step)
+}
+
+func untilStep(start, stop, step int) []int {
+	v := []int{}
+
+	if stop < start {
+		if step >= 0 {
+			return v
+		}
+		for i := start; i > stop; i += step {
+			v = append(v, i)
+		}
+		return v
+	}
+
+	if step <= 0 {
+		return v
+	}
+	for i := start; i < stop; i += step {
+		v = append(v, i)
+	}
+	return v
+}
+
+func floor(a interface{}) float64 {
+	aa := toFloat64(a)
+	return math.Floor(aa)
+}
+
+func ceil(a interface{}) float64 {
+	aa := toFloat64(a)
+	return math.Ceil(aa)
+}
+
+func round(a interface{}, p int, r_opt ...float64) float64 {
+	roundOn := .5
+	if len(r_opt) > 0 {
+		roundOn = r_opt[0]
+	}
+	val := toFloat64(a)
+	places := toFloat64(p)
+
+	var round float64
+	pow := math.Pow(10, places)
+	digit := pow * val
+	_, div := math.Modf(digit)
+	if div >= roundOn {
+		round = math.Ceil(digit)
+	} else {
+		round = math.Floor(digit)
+	}
+	return round / pow
+}
+
+// converts unix octal to decimal
+func toDecimal(v interface{}) int64 {
+	result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64)
+	if err != nil {
+		return 0
+	}
+	return result
+}

+ 28 - 0
vendor/github.com/Masterminds/sprig/reflect.go

@@ -0,0 +1,28 @@
+package sprig
+
+import (
+	"fmt"
+	"reflect"
+)
+
+// typeIs returns true if the src is the type named in target.
+func typeIs(target string, src interface{}) bool {
+	return target == typeOf(src)
+}
+
+func typeIsLike(target string, src interface{}) bool {
+	t := typeOf(src)
+	return target == t || "*"+target == t
+}
+
+func typeOf(src interface{}) string {
+	return fmt.Sprintf("%T", src)
+}
+
+func kindIs(target string, src interface{}) bool {
+	return target == kindOf(src)
+}
+
+func kindOf(src interface{}) string {
+	return reflect.ValueOf(src).Kind().String()
+}

+ 35 - 0
vendor/github.com/Masterminds/sprig/regex.go

@@ -0,0 +1,35 @@
+package sprig
+
+import (
+	"regexp"
+)
+
+func regexMatch(regex string, s string) bool {
+	match, _ := regexp.MatchString(regex, s)
+	return match
+}
+
+func regexFindAll(regex string, s string, n int) []string {
+	r := regexp.MustCompile(regex)
+	return r.FindAllString(s, n)
+}
+
+func regexFind(regex string, s string) string {
+	r := regexp.MustCompile(regex)
+	return r.FindString(s)
+}
+
+func regexReplaceAll(regex string, s string, repl string) string {
+	r := regexp.MustCompile(regex)
+	return r.ReplaceAllString(s, repl)
+}
+
+func regexReplaceAllLiteral(regex string, s string, repl string) string {
+	r := regexp.MustCompile(regex)
+	return r.ReplaceAllLiteralString(s, repl)
+}
+
+func regexSplit(regex string, s string, n int) []string {
+	r := regexp.MustCompile(regex)
+	return r.Split(s, n)
+}

+ 23 - 0
vendor/github.com/Masterminds/sprig/semver.go

@@ -0,0 +1,23 @@
+package sprig
+
+import (
+	sv2 "github.com/Masterminds/semver"
+)
+
+func semverCompare(constraint, version string) (bool, error) {
+	c, err := sv2.NewConstraint(constraint)
+	if err != nil {
+		return false, err
+	}
+
+	v, err := sv2.NewVersion(version)
+	if err != nil {
+		return false, err
+	}
+
+	return c.Check(v), nil
+}
+
+func semver(version string) (*sv2.Version, error) {
+	return sv2.NewVersion(version)
+}

+ 233 - 0
vendor/github.com/Masterminds/sprig/strings.go

@@ -0,0 +1,233 @@
+package sprig
+
+import (
+	"encoding/base32"
+	"encoding/base64"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+
+	util "github.com/Masterminds/goutils"
+)
+
+func base64encode(v string) string {
+	return base64.StdEncoding.EncodeToString([]byte(v))
+}
+
+func base64decode(v string) string {
+	data, err := base64.StdEncoding.DecodeString(v)
+	if err != nil {
+		return err.Error()
+	}
+	return string(data)
+}
+
+func base32encode(v string) string {
+	return base32.StdEncoding.EncodeToString([]byte(v))
+}
+
+func base32decode(v string) string {
+	data, err := base32.StdEncoding.DecodeString(v)
+	if err != nil {
+		return err.Error()
+	}
+	return string(data)
+}
+
+func abbrev(width int, s string) string {
+	if width < 4 {
+		return s
+	}
+	r, _ := util.Abbreviate(s, width)
+	return r
+}
+
+func abbrevboth(left, right int, s string) string {
+	if right < 4 || left > 0 && right < 7 {
+		return s
+	}
+	r, _ := util.AbbreviateFull(s, left, right)
+	return r
+}
+func initials(s string) string {
+	// Wrap this just to eliminate the var args, which templates don't do well.
+	return util.Initials(s)
+}
+
+func randAlphaNumeric(count int) string {
+	// It is not possible, it appears, to actually generate an error here.
+	r, _ := util.CryptoRandomAlphaNumeric(count)
+	return r
+}
+
+func randAlpha(count int) string {
+	r, _ := util.CryptoRandomAlphabetic(count)
+	return r
+}
+
+func randAscii(count int) string {
+	r, _ := util.CryptoRandomAscii(count)
+	return r
+}
+
+func randNumeric(count int) string {
+	r, _ := util.CryptoRandomNumeric(count)
+	return r
+}
+
+func untitle(str string) string {
+	return util.Uncapitalize(str)
+}
+
+func quote(str ...interface{}) string {
+	out := make([]string, 0, len(str))
+	for _, s := range str {
+		if s != nil {
+			out = append(out, fmt.Sprintf("%q", strval(s)))
+		}
+	}
+	return strings.Join(out, " ")
+}
+
+func squote(str ...interface{}) string {
+	out := make([]string, 0, len(str))
+	for _, s := range str {
+		if s != nil {
+			out = append(out, fmt.Sprintf("'%v'", s))
+		}
+	}
+	return strings.Join(out, " ")
+}
+
+func cat(v ...interface{}) string {
+	v = removeNilElements(v)
+	r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
+	return fmt.Sprintf(r, v...)
+}
+
+func indent(spaces int, v string) string {
+	pad := strings.Repeat(" ", spaces)
+	return pad + strings.Replace(v, "\n", "\n"+pad, -1)
+}
+
+func nindent(spaces int, v string) string {
+	return "\n" + indent(spaces, v)
+}
+
+func replace(old, new, src string) string {
+	return strings.Replace(src, old, new, -1)
+}
+
+func plural(one, many string, count int) string {
+	if count == 1 {
+		return one
+	}
+	return many
+}
+
+func strslice(v interface{}) []string {
+	switch v := v.(type) {
+	case []string:
+		return v
+	case []interface{}:
+		b := make([]string, 0, len(v))
+		for _, s := range v {
+			if s != nil {
+				b = append(b, strval(s))
+			}
+		}
+		return b
+	default:
+		val := reflect.ValueOf(v)
+		switch val.Kind() {
+		case reflect.Array, reflect.Slice:
+			l := val.Len()
+			b := make([]string, 0, l)
+			for i := 0; i < l; i++ {
+				value := val.Index(i).Interface()
+				if value != nil {
+					b = append(b, strval(value))
+				}
+			}
+			return b
+		default:
+			if v == nil {
+				return []string{}
+			} else {
+				return []string{strval(v)}
+			}
+		}
+	}
+}
+
+func removeNilElements(v []interface{}) []interface{} {
+	newSlice := make([]interface{}, 0, len(v))
+	for _, i := range v {
+		if i != nil {
+			newSlice = append(newSlice, i)
+		}
+	}
+	return newSlice
+}
+
+func strval(v interface{}) string {
+	switch v := v.(type) {
+	case string:
+		return v
+	case []byte:
+		return string(v)
+	case error:
+		return v.Error()
+	case fmt.Stringer:
+		return v.String()
+	default:
+		return fmt.Sprintf("%v", v)
+	}
+}
+
+func trunc(c int, s string) string {
+	if len(s) <= c {
+		return s
+	}
+	return s[0:c]
+}
+
+func join(sep string, v interface{}) string {
+	return strings.Join(strslice(v), sep)
+}
+
+func split(sep, orig string) map[string]string {
+	parts := strings.Split(orig, sep)
+	res := make(map[string]string, len(parts))
+	for i, v := range parts {
+		res["_"+strconv.Itoa(i)] = v
+	}
+	return res
+}
+
+func splitn(sep string, n int, orig string) map[string]string {
+	parts := strings.SplitN(orig, sep, n)
+	res := make(map[string]string, len(parts))
+	for i, v := range parts {
+		res["_"+strconv.Itoa(i)] = v
+	}
+	return res
+}
+
+// substring creates a substring of the given string.
+//
+// If start is < 0, this calls string[:end].
+//
+// If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:]
+//
+// Otherwise, this calls string[start, end].
+func substring(start, end int, s string) string {
+	if start < 0 {
+		return s[:end]
+	}
+	if end < 0 || end > len(s) {
+		return s[start:]
+	}
+	return s[start:end]
+}

+ 66 - 0
vendor/github.com/Masterminds/sprig/url.go

@@ -0,0 +1,66 @@
+package sprig
+
+import (
+	"fmt"
+	"net/url"
+	"reflect"
+)
+
+func dictGetOrEmpty(dict map[string]interface{}, key string) string {
+	value, ok := dict[key]; if !ok {
+		return ""
+	}
+	tp := reflect.TypeOf(value).Kind()
+	if tp != reflect.String {
+		panic(fmt.Sprintf("unable to parse %s key, must be of type string, but %s found", key, tp.String()))
+	}
+	return reflect.ValueOf(value).String()
+}
+
+// parses given URL to return dict object
+func urlParse(v string) map[string]interface{} {
+	dict := map[string]interface{}{}
+	parsedUrl, err := url.Parse(v)
+	if err != nil {
+		panic(fmt.Sprintf("unable to parse url: %s", err))
+	}
+	dict["scheme"]    = parsedUrl.Scheme
+	dict["host"]      = parsedUrl.Host
+	dict["hostname"]  = parsedUrl.Hostname()
+	dict["path"]      = parsedUrl.Path
+	dict["query"]     = parsedUrl.RawQuery
+	dict["opaque"]    = parsedUrl.Opaque
+	dict["fragment"]  = parsedUrl.Fragment
+	if parsedUrl.User != nil {
+		dict["userinfo"]  = parsedUrl.User.String()
+	} else {
+		dict["userinfo"] = ""
+	}
+
+	return dict
+}
+
+// join given dict to URL string
+func urlJoin(d map[string]interface{}) string {
+	resUrl := url.URL{
+		Scheme:   dictGetOrEmpty(d, "scheme"),
+		Host:     dictGetOrEmpty(d, "host"),
+		Path:     dictGetOrEmpty(d, "path"),
+		RawQuery: dictGetOrEmpty(d, "query"),
+		Opaque:   dictGetOrEmpty(d, "opaque"),
+		Fragment: dictGetOrEmpty(d, "fragment"),
+
+	}
+	userinfo := dictGetOrEmpty(d, "userinfo")
+	var user *url.Userinfo = nil
+	if userinfo != "" {
+		tempUrl, err := url.Parse(fmt.Sprintf("proto://%s@host", userinfo))
+		if err != nil {
+			panic(fmt.Sprintf("unable to parse userinfo in dict: %s", err))
+		}
+		user = tempUrl.User
+	}
+
+	resUrl.User = user
+	return resUrl.String()
+}

+ 1 - 0
vendor/github.com/PuerkitoBio/goquery/.gitattributes

@@ -0,0 +1 @@
+testdata/* linguist-vendored

+ 16 - 0
vendor/github.com/PuerkitoBio/goquery/.gitignore

@@ -0,0 +1,16 @@
+# editor temporary files
+*.sublime-*
+.DS_Store
+*.swp
+#*.*#
+tags
+
+# direnv config
+.env*
+
+# test binaries
+*.test
+
+# coverage and profilte outputs
+*.out
+

+ 17 - 0
vendor/github.com/PuerkitoBio/goquery/.travis.yml

@@ -0,0 +1,17 @@
+language: go
+
+go:
+    - 1.2.x
+    - 1.3.x
+    - 1.4.x
+    - 1.5.x
+    - 1.6.x
+    - 1.7.x
+    - 1.8.x
+    - 1.9.x
+    - 1.10.x
+    - 1.11.x
+    - 1.12.x
+    - 1.13.x
+    - tip
+

+ 12 - 0
vendor/github.com/PuerkitoBio/goquery/LICENSE

@@ -0,0 +1,12 @@
+Copyright (c) 2012-2016, Martin Angers & Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+* 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.
+
+* 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.
+
+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.

+ 183 - 0
vendor/github.com/PuerkitoBio/goquery/README.md

@@ -0,0 +1,183 @@
+# goquery - a little like that j-thing, only in Go
+[![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)
+
+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.
+
+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.
+
+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]...).
+
+## Table of Contents
+
+* [Installation](#installation)
+* [Changelog](#changelog)
+* [API](#api)
+* [Examples](#examples)
+* [Related Projects](#related-projects)
+* [Support](#support)
+* [License](#license)
+
+## Installation
+
+Please note that because of the net/html dependency, goquery requires Go1.1+.
+
+    $ go get github.com/PuerkitoBio/goquery
+
+(optional) To run unit tests:
+
+    $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
+    $ go test
+
+(optional) To run benchmarks (warning: it runs for a few minutes):
+
+    $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
+    $ go test -bench=".*"
+
+## Changelog
+
+**Note that goquery's API is now stable, and will not break.**
+
+*    **2020-02-04 (v1.5.1)** : Update module dependencies.
+*    **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
+*    **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
+*    **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
+*    **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
+*    **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
+*    **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
+*    **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
+*    **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
+*    **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
+*    **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.
+*    **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`).
+*    **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
+*    **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
+*    **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone].
+*    **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.
+*    **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.
+*    **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader.
+*    **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response.
+*    **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.
+*    **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out).
+*    **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.
+*    **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).
+*    **v0.1.0** : Initial release.
+
+## API
+
+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.
+
+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:
+
+*    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()`)
+*    When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`)
+*    The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`)
+*    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()`)
+*    The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`)
+*    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()`)
+
+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).
+
+The complete [godoc reference documentation can be found here][doc].
+
+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):
+
+* `Find("~")` returns an empty selection because the selector string doesn't match anything.
+* `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).
+* `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
+* `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
+
+## Examples
+
+See some tips and tricks in the [wiki][].
+
+Adapted from example_test.go:
+
+```Go
+package main
+
+import (
+  "fmt"
+  "log"
+  "net/http"
+
+  "github.com/PuerkitoBio/goquery"
+)
+
+func ExampleScrape() {
+  // Request the HTML page.
+  res, err := http.Get("http://metalsucks.net")
+  if err != nil {
+    log.Fatal(err)
+  }
+  defer res.Body.Close()
+  if res.StatusCode != 200 {
+    log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
+  }
+
+  // Load the HTML document
+  doc, err := goquery.NewDocumentFromReader(res.Body)
+  if err != nil {
+    log.Fatal(err)
+  }
+
+  // Find the review items
+  doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
+    // For each item found, get the band and title
+    band := s.Find("a").Text()
+    title := s.Find("i").Text()
+    fmt.Printf("Review %d: %s - %s\n", i, band, title)
+  })
+}
+
+func main() {
+  ExampleScrape()
+}
+```
+
+## Related Projects
+
+- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
+- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
+- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
+- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
+- [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
+- [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
+- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
+- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.  
+- [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
+
+## Support
+
+There are a number of ways you can support the project:
+
+* Use it, star it, build something with it, spread the word!
+  - 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!
+* Raise issues to improve the project (note: doc typos and clarifications are issues too!)
+  - Please search existing issues before opening a new one - it may have already been adressed.
+* Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
+  - Make sure new code is tested.
+  - Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
+
+If you desperately want to send money my way, I have a BuyMeACoffee.com page:
+
+<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>
+
+## License
+
+The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
+
+[jquery]: http://jquery.com/
+[go]: http://golang.org/
+[cascadia]: https://github.com/andybalholm/cascadia
+[cascadiacli]: https://github.com/suntong/cascadia
+[bsd]: http://opensource.org/licenses/BSD-3-Clause
+[golic]: http://golang.org/LICENSE
+[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
+[doc]: http://godoc.org/github.com/PuerkitoBio/goquery
+[index]: http://api.jquery.com/index/
+[gonet]: https://github.com/golang/net/
+[html]: http://godoc.org/golang.org/x/net/html
+[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
+[thatguystone]: https://github.com/thatguystone
+[piotr]: https://github.com/piotrkowalczuk
+[goq]: https://github.com/andrewstuart/goq

+ 124 - 0
vendor/github.com/PuerkitoBio/goquery/array.go

@@ -0,0 +1,124 @@
+package goquery
+
+import (
+	"golang.org/x/net/html"
+)
+
+const (
+	maxUint = ^uint(0)
+	maxInt  = int(maxUint >> 1)
+
+	// ToEnd is a special index value that can be used as end index in a call
+	// to Slice so that all elements are selected until the end of the Selection.
+	// It is equivalent to passing (*Selection).Length().
+	ToEnd = maxInt
+)
+
+// First reduces the set of matched elements to the first in the set.
+// It returns a new Selection object, and an empty Selection object if the
+// the selection is empty.
+func (s *Selection) First() *Selection {
+	return s.Eq(0)
+}
+
+// Last reduces the set of matched elements to the last in the set.
+// It returns a new Selection object, and an empty Selection object if
+// the selection is empty.
+func (s *Selection) Last() *Selection {
+	return s.Eq(-1)
+}
+
+// Eq reduces the set of matched elements to the one at the specified index.
+// If a negative index is given, it counts backwards starting at the end of the
+// set. It returns a new Selection object, and an empty Selection object if the
+// index is invalid.
+func (s *Selection) Eq(index int) *Selection {
+	if index < 0 {
+		index += len(s.Nodes)
+	}
+
+	if index >= len(s.Nodes) || index < 0 {
+		return newEmptySelection(s.document)
+	}
+
+	return s.Slice(index, index+1)
+}
+
+// Slice reduces the set of matched elements to a subset specified by a range
+// of indices. The start index is 0-based and indicates the index of the first
+// element to select. The end index is 0-based and indicates the index at which
+// the elements stop being selected (the end index is not selected).
+//
+// The indices may be negative, in which case they represent an offset from the
+// end of the selection.
+//
+// The special value ToEnd may be specified as end index, in which case all elements
+// until the end are selected. This works both for a positive and negative start
+// index.
+func (s *Selection) Slice(start, end int) *Selection {
+	if start < 0 {
+		start += len(s.Nodes)
+	}
+	if end == ToEnd {
+		end = len(s.Nodes)
+	} else if end < 0 {
+		end += len(s.Nodes)
+	}
+	return pushStack(s, s.Nodes[start:end])
+}
+
+// Get retrieves the underlying node at the specified index.
+// Get without parameter is not implemented, since the node array is available
+// on the Selection object.
+func (s *Selection) Get(index int) *html.Node {
+	if index < 0 {
+		index += len(s.Nodes) // Negative index gets from the end
+	}
+	return s.Nodes[index]
+}
+
+// Index returns the position of the first element within the Selection object
+// relative to its sibling elements.
+func (s *Selection) Index() int {
+	if len(s.Nodes) > 0 {
+		return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
+	}
+	return -1
+}
+
+// IndexSelector returns the position of the first element within the
+// Selection object relative to the elements matched by the selector, or -1 if
+// not found.
+func (s *Selection) IndexSelector(selector string) int {
+	if len(s.Nodes) > 0 {
+		sel := s.document.Find(selector)
+		return indexInSlice(sel.Nodes, s.Nodes[0])
+	}
+	return -1
+}
+
+// IndexMatcher returns the position of the first element within the
+// Selection object relative to the elements matched by the matcher, or -1 if
+// not found.
+func (s *Selection) IndexMatcher(m Matcher) int {
+	if len(s.Nodes) > 0 {
+		sel := s.document.FindMatcher(m)
+		return indexInSlice(sel.Nodes, s.Nodes[0])
+	}
+	return -1
+}
+
+// IndexOfNode returns the position of the specified node within the Selection
+// object, or -1 if not found.
+func (s *Selection) IndexOfNode(node *html.Node) int {
+	return indexInSlice(s.Nodes, node)
+}
+
+// IndexOfSelection returns the position of the first node in the specified
+// Selection object within this Selection object, or -1 if not found.
+func (s *Selection) IndexOfSelection(sel *Selection) int {
+	if sel != nil && len(sel.Nodes) > 0 {
+		return indexInSlice(s.Nodes, sel.Nodes[0])
+	}
+	return -1
+}

+ 123 - 0
vendor/github.com/PuerkitoBio/goquery/doc.go

@@ -0,0 +1,123 @@
+// Copyright (c) 2012-2016, Martin Angers & Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * 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.
+// * 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.
+//
+// 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.
+
+/*
+Package goquery implements features similar to jQuery, including the chainable
+syntax, to manipulate and query an HTML document.
+
+It brings a syntax and a set of features similar to jQuery to the Go language.
+It is based on Go's net/html package 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.
+
+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 repository's wiki for various options on how to do this.
+
+Syntax-wise, it is as close as possible to jQuery, with the same method names when
+possible, and that warm and fuzzy chainable interface. jQuery being the
+ultra-popular library that it is, 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()...).
+
+It is hosted on GitHub, along with additional documentation in the README.md
+file: https://github.com/puerkitobio/goquery
+
+Please note that because of the net/html dependency, goquery requires Go1.1+.
+
+The various methods are split into files based on the category of behavior.
+The three dots (...) indicate that various "overloads" are available.
+
+* array.go : array-like positional manipulation of the selection.
+    - Eq()
+    - First()
+    - Get()
+    - Index...()
+    - Last()
+    - Slice()
+
+* expand.go : methods that expand or augment the selection's set.
+    - Add...()
+    - AndSelf()
+    - Union(), which is an alias for AddSelection()
+
+* filter.go : filtering methods, that reduce the selection's set.
+    - End()
+    - Filter...()
+    - Has...()
+    - Intersection(), which is an alias of FilterSelection()
+    - Not...()
+
+* iteration.go : methods to loop over the selection's nodes.
+    - Each()
+    - EachWithBreak()
+    - Map()
+
+* manipulation.go : methods for modifying the document
+    - After...()
+    - Append...()
+    - Before...()
+    - Clone()
+    - Empty()
+    - Prepend...()
+    - Remove...()
+    - ReplaceWith...()
+    - Unwrap()
+    - Wrap...()
+    - WrapAll...()
+    - WrapInner...()
+
+* property.go : methods that inspect and get the node's properties values.
+    - Attr*(), RemoveAttr(), SetAttr()
+    - AddClass(), HasClass(), RemoveClass(), ToggleClass()
+    - Html()
+    - Length()
+    - Size(), which is an alias for Length()
+    - Text()
+
+* query.go : methods that query, or reflect, a node's identity.
+    - Contains()
+    - Is...()
+
+* traversal.go : methods to traverse the HTML document tree.
+    - Children...()
+    - Contents()
+    - Find...()
+    - Next...()
+    - Parent[s]...()
+    - Prev...()
+    - Siblings...()
+
+* type.go : definition of the types exposed by goquery.
+    - Document
+    - Selection
+    - Matcher
+
+* utilities.go : definition of helper functions (and not methods on a *Selection)
+that are not part of jQuery, but are useful to goquery.
+    - NodeName
+    - OuterHtml
+*/
+package goquery

+ 70 - 0
vendor/github.com/PuerkitoBio/goquery/expand.go

@@ -0,0 +1,70 @@
+package goquery
+
+import "golang.org/x/net/html"
+
+// Add adds the selector string's matching nodes to those in the current
+// selection and returns a new Selection object.
+// The selector string is run in the context of the document of the current
+// Selection object.
+func (s *Selection) Add(selector string) *Selection {
+	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...)
+}
+
+// AddMatcher adds the matcher's matching nodes to those in the current
+// selection and returns a new Selection object.
+// The matcher is run in the context of the document of the current
+// Selection object.
+func (s *Selection) AddMatcher(m Matcher) *Selection {
+	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...)
+}
+
+// AddSelection adds the specified Selection object's nodes to those in the
+// current selection and returns a new Selection object.
+func (s *Selection) AddSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.AddNodes()
+	}
+	return s.AddNodes(sel.Nodes...)
+}
+
+// Union is an alias for AddSelection.
+func (s *Selection) Union(sel *Selection) *Selection {
+	return s.AddSelection(sel)
+}
+
+// AddNodes adds the specified nodes to those in the
+// current selection and returns a new Selection object.
+func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
+}
+
+// AndSelf adds the previous set of elements on the stack to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the previous one.
+// Deprecated: This function has been deprecated and is now an alias for AddBack().
+func (s *Selection) AndSelf() *Selection {
+	return s.AddBack()
+}
+
+// AddBack adds the previous set of elements on the stack to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the previous one.
+func (s *Selection) AddBack() *Selection {
+	return s.AddSelection(s.prevSel)
+}
+
+// AddBackFiltered reduces the previous set of elements on the stack to those that
+// match the selector string, and adds them to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the filtered previous one
+func (s *Selection) AddBackFiltered(selector string) *Selection {
+	return s.AddSelection(s.prevSel.Filter(selector))
+}
+
+// AddBackMatcher reduces the previous set of elements on the stack to those that match
+// the mateher, and adds them to the curernt set.
+// It returns a new Selection object containing the current Selection combined
+// with the filtered previous one
+func (s *Selection) AddBackMatcher(m Matcher) *Selection {
+	return s.AddSelection(s.prevSel.FilterMatcher(m))
+}

+ 163 - 0
vendor/github.com/PuerkitoBio/goquery/filter.go

@@ -0,0 +1,163 @@
+package goquery
+
+import "golang.org/x/net/html"
+
+// Filter reduces the set of matched elements to those that match the selector string.
+// It returns a new Selection object for this subset of matching elements.
+func (s *Selection) Filter(selector string) *Selection {
+	return s.FilterMatcher(compileMatcher(selector))
+}
+
+// FilterMatcher reduces the set of matched elements to those that match
+// the given matcher. It returns a new Selection object for this subset
+// of matching elements.
+func (s *Selection) FilterMatcher(m Matcher) *Selection {
+	return pushStack(s, winnow(s, m, true))
+}
+
+// Not removes elements from the Selection that match the selector string.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) Not(selector string) *Selection {
+	return s.NotMatcher(compileMatcher(selector))
+}
+
+// NotMatcher removes elements from the Selection that match the given matcher.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotMatcher(m Matcher) *Selection {
+	return pushStack(s, winnow(s, m, false))
+}
+
+// FilterFunction reduces the set of matched elements to those that pass the function's test.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
+	return pushStack(s, winnowFunction(s, f, true))
+}
+
+// NotFunction removes elements from the Selection that pass the function's test.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
+	return pushStack(s, winnowFunction(s, f, false))
+}
+
+// FilterNodes reduces the set of matched elements to those that match the specified nodes.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, winnowNodes(s, nodes, true))
+}
+
+// NotNodes removes elements from the Selection that match the specified nodes.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, winnowNodes(s, nodes, false))
+}
+
+// FilterSelection reduces the set of matched elements to those that match a
+// node in the specified Selection object.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, winnowNodes(s, nil, true))
+	}
+	return pushStack(s, winnowNodes(s, sel.Nodes, true))
+}
+
+// NotSelection removes elements from the Selection that match a node in the specified
+// Selection object. It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, winnowNodes(s, nil, false))
+	}
+	return pushStack(s, winnowNodes(s, sel.Nodes, false))
+}
+
+// Intersection is an alias for FilterSelection.
+func (s *Selection) Intersection(sel *Selection) *Selection {
+	return s.FilterSelection(sel)
+}
+
+// Has reduces the set of matched elements to those that have a descendant
+// that matches the selector.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) Has(selector string) *Selection {
+	return s.HasSelection(s.document.Find(selector))
+}
+
+// HasMatcher reduces the set of matched elements to those that have a descendant
+// that matches the matcher.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasMatcher(m Matcher) *Selection {
+	return s.HasSelection(s.document.FindMatcher(m))
+}
+
+// HasNodes reduces the set of matched elements to those that have a
+// descendant that matches one of the nodes.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
+	return s.FilterFunction(func(_ int, sel *Selection) bool {
+		// Add all nodes that contain one of the specified nodes
+		for _, n := range nodes {
+			if sel.Contains(n) {
+				return true
+			}
+		}
+		return false
+	})
+}
+
+// HasSelection reduces the set of matched elements to those that have a
+// descendant that matches one of the nodes of the specified Selection object.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.HasNodes()
+	}
+	return s.HasNodes(sel.Nodes...)
+}
+
+// End ends the most recent filtering operation in the current chain and
+// returns the set of matched elements to its previous state.
+func (s *Selection) End() *Selection {
+	if s.prevSel != nil {
+		return s.prevSel
+	}
+	return newEmptySelection(s.document)
+}
+
+// Filter based on the matcher, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
+	// Optimize if keep is requested
+	if keep {
+		return m.Filter(sel.Nodes)
+	}
+	// Use grep
+	return grep(sel, func(i int, s *Selection) bool {
+		return !m.Match(s.Get(0))
+	})
+}
+
+// Filter based on an array of nodes, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
+	if len(nodes)+len(sel.Nodes) < minNodesForSet {
+		return grep(sel, func(i int, s *Selection) bool {
+			return isInSlice(nodes, s.Get(0)) == keep
+		})
+	}
+
+	set := make(map[*html.Node]bool)
+	for _, n := range nodes {
+		set[n] = true
+	}
+	return grep(sel, func(i int, s *Selection) bool {
+		return set[s.Get(0)] == keep
+	})
+}
+
+// Filter based on a function test, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
+	return grep(sel, func(i int, s *Selection) bool {
+		return f(i, s) == keep
+	})
+}

+ 39 - 0
vendor/github.com/PuerkitoBio/goquery/iteration.go

@@ -0,0 +1,39 @@
+package goquery
+
+// Each iterates over a Selection object, executing a function for each
+// matched element. It returns the current Selection object. The function
+// f is called for each element in the selection with the index of the
+// element in that selection starting at 0, and a *Selection that contains
+// only that element.
+func (s *Selection) Each(f func(int, *Selection)) *Selection {
+	for i, n := range s.Nodes {
+		f(i, newSingleSelection(n, s.document))
+	}
+	return s
+}
+
+// EachWithBreak iterates over a Selection object, executing a function for each
+// matched element. It is identical to Each except that it is possible to break
+// out of the loop by returning false in the callback function. It returns the
+// current Selection object.
+func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
+	for i, n := range s.Nodes {
+		if !f(i, newSingleSelection(n, s.document)) {
+			return s
+		}
+	}
+	return s
+}
+
+// Map passes each element in the current matched set through a function,
+// producing a slice of string holding the returned values. The function
+// f is called for each element in the selection with the index of the
+// element in that selection starting at 0, and a *Selection that contains
+// only that element.
+func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
+	for i, n := range s.Nodes {
+		result = append(result, f(i, newSingleSelection(n, s.document)))
+	}
+
+	return result
+}

+ 574 - 0
vendor/github.com/PuerkitoBio/goquery/manipulation.go

@@ -0,0 +1,574 @@
+package goquery
+
+import (
+	"strings"
+
+	"golang.org/x/net/html"
+)
+
+// After applies the selector from the root document and inserts the matched elements
+// after the elements in the set of matched elements.
+//
+// If one of the matched elements in the selection is not currently in the
+// document, it's impossible to insert nodes after it, so it will be ignored.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) After(selector string) *Selection {
+	return s.AfterMatcher(compileMatcher(selector))
+}
+
+// AfterMatcher applies the matcher from the root document and inserts the matched elements
+// after the elements in the set of matched elements.
+//
+// If one of the matched elements in the selection is not currently in the
+// document, it's impossible to insert nodes after it, so it will be ignored.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterMatcher(m Matcher) *Selection {
+	return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// AfterSelection inserts the elements in the selection after each element in the set of matched
+// elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterSelection(sel *Selection) *Selection {
+	return s.AfterNodes(sel.Nodes...)
+}
+
+// AfterHtml parses the html and inserts it after the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterHtml(html string) *Selection {
+	return s.AfterNodes(parseHtml(html)...)
+}
+
+// AfterNodes inserts the nodes after each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
+		if sn.Parent != nil {
+			sn.Parent.InsertBefore(n, sn.NextSibling)
+		}
+	})
+}
+
+// Append appends the elements specified by the selector to the end of each element
+// in the set of matched elements, following those rules:
+//
+// 1) The selector is applied to the root document.
+//
+// 2) Elements that are part of the document will be moved to the new location.
+//
+// 3) If there are multiple locations to append to, cloned nodes will be
+// appended to all target locations except the last one, which will be moved
+// as noted in (2).
+func (s *Selection) Append(selector string) *Selection {
+	return s.AppendMatcher(compileMatcher(selector))
+}
+
+// AppendMatcher appends the elements specified by the matcher to the end of each element
+// in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendMatcher(m Matcher) *Selection {
+	return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// AppendSelection appends the elements in the selection to the end of each element
+// in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendSelection(sel *Selection) *Selection {
+	return s.AppendNodes(sel.Nodes...)
+}
+
+// AppendHtml parses the html and appends it to the set of matched elements.
+func (s *Selection) AppendHtml(html string) *Selection {
+	return s.AppendNodes(parseHtml(html)...)
+}
+
+// AppendNodes appends the specified nodes to each node in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
+		sn.AppendChild(n)
+	})
+}
+
+// Before inserts the matched elements before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) Before(selector string) *Selection {
+	return s.BeforeMatcher(compileMatcher(selector))
+}
+
+// BeforeMatcher inserts the matched elements before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeMatcher(m Matcher) *Selection {
+	return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// BeforeSelection inserts the elements in the selection before each element in the set of matched
+// elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeSelection(sel *Selection) *Selection {
+	return s.BeforeNodes(sel.Nodes...)
+}
+
+// BeforeHtml parses the html and inserts it before the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeHtml(html string) *Selection {
+	return s.BeforeNodes(parseHtml(html)...)
+}
+
+// BeforeNodes inserts the nodes before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
+		if sn.Parent != nil {
+			sn.Parent.InsertBefore(n, sn)
+		}
+	})
+}
+
+// Clone creates a deep copy of the set of matched nodes. The new nodes will not be
+// attached to the document.
+func (s *Selection) Clone() *Selection {
+	ns := newEmptySelection(s.document)
+	ns.Nodes = cloneNodes(s.Nodes)
+	return ns
+}
+
+// Empty removes all children nodes from the set of matched elements.
+// It returns the children nodes in a new Selection.
+func (s *Selection) Empty() *Selection {
+	var nodes []*html.Node
+
+	for _, n := range s.Nodes {
+		for c := n.FirstChild; c != nil; c = n.FirstChild {
+			n.RemoveChild(c)
+			nodes = append(nodes, c)
+		}
+	}
+
+	return pushStack(s, nodes)
+}
+
+// Prepend prepends the elements specified by the selector to each element in
+// the set of matched elements, following the same rules as Append.
+func (s *Selection) Prepend(selector string) *Selection {
+	return s.PrependMatcher(compileMatcher(selector))
+}
+
+// PrependMatcher prepends the elements specified by the matcher to each
+// element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependMatcher(m Matcher) *Selection {
+	return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// PrependSelection prepends the elements in the selection to each element in
+// the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependSelection(sel *Selection) *Selection {
+	return s.PrependNodes(sel.Nodes...)
+}
+
+// PrependHtml parses the html and prepends it to the set of matched elements.
+func (s *Selection) PrependHtml(html string) *Selection {
+	return s.PrependNodes(parseHtml(html)...)
+}
+
+// PrependNodes prepends the specified nodes to each node in the set of
+// matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
+		// sn.FirstChild may be nil, in which case this functions like
+		// sn.AppendChild()
+		sn.InsertBefore(n, sn.FirstChild)
+	})
+}
+
+// Remove removes the set of matched elements from the document.
+// It returns the same selection, now consisting of nodes not in the document.
+func (s *Selection) Remove() *Selection {
+	for _, n := range s.Nodes {
+		if n.Parent != nil {
+			n.Parent.RemoveChild(n)
+		}
+	}
+
+	return s
+}
+
+// RemoveFiltered removes the set of matched elements by selector.
+// It returns the Selection of removed nodes.
+func (s *Selection) RemoveFiltered(selector string) *Selection {
+	return s.RemoveMatcher(compileMatcher(selector))
+}
+
+// RemoveMatcher removes the set of matched elements.
+// It returns the Selection of removed nodes.
+func (s *Selection) RemoveMatcher(m Matcher) *Selection {
+	return s.FilterMatcher(m).Remove()
+}
+
+// ReplaceWith replaces each element in the set of matched elements with the
+// nodes matched by the given selector.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWith(selector string) *Selection {
+	return s.ReplaceWithMatcher(compileMatcher(selector))
+}
+
+// ReplaceWithMatcher replaces each element in the set of matched elements with
+// the nodes matched by the given Matcher.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
+	return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// ReplaceWithSelection replaces each element in the set of matched elements with
+// the nodes from the given Selection.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
+	return s.ReplaceWithNodes(sel.Nodes...)
+}
+
+// ReplaceWithHtml replaces each element in the set of matched elements with
+// the parsed HTML.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithHtml(html string) *Selection {
+	return s.ReplaceWithNodes(parseHtml(html)...)
+}
+
+// ReplaceWithNodes replaces each element in the set of matched elements with
+// the given nodes.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
+	s.AfterNodes(ns...)
+	return s.Remove()
+}
+
+// SetHtml sets the html content of each element in the selection to
+// specified html string.
+func (s *Selection) SetHtml(html string) *Selection {
+	return setHtmlNodes(s, parseHtml(html)...)
+}
+
+// SetText sets the content of each element in the selection to specified content.
+// The provided text string is escaped.
+func (s *Selection) SetText(text string) *Selection {
+	return s.SetHtml(html.EscapeString(text))
+}
+
+// Unwrap removes the parents of the set of matched elements, leaving the matched
+// elements (and their siblings, if any) in their place.
+// It returns the original selection.
+func (s *Selection) Unwrap() *Selection {
+	s.Parent().Each(func(i int, ss *Selection) {
+		// For some reason, jquery allows unwrap to remove the <head> element, so
+		// allowing it here too. Same for <html>. Why it allows those elements to
+		// be unwrapped while not allowing body is a mystery to me.
+		if ss.Nodes[0].Data != "body" {
+			ss.ReplaceWithSelection(ss.Contents())
+		}
+	})
+
+	return s
+}
+
+// Wrap wraps each element in the set of matched elements inside the first
+// element matched by the given selector. The matched child is cloned before
+// being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) Wrap(selector string) *Selection {
+	return s.WrapMatcher(compileMatcher(selector))
+}
+
+// WrapMatcher wraps each element in the set of matched elements inside the
+// first element matched by the given matcher. The matched child is cloned
+// before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapMatcher(m Matcher) *Selection {
+	return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapSelection wraps each element in the set of matched elements inside the
+// first element in the given Selection. The element is cloned before being
+// inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapSelection(sel *Selection) *Selection {
+	return s.wrapNodes(sel.Nodes...)
+}
+
+// WrapHtml wraps each element in the set of matched elements inside the inner-
+// most child of the given HTML.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapHtml(html string) *Selection {
+	return s.wrapNodes(parseHtml(html)...)
+}
+
+// WrapNode wraps each element in the set of matched elements inside the inner-
+// most child of the given node. The given node is copied before being inserted
+// into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapNode(n *html.Node) *Selection {
+	return s.wrapNodes(n)
+}
+
+func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
+	s.Each(func(i int, ss *Selection) {
+		ss.wrapAllNodes(ns...)
+	})
+
+	return s
+}
+
+// WrapAll wraps a single HTML structure, matched by the given selector, around
+// all elements in the set of matched elements. The matched child is cloned
+// before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAll(selector string) *Selection {
+	return s.WrapAllMatcher(compileMatcher(selector))
+}
+
+// WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
+// around all elements in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
+	return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapAllSelection wraps a single HTML structure, the first node of the given
+// Selection, around all elements in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
+	return s.wrapAllNodes(sel.Nodes...)
+}
+
+// WrapAllHtml wraps the given HTML structure around all elements in the set of
+// matched elements. The matched child is cloned before being inserted into the
+// document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllHtml(html string) *Selection {
+	return s.wrapAllNodes(parseHtml(html)...)
+}
+
+func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
+	if len(ns) > 0 {
+		return s.WrapAllNode(ns[0])
+	}
+	return s
+}
+
+// WrapAllNode wraps the given node around the first element in the Selection,
+// making all other nodes in the Selection children of the given node. The node
+// is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllNode(n *html.Node) *Selection {
+	if s.Size() == 0 {
+		return s
+	}
+
+	wrap := cloneNode(n)
+
+	first := s.Nodes[0]
+	if first.Parent != nil {
+		first.Parent.InsertBefore(wrap, first)
+		first.Parent.RemoveChild(first)
+	}
+
+	for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
+		wrap = c
+	}
+
+	newSingleSelection(wrap, s.document).AppendSelection(s)
+
+	return s
+}
+
+// WrapInner wraps an HTML structure, matched by the given selector, around the
+// content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInner(selector string) *Selection {
+	return s.WrapInnerMatcher(compileMatcher(selector))
+}
+
+// WrapInnerMatcher wraps an HTML structure, matched by the given selector,
+// around the content of element in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
+	return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapInnerSelection wraps an HTML structure, matched by the given selector,
+// around the content of element in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
+	return s.wrapInnerNodes(sel.Nodes...)
+}
+
+// WrapInnerHtml wraps an HTML structure, matched by the given selector, around
+// the content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerHtml(html string) *Selection {
+	return s.wrapInnerNodes(parseHtml(html)...)
+}
+
+// WrapInnerNode wraps an HTML structure, matched by the given selector, around
+// the content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
+	return s.wrapInnerNodes(n)
+}
+
+func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
+	if len(ns) == 0 {
+		return s
+	}
+
+	s.Each(func(i int, s *Selection) {
+		contents := s.Contents()
+
+		if contents.Size() > 0 {
+			contents.wrapAllNodes(ns...)
+		} else {
+			s.AppendNodes(cloneNode(ns[0]))
+		}
+	})
+
+	return s
+}
+
+func parseHtml(h string) []*html.Node {
+	// Errors are only returned when the io.Reader returns any error besides
+	// EOF, but strings.Reader never will
+	nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
+	if err != nil {
+		panic("goquery: failed to parse HTML: " + err.Error())
+	}
+	return nodes
+}
+
+func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
+	for _, n := range s.Nodes {
+		for c := n.FirstChild; c != nil; c = n.FirstChild {
+			n.RemoveChild(c)
+		}
+		for _, c := range ns {
+			n.AppendChild(cloneNode(c))
+		}
+	}
+	return s
+}
+
+// Get the first child that is an ElementNode
+func getFirstChildEl(n *html.Node) *html.Node {
+	c := n.FirstChild
+	for c != nil && c.Type != html.ElementNode {
+		c = c.NextSibling
+	}
+	return c
+}
+
+// Deep copy a slice of nodes.
+func cloneNodes(ns []*html.Node) []*html.Node {
+	cns := make([]*html.Node, 0, len(ns))
+
+	for _, n := range ns {
+		cns = append(cns, cloneNode(n))
+	}
+
+	return cns
+}
+
+// Deep copy a node. The new node has clones of all the original node's
+// children but none of its parents or siblings.
+func cloneNode(n *html.Node) *html.Node {
+	nn := &html.Node{
+		Type:     n.Type,
+		DataAtom: n.DataAtom,
+		Data:     n.Data,
+		Attr:     make([]html.Attribute, len(n.Attr)),
+	}
+
+	copy(nn.Attr, n.Attr)
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		nn.AppendChild(cloneNode(c))
+	}
+
+	return nn
+}
+
+func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
+	f func(sn *html.Node, n *html.Node)) *Selection {
+
+	lasti := s.Size() - 1
+
+	// net.Html doesn't provide document fragments for insertion, so to get
+	// things in the correct order with After() and Prepend(), the callback
+	// needs to be called on the reverse of the nodes.
+	if reverse {
+		for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
+			ns[i], ns[j] = ns[j], ns[i]
+		}
+	}
+
+	for i, sn := range s.Nodes {
+		for _, n := range ns {
+			if i != lasti {
+				f(sn, cloneNode(n))
+			} else {
+				if n.Parent != nil {
+					n.Parent.RemoveChild(n)
+				}
+				f(sn, n)
+			}
+		}
+	}
+
+	return s
+}

+ 275 - 0
vendor/github.com/PuerkitoBio/goquery/property.go

@@ -0,0 +1,275 @@
+package goquery
+
+import (
+	"bytes"
+	"regexp"
+	"strings"
+
+	"golang.org/x/net/html"
+)
+
+var rxClassTrim = regexp.MustCompile("[\t\r\n]")
+
+// Attr gets the specified attribute's value for the first element in the
+// Selection. To get the value for each element individually, use a looping
+// construct such as Each or Map method.
+func (s *Selection) Attr(attrName string) (val string, exists bool) {
+	if len(s.Nodes) == 0 {
+		return
+	}
+	return getAttributeValue(attrName, s.Nodes[0])
+}
+
+// AttrOr works like Attr but returns default value if attribute is not present.
+func (s *Selection) AttrOr(attrName, defaultValue string) string {
+	if len(s.Nodes) == 0 {
+		return defaultValue
+	}
+
+	val, exists := getAttributeValue(attrName, s.Nodes[0])
+	if !exists {
+		return defaultValue
+	}
+
+	return val
+}
+
+// RemoveAttr removes the named attribute from each element in the set of matched elements.
+func (s *Selection) RemoveAttr(attrName string) *Selection {
+	for _, n := range s.Nodes {
+		removeAttr(n, attrName)
+	}
+
+	return s
+}
+
+// SetAttr sets the given attribute on each element in the set of matched elements.
+func (s *Selection) SetAttr(attrName, val string) *Selection {
+	for _, n := range s.Nodes {
+		attr := getAttributePtr(attrName, n)
+		if attr == nil {
+			n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
+		} else {
+			attr.Val = val
+		}
+	}
+
+	return s
+}
+
+// Text gets the combined text contents of each element in the set of matched
+// elements, including their descendants.
+func (s *Selection) Text() string {
+	var buf bytes.Buffer
+
+	// Slightly optimized vs calling Each: no single selection object created
+	var f func(*html.Node)
+	f = func(n *html.Node) {
+		if n.Type == html.TextNode {
+			// Keep newlines and spaces, like jQuery
+			buf.WriteString(n.Data)
+		}
+		if n.FirstChild != nil {
+			for c := n.FirstChild; c != nil; c = c.NextSibling {
+				f(c)
+			}
+		}
+	}
+	for _, n := range s.Nodes {
+		f(n)
+	}
+
+	return buf.String()
+}
+
+// Size is an alias for Length.
+func (s *Selection) Size() int {
+	return s.Length()
+}
+
+// Length returns the number of elements in the Selection object.
+func (s *Selection) Length() int {
+	return len(s.Nodes)
+}
+
+// Html gets the HTML contents of the first element in the set of matched
+// elements. It includes text and comment nodes.
+func (s *Selection) Html() (ret string, e error) {
+	// Since there is no .innerHtml, the HTML content must be re-created from
+	// the nodes using html.Render.
+	var buf bytes.Buffer
+
+	if len(s.Nodes) > 0 {
+		for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
+			e = html.Render(&buf, c)
+			if e != nil {
+				return
+			}
+		}
+		ret = buf.String()
+	}
+
+	return
+}
+
+// AddClass adds the given class(es) to each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+func (s *Selection) AddClass(class ...string) *Selection {
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+
+	if classStr == "" {
+		return s
+	}
+
+	tcls := getClassesSlice(classStr)
+	for _, n := range s.Nodes {
+		curClasses, attr := getClassesAndAttr(n, true)
+		for _, newClass := range tcls {
+			if !strings.Contains(curClasses, " "+newClass+" ") {
+				curClasses += newClass + " "
+			}
+		}
+
+		setClasses(n, attr, curClasses)
+	}
+
+	return s
+}
+
+// HasClass determines whether any of the matched elements are assigned the
+// given class.
+func (s *Selection) HasClass(class string) bool {
+	class = " " + class + " "
+	for _, n := range s.Nodes {
+		classes, _ := getClassesAndAttr(n, false)
+		if strings.Contains(classes, class) {
+			return true
+		}
+	}
+	return false
+}
+
+// RemoveClass removes the given class(es) from each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+// If no class name is provided, all classes are removed.
+func (s *Selection) RemoveClass(class ...string) *Selection {
+	var rclasses []string
+
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+	remove := classStr == ""
+
+	if !remove {
+		rclasses = getClassesSlice(classStr)
+	}
+
+	for _, n := range s.Nodes {
+		if remove {
+			removeAttr(n, "class")
+		} else {
+			classes, attr := getClassesAndAttr(n, true)
+			for _, rcl := range rclasses {
+				classes = strings.Replace(classes, " "+rcl+" ", " ", -1)
+			}
+
+			setClasses(n, attr, classes)
+		}
+	}
+
+	return s
+}
+
+// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+func (s *Selection) ToggleClass(class ...string) *Selection {
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+
+	if classStr == "" {
+		return s
+	}
+
+	tcls := getClassesSlice(classStr)
+
+	for _, n := range s.Nodes {
+		classes, attr := getClassesAndAttr(n, true)
+		for _, tcl := range tcls {
+			if strings.Contains(classes, " "+tcl+" ") {
+				classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
+			} else {
+				classes += tcl + " "
+			}
+		}
+
+		setClasses(n, attr, classes)
+	}
+
+	return s
+}
+
+func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
+	if n == nil {
+		return nil
+	}
+
+	for i, a := range n.Attr {
+		if a.Key == attrName {
+			return &n.Attr[i]
+		}
+	}
+	return nil
+}
+
+// Private function to get the specified attribute's value from a node.
+func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) {
+	if a := getAttributePtr(attrName, n); a != nil {
+		val = a.Val
+		exists = true
+	}
+	return
+}
+
+// Get and normalize the "class" attribute from the node.
+func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
+	// Applies only to element nodes
+	if n.Type == html.ElementNode {
+		attr = getAttributePtr("class", n)
+		if attr == nil && create {
+			n.Attr = append(n.Attr, html.Attribute{
+				Key: "class",
+				Val: "",
+			})
+			attr = &n.Attr[len(n.Attr)-1]
+		}
+	}
+
+	if attr == nil {
+		classes = " "
+	} else {
+		classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ")
+	}
+
+	return
+}
+
+func getClassesSlice(classes string) []string {
+	return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ")
+}
+
+func removeAttr(n *html.Node, attrName string) {
+	for i, a := range n.Attr {
+		if a.Key == attrName {
+			n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr =
+				n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1]
+			return
+		}
+	}
+}
+
+func setClasses(n *html.Node, attr *html.Attribute, classes string) {
+	classes = strings.TrimSpace(classes)
+	if classes == "" {
+		removeAttr(n, "class")
+		return
+	}
+
+	attr.Val = classes
+}

+ 49 - 0
vendor/github.com/PuerkitoBio/goquery/query.go

@@ -0,0 +1,49 @@
+package goquery
+
+import "golang.org/x/net/html"
+
+// Is checks the current matched set of elements against a selector and
+// returns true if at least one of these elements matches.
+func (s *Selection) Is(selector string) bool {
+	return s.IsMatcher(compileMatcher(selector))
+}
+
+// IsMatcher checks the current matched set of elements against a matcher and
+// returns true if at least one of these elements matches.
+func (s *Selection) IsMatcher(m Matcher) bool {
+	if len(s.Nodes) > 0 {
+		if len(s.Nodes) == 1 {
+			return m.Match(s.Nodes[0])
+		}
+		return len(m.Filter(s.Nodes)) > 0
+	}
+
+	return false
+}
+
+// IsFunction checks the current matched set of elements against a predicate and
+// returns true if at least one of these elements matches.
+func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
+	return s.FilterFunction(f).Length() > 0
+}
+
+// IsSelection checks the current matched set of elements against a Selection object
+// and returns true if at least one of these elements matches.
+func (s *Selection) IsSelection(sel *Selection) bool {
+	return s.FilterSelection(sel).Length() > 0
+}
+
+// IsNodes checks the current matched set of elements against the specified nodes
+// and returns true if at least one of these elements matches.
+func (s *Selection) IsNodes(nodes ...*html.Node) bool {
+	return s.FilterNodes(nodes...).Length() > 0
+}
+
+// Contains returns true if the specified Node is within,
+// at any depth, one of the nodes in the Selection object.
+// It is NOT inclusive, to behave like jQuery's implementation, and
+// unlike Javascript's .contains, so if the contained
+// node is itself in the selection, it returns false.
+func (s *Selection) Contains(n *html.Node) bool {
+	return sliceContains(s.Nodes, n)
+}

+ 698 - 0
vendor/github.com/PuerkitoBio/goquery/traversal.go

@@ -0,0 +1,698 @@
+package goquery
+
+import "golang.org/x/net/html"
+
+type siblingType int
+
+// Sibling type, used internally when iterating over children at the same
+// level (siblings) to specify which nodes are requested.
+const (
+	siblingPrevUntil siblingType = iota - 3
+	siblingPrevAll
+	siblingPrev
+	siblingAll
+	siblingNext
+	siblingNextAll
+	siblingNextUntil
+	siblingAllIncludingNonElements
+)
+
+// Find gets the descendants of each element in the current set of matched
+// elements, filtered by a selector. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) Find(selector string) *Selection {
+	return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
+}
+
+// FindMatcher gets the descendants of each element in the current set of matched
+// elements, filtered by the matcher. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindMatcher(m Matcher) *Selection {
+	return pushStack(s, findWithMatcher(s.Nodes, m))
+}
+
+// FindSelection gets the descendants of each element in the current
+// Selection, filtered by a Selection. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, nil)
+	}
+	return s.FindNodes(sel.Nodes...)
+}
+
+// FindNodes gets the descendants of each element in the current
+// Selection, filtered by some nodes. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		if sliceContains(s.Nodes, n) {
+			return []*html.Node{n}
+		}
+		return nil
+	}))
+}
+
+// Contents gets the children of each element in the Selection,
+// including text and comment nodes. It returns a new Selection object
+// containing these elements.
+func (s *Selection) Contents() *Selection {
+	return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
+}
+
+// ContentsFiltered gets the children of each element in the Selection,
+// filtered by the specified selector. It returns a new Selection
+// object containing these elements. Since selectors only act on Element nodes,
+// this function is an alias to ChildrenFiltered unless the selector is empty,
+// in which case it is an alias to Contents.
+func (s *Selection) ContentsFiltered(selector string) *Selection {
+	if selector != "" {
+		return s.ChildrenFiltered(selector)
+	}
+	return s.Contents()
+}
+
+// ContentsMatcher gets the children of each element in the Selection,
+// filtered by the specified matcher. It returns a new Selection
+// object containing these elements. Since matchers only act on Element nodes,
+// this function is an alias to ChildrenMatcher.
+func (s *Selection) ContentsMatcher(m Matcher) *Selection {
+	return s.ChildrenMatcher(m)
+}
+
+// Children gets the child elements of each element in the Selection.
+// It returns a new Selection object containing these elements.
+func (s *Selection) Children() *Selection {
+	return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
+}
+
+// ChildrenFiltered gets the child elements of each element in the Selection,
+// filtered by the specified selector. It returns a new
+// Selection object containing these elements.
+func (s *Selection) ChildrenFiltered(selector string) *Selection {
+	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
+}
+
+// ChildrenMatcher gets the child elements of each element in the Selection,
+// filtered by the specified matcher. It returns a new
+// Selection object containing these elements.
+func (s *Selection) ChildrenMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m)
+}
+
+// Parent gets the parent of each element in the Selection. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) Parent() *Selection {
+	return pushStack(s, getParentNodes(s.Nodes))
+}
+
+// ParentFiltered gets the parent of each element in the Selection filtered by a
+// selector. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentFiltered(selector string) *Selection {
+	return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
+}
+
+// ParentMatcher gets the parent of each element in the Selection filtered by a
+// matcher. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getParentNodes(s.Nodes), m)
+}
+
+// Closest gets the first element that matches the selector by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) Closest(selector string) *Selection {
+	cs := compileMatcher(selector)
+	return s.ClosestMatcher(cs)
+}
+
+// ClosestMatcher gets the first element that matches the matcher by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) ClosestMatcher(m Matcher) *Selection {
+	return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
+		// For each node in the selection, test the node itself, then each parent
+		// until a match is found.
+		for ; n != nil; n = n.Parent {
+			if m.Match(n) {
+				return []*html.Node{n}
+			}
+		}
+		return nil
+	}))
+}
+
+// ClosestNodes gets the first element that matches one of the nodes by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
+	set := make(map[*html.Node]bool)
+	for _, n := range nodes {
+		set[n] = true
+	}
+	return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
+		// For each node in the selection, test the node itself, then each parent
+		// until a match is found.
+		for ; n != nil; n = n.Parent {
+			if set[n] {
+				return []*html.Node{n}
+			}
+		}
+		return nil
+	}))
+}
+
+// ClosestSelection gets the first element that matches one of the nodes in the
+// Selection by testing the element itself and traversing up through its ancestors
+// in the DOM tree.
+func (s *Selection) ClosestSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, nil)
+	}
+	return s.ClosestNodes(sel.Nodes...)
+}
+
+// Parents gets the ancestors of each element in the current Selection. It
+// returns a new Selection object with the matched elements.
+func (s *Selection) Parents() *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, nil, nil))
+}
+
+// ParentsFiltered gets the ancestors of each element in the current
+// Selection. It returns a new Selection object with the matched elements.
+func (s *Selection) ParentsFiltered(selector string) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
+}
+
+// ParentsMatcher gets the ancestors of each element in the current
+// Selection. It returns a new Selection object with the matched elements.
+func (s *Selection) ParentsMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m)
+}
+
+// ParentsUntil gets the ancestors of each element in the Selection, up to but
+// not including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsUntil(selector string) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
+}
+
+// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
+// not including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, m, nil))
+}
+
+// ParentsUntilSelection gets the ancestors of each element in the Selection,
+// up to but not including the elements in the specified Selection. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.Parents()
+	}
+	return s.ParentsUntilNodes(sel.Nodes...)
+}
+
+// ParentsUntilNodes gets the ancestors of each element in the Selection,
+// up to but not including the specified nodes. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, nil, nodes))
+}
+
+// ParentsFilteredUntil is like ParentsUntil, with the option to filter the
+// results based on a selector string. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
+}
+
+// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
+// results based on a matcher. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter)
+}
+
+// ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
+}
+
+// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.ParentsMatcher(filter)
+	}
+	return s.ParentsMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
+}
+
+// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter)
+}
+
+// Siblings gets the siblings of each element in the Selection. It returns
+// a new Selection object containing the matched elements.
+func (s *Selection) Siblings() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil))
+}
+
+// SiblingsFiltered gets the siblings of each element in the Selection
+// filtered by a selector. It returns a new Selection object containing the
+// matched elements.
+func (s *Selection) SiblingsFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
+}
+
+// SiblingsMatcher gets the siblings of each element in the Selection
+// filtered by a matcher. It returns a new Selection object containing the
+// matched elements.
+func (s *Selection) SiblingsMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m)
+}
+
+// Next gets the immediately following sibling of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) Next() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil))
+}
+
+// NextFiltered gets the immediately following sibling of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
+}
+
+// NextMatcher gets the immediately following sibling of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m)
+}
+
+// NextAll gets all the following siblings of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) NextAll() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil))
+}
+
+// NextAllFiltered gets all the following siblings of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextAllFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
+}
+
+// NextAllMatcher gets all the following siblings of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextAllMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m)
+}
+
+// Prev gets the immediately preceding sibling of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) Prev() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil))
+}
+
+// PrevFiltered gets the immediately preceding sibling of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
+}
+
+// PrevMatcher gets the immediately preceding sibling of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m)
+}
+
+// PrevAll gets all the preceding siblings of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevAll() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil))
+}
+
+// PrevAllFiltered gets all the preceding siblings of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevAllFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
+}
+
+// PrevAllMatcher gets all the preceding siblings of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m)
+}
+
+// NextUntil gets all following siblings of each element up to but not
+// including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntil(selector string) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		compileMatcher(selector), nil))
+}
+
+// NextUntilMatcher gets all following siblings of each element up to but not
+// including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		m, nil))
+}
+
+// NextUntilSelection gets all following siblings of each element up to but not
+// including the element matched by the Selection. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.NextAll()
+	}
+	return s.NextUntilNodes(sel.Nodes...)
+}
+
+// NextUntilNodes gets all following siblings of each element up to but not
+// including the element matched by the nodes. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes))
+}
+
+// PrevUntil gets all preceding siblings of each element up to but not
+// including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntil(selector string) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		compileMatcher(selector), nil))
+}
+
+// PrevUntilMatcher gets all preceding siblings of each element up to but not
+// including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		m, nil))
+}
+
+// PrevUntilSelection gets all preceding siblings of each element up to but not
+// including the element matched by the Selection. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.PrevAll()
+	}
+	return s.PrevUntilNodes(sel.Nodes...)
+}
+
+// PrevUntilNodes gets all preceding siblings of each element up to but not
+// including the element matched by the nodes. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes))
+}
+
+// NextFilteredUntil is like NextUntil, with the option to filter
+// the results based on a selector string.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
+}
+
+// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
+// the results based on a matcher.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		until, nil), filter)
+}
+
+// NextFilteredUntilSelection is like NextUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
+}
+
+// NextMatcherUntilSelection is like NextUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.NextMatcher(filter)
+	}
+	return s.NextMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// NextFilteredUntilNodes is like NextUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes), compileMatcher(filterSelector))
+}
+
+// NextMatcherUntilNodes is like NextUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes), filter)
+}
+
+// PrevFilteredUntil is like PrevUntil, with the option to filter
+// the results based on a selector string.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
+}
+
+// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
+// the results based on a matcher.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		until, nil), filter)
+}
+
+// PrevFilteredUntilSelection is like PrevUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
+}
+
+// PrevMatcherUntilSelection is like PrevUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.PrevMatcher(filter)
+	}
+	return s.PrevMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// PrevFilteredUntilNodes is like PrevUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes), compileMatcher(filterSelector))
+}
+
+// PrevMatcherUntilNodes is like PrevUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes), filter)
+}
+
+// Filter and push filters the nodes based on a matcher, and pushes the results
+// on the stack, with the srcSel as previous selection.
+func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection {
+	// Create a temporary Selection with the specified nodes to filter using winnow
+	sel := &Selection{nodes, srcSel.document, nil}
+	// Filter based on matcher and push on stack
+	return pushStack(srcSel, winnow(sel, m, true))
+}
+
+// Internal implementation of Find that return raw nodes.
+func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node {
+	// Map nodes to find the matches within the children of each node
+	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
+		// Go down one level, becausejQuery's Find selects only within descendants
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			if c.Type == html.ElementNode {
+				result = append(result, m.MatchAll(c)...)
+			}
+		}
+		return
+	})
+}
+
+// Internal implementation to get all parent nodes, stopping at the specified
+// node (or nil if no stop).
+func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
+		for p := n.Parent; p != nil; p = p.Parent {
+			sel := newSingleSelection(p, nil)
+			if stopm != nil {
+				if sel.IsMatcher(stopm) {
+					break
+				}
+			} else if len(stopNodes) > 0 {
+				if sel.IsNodes(stopNodes...) {
+					break
+				}
+			}
+			if p.Type == html.ElementNode {
+				result = append(result, p)
+			}
+		}
+		return
+	})
+}
+
+// Internal implementation of sibling nodes that return a raw slice of matches.
+func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node {
+	var f func(*html.Node) bool
+
+	// If the requested siblings are ...Until, create the test function to
+	// determine if the until condition is reached (returns true if it is)
+	if st == siblingNextUntil || st == siblingPrevUntil {
+		f = func(n *html.Node) bool {
+			if untilm != nil {
+				// Matcher-based condition
+				sel := newSingleSelection(n, nil)
+				return sel.IsMatcher(untilm)
+			} else if len(untilNodes) > 0 {
+				// Nodes-based condition
+				sel := newSingleSelection(n, nil)
+				return sel.IsNodes(untilNodes...)
+			}
+			return false
+		}
+	}
+
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		return getChildrenWithSiblingType(n.Parent, st, n, f)
+	})
+}
+
+// Gets the children nodes of each node in the specified slice of nodes,
+// based on the sibling type request.
+func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		return getChildrenWithSiblingType(n, st, nil, nil)
+	})
+}
+
+// Gets the children of the specified parent, based on the requested sibling
+// type, skipping a specified node if required.
+func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
+	untilFunc func(*html.Node) bool) (result []*html.Node) {
+
+	// Create the iterator function
+	var iter = func(cur *html.Node) (ret *html.Node) {
+		// Based on the sibling type requested, iterate the right way
+		for {
+			switch st {
+			case siblingAll, siblingAllIncludingNonElements:
+				if cur == nil {
+					// First iteration, start with first child of parent
+					// Skip node if required
+					if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
+						ret = skipNode.NextSibling
+					}
+				} else {
+					// Skip node if required
+					if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
+						ret = skipNode.NextSibling
+					}
+				}
+			case siblingPrev, siblingPrevAll, siblingPrevUntil:
+				if cur == nil {
+					// Start with previous sibling of the skip node
+					ret = skipNode.PrevSibling
+				} else {
+					ret = cur.PrevSibling
+				}
+			case siblingNext, siblingNextAll, siblingNextUntil:
+				if cur == nil {
+					// Start with next sibling of the skip node
+					ret = skipNode.NextSibling
+				} else {
+					ret = cur.NextSibling
+				}
+			default:
+				panic("Invalid sibling type.")
+			}
+			if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
+				return
+			}
+			// Not a valid node, try again from this one
+			cur = ret
+		}
+	}
+
+	for c := iter(nil); c != nil; c = iter(c) {
+		// If this is an ...Until case, test before append (returns true
+		// if the until condition is reached)
+		if st == siblingNextUntil || st == siblingPrevUntil {
+			if untilFunc(c) {
+				return
+			}
+		}
+		result = append(result, c)
+		if st == siblingNext || st == siblingPrev {
+			// Only one node was requested (immediate next or previous), so exit
+			return
+		}
+	}
+	return
+}
+
+// Internal implementation of parent nodes that return a raw slice of Nodes.
+func getParentNodes(nodes []*html.Node) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		if n.Parent != nil && n.Parent.Type == html.ElementNode {
+			return []*html.Node{n.Parent}
+		}
+		return nil
+	})
+}
+
+// Internal map function used by many traversing methods. Takes the source nodes
+// to iterate on and the mapping function that returns an array of nodes.
+// Returns an array of nodes mapped by calling the callback function once for
+// each node in the source nodes.
+func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
+	set := make(map[*html.Node]bool)
+	for i, n := range nodes {
+		if vals := f(i, n); len(vals) > 0 {
+			result = appendWithoutDuplicates(result, vals, set)
+		}
+	}
+	return result
+}

+ 141 - 0
vendor/github.com/PuerkitoBio/goquery/type.go

@@ -0,0 +1,141 @@
+package goquery
+
+import (
+	"errors"
+	"io"
+	"net/http"
+	"net/url"
+
+	"github.com/andybalholm/cascadia"
+
+	"golang.org/x/net/html"
+)
+
+// Document represents an HTML document to be manipulated. Unlike jQuery, which
+// is loaded as part of a DOM document, and thus acts upon 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 class is for. It holds the root
+// document node to manipulate, and can make selections on this document.
+type Document struct {
+	*Selection
+	Url      *url.URL
+	rootNode *html.Node
+}
+
+// NewDocumentFromNode is a Document constructor that takes a root html Node
+// as argument.
+func NewDocumentFromNode(root *html.Node) *Document {
+	return newDocument(root, nil)
+}
+
+// NewDocument is a Document constructor that takes a string URL as argument.
+// It loads the specified document, parses it, and stores the root Document
+// node, ready to be manipulated.
+//
+// Deprecated: Use the net/http standard library package to make the request
+// and validate the response before calling goquery.NewDocumentFromReader
+// with the response's body.
+func NewDocument(url string) (*Document, error) {
+	// Load the URL
+	res, e := http.Get(url)
+	if e != nil {
+		return nil, e
+	}
+	return NewDocumentFromResponse(res)
+}
+
+// NewDocumentFromReader returns a Document from an io.Reader.
+// It returns an error as second value if the reader's data cannot be parsed
+// as html. It does not check if the reader is also an io.Closer, the
+// provided reader is never closed by this call. It is the responsibility
+// of the caller to close it if required.
+func NewDocumentFromReader(r io.Reader) (*Document, error) {
+	root, e := html.Parse(r)
+	if e != nil {
+		return nil, e
+	}
+	return newDocument(root, nil), nil
+}
+
+// NewDocumentFromResponse is another Document constructor that takes an http response as argument.
+// It loads the specified response's document, parses it, and stores the root Document
+// node, ready to be manipulated. The response's body is closed on return.
+//
+// Deprecated: Use goquery.NewDocumentFromReader with the response's body.
+func NewDocumentFromResponse(res *http.Response) (*Document, error) {
+	if res == nil {
+		return nil, errors.New("Response is nil")
+	}
+	defer res.Body.Close()
+	if res.Request == nil {
+		return nil, errors.New("Response.Request is nil")
+	}
+
+	// Parse the HTML into nodes
+	root, e := html.Parse(res.Body)
+	if e != nil {
+		return nil, e
+	}
+
+	// Create and fill the document
+	return newDocument(root, res.Request.URL), nil
+}
+
+// CloneDocument creates a deep-clone of a document.
+func CloneDocument(doc *Document) *Document {
+	return newDocument(cloneNode(doc.rootNode), doc.Url)
+}
+
+// Private constructor, make sure all fields are correctly filled.
+func newDocument(root *html.Node, url *url.URL) *Document {
+	// Create and fill the document
+	d := &Document{nil, url, root}
+	d.Selection = newSingleSelection(root, d)
+	return d
+}
+
+// Selection represents a collection of nodes matching some criteria. The
+// initial Selection can be created by using Document.Find, and then
+// manipulated using the jQuery-like chainable syntax and methods.
+type Selection struct {
+	Nodes    []*html.Node
+	document *Document
+	prevSel  *Selection
+}
+
+// Helper constructor to create an empty selection
+func newEmptySelection(doc *Document) *Selection {
+	return &Selection{nil, doc, nil}
+}
+
+// Helper constructor to create a selection of only one node
+func newSingleSelection(node *html.Node, doc *Document) *Selection {
+	return &Selection{[]*html.Node{node}, doc, nil}
+}
+
+// Matcher is an interface that defines the methods to match
+// HTML nodes against a compiled selector string. Cascadia's
+// Selector implements this interface.
+type Matcher interface {
+	Match(*html.Node) bool
+	MatchAll(*html.Node) []*html.Node
+	Filter([]*html.Node) []*html.Node
+}
+
+// compileMatcher compiles the selector string s and returns
+// the corresponding Matcher. If s is an invalid selector string,
+// it returns a Matcher that fails all matches.
+func compileMatcher(s string) Matcher {
+	cs, err := cascadia.Compile(s)
+	if err != nil {
+		return invalidMatcher{}
+	}
+	return cs
+}
+
+// invalidMatcher is a Matcher that always fails to match.
+type invalidMatcher struct{}
+
+func (invalidMatcher) Match(n *html.Node) bool             { return false }
+func (invalidMatcher) MatchAll(n *html.Node) []*html.Node  { return nil }
+func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil }

+ 161 - 0
vendor/github.com/PuerkitoBio/goquery/utilities.go

@@ -0,0 +1,161 @@
+package goquery
+
+import (
+	"bytes"
+
+	"golang.org/x/net/html"
+)
+
+// used to determine if a set (map[*html.Node]bool) should be used
+// instead of iterating over a slice. The set uses more memory and
+// is slower than slice iteration for small N.
+const minNodesForSet = 1000
+
+var nodeNames = []string{
+	html.ErrorNode:    "#error",
+	html.TextNode:     "#text",
+	html.DocumentNode: "#document",
+	html.CommentNode:  "#comment",
+}
+
+// NodeName returns the node name of the first element in the selection.
+// It tries to behave in a similar way as the DOM's nodeName property
+// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
+//
+// Go's net/html package defines the following node types, listed with
+// the corresponding returned value from this function:
+//
+//     ErrorNode : #error
+//     TextNode : #text
+//     DocumentNode : #document
+//     ElementNode : the element's tag name
+//     CommentNode : #comment
+//     DoctypeNode : the name of the document type
+//
+func NodeName(s *Selection) string {
+	if s.Length() == 0 {
+		return ""
+	}
+	switch n := s.Get(0); n.Type {
+	case html.ElementNode, html.DoctypeNode:
+		return n.Data
+	default:
+		if n.Type >= 0 && int(n.Type) < len(nodeNames) {
+			return nodeNames[n.Type]
+		}
+		return ""
+	}
+}
+
+// OuterHtml returns the outer HTML rendering of the first item in
+// the selection - that is, the HTML including the first element's
+// tag and attributes.
+//
+// Unlike InnerHtml, this is a function and not a method on the Selection,
+// because this is not a jQuery method (in javascript-land, this is
+// a property provided by the DOM).
+func OuterHtml(s *Selection) (string, error) {
+	var buf bytes.Buffer
+
+	if s.Length() == 0 {
+		return "", nil
+	}
+	n := s.Get(0)
+	if err := html.Render(&buf, n); err != nil {
+		return "", err
+	}
+	return buf.String(), nil
+}
+
+// Loop through all container nodes to search for the target node.
+func sliceContains(container []*html.Node, contained *html.Node) bool {
+	for _, n := range container {
+		if nodeContains(n, contained) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Checks if the contained node is within the container node.
+func nodeContains(container *html.Node, contained *html.Node) bool {
+	// Check if the parent of the contained node is the container node, traversing
+	// upward until the top is reached, or the container is found.
+	for contained = contained.Parent; contained != nil; contained = contained.Parent {
+		if container == contained {
+			return true
+		}
+	}
+	return false
+}
+
+// Checks if the target node is in the slice of nodes.
+func isInSlice(slice []*html.Node, node *html.Node) bool {
+	return indexInSlice(slice, node) > -1
+}
+
+// Returns the index of the target node in the slice, or -1.
+func indexInSlice(slice []*html.Node, node *html.Node) int {
+	if node != nil {
+		for i, n := range slice {
+			if n == node {
+				return i
+			}
+		}
+	}
+	return -1
+}
+
+// Appends the new nodes to the target slice, making sure no duplicate is added.
+// There is no check to the original state of the target slice, so it may still
+// contain duplicates. The target slice is returned because append() may create
+// a new underlying array. If targetSet is nil, a local set is created with the
+// target if len(target) + len(nodes) is greater than minNodesForSet.
+func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
+	// if there are not that many nodes, don't use the map, faster to just use nested loops
+	// (unless a non-nil targetSet is passed, in which case the caller knows better).
+	if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
+		for _, n := range nodes {
+			if !isInSlice(target, n) {
+				target = append(target, n)
+			}
+		}
+		return target
+	}
+
+	// if a targetSet is passed, then assume it is reliable, otherwise create one
+	// and initialize it with the current target contents.
+	if targetSet == nil {
+		targetSet = make(map[*html.Node]bool, len(target))
+		for _, n := range target {
+			targetSet[n] = true
+		}
+	}
+	for _, n := range nodes {
+		if !targetSet[n] {
+			target = append(target, n)
+			targetSet[n] = true
+		}
+	}
+
+	return target
+}
+
+// Loop through a selection, returning only those nodes that pass the predicate
+// function.
+func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
+	for i, n := range sel.Nodes {
+		if predicate(i, newSingleSelection(n, sel.document)) {
+			result = append(result, n)
+		}
+	}
+	return result
+}
+
+// Creates a new Selection object based on the specified nodes, and keeps the
+// source Selection object on the stack (linked list).
+func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
+	result := &Selection{nodes, fromSel.document, fromSel}
+	return result
+}

+ 14 - 0
vendor/github.com/andybalholm/cascadia/.travis.yml

@@ -0,0 +1,14 @@
+language: go
+
+go:
+  - 1.3
+  - 1.4
+
+install:
+  - go get github.com/andybalholm/cascadia
+
+script:
+ - go test -v
+
+notifications:
+  email: false

+ 24 - 0
vendor/github.com/andybalholm/cascadia/LICENSE

@@ -0,0 +1,24 @@
+Copyright (c) 2011 Andy Balholm. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * 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.
+
+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
+OWNER 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.

+ 9 - 0
vendor/github.com/andybalholm/cascadia/README.md

@@ -0,0 +1,9 @@
+# cascadia
+
+[![](https://travis-ci.org/andybalholm/cascadia.svg)](https://travis-ci.org/andybalholm/cascadia)
+
+The Cascadia package implements CSS selectors for use with the parse trees produced by the html package.
+
+To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package.
+
+[Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia).

+ 801 - 0
vendor/github.com/andybalholm/cascadia/parser.go

@@ -0,0 +1,801 @@
+// Package cascadia is an implementation of CSS selectors.
+package cascadia
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// a parser for CSS selectors
+type parser struct {
+	s string // the source text
+	i int    // the current position
+}
+
+// parseEscape parses a backslash escape.
+func (p *parser) parseEscape() (result string, err error) {
+	if len(p.s) < p.i+2 || p.s[p.i] != '\\' {
+		return "", errors.New("invalid escape sequence")
+	}
+
+	start := p.i + 1
+	c := p.s[start]
+	switch {
+	case c == '\r' || c == '\n' || c == '\f':
+		return "", errors.New("escaped line ending outside string")
+	case hexDigit(c):
+		// unicode escape (hex)
+		var i int
+		for i = start; i < p.i+6 && i < len(p.s) && hexDigit(p.s[i]); i++ {
+			// empty
+		}
+		v, _ := strconv.ParseUint(p.s[start:i], 16, 21)
+		if len(p.s) > i {
+			switch p.s[i] {
+			case '\r':
+				i++
+				if len(p.s) > i && p.s[i] == '\n' {
+					i++
+				}
+			case ' ', '\t', '\n', '\f':
+				i++
+			}
+		}
+		p.i = i
+		return string(rune(v)), nil
+	}
+
+	// Return the literal character after the backslash.
+	result = p.s[start : start+1]
+	p.i += 2
+	return result, nil
+}
+
+// toLowerASCII returns s with all ASCII capital letters lowercased.
+func toLowerASCII(s string) string {
+	var b []byte
+	for i := 0; i < len(s); i++ {
+		if c := s[i]; 'A' <= c && c <= 'Z' {
+			if b == nil {
+				b = make([]byte, len(s))
+				copy(b, s)
+			}
+			b[i] = s[i] + ('a' - 'A')
+		}
+	}
+
+	if b == nil {
+		return s
+	}
+
+	return string(b)
+}
+
+func hexDigit(c byte) bool {
+	return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
+}
+
+// nameStart returns whether c can be the first character of an identifier
+// (not counting an initial hyphen, or an escape sequence).
+func nameStart(c byte) bool {
+	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127
+}
+
+// nameChar returns whether c can be a character within an identifier
+// (not counting an escape sequence).
+func nameChar(c byte) bool {
+	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 ||
+		c == '-' || '0' <= c && c <= '9'
+}
+
+// parseIdentifier parses an identifier.
+func (p *parser) parseIdentifier() (result string, err error) {
+	startingDash := false
+	if len(p.s) > p.i && p.s[p.i] == '-' {
+		startingDash = true
+		p.i++
+	}
+
+	if len(p.s) <= p.i {
+		return "", errors.New("expected identifier, found EOF instead")
+	}
+
+	if c := p.s[p.i]; !(nameStart(c) || c == '\\') {
+		return "", fmt.Errorf("expected identifier, found %c instead", c)
+	}
+
+	result, err = p.parseName()
+	if startingDash && err == nil {
+		result = "-" + result
+	}
+	return
+}
+
+// parseName parses a name (which is like an identifier, but doesn't have
+// extra restrictions on the first character).
+func (p *parser) parseName() (result string, err error) {
+	i := p.i
+loop:
+	for i < len(p.s) {
+		c := p.s[i]
+		switch {
+		case nameChar(c):
+			start := i
+			for i < len(p.s) && nameChar(p.s[i]) {
+				i++
+			}
+			result += p.s[start:i]
+		case c == '\\':
+			p.i = i
+			val, err := p.parseEscape()
+			if err != nil {
+				return "", err
+			}
+			i = p.i
+			result += val
+		default:
+			break loop
+		}
+	}
+
+	if result == "" {
+		return "", errors.New("expected name, found EOF instead")
+	}
+
+	p.i = i
+	return result, nil
+}
+
+// parseString parses a single- or double-quoted string.
+func (p *parser) parseString() (result string, err error) {
+	i := p.i
+	if len(p.s) < i+2 {
+		return "", errors.New("expected string, found EOF instead")
+	}
+
+	quote := p.s[i]
+	i++
+
+loop:
+	for i < len(p.s) {
+		switch p.s[i] {
+		case '\\':
+			if len(p.s) > i+1 {
+				switch c := p.s[i+1]; c {
+				case '\r':
+					if len(p.s) > i+2 && p.s[i+2] == '\n' {
+						i += 3
+						continue loop
+					}
+					fallthrough
+				case '\n', '\f':
+					i += 2
+					continue loop
+				}
+			}
+			p.i = i
+			val, err := p.parseEscape()
+			if err != nil {
+				return "", err
+			}
+			i = p.i
+			result += val
+		case quote:
+			break loop
+		case '\r', '\n', '\f':
+			return "", errors.New("unexpected end of line in string")
+		default:
+			start := i
+			for i < len(p.s) {
+				if c := p.s[i]; c == quote || c == '\\' || c == '\r' || c == '\n' || c == '\f' {
+					break
+				}
+				i++
+			}
+			result += p.s[start:i]
+		}
+	}
+
+	if i >= len(p.s) {
+		return "", errors.New("EOF in string")
+	}
+
+	// Consume the final quote.
+	i++
+
+	p.i = i
+	return result, nil
+}
+
+// parseRegex parses a regular expression; the end is defined by encountering an
+// unmatched closing ')' or ']' which is not consumed
+func (p *parser) parseRegex() (rx *regexp.Regexp, err error) {
+	i := p.i
+	if len(p.s) < i+2 {
+		return nil, errors.New("expected regular expression, found EOF instead")
+	}
+
+	// number of open parens or brackets;
+	// when it becomes negative, finished parsing regex
+	open := 0
+
+loop:
+	for i < len(p.s) {
+		switch p.s[i] {
+		case '(', '[':
+			open++
+		case ')', ']':
+			open--
+			if open < 0 {
+				break loop
+			}
+		}
+		i++
+	}
+
+	if i >= len(p.s) {
+		return nil, errors.New("EOF in regular expression")
+	}
+	rx, err = regexp.Compile(p.s[p.i:i])
+	p.i = i
+	return rx, err
+}
+
+// skipWhitespace consumes whitespace characters and comments.
+// It returns true if there was actually anything to skip.
+func (p *parser) skipWhitespace() bool {
+	i := p.i
+	for i < len(p.s) {
+		switch p.s[i] {
+		case ' ', '\t', '\r', '\n', '\f':
+			i++
+			continue
+		case '/':
+			if strings.HasPrefix(p.s[i:], "/*") {
+				end := strings.Index(p.s[i+len("/*"):], "*/")
+				if end != -1 {
+					i += end + len("/**/")
+					continue
+				}
+			}
+		}
+		break
+	}
+
+	if i > p.i {
+		p.i = i
+		return true
+	}
+
+	return false
+}
+
+// consumeParenthesis consumes an opening parenthesis and any following
+// whitespace. It returns true if there was actually a parenthesis to skip.
+func (p *parser) consumeParenthesis() bool {
+	if p.i < len(p.s) && p.s[p.i] == '(' {
+		p.i++
+		p.skipWhitespace()
+		return true
+	}
+	return false
+}
+
+// consumeClosingParenthesis consumes a closing parenthesis and any preceding
+// whitespace. It returns true if there was actually a parenthesis to skip.
+func (p *parser) consumeClosingParenthesis() bool {
+	i := p.i
+	p.skipWhitespace()
+	if p.i < len(p.s) && p.s[p.i] == ')' {
+		p.i++
+		return true
+	}
+	p.i = i
+	return false
+}
+
+// parseTypeSelector parses a type selector (one that matches by tag name).
+func (p *parser) parseTypeSelector() (result tagSelector, err error) {
+	tag, err := p.parseIdentifier()
+	if err != nil {
+		return
+	}
+	return tagSelector{tag: toLowerASCII(tag)}, nil
+}
+
+// parseIDSelector parses a selector that matches by id attribute.
+func (p *parser) parseIDSelector() (idSelector, error) {
+	if p.i >= len(p.s) {
+		return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead")
+	}
+	if p.s[p.i] != '#' {
+		return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i])
+	}
+
+	p.i++
+	id, err := p.parseName()
+	if err != nil {
+		return idSelector{}, err
+	}
+
+	return idSelector{id: id}, nil
+}
+
+// parseClassSelector parses a selector that matches by class attribute.
+func (p *parser) parseClassSelector() (classSelector, error) {
+	if p.i >= len(p.s) {
+		return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead")
+	}
+	if p.s[p.i] != '.' {
+		return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i])
+	}
+
+	p.i++
+	class, err := p.parseIdentifier()
+	if err != nil {
+		return classSelector{}, err
+	}
+
+	return classSelector{class: class}, nil
+}
+
+// parseAttributeSelector parses a selector that matches by attribute value.
+func (p *parser) parseAttributeSelector() (attrSelector, error) {
+	if p.i >= len(p.s) {
+		return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead")
+	}
+	if p.s[p.i] != '[' {
+		return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i])
+	}
+
+	p.i++
+	p.skipWhitespace()
+	key, err := p.parseIdentifier()
+	if err != nil {
+		return attrSelector{}, err
+	}
+	key = toLowerASCII(key)
+
+	p.skipWhitespace()
+	if p.i >= len(p.s) {
+		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
+	}
+
+	if p.s[p.i] == ']' {
+		p.i++
+		return attrSelector{key: key, operation: ""}, nil
+	}
+
+	if p.i+2 >= len(p.s) {
+		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
+	}
+
+	op := p.s[p.i : p.i+2]
+	if op[0] == '=' {
+		op = "="
+	} else if op[1] != '=' {
+		return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op)
+	}
+	p.i += len(op)
+
+	p.skipWhitespace()
+	if p.i >= len(p.s) {
+		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
+	}
+	var val string
+	var rx *regexp.Regexp
+	if op == "#=" {
+		rx, err = p.parseRegex()
+	} else {
+		switch p.s[p.i] {
+		case '\'', '"':
+			val, err = p.parseString()
+		default:
+			val, err = p.parseIdentifier()
+		}
+	}
+	if err != nil {
+		return attrSelector{}, err
+	}
+
+	p.skipWhitespace()
+	if p.i >= len(p.s) {
+		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
+	}
+	if p.s[p.i] != ']' {
+		return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i])
+	}
+	p.i++
+
+	switch op {
+	case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=":
+		return attrSelector{key: key, val: val, operation: op, regexp: rx}, nil
+	default:
+		return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op)
+	}
+}
+
+var errExpectedParenthesis = errors.New("expected '(' but didn't find it")
+var errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it")
+var errUnmatchedParenthesis = errors.New("unmatched '('")
+
+// parsePseudoclassSelector parses a pseudoclass selector like :not(p)
+func (p *parser) parsePseudoclassSelector() (out Sel, err error) {
+	if p.i >= len(p.s) {
+		return nil, fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead")
+	}
+	if p.s[p.i] != ':' {
+		return nil, fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i])
+	}
+
+	p.i++
+	if p.s[p.i] == ':' { // we found a pseudo-element
+		p.i++
+	}
+
+	name, err := p.parseIdentifier()
+	if err != nil {
+		return
+	}
+	name = toLowerASCII(name)
+	switch name {
+	case "not", "has", "haschild":
+		if !p.consumeParenthesis() {
+			return out, errExpectedParenthesis
+		}
+		sel, parseErr := p.parseSelectorGroup()
+		if parseErr != nil {
+			return out, parseErr
+		}
+		if !p.consumeClosingParenthesis() {
+			return out, errExpectedClosingParenthesis
+		}
+
+		out = relativePseudoClassSelector{name: name, match: sel}
+
+	case "contains", "containsown":
+		if !p.consumeParenthesis() {
+			return out, errExpectedParenthesis
+		}
+		if p.i == len(p.s) {
+			return out, errUnmatchedParenthesis
+		}
+		var val string
+		switch p.s[p.i] {
+		case '\'', '"':
+			val, err = p.parseString()
+		default:
+			val, err = p.parseIdentifier()
+		}
+		if err != nil {
+			return out, err
+		}
+		val = strings.ToLower(val)
+		p.skipWhitespace()
+		if p.i >= len(p.s) {
+			return out, errors.New("unexpected EOF in pseudo selector")
+		}
+		if !p.consumeClosingParenthesis() {
+			return out, errExpectedClosingParenthesis
+		}
+
+		out = containsPseudoClassSelector{own: name == "containsown", value: val}
+
+	case "matches", "matchesown":
+		if !p.consumeParenthesis() {
+			return out, errExpectedParenthesis
+		}
+		rx, err := p.parseRegex()
+		if err != nil {
+			return out, err
+		}
+		if p.i >= len(p.s) {
+			return out, errors.New("unexpected EOF in pseudo selector")
+		}
+		if !p.consumeClosingParenthesis() {
+			return out, errExpectedClosingParenthesis
+		}
+
+		out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx}
+
+	case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type":
+		if !p.consumeParenthesis() {
+			return out, errExpectedParenthesis
+		}
+		a, b, err := p.parseNth()
+		if err != nil {
+			return out, err
+		}
+		if !p.consumeClosingParenthesis() {
+			return out, errExpectedClosingParenthesis
+		}
+		last := name == "nth-last-child" || name == "nth-last-of-type"
+		ofType := name == "nth-of-type" || name == "nth-last-of-type"
+		out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType}
+
+	case "first-child":
+		out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false}
+	case "last-child":
+		out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true}
+	case "first-of-type":
+		out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false}
+	case "last-of-type":
+		out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true}
+	case "only-child":
+		out = onlyChildPseudoClassSelector{ofType: false}
+	case "only-of-type":
+		out = onlyChildPseudoClassSelector{ofType: true}
+	case "input":
+		out = inputPseudoClassSelector{}
+	case "empty":
+		out = emptyElementPseudoClassSelector{}
+	case "root":
+		out = rootPseudoClassSelector{}
+	case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error":
+		return out, errors.New("pseudo-elements are not yet supported")
+	default:
+		return out, fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name)
+	}
+	return
+}
+
+// parseInteger parses a  decimal integer.
+func (p *parser) parseInteger() (int, error) {
+	i := p.i
+	start := i
+	for i < len(p.s) && '0' <= p.s[i] && p.s[i] <= '9' {
+		i++
+	}
+	if i == start {
+		return 0, errors.New("expected integer, but didn't find it")
+	}
+	p.i = i
+
+	val, err := strconv.Atoi(p.s[start:i])
+	if err != nil {
+		return 0, err
+	}
+
+	return val, nil
+}
+
+// parseNth parses the argument for :nth-child (normally of the form an+b).
+func (p *parser) parseNth() (a, b int, err error) {
+	// initial state
+	if p.i >= len(p.s) {
+		goto eof
+	}
+	switch p.s[p.i] {
+	case '-':
+		p.i++
+		goto negativeA
+	case '+':
+		p.i++
+		goto positiveA
+	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+		goto positiveA
+	case 'n', 'N':
+		a = 1
+		p.i++
+		goto readN
+	case 'o', 'O', 'e', 'E':
+		id, nameErr := p.parseName()
+		if nameErr != nil {
+			return 0, 0, nameErr
+		}
+		id = toLowerASCII(id)
+		if id == "odd" {
+			return 2, 1, nil
+		}
+		if id == "even" {
+			return 2, 0, nil
+		}
+		return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", id)
+	default:
+		goto invalid
+	}
+
+positiveA:
+	if p.i >= len(p.s) {
+		goto eof
+	}
+	switch p.s[p.i] {
+	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+		a, err = p.parseInteger()
+		if err != nil {
+			return 0, 0, err
+		}
+		goto readA
+	case 'n', 'N':
+		a = 1
+		p.i++
+		goto readN
+	default:
+		goto invalid
+	}
+
+negativeA:
+	if p.i >= len(p.s) {
+		goto eof
+	}
+	switch p.s[p.i] {
+	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+		a, err = p.parseInteger()
+		if err != nil {
+			return 0, 0, err
+		}
+		a = -a
+		goto readA
+	case 'n', 'N':
+		a = -1
+		p.i++
+		goto readN
+	default:
+		goto invalid
+	}
+
+readA:
+	if p.i >= len(p.s) {
+		goto eof
+	}
+	switch p.s[p.i] {
+	case 'n', 'N':
+		p.i++
+		goto readN
+	default:
+		// The number we read as a is actually b.
+		return 0, a, nil
+	}
+
+readN:
+	p.skipWhitespace()
+	if p.i >= len(p.s) {
+		goto eof
+	}
+	switch p.s[p.i] {
+	case '+':
+		p.i++
+		p.skipWhitespace()
+		b, err = p.parseInteger()
+		if err != nil {
+			return 0, 0, err
+		}
+		return a, b, nil
+	case '-':
+		p.i++
+		p.skipWhitespace()
+		b, err = p.parseInteger()
+		if err != nil {
+			return 0, 0, err
+		}
+		return a, -b, nil
+	default:
+		return a, 0, nil
+	}
+
+eof:
+	return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b")
+
+invalid:
+	return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b")
+}
+
+// parseSimpleSelectorSequence parses a selector sequence that applies to
+// a single element.
+func (p *parser) parseSimpleSelectorSequence() (Sel, error) {
+	var selectors []Sel
+
+	if p.i >= len(p.s) {
+		return nil, errors.New("expected selector, found EOF instead")
+	}
+
+	switch p.s[p.i] {
+	case '*':
+		// It's the universal selector. Just skip over it, since it doesn't affect the meaning.
+		p.i++
+	case '#', '.', '[', ':':
+		// There's no type selector. Wait to process the other till the main loop.
+	default:
+		r, err := p.parseTypeSelector()
+		if err != nil {
+			return nil, err
+		}
+		selectors = append(selectors, r)
+	}
+
+loop:
+	for p.i < len(p.s) {
+		var (
+			ns  Sel
+			err error
+		)
+		switch p.s[p.i] {
+		case '#':
+			ns, err = p.parseIDSelector()
+		case '.':
+			ns, err = p.parseClassSelector()
+		case '[':
+			ns, err = p.parseAttributeSelector()
+		case ':':
+			ns, err = p.parsePseudoclassSelector()
+		default:
+			break loop
+		}
+		if err != nil {
+			return nil, err
+		}
+
+		selectors = append(selectors, ns)
+	}
+	if len(selectors) == 1 { // no need wrap the selectors in compoundSelector
+		return selectors[0], nil
+	}
+	return compoundSelector{selectors: selectors}, nil
+}
+
+// parseSelector parses a selector that may include combinators.
+func (p *parser) parseSelector() (Sel, error) {
+	p.skipWhitespace()
+	result, err := p.parseSimpleSelectorSequence()
+	if err != nil {
+		return nil, err
+	}
+
+	for {
+		var (
+			combinator byte
+			c          Sel
+		)
+		if p.skipWhitespace() {
+			combinator = ' '
+		}
+		if p.i >= len(p.s) {
+			return result, nil
+		}
+
+		switch p.s[p.i] {
+		case '+', '>', '~':
+			combinator = p.s[p.i]
+			p.i++
+			p.skipWhitespace()
+		case ',', ')':
+			// These characters can't begin a selector, but they can legally occur after one.
+			return result, nil
+		}
+
+		if combinator == 0 {
+			return result, nil
+		}
+
+		c, err = p.parseSimpleSelectorSequence()
+		if err != nil {
+			return nil, err
+		}
+		result = combinedSelector{first: result, combinator: combinator, second: c}
+	}
+}
+
+// parseSelectorGroup parses a group of selectors, separated by commas.
+func (p *parser) parseSelectorGroup() (SelectorGroup, error) {
+	current, err := p.parseSelector()
+	if err != nil {
+		return nil, err
+	}
+	result := SelectorGroup{current}
+
+	for p.i < len(p.s) {
+		if p.s[p.i] != ',' {
+			break
+		}
+		p.i++
+		c, err := p.parseSelector()
+		if err != nil {
+			return nil, err
+		}
+		result = append(result, c)
+	}
+	return result, nil
+}

+ 833 - 0
vendor/github.com/andybalholm/cascadia/selector.go

@@ -0,0 +1,833 @@
+package cascadia
+
+import (
+	"bytes"
+	"fmt"
+	"regexp"
+	"strings"
+
+	"golang.org/x/net/html"
+)
+
+// Matcher is the interface for basic selector functionality.
+// Match returns whether a selector matches n.
+type Matcher interface {
+	Match(n *html.Node) bool
+}
+
+// Sel is the interface for all the functionality provided by selectors.
+// It is currently the same as Matcher, but other methods may be added in the
+// future.
+type Sel interface {
+	Matcher
+	Specificity() Specificity
+}
+
+// Parse parses a selector.
+func Parse(sel string) (Sel, error) {
+	p := &parser{s: sel}
+	compiled, err := p.parseSelector()
+	if err != nil {
+		return nil, err
+	}
+
+	if p.i < len(sel) {
+		return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
+	}
+
+	return compiled, nil
+}
+
+// ParseGroup parses a selector, or a group of selectors separated by commas.
+func ParseGroup(sel string) (SelectorGroup, error) {
+	p := &parser{s: sel}
+	compiled, err := p.parseSelectorGroup()
+	if err != nil {
+		return nil, err
+	}
+
+	if p.i < len(sel) {
+		return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
+	}
+
+	return compiled, nil
+}
+
+// A Selector is a function which tells whether a node matches or not.
+//
+// This type is maintained for compatibility; I recommend using the newer and
+// more idiomatic interfaces Sel and Matcher.
+type Selector func(*html.Node) bool
+
+// Compile parses a selector and returns, if successful, a Selector object
+// that can be used to match against html.Node objects.
+func Compile(sel string) (Selector, error) {
+	compiled, err := ParseGroup(sel)
+	if err != nil {
+		return nil, err
+	}
+
+	return Selector(compiled.Match), nil
+}
+
+// MustCompile is like Compile, but panics instead of returning an error.
+func MustCompile(sel string) Selector {
+	compiled, err := Compile(sel)
+	if err != nil {
+		panic(err)
+	}
+	return compiled
+}
+
+// MatchAll returns a slice of the nodes that match the selector,
+// from n and its children.
+func (s Selector) MatchAll(n *html.Node) []*html.Node {
+	return s.matchAllInto(n, nil)
+}
+
+func (s Selector) matchAllInto(n *html.Node, storage []*html.Node) []*html.Node {
+	if s(n) {
+		storage = append(storage, n)
+	}
+
+	for child := n.FirstChild; child != nil; child = child.NextSibling {
+		storage = s.matchAllInto(child, storage)
+	}
+
+	return storage
+}
+
+func queryInto(n *html.Node, m Matcher, storage []*html.Node) []*html.Node {
+	for child := n.FirstChild; child != nil; child = child.NextSibling {
+		if m.Match(child) {
+			storage = append(storage, child)
+		}
+		storage = queryInto(child, m, storage)
+	}
+
+	return storage
+}
+
+// QueryAll returns a slice of all the nodes that match m, from the descendants
+// of n.
+func QueryAll(n *html.Node, m Matcher) []*html.Node {
+	return queryInto(n, m, nil)
+}
+
+// Match returns true if the node matches the selector.
+func (s Selector) Match(n *html.Node) bool {
+	return s(n)
+}
+
+// MatchFirst returns the first node that matches s, from n and its children.
+func (s Selector) MatchFirst(n *html.Node) *html.Node {
+	if s.Match(n) {
+		return n
+	}
+
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		m := s.MatchFirst(c)
+		if m != nil {
+			return m
+		}
+	}
+	return nil
+}
+
+// Query returns the first node that matches m, from the descendants of n.
+// If none matches, it returns nil.
+func Query(n *html.Node, m Matcher) *html.Node {
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		if m.Match(c) {
+			return c
+		}
+		if matched := Query(c, m); matched != nil {
+			return matched
+		}
+	}
+
+	return nil
+}
+
+// Filter returns the nodes in nodes that match the selector.
+func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) {
+	for _, n := range nodes {
+		if s(n) {
+			result = append(result, n)
+		}
+	}
+	return result
+}
+
+// Filter returns the nodes that match m.
+func Filter(nodes []*html.Node, m Matcher) (result []*html.Node) {
+	for _, n := range nodes {
+		if m.Match(n) {
+			result = append(result, n)
+		}
+	}
+	return result
+}
+
+type tagSelector struct {
+	tag string
+}
+
+// Matches elements with a given tag name.
+func (t tagSelector) Match(n *html.Node) bool {
+	return n.Type == html.ElementNode && n.Data == t.tag
+}
+
+func (c tagSelector) Specificity() Specificity {
+	return Specificity{0, 0, 1}
+}
+
+type classSelector struct {
+	class string
+}
+
+// Matches elements by class attribute.
+func (t classSelector) Match(n *html.Node) bool {
+	return matchAttribute(n, "class", func(s string) bool {
+		return matchInclude(t.class, s)
+	})
+}
+
+func (c classSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type idSelector struct {
+	id string
+}
+
+// Matches elements by id attribute.
+func (t idSelector) Match(n *html.Node) bool {
+	return matchAttribute(n, "id", func(s string) bool {
+		return s == t.id
+	})
+}
+
+func (c idSelector) Specificity() Specificity {
+	return Specificity{1, 0, 0}
+}
+
+type attrSelector struct {
+	key, val, operation string
+	regexp              *regexp.Regexp
+}
+
+// Matches elements by attribute value.
+func (t attrSelector) Match(n *html.Node) bool {
+	switch t.operation {
+	case "":
+		return matchAttribute(n, t.key, func(string) bool { return true })
+	case "=":
+		return matchAttribute(n, t.key, func(s string) bool { return s == t.val })
+	case "!=":
+		return attributeNotEqualMatch(t.key, t.val, n)
+	case "~=":
+		// matches elements where the attribute named key is a whitespace-separated list that includes val.
+		return matchAttribute(n, t.key, func(s string) bool { return matchInclude(t.val, s) })
+	case "|=":
+		return attributeDashMatch(t.key, t.val, n)
+	case "^=":
+		return attributePrefixMatch(t.key, t.val, n)
+	case "$=":
+		return attributeSuffixMatch(t.key, t.val, n)
+	case "*=":
+		return attributeSubstringMatch(t.key, t.val, n)
+	case "#=":
+		return attributeRegexMatch(t.key, t.regexp, n)
+	default:
+		panic(fmt.Sprintf("unsuported operation : %s", t.operation))
+	}
+}
+
+// matches elements where the attribute named key satisifes the function f.
+func matchAttribute(n *html.Node, key string, f func(string) bool) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+	for _, a := range n.Attr {
+		if a.Key == key && f(a.Val) {
+			return true
+		}
+	}
+	return false
+}
+
+// attributeNotEqualMatch matches elements where
+// the attribute named key does not have the value val.
+func attributeNotEqualMatch(key, val string, n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+	for _, a := range n.Attr {
+		if a.Key == key && a.Val == val {
+			return false
+		}
+	}
+	return true
+}
+
+// returns true if s is a whitespace-separated list that includes val.
+func matchInclude(val, s string) bool {
+	for s != "" {
+		i := strings.IndexAny(s, " \t\r\n\f")
+		if i == -1 {
+			return s == val
+		}
+		if s[:i] == val {
+			return true
+		}
+		s = s[i+1:]
+	}
+	return false
+}
+
+//  matches elements where the attribute named key equals val or starts with val plus a hyphen.
+func attributeDashMatch(key, val string, n *html.Node) bool {
+	return matchAttribute(n, key,
+		func(s string) bool {
+			if s == val {
+				return true
+			}
+			if len(s) <= len(val) {
+				return false
+			}
+			if s[:len(val)] == val && s[len(val)] == '-' {
+				return true
+			}
+			return false
+		})
+}
+
+// attributePrefixMatch returns a Selector that matches elements where
+// the attribute named key starts with val.
+func attributePrefixMatch(key, val string, n *html.Node) bool {
+	return matchAttribute(n, key,
+		func(s string) bool {
+			if strings.TrimSpace(s) == "" {
+				return false
+			}
+			return strings.HasPrefix(s, val)
+		})
+}
+
+// attributeSuffixMatch matches elements where
+// the attribute named key ends with val.
+func attributeSuffixMatch(key, val string, n *html.Node) bool {
+	return matchAttribute(n, key,
+		func(s string) bool {
+			if strings.TrimSpace(s) == "" {
+				return false
+			}
+			return strings.HasSuffix(s, val)
+		})
+}
+
+// attributeSubstringMatch matches nodes where
+// the attribute named key contains val.
+func attributeSubstringMatch(key, val string, n *html.Node) bool {
+	return matchAttribute(n, key,
+		func(s string) bool {
+			if strings.TrimSpace(s) == "" {
+				return false
+			}
+			return strings.Contains(s, val)
+		})
+}
+
+// attributeRegexMatch  matches nodes where
+// the attribute named key matches the regular expression rx
+func attributeRegexMatch(key string, rx *regexp.Regexp, n *html.Node) bool {
+	return matchAttribute(n, key,
+		func(s string) bool {
+			return rx.MatchString(s)
+		})
+}
+
+func (c attrSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+// ---------------- Pseudo class selectors ----------------
+// we use severals concrete types of pseudo-class selectors
+
+type relativePseudoClassSelector struct {
+	name  string // one of "not", "has", "haschild"
+	match SelectorGroup
+}
+
+func (s relativePseudoClassSelector) Match(n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+	switch s.name {
+	case "not":
+		// matches elements that do not match a.
+		return !s.match.Match(n)
+	case "has":
+		//  matches elements with any descendant that matches a.
+		return hasDescendantMatch(n, s.match)
+	case "haschild":
+		// matches elements with a child that matches a.
+		return hasChildMatch(n, s.match)
+	default:
+		panic(fmt.Sprintf("unsupported relative pseudo class selector : %s", s.name))
+	}
+}
+
+// hasChildMatch returns whether n has any child that matches a.
+func hasChildMatch(n *html.Node, a Matcher) bool {
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		if a.Match(c) {
+			return true
+		}
+	}
+	return false
+}
+
+// hasDescendantMatch performs a depth-first search of n's descendants,
+// testing whether any of them match a. It returns true as soon as a match is
+// found, or false if no match is found.
+func hasDescendantMatch(n *html.Node, a Matcher) bool {
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		if a.Match(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) {
+			return true
+		}
+	}
+	return false
+}
+
+// Specificity returns the specificity of the most specific selectors
+// in the pseudo-class arguments.
+// See https://www.w3.org/TR/selectors/#specificity-rules
+func (s relativePseudoClassSelector) Specificity() Specificity {
+	var max Specificity
+	for _, sel := range s.match {
+		newSpe := sel.Specificity()
+		if max.Less(newSpe) {
+			max = newSpe
+		}
+	}
+	return max
+}
+
+type containsPseudoClassSelector struct {
+	own   bool
+	value string
+}
+
+func (s containsPseudoClassSelector) Match(n *html.Node) bool {
+	var text string
+	if s.own {
+		// matches nodes that directly contain the given text
+		text = strings.ToLower(nodeOwnText(n))
+	} else {
+		// matches nodes that contain the given text.
+		text = strings.ToLower(nodeText(n))
+	}
+	return strings.Contains(text, s.value)
+}
+
+func (s containsPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type regexpPseudoClassSelector struct {
+	own    bool
+	regexp *regexp.Regexp
+}
+
+func (s regexpPseudoClassSelector) Match(n *html.Node) bool {
+	var text string
+	if s.own {
+		// matches nodes whose text directly matches the specified regular expression
+		text = nodeOwnText(n)
+	} else {
+		// matches nodes whose text matches the specified regular expression
+		text = nodeText(n)
+	}
+	return s.regexp.MatchString(text)
+}
+
+// writeNodeText writes the text contained in n and its descendants to b.
+func writeNodeText(n *html.Node, b *bytes.Buffer) {
+	switch n.Type {
+	case html.TextNode:
+		b.WriteString(n.Data)
+	case html.ElementNode:
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			writeNodeText(c, b)
+		}
+	}
+}
+
+// nodeText returns the text contained in n and its descendants.
+func nodeText(n *html.Node) string {
+	var b bytes.Buffer
+	writeNodeText(n, &b)
+	return b.String()
+}
+
+// nodeOwnText returns the contents of the text nodes that are direct
+// children of n.
+func nodeOwnText(n *html.Node) string {
+	var b bytes.Buffer
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		if c.Type == html.TextNode {
+			b.WriteString(c.Data)
+		}
+	}
+	return b.String()
+}
+
+func (s regexpPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type nthPseudoClassSelector struct {
+	a, b         int
+	last, ofType bool
+}
+
+func (s nthPseudoClassSelector) Match(n *html.Node) bool {
+	if s.a == 0 {
+		if s.last {
+			return simpleNthLastChildMatch(s.b, s.ofType, n)
+		} else {
+			return simpleNthChildMatch(s.b, s.ofType, n)
+		}
+	}
+	return nthChildMatch(s.a, s.b, s.last, s.ofType, n)
+}
+
+// nthChildMatch implements :nth-child(an+b).
+// If last is true, implements :nth-last-child instead.
+// If ofType is true, implements :nth-of-type instead.
+func nthChildMatch(a, b int, last, ofType bool, n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+
+	parent := n.Parent
+	if parent == nil {
+		return false
+	}
+
+	if parent.Type == html.DocumentNode {
+		return false
+	}
+
+	i := -1
+	count := 0
+	for c := parent.FirstChild; c != nil; c = c.NextSibling {
+		if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
+			continue
+		}
+		count++
+		if c == n {
+			i = count
+			if !last {
+				break
+			}
+		}
+	}
+
+	if i == -1 {
+		// This shouldn't happen, since n should always be one of its parent's children.
+		return false
+	}
+
+	if last {
+		i = count - i + 1
+	}
+
+	i -= b
+	if a == 0 {
+		return i == 0
+	}
+
+	return i%a == 0 && i/a >= 0
+}
+
+// simpleNthChildMatch implements :nth-child(b).
+// If ofType is true, implements :nth-of-type instead.
+func simpleNthChildMatch(b int, ofType bool, n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+
+	parent := n.Parent
+	if parent == nil {
+		return false
+	}
+
+	if parent.Type == html.DocumentNode {
+		return false
+	}
+
+	count := 0
+	for c := parent.FirstChild; c != nil; c = c.NextSibling {
+		if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
+			continue
+		}
+		count++
+		if c == n {
+			return count == b
+		}
+		if count >= b {
+			return false
+		}
+	}
+	return false
+}
+
+// simpleNthLastChildMatch implements :nth-last-child(b).
+// If ofType is true, implements :nth-last-of-type instead.
+func simpleNthLastChildMatch(b int, ofType bool, n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+
+	parent := n.Parent
+	if parent == nil {
+		return false
+	}
+
+	if parent.Type == html.DocumentNode {
+		return false
+	}
+
+	count := 0
+	for c := parent.LastChild; c != nil; c = c.PrevSibling {
+		if c.Type != html.ElementNode || (ofType && c.Data != n.Data) {
+			continue
+		}
+		count++
+		if c == n {
+			return count == b
+		}
+		if count >= b {
+			return false
+		}
+	}
+	return false
+}
+
+// Specificity for nth-child pseudo-class.
+// Does not support a list of selectors
+func (s nthPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type onlyChildPseudoClassSelector struct {
+	ofType bool
+}
+
+// Match implements :only-child.
+// If `ofType` is true, it implements :only-of-type instead.
+func (s onlyChildPseudoClassSelector) Match(n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+
+	parent := n.Parent
+	if parent == nil {
+		return false
+	}
+
+	if parent.Type == html.DocumentNode {
+		return false
+	}
+
+	count := 0
+	for c := parent.FirstChild; c != nil; c = c.NextSibling {
+		if (c.Type != html.ElementNode) || (s.ofType && c.Data != n.Data) {
+			continue
+		}
+		count++
+		if count > 1 {
+			return false
+		}
+	}
+
+	return count == 1
+}
+
+func (s onlyChildPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type inputPseudoClassSelector struct{}
+
+// Matches input, select, textarea and button elements.
+func (s inputPseudoClassSelector) Match(n *html.Node) bool {
+	return n.Type == html.ElementNode && (n.Data == "input" || n.Data == "select" || n.Data == "textarea" || n.Data == "button")
+}
+
+func (s inputPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type emptyElementPseudoClassSelector struct{}
+
+// Matches empty elements.
+func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		switch c.Type {
+		case html.ElementNode, html.TextNode:
+			return false
+		}
+	}
+
+	return true
+}
+
+func (s emptyElementPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type rootPseudoClassSelector struct{}
+
+// Match implements :root
+func (s rootPseudoClassSelector) Match(n *html.Node) bool {
+	if n.Type != html.ElementNode {
+		return false
+	}
+	if n.Parent == nil {
+		return false
+	}
+	return n.Parent.Type == html.DocumentNode
+}
+
+func (s rootPseudoClassSelector) Specificity() Specificity {
+	return Specificity{0, 1, 0}
+}
+
+type compoundSelector struct {
+	selectors []Sel
+}
+
+// Matches elements if each sub-selectors matches.
+func (t compoundSelector) Match(n *html.Node) bool {
+	if len(t.selectors) == 0 {
+		return n.Type == html.ElementNode
+	}
+
+	for _, sel := range t.selectors {
+		if !sel.Match(n) {
+			return false
+		}
+	}
+	return true
+}
+
+func (s compoundSelector) Specificity() Specificity {
+	var out Specificity
+	for _, sel := range s.selectors {
+		out = out.Add(sel.Specificity())
+	}
+	return out
+}
+
+type combinedSelector struct {
+	first      Sel
+	combinator byte
+	second     Sel
+}
+
+func (t combinedSelector) Match(n *html.Node) bool {
+	if t.first == nil {
+		return false // maybe we should panic
+	}
+	switch t.combinator {
+	case 0:
+		return t.first.Match(n)
+	case ' ':
+		return descendantMatch(t.first, t.second, n)
+	case '>':
+		return childMatch(t.first, t.second, n)
+	case '+':
+		return siblingMatch(t.first, t.second, true, n)
+	case '~':
+		return siblingMatch(t.first, t.second, false, n)
+	default:
+		panic("unknown combinator")
+	}
+}
+
+// matches an element if it matches d and has an ancestor that matches a.
+func descendantMatch(a, d Matcher, n *html.Node) bool {
+	if !d.Match(n) {
+		return false
+	}
+
+	for p := n.Parent; p != nil; p = p.Parent {
+		if a.Match(p) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// matches an element if it matches d and its parent matches a.
+func childMatch(a, d Matcher, n *html.Node) bool {
+	return d.Match(n) && n.Parent != nil && a.Match(n.Parent)
+}
+
+// matches an element if it matches s2 and is preceded by an element that matches s1.
+// If adjacent is true, the sibling must be immediately before the element.
+func siblingMatch(s1, s2 Matcher, adjacent bool, n *html.Node) bool {
+	if !s2.Match(n) {
+		return false
+	}
+
+	if adjacent {
+		for n = n.PrevSibling; n != nil; n = n.PrevSibling {
+			if n.Type == html.TextNode || n.Type == html.CommentNode {
+				continue
+			}
+			return s1.Match(n)
+		}
+		return false
+	}
+
+	// Walk backwards looking for element that matches s1
+	for c := n.PrevSibling; c != nil; c = c.PrevSibling {
+		if s1.Match(c) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func (s combinedSelector) Specificity() Specificity {
+	spec := s.first.Specificity()
+	if s.second != nil {
+		spec = spec.Add(s.second.Specificity())
+	}
+	return spec
+}
+
+// A SelectorGroup is a list of selectors, which matches if any of the
+// individual selectors matches.
+type SelectorGroup []Sel
+
+// Match returns true if the node matches one of the single selectors.
+func (s SelectorGroup) Match(n *html.Node) bool {
+	for _, sel := range s {
+		if sel.Match(n) {
+			return true
+		}
+	}
+	return false
+}

+ 26 - 0
vendor/github.com/andybalholm/cascadia/specificity.go

@@ -0,0 +1,26 @@
+package cascadia
+
+// Specificity is the CSS specificity as defined in
+// https://www.w3.org/TR/selectors/#specificity-rules
+// with the convention Specificity = [A,B,C].
+type Specificity [3]int
+
+// returns `true` if s < other (strictly), false otherwise
+func (s Specificity) Less(other Specificity) bool {
+	for i := range s {
+		if s[i] < other[i] {
+			return true
+		}
+		if s[i] > other[i] {
+			return false
+		}
+	}
+	return false
+}
+
+func (s Specificity) Add(other Specificity) Specificity {
+	for i, sp := range other {
+		s[i] += sp
+	}
+	return s
+}

+ 9 - 0
vendor/github.com/google/uuid/.travis.yml

@@ -0,0 +1,9 @@
+language: go
+
+go:
+  - 1.4.3
+  - 1.5.3
+  - tip
+
+script:
+  - go test -v ./...

+ 10 - 0
vendor/github.com/google/uuid/CONTRIBUTING.md

@@ -0,0 +1,10 @@
+# How to contribute
+
+We definitely welcome patches and contribution to this project!
+
+### Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License Agreement](https://cla.developers.google.com/clas).
+
+You may have already signed it for other Google projects.

+ 9 - 0
vendor/github.com/google/uuid/CONTRIBUTORS

@@ -0,0 +1,9 @@
+Paul Borman <borman@google.com>
+bmatsuo
+shawnps
+theory
+jboverfelt
+dsymonds
+cd1
+wallclockbuilder
+dansouza

+ 27 - 0
vendor/github.com/google/uuid/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2009,2014 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * 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.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+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
+OWNER 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.

+ 19 - 0
vendor/github.com/google/uuid/README.md

@@ -0,0 +1,19 @@
+# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master)
+The uuid package generates and inspects UUIDs based on
+[RFC 4122](http://tools.ietf.org/html/rfc4122)
+and DCE 1.1: Authentication and Security Services. 
+
+This package is based on the github.com/pborman/uuid package (previously named
+code.google.com/p/go-uuid).  It differs from these earlier packages in that
+a UUID is a 16 byte array rather than a byte slice.  One loss due to this
+change is the ability to represent an invalid UUID (vs a NIL UUID).
+
+###### Install
+`go get github.com/google/uuid`
+
+###### Documentation 
+[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid)
+
+Full `go doc` style documentation for the package can be viewed online without
+installing this package by using the GoDoc site here: 
+http://pkg.go.dev/github.com/google/uuid

+ 80 - 0
vendor/github.com/google/uuid/dce.go

@@ -0,0 +1,80 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+	"fmt"
+	"os"
+)
+
+// A Domain represents a Version 2 domain
+type Domain byte
+
+// Domain constants for DCE Security (Version 2) UUIDs.
+const (
+	Person = Domain(0)
+	Group  = Domain(1)
+	Org    = Domain(2)
+)
+
+// NewDCESecurity returns a DCE Security (Version 2) UUID.
+//
+// The domain should be one of Person, Group or Org.
+// On a POSIX system the id should be the users UID for the Person
+// domain and the users GID for the Group.  The meaning of id for
+// the domain Org or on non-POSIX systems is site defined.
+//
+// For a given domain/id pair the same token may be returned for up to
+// 7 minutes and 10 seconds.
+func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
+	uuid, err := NewUUID()
+	if err == nil {
+		uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
+		uuid[9] = byte(domain)
+		binary.BigEndian.PutUint32(uuid[0:], id)
+	}
+	return uuid, err
+}
+
+// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
+// domain with the id returned by os.Getuid.
+//
+//  NewDCESecurity(Person, uint32(os.Getuid()))
+func NewDCEPerson() (UUID, error) {
+	return NewDCESecurity(Person, uint32(os.Getuid()))
+}
+
+// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
+// domain with the id returned by os.Getgid.
+//
+//  NewDCESecurity(Group, uint32(os.Getgid()))
+func NewDCEGroup() (UUID, error) {
+	return NewDCESecurity(Group, uint32(os.Getgid()))
+}
+
+// Domain returns the domain for a Version 2 UUID.  Domains are only defined
+// for Version 2 UUIDs.
+func (uuid UUID) Domain() Domain {
+	return Domain(uuid[9])
+}
+
+// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
+// UUIDs.
+func (uuid UUID) ID() uint32 {
+	return binary.BigEndian.Uint32(uuid[0:4])
+}
+
+func (d Domain) String() string {
+	switch d {
+	case Person:
+		return "Person"
+	case Group:
+		return "Group"
+	case Org:
+		return "Org"
+	}
+	return fmt.Sprintf("Domain%d", int(d))
+}

+ 12 - 0
vendor/github.com/google/uuid/doc.go

@@ -0,0 +1,12 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package uuid generates and inspects UUIDs.
+//
+// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
+// Services.
+//
+// A UUID is a 16 byte (128 bit) array.  UUIDs may be used as keys to
+// maps or compared directly.
+package uuid

+ 53 - 0
vendor/github.com/google/uuid/hash.go

@@ -0,0 +1,53 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"crypto/md5"
+	"crypto/sha1"
+	"hash"
+)
+
+// Well known namespace IDs and UUIDs
+var (
+	NameSpaceDNS  = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
+	NameSpaceURL  = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
+	NameSpaceOID  = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
+	NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
+	Nil           UUID // empty UUID, all zeros
+)
+
+// NewHash returns a new UUID derived from the hash of space concatenated with
+// data generated by h.  The hash should be at least 16 byte in length.  The
+// first 16 bytes of the hash are used to form the UUID.  The version of the
+// UUID will be the lower 4 bits of version.  NewHash is used to implement
+// NewMD5 and NewSHA1.
+func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
+	h.Reset()
+	h.Write(space[:]) //nolint:errcheck
+	h.Write(data)     //nolint:errcheck
+	s := h.Sum(nil)
+	var uuid UUID
+	copy(uuid[:], s)
+	uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
+	uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
+	return uuid
+}
+
+// NewMD5 returns a new MD5 (Version 3) UUID based on the
+// supplied name space and data.  It is the same as calling:
+//
+//  NewHash(md5.New(), space, data, 3)
+func NewMD5(space UUID, data []byte) UUID {
+	return NewHash(md5.New(), space, data, 3)
+}
+
+// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
+// supplied name space and data.  It is the same as calling:
+//
+//  NewHash(sha1.New(), space, data, 5)
+func NewSHA1(space UUID, data []byte) UUID {
+	return NewHash(sha1.New(), space, data, 5)
+}

+ 38 - 0
vendor/github.com/google/uuid/marshal.go

@@ -0,0 +1,38 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "fmt"
+
+// MarshalText implements encoding.TextMarshaler.
+func (uuid UUID) MarshalText() ([]byte, error) {
+	var js [36]byte
+	encodeHex(js[:], uuid)
+	return js[:], nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (uuid *UUID) UnmarshalText(data []byte) error {
+	id, err := ParseBytes(data)
+	if err != nil {
+		return err
+	}
+	*uuid = id
+	return nil
+}
+
+// MarshalBinary implements encoding.BinaryMarshaler.
+func (uuid UUID) MarshalBinary() ([]byte, error) {
+	return uuid[:], nil
+}
+
+// UnmarshalBinary implements encoding.BinaryUnmarshaler.
+func (uuid *UUID) UnmarshalBinary(data []byte) error {
+	if len(data) != 16 {
+		return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
+	}
+	copy(uuid[:], data)
+	return nil
+}

+ 90 - 0
vendor/github.com/google/uuid/node.go

@@ -0,0 +1,90 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"sync"
+)
+
+var (
+	nodeMu sync.Mutex
+	ifname string  // name of interface being used
+	nodeID [6]byte // hardware for version 1 UUIDs
+	zeroID [6]byte // nodeID with only 0's
+)
+
+// NodeInterface returns the name of the interface from which the NodeID was
+// derived.  The interface "user" is returned if the NodeID was set by
+// SetNodeID.
+func NodeInterface() string {
+	defer nodeMu.Unlock()
+	nodeMu.Lock()
+	return ifname
+}
+
+// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
+// If name is "" then the first usable interface found will be used or a random
+// Node ID will be generated.  If a named interface cannot be found then false
+// is returned.
+//
+// SetNodeInterface never fails when name is "".
+func SetNodeInterface(name string) bool {
+	defer nodeMu.Unlock()
+	nodeMu.Lock()
+	return setNodeInterface(name)
+}
+
+func setNodeInterface(name string) bool {
+	iname, addr := getHardwareInterface(name) // null implementation for js
+	if iname != "" && addr != nil {
+		ifname = iname
+		copy(nodeID[:], addr)
+		return true
+	}
+
+	// We found no interfaces with a valid hardware address.  If name
+	// does not specify a specific interface generate a random Node ID
+	// (section 4.1.6)
+	if name == "" {
+		ifname = "random"
+		randomBits(nodeID[:])
+		return true
+	}
+	return false
+}
+
+// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
+// if not already set.
+func NodeID() []byte {
+	defer nodeMu.Unlock()
+	nodeMu.Lock()
+	if nodeID == zeroID {
+		setNodeInterface("")
+	}
+	nid := nodeID
+	return nid[:]
+}
+
+// SetNodeID sets the Node ID to be used for Version 1 UUIDs.  The first 6 bytes
+// of id are used.  If id is less than 6 bytes then false is returned and the
+// Node ID is not set.
+func SetNodeID(id []byte) bool {
+	if len(id) < 6 {
+		return false
+	}
+	defer nodeMu.Unlock()
+	nodeMu.Lock()
+	copy(nodeID[:], id)
+	ifname = "user"
+	return true
+}
+
+// NodeID returns the 6 byte node id encoded in uuid.  It returns nil if uuid is
+// not valid.  The NodeID is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) NodeID() []byte {
+	var node [6]byte
+	copy(node[:], uuid[10:])
+	return node[:]
+}

+ 12 - 0
vendor/github.com/google/uuid/node_js.go

@@ -0,0 +1,12 @@
+// Copyright 2017 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js
+
+package uuid
+
+// getHardwareInterface returns nil values for the JS version of the code.
+// This remvoves the "net" dependency, because it is not used in the browser.
+// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
+func getHardwareInterface(name string) (string, []byte) { return "", nil }

+ 33 - 0
vendor/github.com/google/uuid/node_net.go

@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !js
+
+package uuid
+
+import "net"
+
+var interfaces []net.Interface // cached list of interfaces
+
+// getHardwareInterface returns the name and hardware address of interface name.
+// If name is "" then the name and hardware address of one of the system's
+// interfaces is returned.  If no interfaces are found (name does not exist or
+// there are no interfaces) then "", nil is returned.
+//
+// Only addresses of at least 6 bytes are returned.
+func getHardwareInterface(name string) (string, []byte) {
+	if interfaces == nil {
+		var err error
+		interfaces, err = net.Interfaces()
+		if err != nil {
+			return "", nil
+		}
+	}
+	for _, ifs := range interfaces {
+		if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
+			return ifs.Name, ifs.HardwareAddr
+		}
+	}
+	return "", nil
+}

+ 118 - 0
vendor/github.com/google/uuid/null.go

@@ -0,0 +1,118 @@
+// Copyright 2021 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"bytes"
+	"database/sql/driver"
+	"encoding/json"
+	"fmt"
+)
+
+var jsonNull = []byte("null")
+
+// NullUUID represents a UUID that may be null.
+// NullUUID implements the SQL driver.Scanner interface so
+// it can be used as a scan destination:
+//
+//  var u uuid.NullUUID
+//  err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
+//  ...
+//  if u.Valid {
+//     // use u.UUID
+//  } else {
+//     // NULL value
+//  }
+//
+type NullUUID struct {
+	UUID  UUID
+	Valid bool // Valid is true if UUID is not NULL
+}
+
+// Scan implements the SQL driver.Scanner interface.
+func (nu *NullUUID) Scan(value interface{}) error {
+	if value == nil {
+		nu.UUID, nu.Valid = Nil, false
+		return nil
+	}
+
+	err := nu.UUID.Scan(value)
+	if err != nil {
+		nu.Valid = false
+		return err
+	}
+
+	nu.Valid = true
+	return nil
+}
+
+// Value implements the driver Valuer interface.
+func (nu NullUUID) Value() (driver.Value, error) {
+	if !nu.Valid {
+		return nil, nil
+	}
+	// Delegate to UUID Value function
+	return nu.UUID.Value()
+}
+
+// MarshalBinary implements encoding.BinaryMarshaler.
+func (nu NullUUID) MarshalBinary() ([]byte, error) {
+	if nu.Valid {
+		return nu.UUID[:], nil
+	}
+
+	return []byte(nil), nil
+}
+
+// UnmarshalBinary implements encoding.BinaryUnmarshaler.
+func (nu *NullUUID) UnmarshalBinary(data []byte) error {
+	if len(data) != 16 {
+		return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
+	}
+	copy(nu.UUID[:], data)
+	nu.Valid = true
+	return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (nu NullUUID) MarshalText() ([]byte, error) {
+	if nu.Valid {
+		return nu.UUID.MarshalText()
+	}
+
+	return jsonNull, nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (nu *NullUUID) UnmarshalText(data []byte) error {
+	id, err := ParseBytes(data)
+	if err != nil {
+		nu.Valid = false
+		return err
+	}
+	nu.UUID = id
+	nu.Valid = true
+	return nil
+}
+
+// MarshalJSON implements json.Marshaler.
+func (nu NullUUID) MarshalJSON() ([]byte, error) {
+	if nu.Valid {
+		return json.Marshal(nu.UUID)
+	}
+
+	return jsonNull, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (nu *NullUUID) UnmarshalJSON(data []byte) error {
+	if bytes.Equal(data, jsonNull) {
+		*nu = NullUUID{}
+		return nil // valid null UUID
+	}
+	err := json.Unmarshal(data, &nu.UUID)
+	nu.Valid = err == nil
+	return err
+}

+ 59 - 0
vendor/github.com/google/uuid/sql.go

@@ -0,0 +1,59 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"database/sql/driver"
+	"fmt"
+)
+
+// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
+// Currently, database types that map to string and []byte are supported. Please
+// consult database-specific driver documentation for matching types.
+func (uuid *UUID) Scan(src interface{}) error {
+	switch src := src.(type) {
+	case nil:
+		return nil
+
+	case string:
+		// if an empty UUID comes from a table, we return a null UUID
+		if src == "" {
+			return nil
+		}
+
+		// see Parse for required string format
+		u, err := Parse(src)
+		if err != nil {
+			return fmt.Errorf("Scan: %v", err)
+		}
+
+		*uuid = u
+
+	case []byte:
+		// if an empty UUID comes from a table, we return a null UUID
+		if len(src) == 0 {
+			return nil
+		}
+
+		// assumes a simple slice of bytes if 16 bytes
+		// otherwise attempts to parse
+		if len(src) != 16 {
+			return uuid.Scan(string(src))
+		}
+		copy((*uuid)[:], src)
+
+	default:
+		return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
+	}
+
+	return nil
+}
+
+// Value implements sql.Valuer so that UUIDs can be written to databases
+// transparently. Currently, UUIDs map to strings. Please consult
+// database-specific driver documentation for matching types.
+func (uuid UUID) Value() (driver.Value, error) {
+	return uuid.String(), nil
+}

+ 123 - 0
vendor/github.com/google/uuid/time.go

@@ -0,0 +1,123 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+	"sync"
+	"time"
+)
+
+// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
+// 1582.
+type Time int64
+
+const (
+	lillian    = 2299160          // Julian day of 15 Oct 1582
+	unix       = 2440587          // Julian day of 1 Jan 1970
+	epoch      = unix - lillian   // Days between epochs
+	g1582      = epoch * 86400    // seconds between epochs
+	g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
+)
+
+var (
+	timeMu   sync.Mutex
+	lasttime uint64 // last time we returned
+	clockSeq uint16 // clock sequence for this run
+
+	timeNow = time.Now // for testing
+)
+
+// UnixTime converts t the number of seconds and nanoseconds using the Unix
+// epoch of 1 Jan 1970.
+func (t Time) UnixTime() (sec, nsec int64) {
+	sec = int64(t - g1582ns100)
+	nsec = (sec % 10000000) * 100
+	sec /= 10000000
+	return sec, nsec
+}
+
+// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
+// clock sequence as well as adjusting the clock sequence as needed.  An error
+// is returned if the current time cannot be determined.
+func GetTime() (Time, uint16, error) {
+	defer timeMu.Unlock()
+	timeMu.Lock()
+	return getTime()
+}
+
+func getTime() (Time, uint16, error) {
+	t := timeNow()
+
+	// If we don't have a clock sequence already, set one.
+	if clockSeq == 0 {
+		setClockSequence(-1)
+	}
+	now := uint64(t.UnixNano()/100) + g1582ns100
+
+	// If time has gone backwards with this clock sequence then we
+	// increment the clock sequence
+	if now <= lasttime {
+		clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
+	}
+	lasttime = now
+	return Time(now), clockSeq, nil
+}
+
+// ClockSequence returns the current clock sequence, generating one if not
+// already set.  The clock sequence is only used for Version 1 UUIDs.
+//
+// The uuid package does not use global static storage for the clock sequence or
+// the last time a UUID was generated.  Unless SetClockSequence is used, a new
+// random clock sequence is generated the first time a clock sequence is
+// requested by ClockSequence, GetTime, or NewUUID.  (section 4.2.1.1)
+func ClockSequence() int {
+	defer timeMu.Unlock()
+	timeMu.Lock()
+	return clockSequence()
+}
+
+func clockSequence() int {
+	if clockSeq == 0 {
+		setClockSequence(-1)
+	}
+	return int(clockSeq & 0x3fff)
+}
+
+// SetClockSequence sets the clock sequence to the lower 14 bits of seq.  Setting to
+// -1 causes a new sequence to be generated.
+func SetClockSequence(seq int) {
+	defer timeMu.Unlock()
+	timeMu.Lock()
+	setClockSequence(seq)
+}
+
+func setClockSequence(seq int) {
+	if seq == -1 {
+		var b [2]byte
+		randomBits(b[:]) // clock sequence
+		seq = int(b[0])<<8 | int(b[1])
+	}
+	oldSeq := clockSeq
+	clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
+	if oldSeq != clockSeq {
+		lasttime = 0
+	}
+}
+
+// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
+// uuid.  The time is only defined for version 1 and 2 UUIDs.
+func (uuid UUID) Time() Time {
+	time := int64(binary.BigEndian.Uint32(uuid[0:4]))
+	time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
+	time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
+	return Time(time)
+}
+
+// ClockSequence returns the clock sequence encoded in uuid.
+// The clock sequence is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) ClockSequence() int {
+	return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
+}

+ 43 - 0
vendor/github.com/google/uuid/util.go

@@ -0,0 +1,43 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"io"
+)
+
+// randomBits completely fills slice b with random data.
+func randomBits(b []byte) {
+	if _, err := io.ReadFull(rander, b); err != nil {
+		panic(err.Error()) // rand should never fail
+	}
+}
+
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = [256]byte{
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// xtob converts hex characters x1 and x2 into a byte.
+func xtob(x1, x2 byte) (byte, bool) {
+	b1 := xvalues[x1]
+	b2 := xvalues[x2]
+	return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}

+ 294 - 0
vendor/github.com/google/uuid/uuid.go

@@ -0,0 +1,294 @@
+// Copyright 2018 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"strings"
+	"sync"
+)
+
+// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
+// 4122.
+type UUID [16]byte
+
+// A Version represents a UUID's version.
+type Version byte
+
+// A Variant represents a UUID's variant.
+type Variant byte
+
+// Constants returned by Variant.
+const (
+	Invalid   = Variant(iota) // Invalid UUID
+	RFC4122                   // The variant specified in RFC4122
+	Reserved                  // Reserved, NCS backward compatibility.
+	Microsoft                 // Reserved, Microsoft Corporation backward compatibility.
+	Future                    // Reserved for future definition.
+)
+
+const randPoolSize = 16 * 16
+
+var (
+	rander      = rand.Reader // random function
+	poolEnabled = false
+	poolMu      sync.Mutex
+	poolPos     = randPoolSize     // protected with poolMu
+	pool        [randPoolSize]byte // protected with poolMu
+)
+
+type invalidLengthError struct{ len int }
+
+func (err invalidLengthError) Error() string {
+	return fmt.Sprintf("invalid UUID length: %d", err.len)
+}
+
+// IsInvalidLengthError is matcher function for custom error invalidLengthError
+func IsInvalidLengthError(err error) bool {
+	_, ok := err.(invalidLengthError)
+	return ok
+}
+
+// Parse decodes s into a UUID or returns an error.  Both the standard UUID
+// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
+// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
+// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
+func Parse(s string) (UUID, error) {
+	var uuid UUID
+	switch len(s) {
+	// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+	case 36:
+
+	// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+	case 36 + 9:
+		if strings.ToLower(s[:9]) != "urn:uuid:" {
+			return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
+		}
+		s = s[9:]
+
+	// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+	case 36 + 2:
+		s = s[1:]
+
+	// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+	case 32:
+		var ok bool
+		for i := range uuid {
+			uuid[i], ok = xtob(s[i*2], s[i*2+1])
+			if !ok {
+				return uuid, errors.New("invalid UUID format")
+			}
+		}
+		return uuid, nil
+	default:
+		return uuid, invalidLengthError{len(s)}
+	}
+	// s is now at least 36 bytes long
+	// it must be of the form  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+		return uuid, errors.New("invalid UUID format")
+	}
+	for i, x := range [16]int{
+		0, 2, 4, 6,
+		9, 11,
+		14, 16,
+		19, 21,
+		24, 26, 28, 30, 32, 34} {
+		v, ok := xtob(s[x], s[x+1])
+		if !ok {
+			return uuid, errors.New("invalid UUID format")
+		}
+		uuid[i] = v
+	}
+	return uuid, nil
+}
+
+// ParseBytes is like Parse, except it parses a byte slice instead of a string.
+func ParseBytes(b []byte) (UUID, error) {
+	var uuid UUID
+	switch len(b) {
+	case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+	case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+		if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
+			return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
+		}
+		b = b[9:]
+	case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+		b = b[1:]
+	case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+		var ok bool
+		for i := 0; i < 32; i += 2 {
+			uuid[i/2], ok = xtob(b[i], b[i+1])
+			if !ok {
+				return uuid, errors.New("invalid UUID format")
+			}
+		}
+		return uuid, nil
+	default:
+		return uuid, invalidLengthError{len(b)}
+	}
+	// s is now at least 36 bytes long
+	// it must be of the form  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+	if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
+		return uuid, errors.New("invalid UUID format")
+	}
+	for i, x := range [16]int{
+		0, 2, 4, 6,
+		9, 11,
+		14, 16,
+		19, 21,
+		24, 26, 28, 30, 32, 34} {
+		v, ok := xtob(b[x], b[x+1])
+		if !ok {
+			return uuid, errors.New("invalid UUID format")
+		}
+		uuid[i] = v
+	}
+	return uuid, nil
+}
+
+// MustParse is like Parse but panics if the string cannot be parsed.
+// It simplifies safe initialization of global variables holding compiled UUIDs.
+func MustParse(s string) UUID {
+	uuid, err := Parse(s)
+	if err != nil {
+		panic(`uuid: Parse(` + s + `): ` + err.Error())
+	}
+	return uuid
+}
+
+// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
+// does not have a length of 16. The bytes are copied from the slice.
+func FromBytes(b []byte) (uuid UUID, err error) {
+	err = uuid.UnmarshalBinary(b)
+	return uuid, err
+}
+
+// Must returns uuid if err is nil and panics otherwise.
+func Must(uuid UUID, err error) UUID {
+	if err != nil {
+		panic(err)
+	}
+	return uuid
+}
+
+// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+// , or "" if uuid is invalid.
+func (uuid UUID) String() string {
+	var buf [36]byte
+	encodeHex(buf[:], uuid)
+	return string(buf[:])
+}
+
+// URN returns the RFC 2141 URN form of uuid,
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,  or "" if uuid is invalid.
+func (uuid UUID) URN() string {
+	var buf [36 + 9]byte
+	copy(buf[:], "urn:uuid:")
+	encodeHex(buf[9:], uuid)
+	return string(buf[:])
+}
+
+func encodeHex(dst []byte, uuid UUID) {
+	hex.Encode(dst, uuid[:4])
+	dst[8] = '-'
+	hex.Encode(dst[9:13], uuid[4:6])
+	dst[13] = '-'
+	hex.Encode(dst[14:18], uuid[6:8])
+	dst[18] = '-'
+	hex.Encode(dst[19:23], uuid[8:10])
+	dst[23] = '-'
+	hex.Encode(dst[24:], uuid[10:])
+}
+
+// Variant returns the variant encoded in uuid.
+func (uuid UUID) Variant() Variant {
+	switch {
+	case (uuid[8] & 0xc0) == 0x80:
+		return RFC4122
+	case (uuid[8] & 0xe0) == 0xc0:
+		return Microsoft
+	case (uuid[8] & 0xe0) == 0xe0:
+		return Future
+	default:
+		return Reserved
+	}
+}
+
+// Version returns the version of uuid.
+func (uuid UUID) Version() Version {
+	return Version(uuid[6] >> 4)
+}
+
+func (v Version) String() string {
+	if v > 15 {
+		return fmt.Sprintf("BAD_VERSION_%d", v)
+	}
+	return fmt.Sprintf("VERSION_%d", v)
+}
+
+func (v Variant) String() string {
+	switch v {
+	case RFC4122:
+		return "RFC4122"
+	case Reserved:
+		return "Reserved"
+	case Microsoft:
+		return "Microsoft"
+	case Future:
+		return "Future"
+	case Invalid:
+		return "Invalid"
+	}
+	return fmt.Sprintf("BadVariant%d", int(v))
+}
+
+// SetRand sets the random number generator to r, which implements io.Reader.
+// If r.Read returns an error when the package requests random data then
+// a panic will be issued.
+//
+// Calling SetRand with nil sets the random number generator to the default
+// generator.
+func SetRand(r io.Reader) {
+	if r == nil {
+		rander = rand.Reader
+		return
+	}
+	rander = r
+}
+
+// EnableRandPool enables internal randomness pool used for Random
+// (Version 4) UUID generation. The pool contains random bytes read from
+// the random number generator on demand in batches. Enabling the pool
+// may improve the UUID generation throughput significantly.
+//
+// Since the pool is stored on the Go heap, this feature may be a bad fit
+// for security sensitive applications.
+//
+// Both EnableRandPool and DisableRandPool are not thread-safe and should
+// only be called when there is no possibility that New or any other
+// UUID Version 4 generation function will be called concurrently.
+func EnableRandPool() {
+	poolEnabled = true
+}
+
+// DisableRandPool disables the randomness pool if it was previously
+// enabled with EnableRandPool.
+//
+// Both EnableRandPool and DisableRandPool are not thread-safe and should
+// only be called when there is no possibility that New or any other
+// UUID Version 4 generation function will be called concurrently.
+func DisableRandPool() {
+	poolEnabled = false
+	defer poolMu.Unlock()
+	poolMu.Lock()
+	poolPos = randPoolSize
+}

+ 44 - 0
vendor/github.com/google/uuid/version1.go

@@ -0,0 +1,44 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+)
+
+// NewUUID returns a Version 1 UUID based on the current NodeID and clock
+// sequence, and the current time.  If the NodeID has not been set by SetNodeID
+// or SetNodeInterface then it will be set automatically.  If the NodeID cannot
+// be set NewUUID returns nil.  If clock sequence has not been set by
+// SetClockSequence then it will be set automatically.  If GetTime fails to
+// return the current NewUUID returns nil and an error.
+//
+// In most cases, New should be used.
+func NewUUID() (UUID, error) {
+	var uuid UUID
+	now, seq, err := GetTime()
+	if err != nil {
+		return uuid, err
+	}
+
+	timeLow := uint32(now & 0xffffffff)
+	timeMid := uint16((now >> 32) & 0xffff)
+	timeHi := uint16((now >> 48) & 0x0fff)
+	timeHi |= 0x1000 // Version 1
+
+	binary.BigEndian.PutUint32(uuid[0:], timeLow)
+	binary.BigEndian.PutUint16(uuid[4:], timeMid)
+	binary.BigEndian.PutUint16(uuid[6:], timeHi)
+	binary.BigEndian.PutUint16(uuid[8:], seq)
+
+	nodeMu.Lock()
+	if nodeID == zeroID {
+		setNodeInterface("")
+	}
+	copy(uuid[10:], nodeID[:])
+	nodeMu.Unlock()
+
+	return uuid, nil
+}

+ 76 - 0
vendor/github.com/google/uuid/version4.go

@@ -0,0 +1,76 @@
+// Copyright 2016 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "io"
+
+// New creates a new random UUID or panics.  New is equivalent to
+// the expression
+//
+//    uuid.Must(uuid.NewRandom())
+func New() UUID {
+	return Must(NewRandom())
+}
+
+// NewString creates a new random UUID and returns it as a string or panics.
+// NewString is equivalent to the expression
+//
+//    uuid.New().String()
+func NewString() string {
+	return Must(NewRandom()).String()
+}
+
+// NewRandom returns a Random (Version 4) UUID.
+//
+// The strength of the UUIDs is based on the strength of the crypto/rand
+// package.
+//
+// Uses the randomness pool if it was enabled with EnableRandPool.
+//
+// A note about uniqueness derived from the UUID Wikipedia entry:
+//
+//  Randomly generated UUIDs have 122 random bits.  One's annual risk of being
+//  hit by a meteorite is estimated to be one chance in 17 billion, that
+//  means the probability is about 0.00000000006 (6 × 10−11),
+//  equivalent to the odds of creating a few tens of trillions of UUIDs in a
+//  year and having one duplicate.
+func NewRandom() (UUID, error) {
+	if !poolEnabled {
+		return NewRandomFromReader(rander)
+	}
+	return newRandomFromPool()
+}
+
+// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
+func NewRandomFromReader(r io.Reader) (UUID, error) {
+	var uuid UUID
+	_, err := io.ReadFull(r, uuid[:])
+	if err != nil {
+		return Nil, err
+	}
+	uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+	uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+	return uuid, nil
+}
+
+func newRandomFromPool() (UUID, error) {
+	var uuid UUID
+	poolMu.Lock()
+	if poolPos == randPoolSize {
+		_, err := io.ReadFull(rander, pool[:])
+		if err != nil {
+			poolMu.Unlock()
+			return Nil, err
+		}
+		poolPos = 0
+	}
+	copy(uuid[:], pool[poolPos:(poolPos+16)])
+	poolPos += 16
+	poolMu.Unlock()
+
+	uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+	uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+	return uuid, nil
+}

+ 27 - 0
vendor/github.com/gorilla/css/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2013, Gorilla web toolkit
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+  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.
+
+  Neither the name of the {organization} nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+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.

+ 33 - 0
vendor/github.com/gorilla/css/scanner/doc.go

@@ -0,0 +1,33 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package gorilla/css/scanner generates tokens for a CSS3 input.
+
+It follows the CSS3 specification located at:
+
+	http://www.w3.org/TR/css3-syntax/
+
+To use it, create a new scanner for a given CSS string and call Next() until
+the token returned has type TokenEOF or TokenError:
+
+	s := scanner.New(myCSS)
+	for {
+		token := s.Next()
+		if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
+			break
+		}
+		// Do something with the token...
+	}
+
+Following the CSS3 specification, an error can only occur when the scanner
+finds an unclosed quote or unclosed comment. In these cases the text becomes
+"untokenizable". Everything else is tokenizable and it is up to a parser
+to make sense of the token stream (or ignore nonsensical token sequences).
+
+Note: the scanner doesn't perform lexical analysis or, in other words, it
+doesn't care about the token context. It is intended to be used by a
+lexer or parser.
+*/
+package scanner

+ 356 - 0
vendor/github.com/gorilla/css/scanner/scanner.go

@@ -0,0 +1,356 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package scanner
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+// tokenType identifies the type of lexical tokens.
+type tokenType int
+
+// String returns a string representation of the token type.
+func (t tokenType) String() string {
+	return tokenNames[t]
+}
+
+// Token represents a token and the corresponding string.
+type Token struct {
+	Type   tokenType
+	Value  string
+	Line   int
+	Column int
+}
+
+// String returns a string representation of the token.
+func (t *Token) String() string {
+	if len(t.Value) > 10 {
+		return fmt.Sprintf("%s (line: %d, column: %d): %.10q...",
+			t.Type, t.Line, t.Column, t.Value)
+	}
+	return fmt.Sprintf("%s (line: %d, column: %d): %q",
+		t.Type, t.Line, t.Column, t.Value)
+}
+
+// All tokens -----------------------------------------------------------------
+
+// The complete list of tokens in CSS3.
+const (
+	// Scanner flags.
+	TokenError tokenType = iota
+	TokenEOF
+	// From now on, only tokens from the CSS specification.
+	TokenIdent
+	TokenAtKeyword
+	TokenString
+	TokenHash
+	TokenNumber
+	TokenPercentage
+	TokenDimension
+	TokenURI
+	TokenUnicodeRange
+	TokenCDO
+	TokenCDC
+	TokenS
+	TokenComment
+	TokenFunction
+	TokenIncludes
+	TokenDashMatch
+	TokenPrefixMatch
+	TokenSuffixMatch
+	TokenSubstringMatch
+	TokenChar
+	TokenBOM
+)
+
+// tokenNames maps tokenType's to their names. Used for conversion to string.
+var tokenNames = map[tokenType]string{
+	TokenError:          "error",
+	TokenEOF:            "EOF",
+	TokenIdent:          "IDENT",
+	TokenAtKeyword:      "ATKEYWORD",
+	TokenString:         "STRING",
+	TokenHash:           "HASH",
+	TokenNumber:         "NUMBER",
+	TokenPercentage:     "PERCENTAGE",
+	TokenDimension:      "DIMENSION",
+	TokenURI:            "URI",
+	TokenUnicodeRange:   "UNICODE-RANGE",
+	TokenCDO:            "CDO",
+	TokenCDC:            "CDC",
+	TokenS:              "S",
+	TokenComment:        "COMMENT",
+	TokenFunction:       "FUNCTION",
+	TokenIncludes:       "INCLUDES",
+	TokenDashMatch:      "DASHMATCH",
+	TokenPrefixMatch:    "PREFIXMATCH",
+	TokenSuffixMatch:    "SUFFIXMATCH",
+	TokenSubstringMatch: "SUBSTRINGMATCH",
+	TokenChar:           "CHAR",
+	TokenBOM:            "BOM",
+}
+
+// Macros and productions -----------------------------------------------------
+// http://www.w3.org/TR/css3-syntax/#tokenization
+
+var macroRegexp = regexp.MustCompile(`\{[a-z]+\}`)
+
+// macros maps macro names to patterns to be expanded.
+var macros = map[string]string{
+	// must be escaped: `\.+*?()|[]{}^$`
+	"ident":      `-?{nmstart}{nmchar}*`,
+	"name":       `{nmchar}+`,
+	"nmstart":    `[a-zA-Z_]|{nonascii}|{escape}`,
+	"nonascii":   "[\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
+	"unicode":    `\\[0-9a-fA-F]{1,6}{wc}?`,
+	"escape":     "{unicode}|\\\\[\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
+	"nmchar":     `[a-zA-Z0-9_-]|{nonascii}|{escape}`,
+	"num":        `[0-9]*\.[0-9]+|[0-9]+`,
+	"string":     `"(?:{stringchar}|')*"|'(?:{stringchar}|")*'`,
+	"stringchar": `{urlchar}|[ ]|\\{nl}`,
+	"nl":         `[\n\r\f]|\r\n`,
+	"w":          `{wc}*`,
+	"wc":         `[\t\n\f\r ]`,
+
+	// urlchar should accept [(ascii characters minus those that need escaping)|{nonascii}|{escape}]
+	// ASCII characters range = `[\u0020-\u007e]`
+	// Skip space \u0020 = `[\u0021-\u007e]`
+	// Skip quotation mark \0022 = `[\u0021\u0023-\u007e]`
+	// Skip apostrophe \u0027 = `[\u0021\u0023-\u0026\u0028-\u007e]`
+	// Skip reverse solidus \u005c = `[\u0021\u0023-\u0026\u0028-\u005b\u005d\u007e]`
+	// Finally, the left square bracket (\u005b) and right (\u005d) needs escaping themselves
+	"urlchar": "[\u0021\u0023-\u0026\u0028-\\\u005b\\\u005d-\u007E]|{nonascii}|{escape}",
+}
+
+// productions maps the list of tokens to patterns to be expanded.
+var productions = map[tokenType]string{
+	// Unused regexps (matched using other methods) are commented out.
+	TokenIdent:        `{ident}`,
+	TokenAtKeyword:    `@{ident}`,
+	TokenString:       `{string}`,
+	TokenHash:         `#{name}`,
+	TokenNumber:       `{num}`,
+	TokenPercentage:   `{num}%`,
+	TokenDimension:    `{num}{ident}`,
+	TokenURI:          `url\({w}(?:{string}|{urlchar}*?){w}\)`,
+	TokenUnicodeRange: `U\+[0-9A-F\?]{1,6}(?:-[0-9A-F]{1,6})?`,
+	//TokenCDO:            `<!--`,
+	TokenCDC:      `-->`,
+	TokenS:        `{wc}+`,
+	TokenComment:  `/\*[^\*]*[\*]+(?:[^/][^\*]*[\*]+)*/`,
+	TokenFunction: `{ident}\(`,
+	//TokenIncludes:       `~=`,
+	//TokenDashMatch:      `\|=`,
+	//TokenPrefixMatch:    `\^=`,
+	//TokenSuffixMatch:    `\$=`,
+	//TokenSubstringMatch: `\*=`,
+	//TokenChar:           `[^"']`,
+	//TokenBOM:            "\uFEFF",
+}
+
+// matchers maps the list of tokens to compiled regular expressions.
+//
+// The map is filled on init() using the macros and productions defined in
+// the CSS specification.
+var matchers = map[tokenType]*regexp.Regexp{}
+
+// matchOrder is the order to test regexps when first-char shortcuts
+// can't be used.
+var matchOrder = []tokenType{
+	TokenURI,
+	TokenFunction,
+	TokenUnicodeRange,
+	TokenIdent,
+	TokenDimension,
+	TokenPercentage,
+	TokenNumber,
+	TokenCDC,
+}
+
+func init() {
+	// replace macros and compile regexps for productions.
+	replaceMacro := func(s string) string {
+		return "(?:" + macros[s[1:len(s)-1]] + ")"
+	}
+	for t, s := range productions {
+		for macroRegexp.MatchString(s) {
+			s = macroRegexp.ReplaceAllStringFunc(s, replaceMacro)
+		}
+		matchers[t] = regexp.MustCompile("^(?:" + s + ")")
+	}
+}
+
+// Scanner --------------------------------------------------------------------
+
+// New returns a new CSS scanner for the given input.
+func New(input string) *Scanner {
+	// Normalize newlines.
+	input = strings.Replace(input, "\r\n", "\n", -1)
+	return &Scanner{
+		input: input,
+		row:   1,
+		col:   1,
+	}
+}
+
+// Scanner scans an input and emits tokens following the CSS3 specification.
+type Scanner struct {
+	input string
+	pos   int
+	row   int
+	col   int
+	err   *Token
+}
+
+// Next returns the next token from the input.
+//
+// At the end of the input the token type is TokenEOF.
+//
+// If the input can't be tokenized the token type is TokenError. This occurs
+// in case of unclosed quotation marks or comments.
+func (s *Scanner) Next() *Token {
+	if s.err != nil {
+		return s.err
+	}
+	if s.pos >= len(s.input) {
+		s.err = &Token{TokenEOF, "", s.row, s.col}
+		return s.err
+	}
+	if s.pos == 0 {
+		// Test BOM only once, at the beginning of the file.
+		if strings.HasPrefix(s.input, "\uFEFF") {
+			return s.emitSimple(TokenBOM, "\uFEFF")
+		}
+	}
+	// There's a lot we can guess based on the first byte so we'll take a
+	// shortcut before testing multiple regexps.
+	input := s.input[s.pos:]
+	switch input[0] {
+	case '\t', '\n', '\f', '\r', ' ':
+		// Whitespace.
+		return s.emitToken(TokenS, matchers[TokenS].FindString(input))
+	case '.':
+		// Dot is too common to not have a quick check.
+		// We'll test if this is a Char; if it is followed by a number it is a
+		// dimension/percentage/number, and this will be matched later.
+		if len(input) > 1 && !unicode.IsDigit(rune(input[1])) {
+			return s.emitSimple(TokenChar, ".")
+		}
+	case '#':
+		// Another common one: Hash or Char.
+		if match := matchers[TokenHash].FindString(input); match != "" {
+			return s.emitToken(TokenHash, match)
+		}
+		return s.emitSimple(TokenChar, "#")
+	case '@':
+		// Another common one: AtKeyword or Char.
+		if match := matchers[TokenAtKeyword].FindString(input); match != "" {
+			return s.emitSimple(TokenAtKeyword, match)
+		}
+		return s.emitSimple(TokenChar, "@")
+	case ':', ',', ';', '%', '&', '+', '=', '>', '(', ')', '[', ']', '{', '}':
+		// More common chars.
+		return s.emitSimple(TokenChar, string(input[0]))
+	case '"', '\'':
+		// String or error.
+		match := matchers[TokenString].FindString(input)
+		if match != "" {
+			return s.emitToken(TokenString, match)
+		}
+
+		s.err = &Token{TokenError, "unclosed quotation mark", s.row, s.col}
+		return s.err
+	case '/':
+		// Comment, error or Char.
+		if len(input) > 1 && input[1] == '*' {
+			match := matchers[TokenComment].FindString(input)
+			if match != "" {
+				return s.emitToken(TokenComment, match)
+			} else {
+				s.err = &Token{TokenError, "unclosed comment", s.row, s.col}
+				return s.err
+			}
+		}
+		return s.emitSimple(TokenChar, "/")
+	case '~':
+		// Includes or Char.
+		return s.emitPrefixOrChar(TokenIncludes, "~=")
+	case '|':
+		// DashMatch or Char.
+		return s.emitPrefixOrChar(TokenDashMatch, "|=")
+	case '^':
+		// PrefixMatch or Char.
+		return s.emitPrefixOrChar(TokenPrefixMatch, "^=")
+	case '$':
+		// SuffixMatch or Char.
+		return s.emitPrefixOrChar(TokenSuffixMatch, "$=")
+	case '*':
+		// SubstringMatch or Char.
+		return s.emitPrefixOrChar(TokenSubstringMatch, "*=")
+	case '<':
+		// CDO or Char.
+		return s.emitPrefixOrChar(TokenCDO, "<!--")
+	}
+	// Test all regexps, in order.
+	for _, token := range matchOrder {
+		if match := matchers[token].FindString(input); match != "" {
+			return s.emitToken(token, match)
+		}
+	}
+	// We already handled unclosed quotation marks and comments,
+	// so this can only be a Char.
+	r, width := utf8.DecodeRuneInString(input)
+	token := &Token{TokenChar, string(r), s.row, s.col}
+	s.col += width
+	s.pos += width
+	return token
+}
+
+// updatePosition updates input coordinates based on the consumed text.
+func (s *Scanner) updatePosition(text string) {
+	width := utf8.RuneCountInString(text)
+	lines := strings.Count(text, "\n")
+	s.row += lines
+	if lines == 0 {
+		s.col += width
+	} else {
+		s.col = utf8.RuneCountInString(text[strings.LastIndex(text, "\n"):])
+	}
+	s.pos += len(text) // while col is a rune index, pos is a byte index
+}
+
+// emitToken returns a Token for the string v and updates the scanner position.
+func (s *Scanner) emitToken(t tokenType, v string) *Token {
+	token := &Token{t, v, s.row, s.col}
+	s.updatePosition(v)
+	return token
+}
+
+// emitSimple returns a Token for the string v and updates the scanner
+// position in a simplified manner.
+//
+// The string is known to have only ASCII characters and to not have a newline.
+func (s *Scanner) emitSimple(t tokenType, v string) *Token {
+	token := &Token{t, v, s.row, s.col}
+	s.col += len(v)
+	s.pos += len(v)
+	return token
+}
+
+// emitPrefixOrChar returns a Token for type t if the current position
+// matches the given prefix. Otherwise it returns a Char token using the
+// first character from the prefix.
+//
+// The prefix is known to have only ASCII characters and to not have a newline.
+func (s *Scanner) emitPrefixOrChar(t tokenType, prefix string) *Token {
+	if strings.HasPrefix(s.input[s.pos:], prefix) {
+		return s.emitSimple(t, prefix)
+	}
+	return s.emitSimple(TokenChar, string(prefix[0]))
+}

+ 24 - 0
vendor/github.com/huandu/xstrings/.gitignore

@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 7 - 0
vendor/github.com/huandu/xstrings/.travis.yml

@@ -0,0 +1,7 @@
+language: go
+install:
+  - go get golang.org/x/tools/cmd/cover
+  - go get github.com/mattn/goveralls
+script:
+  - go test -v -covermode=count -coverprofile=coverage.out
+  - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ ! -z "$COVERALLS_TOKEN" ]; then $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN; fi'

+ 23 - 0
vendor/github.com/huandu/xstrings/CONTRIBUTING.md

@@ -0,0 +1,23 @@
+# Contributing #
+
+Thanks for your contribution in advance. No matter what you will contribute to this project, pull request or bug report or feature discussion, it's always highly appreciated.
+
+## New API or feature ##
+
+I want to speak more about how to add new functions to this package.
+
+Package `xstring` is a collection of useful string functions which should be implemented in Go. It's a bit subject to say which function should be included and which should not. I set up following rules in order to make it clear and as objective as possible.
+
+* Rule 1: Only string algorithm, which takes string as input, can be included.
+* Rule 2: If a function has been implemented in package `string`, it must not be included.
+* Rule 3: If a function is not language neutral, it must not be included.
+* Rule 4: If a function is a part of standard library in other languages, it can be included.
+* Rule 5: If a function is quite useful in some famous framework or library, it can be included.
+
+New function must be discussed in project issues before submitting any code. If a pull request with new functions is sent without any ref issue, it will be rejected.
+
+## Pull request ##
+
+Pull request is always welcome. Just make sure you have run `go fmt` and all test cases passed before submit.
+
+If the pull request is to add a new API or feature, don't forget to update README.md and add new API in function list.

+ 22 - 0
vendor/github.com/huandu/xstrings/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Huan Du
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 117 - 0
vendor/github.com/huandu/xstrings/README.md

@@ -0,0 +1,117 @@
+# xstrings #
+
+[![Build Status](https://travis-ci.org/huandu/xstrings.svg?branch=master)](https://travis-ci.org/huandu/xstrings)
+[![GoDoc](https://godoc.org/github.com/huandu/xstrings?status.svg)](https://godoc.org/github.com/huandu/xstrings)
+[![Go Report](https://goreportcard.com/badge/github.com/huandu/xstrings)](https://goreportcard.com/report/github.com/huandu/xstrings)
+[![Coverage Status](https://coveralls.io/repos/github/huandu/xstrings/badge.svg?branch=master)](https://coveralls.io/github/huandu/xstrings?branch=master)
+
+Go package [xstrings](https://godoc.org/github.com/huandu/xstrings) is a collection of string functions, which are widely used in other languages but absent in Go package [strings](http://golang.org/pkg/strings).
+
+All functions are well tested and carefully tuned for performance.
+
+## Propose a new function ##
+
+Please review [contributing guideline](CONTRIBUTING.md) and [create new issue](https://github.com/huandu/xstrings/issues) to state why it should be included.
+
+## Install ##
+
+Use `go get` to install this library.
+
+    go get github.com/huandu/xstrings
+
+## API document ##
+
+See [GoDoc](https://godoc.org/github.com/huandu/xstrings) for full document.
+
+## Function list ##
+
+Go functions have a unique naming style. One, who has experience in other language but new in Go, may have difficulties to find out right string function to use.
+
+Here is a list of functions in [strings](http://golang.org/pkg/strings) and [xstrings](https://godoc.org/github.com/huandu/xstrings) with enough extra information about how to map these functions to their friends in other languages. Hope this list could be helpful for fresh gophers.
+
+### Package `xstrings` functions ###
+
+*Keep this table sorted by Function in ascending order.*
+
+| Function | Friends | # |
+| -------- | ------- | --- |
+| [Center](https://godoc.org/github.com/huandu/xstrings#Center) | `str.center` in Python; `String#center` in Ruby | [#30](https://github.com/huandu/xstrings/issues/30) |
+| [Count](https://godoc.org/github.com/huandu/xstrings#Count) | `String#count` in Ruby | [#16](https://github.com/huandu/xstrings/issues/16) |
+| [Delete](https://godoc.org/github.com/huandu/xstrings#Delete) | `String#delete` in Ruby | [#17](https://github.com/huandu/xstrings/issues/17) |
+| [ExpandTabs](https://godoc.org/github.com/huandu/xstrings#ExpandTabs) | `str.expandtabs` in Python | [#27](https://github.com/huandu/xstrings/issues/27) |
+| [FirstRuneToLower](https://godoc.org/github.com/huandu/xstrings#FirstRuneToLower) | `lcfirst` in PHP or Perl | [#15](https://github.com/huandu/xstrings/issues/15) |
+| [FirstRuneToUpper](https://godoc.org/github.com/huandu/xstrings#FirstRuneToUpper) | `String#capitalize` in Ruby; `ucfirst` in PHP or Perl | [#15](https://github.com/huandu/xstrings/issues/15) |
+| [Insert](https://godoc.org/github.com/huandu/xstrings#Insert) | `String#insert` in Ruby | [#18](https://github.com/huandu/xstrings/issues/18) |
+| [LastPartition](https://godoc.org/github.com/huandu/xstrings#LastPartition) | `str.rpartition` in Python; `String#rpartition` in Ruby | [#19](https://github.com/huandu/xstrings/issues/19) |
+| [LeftJustify](https://godoc.org/github.com/huandu/xstrings#LeftJustify) | `str.ljust` in Python; `String#ljust` in Ruby | [#28](https://github.com/huandu/xstrings/issues/28) |
+| [Len](https://godoc.org/github.com/huandu/xstrings#Len) | `mb_strlen` in PHP | [#23](https://github.com/huandu/xstrings/issues/23) |
+| [Partition](https://godoc.org/github.com/huandu/xstrings#Partition) | `str.partition` in Python; `String#partition` in Ruby | [#10](https://github.com/huandu/xstrings/issues/10) |
+| [Reverse](https://godoc.org/github.com/huandu/xstrings#Reverse) | `String#reverse` in Ruby; `strrev` in PHP; `reverse` in Perl | [#7](https://github.com/huandu/xstrings/issues/7) |
+| [RightJustify](https://godoc.org/github.com/huandu/xstrings#RightJustify) | `str.rjust` in Python; `String#rjust` in Ruby | [#29](https://github.com/huandu/xstrings/issues/29) |
+| [RuneWidth](https://godoc.org/github.com/huandu/xstrings#RuneWidth) | - | [#27](https://github.com/huandu/xstrings/issues/27) |
+| [Scrub](https://godoc.org/github.com/huandu/xstrings#Scrub) | `String#scrub` in Ruby | [#20](https://github.com/huandu/xstrings/issues/20) |
+| [Shuffle](https://godoc.org/github.com/huandu/xstrings#Shuffle) | `str_shuffle` in PHP | [#13](https://github.com/huandu/xstrings/issues/13) |
+| [ShuffleSource](https://godoc.org/github.com/huandu/xstrings#ShuffleSource) | `str_shuffle` in PHP | [#13](https://github.com/huandu/xstrings/issues/13) |
+| [Slice](https://godoc.org/github.com/huandu/xstrings#Slice) | `mb_substr` in PHP | [#9](https://github.com/huandu/xstrings/issues/9) |
+| [Squeeze](https://godoc.org/github.com/huandu/xstrings#Squeeze) | `String#squeeze` in Ruby | [#11](https://github.com/huandu/xstrings/issues/11) |
+| [Successor](https://godoc.org/github.com/huandu/xstrings#Successor) | `String#succ` or `String#next` in Ruby | [#22](https://github.com/huandu/xstrings/issues/22) |
+| [SwapCase](https://godoc.org/github.com/huandu/xstrings#SwapCase) | `str.swapcase` in Python; `String#swapcase` in Ruby | [#12](https://github.com/huandu/xstrings/issues/12) |
+| [ToCamelCase](https://godoc.org/github.com/huandu/xstrings#ToCamelCase) | `String#camelize` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) |
+| [ToKebab](https://godoc.org/github.com/huandu/xstrings#ToKebabCase) | - | [#41](https://github.com/huandu/xstrings/issues/41) |
+| [ToSnakeCase](https://godoc.org/github.com/huandu/xstrings#ToSnakeCase) | `String#underscore` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) |
+| [Translate](https://godoc.org/github.com/huandu/xstrings#Translate) | `str.translate` in Python; `String#tr` in Ruby; `strtr` in PHP; `tr///` in Perl | [#21](https://github.com/huandu/xstrings/issues/21) |
+| [Width](https://godoc.org/github.com/huandu/xstrings#Width) | `mb_strwidth` in PHP | [#26](https://github.com/huandu/xstrings/issues/26) |
+| [WordCount](https://godoc.org/github.com/huandu/xstrings#WordCount) | `str_word_count` in PHP | [#14](https://github.com/huandu/xstrings/issues/14) |
+| [WordSplit](https://godoc.org/github.com/huandu/xstrings#WordSplit) | - | [#14](https://github.com/huandu/xstrings/issues/14) |
+
+### Package `strings` functions ###
+
+*Keep this table sorted by Function in ascending order.*
+
+| Function | Friends |
+| -------- | ------- |
+| [Contains](http://golang.org/pkg/strings/#Contains) | `String#include?` in Ruby |
+| [ContainsAny](http://golang.org/pkg/strings/#ContainsAny) | - |
+| [ContainsRune](http://golang.org/pkg/strings/#ContainsRune) | - |
+| [Count](http://golang.org/pkg/strings/#Count) | `str.count` in Python; `substr_count` in PHP |
+| [EqualFold](http://golang.org/pkg/strings/#EqualFold) | `stricmp` in PHP; `String#casecmp` in Ruby |
+| [Fields](http://golang.org/pkg/strings/#Fields) | `str.split` in Python; `split` in Perl; `String#split` in Ruby |
+| [FieldsFunc](http://golang.org/pkg/strings/#FieldsFunc) | - |
+| [HasPrefix](http://golang.org/pkg/strings/#HasPrefix) | `str.startswith` in Python; `String#start_with?` in Ruby |
+| [HasSuffix](http://golang.org/pkg/strings/#HasSuffix) | `str.endswith` in Python; `String#end_with?` in Ruby |
+| [Index](http://golang.org/pkg/strings/#Index) | `str.index` in Python; `String#index` in Ruby; `strpos` in PHP; `index` in Perl |
+| [IndexAny](http://golang.org/pkg/strings/#IndexAny) | - |
+| [IndexByte](http://golang.org/pkg/strings/#IndexByte) | - |
+| [IndexFunc](http://golang.org/pkg/strings/#IndexFunc) | - |
+| [IndexRune](http://golang.org/pkg/strings/#IndexRune) | - |
+| [Join](http://golang.org/pkg/strings/#Join) | `str.join` in Python; `Array#join` in Ruby; `implode` in PHP; `join` in Perl |
+| [LastIndex](http://golang.org/pkg/strings/#LastIndex) | `str.rindex` in Python; `String#rindex`; `strrpos` in PHP; `rindex` in Perl |
+| [LastIndexAny](http://golang.org/pkg/strings/#LastIndexAny) | - |
+| [LastIndexFunc](http://golang.org/pkg/strings/#LastIndexFunc) | - |
+| [Map](http://golang.org/pkg/strings/#Map) | `String#each_codepoint` in Ruby |
+| [Repeat](http://golang.org/pkg/strings/#Repeat) | operator `*` in Python and Ruby; `str_repeat` in PHP |
+| [Replace](http://golang.org/pkg/strings/#Replace) | `str.replace` in Python; `String#sub` in Ruby; `str_replace` in PHP |
+| [Split](http://golang.org/pkg/strings/#Split) | `str.split` in Python; `String#split` in Ruby; `explode` in PHP; `split` in Perl |
+| [SplitAfter](http://golang.org/pkg/strings/#SplitAfter) | - |
+| [SplitAfterN](http://golang.org/pkg/strings/#SplitAfterN) | - |
+| [SplitN](http://golang.org/pkg/strings/#SplitN) | `str.split` in Python; `String#split` in Ruby; `explode` in PHP; `split` in Perl |
+| [Title](http://golang.org/pkg/strings/#Title) | `str.title` in Python |
+| [ToLower](http://golang.org/pkg/strings/#ToLower) | `str.lower` in Python; `String#downcase` in Ruby; `strtolower` in PHP; `lc` in Perl |
+| [ToLowerSpecial](http://golang.org/pkg/strings/#ToLowerSpecial) | - |
+| [ToTitle](http://golang.org/pkg/strings/#ToTitle) | - |
+| [ToTitleSpecial](http://golang.org/pkg/strings/#ToTitleSpecial) | - |
+| [ToUpper](http://golang.org/pkg/strings/#ToUpper) | `str.upper` in Python; `String#upcase` in Ruby; `strtoupper` in PHP; `uc` in Perl |
+| [ToUpperSpecial](http://golang.org/pkg/strings/#ToUpperSpecial) | - |
+| [Trim](http://golang.org/pkg/strings/#Trim) | `str.strip` in Python; `String#strip` in Ruby; `trim` in PHP |
+| [TrimFunc](http://golang.org/pkg/strings/#TrimFunc) | - |
+| [TrimLeft](http://golang.org/pkg/strings/#TrimLeft) | `str.lstrip` in Python; `String#lstrip` in Ruby; `ltrim` in PHP |
+| [TrimLeftFunc](http://golang.org/pkg/strings/#TrimLeftFunc) | - |
+| [TrimPrefix](http://golang.org/pkg/strings/#TrimPrefix) | - |
+| [TrimRight](http://golang.org/pkg/strings/#TrimRight) | `str.rstrip` in Python; `String#rstrip` in Ruby; `rtrim` in PHP |
+| [TrimRightFunc](http://golang.org/pkg/strings/#TrimRightFunc) | - |
+| [TrimSpace](http://golang.org/pkg/strings/#TrimSpace) | `str.strip` in Python; `String#strip` in Ruby; `trim` in PHP |
+| [TrimSuffix](http://golang.org/pkg/strings/#TrimSuffix) | `String#chomp` in Ruby; `chomp` in Perl |
+
+## License ##
+
+This library is licensed under MIT license. See LICENSE for details.

+ 21 - 0
vendor/github.com/huandu/xstrings/common.go

@@ -0,0 +1,21 @@
+// Copyright 2015 Huan Du. All rights reserved.
+// Licensed under the MIT license that can be found in the LICENSE file.
+
+package xstrings
+
+const bufferMaxInitGrowSize = 2048
+
+// Lazy initialize a buffer.
+func allocBuffer(orig, cur string) *stringBuilder {
+	output := &stringBuilder{}
+	maxSize := len(orig) * 4
+
+	// Avoid to reserve too much memory at once.
+	if maxSize > bufferMaxInitGrowSize {
+		maxSize = bufferMaxInitGrowSize
+	}
+
+	output.Grow(maxSize)
+	output.WriteString(orig[:len(orig)-len(cur)])
+	return output
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio