From a781485c0a549b3294b6c2294c245008ca981cf9 Mon Sep 17 00:00:00 2001
From: Sebastian Frank This is some sample code. This user registered {{ user.register_date|naturaltime }}. The user's biography:
+ {{ user.biography|markdown|truncatewords_html:15 }}
+ read more
+ This user is an admin! ... ")
+ opened = true
+ }
+
+ b.WriteString(line)
+
+ if idx < lenlines-1 && strings.TrimSpace(lines[idx]) != "" {
+ // We've not reached the end
+ if strings.TrimSpace(lines[idx+1]) == "" {
+ // Next line is empty
+ if opened {
+ b.WriteString(" ")
+ par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))]
+ writer.WriteString(par)
+ writer.WriteString(" ")
+ par := tagLoremParagraphs[i%len(tagLoremParagraphs)]
+ writer.WriteString(par)
+ writer.WriteString(" This user registered {{ user.register_date|naturaltime }}. The user's biography:
+ {{ user.biography|markdown|truncatewords_html:15 }}
+ read more
+ This user is an admin! ... ")
+ opened = true
+ }
+
+ b.WriteString(line)
+
+ if idx < lenlines-1 && strings.TrimSpace(lines[idx]) != "" {
+ // We've not reached the end
+ if strings.TrimSpace(lines[idx+1]) == "" {
+ // Next line is empty
+ if opened {
+ b.WriteString(" ")
+ par := tagLoremParagraphs[rand.Intn(len(tagLoremParagraphs))]
+ writer.WriteString(par)
+ writer.WriteString(" ")
+ par := tagLoremParagraphs[i%len(tagLoremParagraphs)]
+ writer.WriteString(par)
+ writer.WriteString("func main() {
+fmt.Println("Hi")
+}
+
+```
+
+## Real-life example
+
+In [smallblog](https://github.com/Depado/smallblog) I'm using bfchroma to render
+my articles. It's using a combination of both bfchroma's options and blackfriday
+extensions and flags.
+
+```go
+package main
+
+import (
+ "github.com/Depado/bfchroma"
+ "github.com/alecthomas/chroma/formatters/html"
+ bf "github.com/russross/blackfriday/v2"
+)
+
+// Defines the extensions that are used
+var exts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
+ bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
+ bf.DefinitionLists | bf.Footnotes
+
+// Defines the HTML rendering flags that are used
+var flags = bf.UseXHTML | bf.Smartypants | bf.SmartypantsFractions |
+ bf.SmartypantsDashes | bf.SmartypantsLatexDashes | bf.TOC
+
+// render will take a []byte input and will render it using a new renderer each
+// time because reusing the same can mess with TOC and header IDs
+func render(input []byte) []byte {
+ return bf.Run(
+ input,
+ bf.WithRenderer(
+ bfchroma.NewRenderer(
+ bfchroma.WithoutAutodetect(),
+ bfchroma.ChromaOptions(
+ html.WithLineNumbers(),
+ ),
+ bfchroma.Extend(
+ bf.NewHTMLRenderer(bf.HTMLRendererParameters{
+ Flags: flags,
+ }),
+ ),
+ ),
+ ),
+ bf.WithExtensions(exts),
+ )
+}
+```
+
+## Classes
+
+If you have loads of code in your markdown, you might want to consider using
+`html.WithClasses()` in your `bfchroma.ChromaOptions()`. The CSS of the style
+you chose can then be accessed like this :
+
+```go
+r := bfchroma.NewRenderer(
+ bfchroma.WithoutAutodetect(),
+ bfchroma.Extend(
+ bf.NewHTMLRenderer(bf.HTMLRendererParameters{Flags: flags}),
+ ),
+ bfchroma.Style("monokai"),
+ bfchroma.ChromaOptions(html.WithClasses()),
+)
+
+var css template.CSS
+
+b := new(bytes.Buffer)
+if err := r.ChromaCSS(b); err != nil {
+ logrus.WithError(err).Warning("Couldn't write CSS")
+}
+css = template.CSS(b.String())
+
+bf.Run(input, bf.WithRenderer(r), bf.WithExtensions(exts))
+```
+
+This way, you can pass your `css` var to any template and render it along the
+rendered markdown.
diff --git a/vendor/github.com/Depado/bfchroma/go.mod b/vendor/github.com/Depado/bfchroma/go.mod
new file mode 100644
index 0000000..fc0c3bd
--- /dev/null
+++ b/vendor/github.com/Depado/bfchroma/go.mod
@@ -0,0 +1,11 @@
+module github.com/Depado/bfchroma
+
+go 1.14
+
+require (
+ github.com/alecthomas/chroma v0.7.3
+ github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
+ github.com/russross/blackfriday/v2 v2.0.1
+ github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+ github.com/stretchr/testify v1.6.1
+)
diff --git a/vendor/github.com/Depado/bfchroma/go.sum b/vendor/github.com/Depado/bfchroma/go.sum
new file mode 100644
index 0000000..4677ed2
--- /dev/null
+++ b/vendor/github.com/Depado/bfchroma/go.sum
@@ -0,0 +1,46 @@
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
+github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMAeI=
+github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
+github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
+github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c h1:MVVbswUlqicyj8P/JljoocA7AyCo62gzD0O7jfvrhtE=
+github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
+github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/Depado/bfchroma/renderer.go b/vendor/github.com/Depado/bfchroma/renderer.go
new file mode 100644
index 0000000..e9e997a
--- /dev/null
+++ b/vendor/github.com/Depado/bfchroma/renderer.go
@@ -0,0 +1,147 @@
+// Package bfchroma provides an easy and extensible blackfriday renderer that
+// uses the chroma syntax highlighter to render code blocks.
+package bfchroma
+
+import (
+ "io"
+
+ "github.com/alecthomas/chroma"
+ "github.com/alecthomas/chroma/formatters/html"
+ "github.com/alecthomas/chroma/lexers"
+ "github.com/alecthomas/chroma/styles"
+ bf "github.com/russross/blackfriday/v2"
+)
+
+// Option defines the functional option type
+type Option func(r *Renderer)
+
+// Style is a function option allowing to set the style used by chroma
+// Default : "monokai"
+func Style(s string) Option {
+ return func(r *Renderer) {
+ r.Style = styles.Get(s)
+ }
+}
+
+// ChromaStyle is an option to directly set the style of the renderer using a
+// chroma style instead of a string
+func ChromaStyle(s *chroma.Style) Option {
+ return func(r *Renderer) {
+ r.Style = s
+ }
+}
+
+// WithoutAutodetect disables chroma's language detection when no codeblock
+// extra information is given. It will fallback to a sane default instead of
+// trying to detect the language.
+func WithoutAutodetect() Option {
+ return func(r *Renderer) {
+ r.Autodetect = false
+ }
+}
+
+// EmbedCSS will embed CSS needed for html.WithClasses() in beginning of the document
+func EmbedCSS() Option {
+ return func(r *Renderer) {
+ r.embedCSS = true
+ }
+}
+
+// ChromaOptions allows to pass Chroma html.Option such as Standalone()
+// WithClasses(), ClassPrefix(prefix)...
+func ChromaOptions(options ...html.Option) Option {
+ return func(r *Renderer) {
+ r.ChromaOptions = options
+ }
+}
+
+// Extend allows to specify the blackfriday renderer which is extended
+func Extend(br bf.Renderer) Option {
+ return func(r *Renderer) {
+ r.Base = br
+ }
+}
+
+// NewRenderer will return a new bfchroma renderer with sane defaults
+func NewRenderer(options ...Option) *Renderer {
+ r := &Renderer{
+ Base: bf.NewHTMLRenderer(bf.HTMLRendererParameters{
+ Flags: bf.CommonHTMLFlags,
+ }),
+ Style: styles.Monokai,
+ Autodetect: true,
+ }
+ for _, option := range options {
+ option(r)
+ }
+ r.Formatter = html.New(r.ChromaOptions...)
+ return r
+}
+
+// RenderWithChroma will render the given text to the w io.Writer
+func (r *Renderer) RenderWithChroma(w io.Writer, text []byte, data bf.CodeBlockData) error {
+ var lexer chroma.Lexer
+
+ // Determining the lexer to use
+ if len(data.Info) > 0 {
+ lexer = lexers.Get(string(data.Info))
+ } else if r.Autodetect {
+ lexer = lexers.Analyse(string(text))
+ }
+ if lexer == nil {
+ lexer = lexers.Fallback
+ }
+
+ // Tokenize the code
+ iterator, err := lexer.Tokenise(nil, string(text))
+ if err != nil {
+ return err
+ }
+ return r.Formatter.Format(w, r.Style, iterator)
+}
+
+// Renderer is a custom Blackfriday renderer that uses the capabilities of
+// chroma to highlight code with triple backtick notation
+type Renderer struct {
+ Base bf.Renderer
+ Autodetect bool
+ ChromaOptions []html.Option
+ Style *chroma.Style
+ Formatter *html.Formatter
+ embedCSS bool
+}
+
+// RenderNode satisfies the Renderer interface
+func (r *Renderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
+ switch node.Type {
+ case bf.Document:
+ if entering && r.embedCSS {
+ w.Write([]byte(""))
+ }
+ return r.Base.RenderNode(w, node, entering)
+ case bf.CodeBlock:
+ if err := r.RenderWithChroma(w, node.Literal, node.CodeBlockData); err != nil {
+ return r.Base.RenderNode(w, node, entering)
+ }
+ return bf.SkipChildren
+ default:
+ return r.Base.RenderNode(w, node, entering)
+ }
+}
+
+// RenderHeader satisfies the Renderer interface
+func (r *Renderer) RenderHeader(w io.Writer, ast *bf.Node) {
+ r.Base.RenderHeader(w, ast)
+}
+
+// RenderFooter satisfies the Renderer interface
+func (r *Renderer) RenderFooter(w io.Writer, ast *bf.Node) {
+ r.Base.RenderFooter(w, ast)
+}
+
+// ChromaCSS returns CSS used with chroma's html.WithClasses() option
+func (r *Renderer) ChromaCSS(w io.Writer) error {
+ return r.Formatter.WriteCSS(w, r.Style)
+}
\ No newline at end of file
diff --git a/vendor/github.com/alecthomas/chroma/.gitignore b/vendor/github.com/alecthomas/chroma/.gitignore
new file mode 100644
index 0000000..ccacd12
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/.gitignore
@@ -0,0 +1,19 @@
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+/cmd/chroma/chroma
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+_models/
+
+_examples/
diff --git a/vendor/github.com/alecthomas/chroma/.golangci.yml b/vendor/github.com/alecthomas/chroma/.golangci.yml
new file mode 100644
index 0000000..ba75573
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/.golangci.yml
@@ -0,0 +1,76 @@
+run:
+ tests: true
+ skip-dirs:
+ - _examples
+
+output:
+ print-issued-lines: false
+
+linters:
+ enable-all: true
+ disable:
+ - maligned
+ - megacheck
+ - lll
+ - gocyclo
+ - dupl
+ - gochecknoglobals
+ - funlen
+ - godox
+ - wsl
+ - gomnd
+ - gocognit
+ - goerr113
+ - nolintlint
+ - testpackage
+ - godot
+ - nestif
+ - paralleltest
+ - nlreturn
+ - cyclop
+ - exhaustivestruct
+ - gci
+ - gofumpt
+ - errorlint
+ - exhaustive
+ - ifshort
+ - wrapcheck
+ - stylecheck
+
+linters-settings:
+ govet:
+ check-shadowing: true
+ gocyclo:
+ min-complexity: 10
+ dupl:
+ threshold: 100
+ goconst:
+ min-len: 8
+ min-occurrences: 3
+ forbidigo:
+ forbid:
+ - (Must)?NewLexer
+ exclude_godoc_examples: false
+
+
+issues:
+ max-per-linter: 0
+ max-same: 0
+ exclude-use-default: false
+ exclude:
+ # Captured by errcheck.
+ - '^(G104|G204):'
+ # Very commonly not checked.
+ - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
+ - 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos) should have comment or be unexported'
+ - 'composite literal uses unkeyed fields'
+ - 'declaration of "err" shadows declaration'
+ - 'should not use dot imports'
+ - 'Potential file inclusion via variable'
+ - 'should have comment or be unexported'
+ - 'comment on exported var .* should be of the form'
+ - 'at least one file in a package should have a package comment'
+ - 'string literal contains the Unicode'
+ - 'methods on the same type should have the same receiver name'
+ - '_TokenType_name should be _TokenTypeName'
+ - '`_TokenType_map` should be `_TokenTypeMap`'
diff --git a/vendor/github.com/alecthomas/chroma/.goreleaser.yml b/vendor/github.com/alecthomas/chroma/.goreleaser.yml
new file mode 100644
index 0000000..8cd7592
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/.goreleaser.yml
@@ -0,0 +1,37 @@
+project_name: chroma
+release:
+ github:
+ owner: alecthomas
+ name: chroma
+brews:
+ -
+ install: bin.install "chroma"
+env:
+ - CGO_ENABLED=0
+builds:
+- goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - arm64
+ - amd64
+ - "386"
+ goarm:
+ - "6"
+ dir: ./cmd/chroma
+ main: .
+ ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
+ binary: chroma
+archives:
+ -
+ format: tar.gz
+ name_template: '{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{
+ .Arm }}{{ end }}'
+ files:
+ - COPYING
+ - README*
+snapshot:
+ name_template: SNAPSHOT-{{ .Commit }}
+checksum:
+ name_template: '{{ .ProjectName }}-{{ .Version }}-checksums.txt'
diff --git a/vendor/github.com/alecthomas/chroma/COPYING b/vendor/github.com/alecthomas/chroma/COPYING
new file mode 100644
index 0000000..92dc39f
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/COPYING
@@ -0,0 +1,19 @@
+Copyright (C) 2017 Alec Thomas
+
+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.
diff --git a/vendor/github.com/alecthomas/chroma/Makefile b/vendor/github.com/alecthomas/chroma/Makefile
new file mode 100644
index 0000000..34e3c41
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/Makefile
@@ -0,0 +1,19 @@
+.PHONY: chromad upload all
+
+VERSION ?= $(shell git describe --tags --dirty --always)
+
+all: README.md tokentype_string.go
+
+README.md: lexers/*/*.go
+ ./table.py
+
+tokentype_string.go: types.go
+ go generate
+
+chromad:
+ rm -f chromad
+ (export CGOENABLED=0 GOOS=linux GOARCH=amd64; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .)
+
+upload: chromad
+ scp chromad root@swapoff.org: && \
+ ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart'
diff --git a/vendor/github.com/alecthomas/chroma/README.md b/vendor/github.com/alecthomas/chroma/README.md
new file mode 100644
index 0000000..b62b847
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/README.md
@@ -0,0 +1,285 @@
+# Chroma — A general purpose syntax highlighter in pure Go
+[![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CI](https://github.com/alecthomas/chroma/actions/workflows/ci.yml/badge.svg)](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/)
+
+> **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly.
+
+Chroma takes source code and other structured text and converts it into syntax
+highlighted HTML, ANSI-coloured text, etc.
+
+Chroma is based heavily on [Pygments](http://pygments.org/), and includes
+translators for Pygments lexers and styles.
+
+
+## Table of Contents
+
+
+
+1. [Table of Contents](#table-of-contents)
+2. [Supported languages](#supported-languages)
+3. [Try it](#try-it)
+4. [Using the library](#using-the-library)
+ 1. [Quick start](#quick-start)
+ 2. [Identifying the language](#identifying-the-language)
+ 3. [Formatting the output](#formatting-the-output)
+ 4. [The HTML formatter](#the-html-formatter)
+5. [More detail](#more-detail)
+ 1. [Lexers](#lexers)
+ 2. [Formatters](#formatters)
+ 3. [Styles](#styles)
+6. [Command-line interface](#command-line-interface)
+7. [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
+
+
+
+
+## Supported languages
+
+Prefix | Language
+:----: | --------
+A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk
+B | Ballerina, Base Makefile, Bash, Batchfile, BibTeX, Bicep, BlitzBasic, BNF, Brainfuck
+C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython
+D | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan
+E | EBNF, Elixir, Elm, EmacsLisp, Erlang
+F | Factor, Fish, Forth, Fortran, FSharp
+G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy
+H | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HTML, HTTP, Hy
+I | Idris, Igor, INI, Io
+J | J, Java, JavaScript, JSON, Julia, Jungle
+K | Kotlin
+L | Lighttpd configuration file, LLVM, Lua
+M | Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
+N | NASM, Newspeak, Nginx configuration file, Nim, Nix
+O | Objective-C, OCaml, Octave, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode
+P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, PromQL, Protocol Buffer, Puppet, Python 2, Python
+Q | QBasic
+R | R, Racket, Ragel, Raku, react, ReasonML, reg, reStructuredText, Rexx, Ruby, Rust
+S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Standard ML, Stylus, Svelte, Swift, SYSTEMD, systemverilog
+T | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
+V | VB.net, verilog, VHDL, VimL, vue
+W | WDTE
+X | XML, Xorg
+Y | YAML, YANG
+Z | Zig
+
+
+_I will attempt to keep this section up to date, but an authoritative list can be
+displayed with `chroma --list`._
+
+
+## Try it
+
+Try out various languages and styles on the [Chroma Playground](https://swapoff.org/chroma/playground/).
+
+
+## Using the library
+
+Chroma, like Pygments, has the concepts of
+[lexers](https://github.com/alecthomas/chroma/tree/master/lexers),
+[formatters](https://github.com/alecthomas/chroma/tree/master/formatters) and
+[styles](https://github.com/alecthomas/chroma/tree/master/styles).
+
+Lexers convert source text into a stream of tokens, styles specify how token
+types are mapped to colours, and formatters convert tokens and styles into
+formatted output.
+
+A package exists for each of these, containing a global `Registry` variable
+with all of the registered implementations. There are also helper functions
+for using the registry in each package, such as looking up lexers by name or
+matching filenames, etc.
+
+In all cases, if a lexer, formatter or style can not be determined, `nil` will
+be returned. In this situation you may want to default to the `Fallback`
+value in each respective package, which provides sane defaults.
+
+
+### Quick start
+
+A convenience function exists that can be used to simply format some source
+text, without any effort:
+
+```go
+err := quick.Highlight(os.Stdout, someSourceCode, "go", "html", "monokai")
+```
+
+
+### Identifying the language
+
+To highlight code, you'll first have to identify what language the code is
+written in. There are three primary ways to do that:
+
+1. Detect the language from its filename.
+
+ ```go
+ lexer := lexers.Match("foo.go")
+ ```
+
+3. Explicitly specify the language by its Chroma syntax ID (a full list is available from `lexers.Names()`).
+
+ ```go
+ lexer := lexers.Get("go")
+ ```
+
+3. Detect the language from its content.
+
+ ```go
+ lexer := lexers.Analyse("package main\n\nfunc main()\n{\n}\n")
+ ```
+
+In all cases, `nil` will be returned if the language can not be identified.
+
+```go
+if lexer == nil {
+ lexer = lexers.Fallback
+}
+```
+
+At this point, it should be noted that some lexers can be extremely chatty. To
+mitigate this, you can use the coalescing lexer to coalesce runs of identical
+token types into a single token:
+
+```go
+lexer = chroma.Coalesce(lexer)
+```
+
+
+### Formatting the output
+
+Once a language is identified you will need to pick a formatter and a style (theme).
+
+```go
+style := styles.Get("swapoff")
+if style == nil {
+ style = styles.Fallback
+}
+formatter := formatters.Get("html")
+if formatter == nil {
+ formatter = formatters.Fallback
+}
+```
+
+Then obtain an iterator over the tokens:
+
+```go
+contents, err := ioutil.ReadAll(r)
+iterator, err := lexer.Tokenise(nil, string(contents))
+```
+
+And finally, format the tokens from the iterator:
+
+```go
+err := formatter.Format(w, style, iterator)
+```
+
+
+### The HTML formatter
+
+By default the `html` registered formatter generates standalone HTML with
+embedded CSS. More flexibility is available through the `formatters/html` package.
+
+Firstly, the output generated by the formatter can be customised with the
+following constructor options:
+
+- `Standalone()` - generate standalone HTML with embedded CSS.
+- `WithClasses()` - use classes rather than inlined style attributes.
+- `ClassPrefix(prefix)` - prefix each generated CSS class.
+- `TabWidth(width)` - Set the rendered tab width, in characters.
+- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
+- `LinkableLineNumbers()` - Make the line numbers linkable and be a link to themselves.
+- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
+- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
+
+If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
+
+```go
+formatter := html.New(html.WithClasses())
+err := formatter.WriteCSS(w, style)
+```
+
+
+## More detail
+
+
+### Lexers
+
+See the [Pygments documentation](http://pygments.org/docs/lexerdevelopment/)
+for details on implementing lexers. Most concepts apply directly to Chroma,
+but see existing lexer implementations for real examples.
+
+In many cases lexers can be automatically converted directly from Pygments by
+using the included Python 3 script `pygments2chroma.py`. I use something like
+the following:
+
+```sh
+python3 _tools/pygments2chroma.py \
+ pygments.lexers.jvm.KotlinLexer \
+ > lexers/k/kotlin.go \
+ && gofmt -s -w lexers/k/kotlin.go
+```
+
+See notes in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt)
+for a list of lexers, and notes on some of the issues importing them.
+
+
+### Formatters
+
+Chroma supports HTML output, as well as terminal output in 8 colour, 256 colour, and true-colour.
+
+A `noop` formatter is included that outputs the token text only, and a `tokens`
+formatter outputs raw tokens. The latter is useful for debugging lexers.
+
+
+### Styles
+
+Chroma styles use the [same syntax](http://pygments.org/docs/styles/) as Pygments.
+
+All Pygments styles have been converted to Chroma using the `_tools/style.py` script.
+
+When you work with one of [Chroma's styles](https://github.com/alecthomas/chroma/tree/master/styles), know that the `chroma.Background` token type provides the default style for tokens. It does so by defining a foreground color and background color.
+
+For example, this gives each token name not defined in the style a default color of `#f8f8f8` and uses `#000000` for the highlighted code block's background:
+
+~~~go
+chroma.Background: "#f8f8f2 bg:#000000",
+~~~
+
+Also, token types in a style file are hierarchical. For instance, when `CommentSpecial` is not defined, Chroma uses the token style from `Comment`. So when several comment tokens use the same color, you'll only need to define `Comment` and override the one that has a different color.
+
+For a quick overview of the available styles and how they look, check out the [Chroma Style Gallery](https://xyproto.github.io/splash/docs/).
+
+
+## Command-line interface
+
+A command-line interface to Chroma is included.
+
+Binaries are available to install from [the releases page](https://github.com/alecthomas/chroma/releases).
+
+The CLI can be used as a preprocessor to colorise output of `less(1)`,
+see documentation for the `LESSOPEN` environment variable.
+
+The `--fail` flag can be used to suppress output and return with exit status
+1 to facilitate falling back to some other preprocessor in case chroma
+does not resolve a specific lexer to use for the given file. For example:
+
+```shell
+export LESSOPEN='| p() { chroma --fail "$1" || cat "$1"; }; p "%s"'
+```
+
+Replace `cat` with your favourite fallback preprocessor.
+
+When invoked as `.lessfilter`, the `--fail` flag is automatically turned
+on under the hood for easy integration with [lesspipe shipping with
+Debian and derivatives](https://manpages.debian.org/lesspipe#USER_DEFINED_FILTERS);
+for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`.
+
+
+## What's missing compared to Pygments?
+
+- Quite a few lexers, for various reasons (pull-requests welcome):
+ - Pygments lexers for complex languages often include custom code to
+ handle certain aspects, such as Raku's ability to nest code inside
+ regular expressions. These require time and effort to convert.
+ - I mostly only converted languages I had heard of, to reduce the porting cost.
+- Some more esoteric features of Pygments are omitted for simplicity.
+- Though the Chroma API supports content detection, very few languages support them.
+ I have plans to implement a statistical analyser at some point, but not enough time.
diff --git a/vendor/github.com/alecthomas/chroma/coalesce.go b/vendor/github.com/alecthomas/chroma/coalesce.go
new file mode 100644
index 0000000..f504895
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/coalesce.go
@@ -0,0 +1,35 @@
+package chroma
+
+// Coalesce is a Lexer interceptor that collapses runs of common types into a single token.
+func Coalesce(lexer Lexer) Lexer { return &coalescer{lexer} }
+
+type coalescer struct{ Lexer }
+
+func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
+ var prev Token
+ it, err := d.Lexer.Tokenise(options, text)
+ if err != nil {
+ return nil, err
+ }
+ return func() Token {
+ for token := it(); token != (EOF); token = it() {
+ if len(token.Value) == 0 {
+ continue
+ }
+ if prev == EOF {
+ prev = token
+ } else {
+ if prev.Type == token.Type && len(prev.Value) < 8192 {
+ prev.Value += token.Value
+ } else {
+ out := prev
+ prev = token
+ return out
+ }
+ }
+ }
+ out := prev
+ prev = EOF
+ return out
+ }, nil
+}
diff --git a/vendor/github.com/alecthomas/chroma/colour.go b/vendor/github.com/alecthomas/chroma/colour.go
new file mode 100644
index 0000000..15d794c
--- /dev/null
+++ b/vendor/github.com/alecthomas/chroma/colour.go
@@ -0,0 +1,164 @@
+package chroma
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+)
+
+// ANSI2RGB maps ANSI colour names, as supported by Chroma, to hex RGB values.
+var ANSI2RGB = map[string]string{
+ "#ansiblack": "000000",
+ "#ansidarkred": "7f0000",
+ "#ansidarkgreen": "007f00",
+ "#ansibrown": "7f7fe0",
+ "#ansidarkblue": "00007f",
+ "#ansipurple": "7f007f",
+ "#ansiteal": "007f7f",
+ "#ansilightgray": "e5e5e5",
+ // Normal
+ "#ansidarkgray": "555555",
+ "#ansired": "ff0000",
+ "#ansigreen": "00ff00",
+ "#ansiyellow": "ffff00",
+ "#ansiblue": "0000ff",
+ "#ansifuchsia": "ff00ff",
+ "#ansiturquoise": "00ffff",
+ "#ansiwhite": "ffffff",
+
+ // Aliases without the "ansi" prefix, because...why?
+ "#black": "000000",
+ "#darkred": "7f0000",
+ "#darkgreen": "007f00",
+ "#brown": "7f7fe0",
+ "#darkblue": "00007f",
+ "#purple": "7f007f",
+ "#teal": "007f7f",
+ "#lightgray": "e5e5e5",
+ // Normal
+ "#darkgray": "555555",
+ "#red": "ff0000",
+ "#green": "00ff00",
+ "#yellow": "ffff00",
+ "#blue": "0000ff",
+ "#fuchsia": "ff00ff",
+ "#turquoise": "00ffff",
+ "#white": "ffffff",
+}
+
+// Colour represents an RGB colour.
+type Colour int32
+
+// NewColour creates a Colour directly from RGB values.
+func NewColour(r, g, b uint8) Colour {
+ return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b))
+}
+
+// Distance between this colour and another.
+//
+// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
+// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
+func (c Colour) Distance(e2 Colour) float64 {
+ ar, ag, ab := int64(c.Red()), int64(c.Green()), int64(c.Blue())
+ br, bg, bb := int64(e2.Red()), int64(e2.Green()), int64(e2.Blue())
+ rmean := (ar + br) / 2
+ r := ar - br
+ g := ag - bg
+ b := ab - bb
+ return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
+}
+
+// Brighten returns a copy of this colour with its brightness adjusted.
+//
+// If factor is negative, the colour is darkened.
+//
+// Uses approach described here (http://www.pvladov.com/2012/09/make-color-lighter-or-darker.html).
+func (c Colour) Brighten(factor float64) Colour {
+ r := float64(c.Red())
+ g := float64(c.Green())
+ b := float64(c.Blue())
+
+ if factor < 0 {
+ factor++
+ r *= factor
+ g *= factor
+ b *= factor
+ } else {
+ r = (255-r)*factor + r
+ g = (255-g)*factor + g
+ b = (255-b)*factor + b
+ }
+ return NewColour(uint8(r), uint8(g), uint8(b))
+}
+
+// BrightenOrDarken brightens a colour if it is < 0.5 brighteness or darkens if > 0.5 brightness.
+func (c Colour) BrightenOrDarken(factor float64) Colour {
+ if c.Brightness() < 0.5 {
+ return c.Brighten(factor)
+ }
+ return c.Brighten(-factor)
+}
+
+// Brightness of the colour (roughly) in the range 0.0 to 1.0
+func (c Colour) Brightness() float64 {
+ return (float64(c.Red()) + float64(c.Green()) + float64(c.Blue())) / 255.0 / 3.0
+}
+
+// ParseColour in the forms #rgb, #rrggbb, #ansi element.
+ // The code flag tells whether this block surrounds
+ // highlighted code. This will be false when surrounding
+ // line numbers.
+ Start(code bool, styleAttr string) string
+
+ // End is called to write the end
element.
+ End(code bool) string
+}
+
+type preWrapper struct {
+ start func(code bool, styleAttr string) string
+ end func(code bool) string
+}
+
+func (p preWrapper) Start(code bool, styleAttr string) string {
+ return p.start(code, styleAttr)
+}
+
+func (p preWrapper) End(code bool) string {
+ return p.end(code)
+}
+
+var (
+ nopPreWrapper = preWrapper{
+ start: func(code bool, styleAttr string) string { return "" },
+ end: func(code bool) string { return "" },
+ }
+ defaultPreWrapper = preWrapper{
+ start: func(code bool, styleAttr string) string {
+ if code {
+ return fmt.Sprintf(`
`
+ }
+
+ return ``
+ },
+ }
+)
+
+// Formatter that generates HTML.
+type Formatter struct {
+ standalone bool
+ prefix string
+ Classes bool // Exported field to detect when classes are being used
+ allClasses bool
+ preWrapper PreWrapper
+ tabWidth int
+ wrapLongLines bool
+ lineNumbers bool
+ lineNumbersInTable bool
+ linkableLineNumbers bool
+ lineNumbersIDPrefix string
+ highlightRanges highlightRanges
+ baseLineNumber int
+}
+
+type highlightRanges [][2]int
+
+func (h highlightRanges) Len() int { return len(h) }
+func (h highlightRanges) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+func (h highlightRanges) Less(i, j int) bool { return h[i][0] < h[j][0] }
+
+func (f *Formatter) Format(w io.Writer, style *chroma.Style, iterator chroma.Iterator) (err error) {
+ return f.writeHTML(w, style, iterator.Tokens())
+}
+
+// We deliberately don't use html/template here because it is two orders of magnitude slower (benchmarked).
+//
+// OTOH we need to be super careful about correct escaping...
+func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.Token) (err error) { // nolint: gocyclo
+ css := f.styleToCSS(style)
+ if !f.Classes {
+ for t, style := range css {
+ css[t] = compressStyle(style)
+ }
+ }
+ if f.standalone {
+ fmt.Fprint(w, "\n")
+ if f.Classes {
+ fmt.Fprint(w, "")
+ }
+ fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.Background))
+ }
+
+ wrapInTable := f.lineNumbers && f.lineNumbersInTable
+
+ lines := chroma.SplitTokensIntoLines(tokens)
+ lineDigits := len(fmt.Sprintf("%d", f.baseLineNumber+len(lines)-1))
+ highlightIndex := 0
+
+ if wrapInTable {
+ // List line numbers in its own `, styleAttr)
+ }
+
+ return fmt.Sprintf(`
`, styleAttr)
+ },
+ end: func(code bool) string {
+ if code {
+ return `
+ fmt.Fprintf(w, "
+
+bahasabrasilgalegomagyarpolskisrpskiردوä¸æ–‡ç®€ä½“ç¹é«”ä¿¡æ¯ä¸å›½æˆ‘们一个公å¸ç®¡ç†è®ºå›å¯ä»¥æœåŠ¡æ—¶é—´ä¸ªäººäº§å“自己ä¼ä¸šæŸ¥çœ‹å·¥ä½œè”系没有网站所有评论ä¸å¿ƒæ–‡ç« 用户首页作者技术问题相关下载æœç´¢ä½¿ç”¨è½¯ä»¶åœ¨çº¿ä¸»é¢˜èµ„料视频回å¤æ³¨å†Œç½‘络收è—内容推è市场消æ¯ç©ºé—´å‘布什么好å‹ç”Ÿæ´»å›¾ç‰‡å‘展如果手机新闻最新方å¼åŒ—京æ供关于更多这个系统知é“游æˆå¹¿å‘Šå…¶ä»–å‘表安全第一会员进行点击版æƒç”µå世界设计å…è´¹æ•™è‚²åŠ å…¥æ´»åŠ¨ä»–ä»¬å•†å“åšå®¢çŽ°åœ¨ä¸Šæµ·å¦‚何已ç»ç•™è¨€è¯¦ç»†ç¤¾åŒºç™»å½•æœ¬ç«™éœ€è¦ä»·æ ¼æ”¯æŒå›½é™…链接国家建设朋å‹é˜…读法律ä½ç½®ç»æµŽé€‰æ‹©è¿™æ ·å½“å‰åˆ†ç±»æŽ’è¡Œå› ä¸ºäº¤æ˜“æœ€åŽéŸ³ä¹ä¸èƒ½é€šè¿‡è¡Œä¸šç§‘技å¯èƒ½è®¾å¤‡åˆä½œå¤§å®¶ç¤¾ä¼šç ”究专业全部项目这里还是开始情况电脑文件å“牌帮助文化资æºå¤§å¦å¦ä¹ 地å€æµè§ˆæŠ•èµ„工程è¦æ±‚怎么时候功能主è¦ç›®å‰èµ„讯城市方法电影招è˜å£°æ˜Žä»»ä½•å¥åº·æ•°æ®ç¾Žå›½æ±½è½¦ä»‹ç»ä½†æ˜¯äº¤æµç”Ÿäº§æ‰€ä»¥ç”µè¯æ˜¾ç¤ºä¸€äº›å•ä½äººå‘˜åˆ†æžåœ°å›¾æ—…游工具å¦ç”Ÿç³»åˆ—网å‹å¸–å密ç 频é“控制地区基本全国网上é‡è¦ç¬¬äºŒå–œæ¬¢è¿›å…¥å‹æƒ…这些考试å‘现培è®ä»¥ä¸Šæ”¿åºœæˆä¸ºçŽ¯å¢ƒé¦™æ¸¯åŒæ—¶å¨±ä¹å‘é€ä¸€å®šå¼€å‘作å“æ ‡å‡†æ¬¢è¿Žè§£å†³åœ°æ–¹ä¸€ä¸‹ä»¥åŠè´£ä»»æˆ–者客户代表积分女人数ç 销售出现离线应用列表ä¸åŒç¼–辑统计查询ä¸è¦æœ‰å…³æœºæž„很多æ’放组织政ç–直接能力æ¥æºæ™‚間看到çƒé—¨å…³é”®ä¸“区éžå¸¸è‹±è¯ç™¾åº¦å¸Œæœ›ç¾Žå¥³æ¯”较知识规定建议部门æ„è§ç²¾å½©æ—¥æœ¬æ高å‘言方é¢åŸºé‡‘处ç†æƒé™å½±ç‰‡é“¶è¡Œè¿˜æœ‰åˆ†äº«ç‰©å“ç»è¥æ·»åŠ 专家这ç§è¯é¢˜èµ·æ¥ä¸šåŠ¡å…¬å‘Šè®°å½•ç®€ä»‹è´¨é‡ç”·äººå½±å“引用报告部分快速咨询时尚注æ„申请å¦æ ¡åº”该历å²åªæ˜¯è¿”回è´ä¹°å称为了æˆåŠŸè¯´æ˜Žä¾›åº”å©å专题程åºä¸€èˆ¬æœƒå“¡åªæœ‰å…¶å®ƒä¿æŠ¤è€Œä¸”今天窗å£åŠ¨æ€çŠ¶æ€ç‰¹åˆ«è®¤ä¸ºå¿…须更新å°è¯´æˆ‘å€‘ä½œä¸ºåª’ä½“åŒ…æ‹¬é‚£ä¹ˆä¸€æ ·å›½å†…æ˜¯å¦æ ¹æ®ç”µè§†å¦é™¢å…·æœ‰è¿‡ç¨‹ç”±äºŽäººæ‰å‡ºæ¥ä¸è¿‡æ£åœ¨æ˜Žæ˜Ÿæ•…äº‹å…³ç³»æ ‡é¢˜å•†åŠ¡è¾“å…¥ä¸€ç›´åŸºç¡€æ•™å¦äº†è§£å»ºç‘结果全çƒé€šçŸ¥è®¡åˆ’对于艺术相册å‘生真的建立ç‰çº§ç±»åž‹ç»éªŒå®žçŽ°åˆ¶ä½œæ¥è‡ªæ ‡ç¾ä»¥ä¸‹åŽŸåˆ›æ— 法其ä¸å€‹äººä¸€åˆ‡æŒ‡å—å…³é—é›†å›¢ç¬¬ä¸‰å…³æ³¨å› æ¤ç…§ç‰‡æ·±åœ³å•†ä¸šå¹¿å·žæ—¥æœŸé«˜çº§æœ€è¿‘综åˆè¡¨ç¤ºä¸“辑行为交通评价觉得精åŽå®¶åºå®Œæˆæ„Ÿè§‰å®‰è£…得到邮件制度食å“虽然转载报价记者方案行政人民用å“东西æ出酒店然åŽä»˜æ¬¾çƒç‚¹ä»¥å‰å®Œå…¨å‘帖设置领导工业医院看看ç»å…¸åŽŸå› å¹³å°å„ç§å¢žåŠ æ料新增之åŽèŒä¸šæ•ˆæžœä»Šå¹´è®ºæ–‡æˆ‘国告诉版主修改å‚与打å°å¿«ä¹æœºæ¢°è§‚点å˜åœ¨ç²¾ç¥žèŽ·å¾—利用继ç»ä½ 们这么模å¼è¯è¨€èƒ½å¤Ÿé›…虎æ“ä½œé£Žæ ¼ä¸€èµ·ç§‘å¦ä½“育çŸä¿¡æ¡ä»¶æ²»ç–—è¿åŠ¨äº§ä¸šä¼šè®®å¯¼èˆªå…ˆç”Ÿè”盟å¯æ˜¯å•é¡Œç»“构作用调查資料自动负责农业访问实施接å—讨论那个åé¦ˆåŠ å¼ºå¥³æ€§èŒƒå›´æœå‹™ä¼‘闲今日客æœè§€çœ‹å‚åŠ çš„è¯ä¸€ç‚¹ä¿è¯å›¾ä¹¦æœ‰æ•ˆæµ‹è¯•ç§»åŠ¨æ‰èƒ½å†³å®šè‚¡ç¥¨ä¸æ–需求ä¸å¾—办法之间采用è¥é”€æŠ•è¯‰ç›®æ ‡çˆ±æƒ…摄影有些複製文å¦æœºä¼šæ•°å—装修è´ç‰©å†œæ‘å…¨é¢ç²¾å“其实事情水平æç¤ºä¸Šå¸‚è°¢è°¢æ™®é€šæ•™å¸ˆä¸Šä¼ ç±»åˆ«æŒæ›²æ‹¥æœ‰åˆ›æ–°é…件åªè¦æ—¶ä»£è³‡è¨Šè¾¾åˆ°äººç”Ÿè®¢é˜…è€å¸ˆå±•ç¤ºå¿ƒç†è´´å網站主題自然级别简å•æ”¹é©é‚£äº›æ¥è¯´æ‰“开代ç åˆ é™¤è¯åˆ¸èŠ‚ç›®é‡ç‚¹æ¬¡æ•¸å¤šå°‘规划资金找到以åŽå¤§å…¨ä¸»é¡µæœ€ä½³å›žç”天下ä¿éšœçŽ°ä»£æ£€æŸ¥æŠ•ç¥¨å°æ—¶æ²’有æ£å¸¸ç”šè‡³ä»£ç†ç›®å½•å…¬å¼€å¤åˆ¶é‡‘èžå¹¸ç¦ç‰ˆæœ¬å½¢æˆå‡†å¤‡è¡Œæƒ…回到æ€æƒ³æ€Žæ ·å议认è¯æœ€å¥½äº§ç”ŸæŒ‰ç…§æœè£…广东动漫采è´æ–°æ‰‹ç»„图é¢æ¿å‚考政治容易天地努力人们å‡çº§é€Ÿåº¦äººç‰©è°ƒæ•´æµè¡Œé€ æˆæ–‡å—韩国贸易开展相關表现影视如æ¤ç¾Žå®¹å¤§å°æŠ¥é“æ¡æ¬¾å¿ƒæƒ…许多法规家居书店连接立å³ä¸¾æŠ¥æŠ€å·§å¥¥è¿ç™»å…¥ä»¥æ¥ç†è®ºäº‹ä»¶è‡ªç”±ä¸åŽåŠžå…¬å¦ˆå¦ˆçœŸæ£ä¸é”™å…¨æ–‡åˆåŒä»·å€¼åˆ«äººç›‘ç£å…·ä½“世纪团队创业承担增长有人ä¿æŒå•†å®¶ç»´ä¿®å°æ¹¾å·¦å³è‚¡ä»½ç”案实际电信ç»ç†ç”Ÿå‘½å®£ä¼ 任务æ£å¼ç‰¹è‰²ä¸‹æ¥å会åªèƒ½å½“然é‡æ–°å…§å®¹æŒ‡å¯¼è¿è¡Œæ—¥å¿—賣家超过土地浙江支付推出站长æå·žæ‰§è¡Œåˆ¶é€ ä¹‹ä¸€æŽ¨å¹¿çŽ°åœºæè¿°å˜åŒ–ä¼ ç»ŸæŒæ‰‹ä¿é™©è¯¾ç¨‹åŒ»ç–—ç»è¿‡è¿‡åŽ»ä¹‹å‰æ”¶å…¥å¹´åº¦æ‚志美丽最高登陆未æ¥åŠ å·¥å…责教程版å—身体é‡åº†å‡ºå”®æˆæœ¬å½¢å¼åœŸè±†å‡ºåƒ¹ä¸œæ–¹é‚®ç®±å—京求èŒå–å¾—èŒä½ç›¸ä¿¡é¡µé¢åˆ†é’Ÿç½‘页确定图例网å€ç§¯æžé”™è¯¯ç›®çš„å®è´æœºå…³é£Žé™©æŽˆæƒç—…æ¯’å® ç‰©é™¤äº†è©•è«–ç–¾ç—…åŠæ—¶æ±‚è´ç«™ç‚¹å„¿ç«¥æ¯å¤©ä¸å¤®è®¤è¯†æ¯ä¸ªå¤©æ´¥å—体å°ç£ç»´æŠ¤æœ¬é¡µä¸ªæ€§å®˜æ–¹å¸¸è§ç›¸æœºæˆ˜ç•¥åº”å½“å¾‹å¸ˆæ–¹ä¾¿æ ¡å›è‚¡å¸‚房屋æ 目员工导致çªç„¶é“具本网结åˆæ¡£æ¡ˆåŠ³åŠ¨å¦å¤–美元引起改å˜ç¬¬å››ä¼šè®¡èªªæ˜Žéšç§å®å®è§„范消费共åŒå¿˜è®°ä½“系带æ¥åå—ç™¼è¡¨å¼€æ”¾åŠ ç›Ÿå—到二手大é‡æˆäººæ•°é‡å…±äº«åŒºåŸŸå¥³å©åŽŸåˆ™æ‰€åœ¨ç»“æŸé€šä¿¡è¶…级é…置当时优秀性感房产éŠæˆ²å‡ºå£æ交就业ä¿å¥ç¨‹åº¦å‚数事业整个山东情感特殊分類æœå°‹å±žäºŽé—¨æˆ·è´¢åŠ¡å£°éŸ³åŠå…¶è´¢ç»åšæŒå¹²éƒ¨æˆç«‹åˆ©ç›Šè€ƒè™‘æˆéƒ½åŒ…装用戶比赛文明招商完整真是眼ç›ä¼™ä¼´å¨æœ›é¢†åŸŸå«ç”Ÿä¼˜æƒ 論壇公共良好充分符åˆé™„件特点ä¸å¯è‹±æ–‡èµ„äº§æ ¹æœ¬æ˜Žæ˜¾å¯†ç¢¼å…¬ä¼—æ°‘æ—æ›´åŠ äº«å—åŒå¦å¯åŠ¨é€‚åˆåŽŸæ¥é—®ç”本文美食绿色稳定终于生物供求æœç‹åŠ›é‡ä¸¥é‡æ°¸è¿œå†™çœŸæœ‰é™ç«žäº‰å¯¹è±¡è´¹ç”¨ä¸å¥½ç»å¯¹å分促进点评影音优势ä¸å°‘欣èµå¹¶ä¸”有点方å‘å…¨æ–°ä¿¡ç”¨è®¾æ–½å½¢è±¡èµ„æ ¼çªç ´éšç€é‡å¤§äºŽæ˜¯æ¯•ä¸šæ™ºèƒ½åŒ–å·¥å®Œç¾Žå•†åŸŽç»Ÿä¸€å‡ºç‰ˆæ‰“é€ ç”¢å“概况用于ä¿ç•™å› ç´ ä¸åœ‹å˜å‚¨è´´å›¾æœ€æ„›é•¿æœŸå£ä»·ç†è´¢åŸºåœ°å®‰æŽ’æ¦æ±‰é‡Œé¢åˆ›å»ºå¤©ç©ºé¦–先完善驱动下é¢ä¸å†è¯šä¿¡æ„义阳光英国漂亮军事玩家群众农民å³å¯å稱家具动画想到注明å°å¦æ€§èƒ½è€ƒç ”硬件观看清楚æžç¬‘首é 黄金适用江è‹çœŸå®žä¸»ç®¡é˜¶æ®µè¨»å†Šç¿»è¯‘æƒåˆ©åšå¥½ä¼¼ä¹Žé€šè®¯æ–½å·¥ç‹€æ…‹ä¹Ÿè®¸çŽ¯ä¿åŸ¹å…»æ¦‚念大型机票ç†è§£åŒ¿åcuandoenviarmadridbuscariniciotiempoporquecuentaestadopuedenjuegoscontraestánnombretienenperfilmaneraamigosciudadcentroaunquepuedesdentroprimerpreciosegúnbuenosvolverpuntossemanahabÃaagostonuevosunidoscarlosequiponiñosmuchosalgunacorreoimagenpartirarribamarÃahombreempleoverdadcambiomuchasfueronpasadolÃneaparecenuevascursosestabaquierolibroscuantoaccesomiguelvarioscuatrotienesgruposseráneuropamediosfrenteacercademásofertacochesmodeloitalialetrasalgúncompracualesexistecuerposiendoprensallegarviajesdineromurciapodrápuestodiariopuebloquieremanuelpropiocrisisciertoseguromuertefuentecerrargrandeefectopartesmedidapropiaofrecetierrae-mailvariasformasfuturoobjetoseguirriesgonormasmismosúnicocaminositiosrazóndebidopruebatoledotenÃajesúsesperococinaorigentiendacientocádizhablarserÃalatinafuerzaestiloguerraentraréxitolópezagendavÃdeoevitarpaginametrosjavierpadresfácilcabezaáreassalidaenvÃojapónabusosbienestextosllevarpuedanfuertecomúnclaseshumanotenidobilbaounidadestáseditarcreadoдлÑчтокакилиÑтовÑеегопритакещеужеКакбезбылониВÑеподÐтотомчемнетлетразонагдемнеДлÑПринаÑнихтемктогодвоттамСШÐмаÑЧтоваÑвамемуТакдванамÑтиÑтуВамтехпротутнадднÑВоттринейВаÑнимÑамтотрубОнимирнееОООлицÑтаОнанемдоммойдвеоноÑудकेहैकीसेकाकोऔरपरनेà¤à¤•à¤•à¤¿à¤à¥€à¤‡à¤¸à¤•à¤°à¤¤à¥‹à¤¹à¥‹à¤†à¤ªà¤¹à¥€à¤¯à¤¹à¤¯à¤¾à¤¤à¤•à¤¥à¤¾jagranआजजोअबदोगईजागà¤à¤¹à¤®à¤‡à¤¨à¤µà¤¹à¤¯à¥‡à¤¥à¥‡à¤¥à¥€à¤˜à¤°à¤œà¤¬à¤¦à¥€à¤•à¤ˆà¤œà¥€à¤µà¥‡à¤¨à¤ˆà¤¨à¤à¤¹à¤°à¤‰à¤¸à¤®à¥‡à¤•à¤®à¤µà¥‹à¤²à¥‡à¤¸à¤¬à¤®à¤ˆà¤¦à¥‡à¤“रआमबसà¤à¤°à¤¬à¤¨à¤šà¤²à¤®à¤¨à¤†à¤—सीलीعلىإلىهذاآخرعددالىهذهصورغيركانولابينعرضذلكهنايومقالعليانالكنØتىقبلوØةاخرÙقطعبدركنإذاكمااØدإلاÙيهبعضكيÙبØثومنوهوأناجدالهاسلمعندليسعبرصلىمنذبهاأنهمثلكنتالاØيثمصرشرØØولوÙياذالكلمرةانتالÙأبوخاصأنتانهاليعضووقدابنخيربنتلكمشاءوهيابوقصصومارقمأØدنØنعدمرأياØةكتبدونيجبمنهتØتجهةسنةيتمكرةغزةنÙسبيتللهلناتلكقلبلماعنهأولشيءنورأماÙيكبكلذاترتببأنهمسانكبيعÙقدØسنلهمشعرأهلشهرقطرطلبprofileservicedefaulthimselfdetailscontentsupportstartedmessagesuccessfashion
\n")
+ fmt.Fprint(w, "", f.styleAttr(css, chroma.LineTable))
+ fmt.Fprintf(w, " \n", f.styleAttr(css, chroma.LineTableTD))
+ fmt.Fprintf(w, f.preWrapper.Start(false, f.styleAttr(css, chroma.PreWrapper)))
+ for index := range lines {
+ line := f.baseLineNumber + index
+ highlight, next := f.shouldHighlight(highlightIndex, line)
+ if next {
+ highlightIndex++
+ }
+ if highlight {
+ fmt.Fprintf(w, "", f.styleAttr(css, chroma.LineHighlight))
+ }
+
+ fmt.Fprintf(w, "%s\n", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
+
+ if highlight {
+ fmt.Fprintf(w, "")
+ }
+ }
+ fmt.Fprint(w, f.preWrapper.End(false))
+ fmt.Fprint(w, " \n")
+ fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.LineTableTD, "width:100%"))
+ }
+
+ fmt.Fprintf(w, f.preWrapper.Start(true, f.styleAttr(css, chroma.PreWrapper)))
+
+ highlightIndex = 0
+ for index, tokens := range lines {
+ // 1-based line number.
+ line := f.baseLineNumber + index
+ highlight, next := f.shouldHighlight(highlightIndex, line)
+ if next {
+ highlightIndex++
+ }
+
+ // Start of Line
+ fmt.Fprint(w, ``)
+ } else {
+ fmt.Fprintf(w, "%s>", f.styleAttr(css, chroma.Line))
+ }
+
+ // Line number
+ if f.lineNumbers && !wrapInTable {
+ fmt.Fprintf(w, "%s", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
+ }
+
+ fmt.Fprintf(w, ``, f.styleAttr(css, chroma.CodeLine))
+
+ for _, token := range tokens {
+ html := html.EscapeString(token.String())
+ attr := f.styleAttr(css, token.Type)
+ if attr != "" {
+ html = fmt.Sprintf("%s", attr, html)
+ }
+ fmt.Fprint(w, html)
+ }
+
+ fmt.Fprint(w, ``) // End of CodeLine
+
+ fmt.Fprint(w, ``) // End of Line
+ }
+
+ fmt.Fprintf(w, f.preWrapper.End(true))
+
+ if wrapInTable {
+ fmt.Fprint(w, " , B
]{0,})> ]{0,})>([\d]{0,}\.)(.*)((
([\w\W\s\d][^<>]{0,})|[\s]{0,}))<\/a><\/TD>]{0,})>([\w\W\s\d][^<>]{0,})<\/TD> ]{0,})>([\w\W\s\d][^<>]{0,})<\/TD><\/TR>/is
+
+ 0: 43.Word Processor
(N-1286)Lega lstaff.com CA - Statewide
+ 1: BGCOLOR='#DBE9E9'
+ 2: align=left valign=top
+ 3: 43.
+ 4: Word Processor43.Word Processor
(N-1286)Lega lstaff.com CA - Statewide
(N-1286)
+ 5:
+ 6:
+ 7: (?
(?
+ = 40) || (user.karma > calc_avg_karma(userlist)+5) %} class="karma-good"{%
+ endif %}>
+
+
+ {{ user }}
+
+
+
+ Our admins
+ {% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}
+
+ Our members
+ {% for user in userlist %} {{ user_details(user) }} {% endfor %}
+
+
+```
+
+## Features
+
+- Syntax- and feature-set-compatible with [Django 1.7](https://django.readthedocs.io/en/1.7.x/topics/templates.html)
+- [Advanced C-like expressions](https://github.com/flosch/pongo2/blob/master/template_tests/expressions.tpl).
+- [Complex function calls within expressions](https://github.com/flosch/pongo2/blob/master/template_tests/function_calls_wrapper.tpl).
+- [Easy API to create new filters and tags](http://godoc.org/github.com/flosch/pongo2#RegisterFilter) ([including parsing arguments](http://godoc.org/github.com/flosch/pongo2#Parser))
+- Additional features:
+ - Macros including importing macros from other files (see [template_tests/macro.tpl](https://github.com/flosch/pongo2/blob/master/template_tests/macro.tpl))
+ - [Template sandboxing](https://godoc.org/github.com/flosch/pongo2#TemplateSet) ([directory patterns](http://golang.org/pkg/path/filepath/#Match), banned tags/filters)
+
+## Caveats
+
+### Filters
+
+- **date** / **time**: The `date` and `time` filter are taking the Golang specific time- and date-format (not Django's one) currently. [Take a look on the format here](http://golang.org/pkg/time/#Time.Format).
+- **stringformat**: `stringformat` does **not** take Python's string format syntax as a parameter, instead it takes Go's. Essentially `{{ 3.14|stringformat:"pi is %.2f" }}` is `fmt.Sprintf("pi is %.2f", 3.14)`.
+- **escape** / **force_escape**: Unlike Django's behaviour, the `escape`-filter is applied immediately. Therefore there is no need for a `force_escape`-filter yet.
+
+### Tags
+
+- **for**: All the `forloop` fields (like `forloop.counter`) are written with a capital letter at the beginning. For example, the `counter` can be accessed by `forloop.Counter` and the parentloop by `forloop.Parentloop`.
+- **now**: takes Go's time format (see **date** and **time**-filter).
+
+### Misc
+
+- **not in-operator**: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it):
+ `{% if key in map %}Key is in map{% else %}Key not in map{% endif %}` or `{% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}`.
+
+## Add-ons, libraries and helpers
+
+### Official
+
+- [pongo2-addons](https://github.com/flosch/pongo2-addons) - Official additional filters/tags for pongo2 (for example a **markdown**-filter). They are in their own repository because they're relying on 3rd-party-libraries.
+
+### 3rd-party
+
+- [beego-pongo2](https://github.com/oal/beego-pongo2) - A tiny little helper for using Pongo2 with [Beego](https://github.com/astaxie/beego).
+- [beego-pongo2.v2](https://github.com/ipfans/beego-pongo2.v2) - Same as `beego-pongo2`, but for pongo2 v2.
+- [macaron-pongo2](https://github.com/macaron-contrib/pongo2) - pongo2 support for [Macaron](https://github.com/Unknwon/macaron), a modular web framework.
+- [ginpongo2](https://github.com/ngerakines/ginpongo2) - middleware for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [Build'n support for Iris' template engine](https://github.com/kataras/iris)
+- [pongo2gin](https://gitlab.com/go-box/pongo2gin) - alternative renderer for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [pongo2-trans](https://github.com/digitalcrab/pongo2trans) - `trans`-tag implementation for internationalization
+- [tpongo2](https://github.com/tango-contrib/tpongo2) - pongo2 support for [Tango](https://github.com/lunny/tango), a micro-kernel & pluggable web framework.
+- [p2cli](https://github.com/wrouesnel/p2cli) - command line templating utility based on pongo2
+
+Please add your project to this list and send me a pull request when you've developed something nice for pongo2.
+
+## Who's using pongo2
+
+[I'm compiling a list of pongo2 users](https://github.com/flosch/pongo2/issues/241). Add your project or company!
+
+## API-usage examples
+
+Please see the documentation for a full list of provided API methods.
+
+### A tiny example (template string)
+
+```go
+// Compile the template first (i. e. creating the AST)
+tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+if err != nil {
+ panic(err)
+}
+// Now you can render the template with the given
+// pongo2.Context how often you want to.
+out, err := tpl.Execute(pongo2.Context{"name": "florian"})
+if err != nil {
+ panic(err)
+}
+fmt.Println(out) // Output: Hello Florian!
+```
+
+## Example server-usage (template file)
+
+```go
+package main
+
+import (
+ "github.com/flosch/pongo2"
+ "net/http"
+)
+
+// Pre-compiling the templates at application startup using the
+// little Must()-helper function (Must() will panic if FromFile()
+// or FromString() will return with an error - that's it).
+// It's faster to pre-compile it anywhere at startup and only
+// execute the template later.
+var tplExample = pongo2.Must(pongo2.FromFile("example.html"))
+
+func examplePage(w http.ResponseWriter, r *http.Request) {
+ // Execute the template per HTTP request
+ err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func main() {
+ http.HandleFunc("/", examplePage)
+ http.ListenAndServe(":8080", nil)
+}
+```
diff --git a/vendor/github.com/flosch/pongo2/context.go b/vendor/github.com/flosch/pongo2/context.go
new file mode 100644
index 0000000..dbc5e3e
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/context.go
@@ -0,0 +1,137 @@
+package pongo2
+
+import (
+ "fmt"
+ "regexp"
+
+ "errors"
+)
+
+var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$")
+
+var autoescape = true
+
+func SetAutoescape(newValue bool) {
+ autoescape = newValue
+}
+
+// A Context type provides constants, variables, instances or functions to a template.
+//
+// pongo2 automatically provides meta-information or functions through the "pongo2"-key.
+// Currently, context["pongo2"] contains the following keys:
+// 1. version: returns the version string
+//
+// Template examples for accessing items from your context:
+// {{ myconstant }}
+// {{ myfunc("test", 42) }}
+// {{ user.name }}
+// {{ pongo2.version }}
+type Context map[string]interface{}
+
+func (c Context) checkForValidIdentifiers() *Error {
+ for k, v := range c {
+ if !reIdentifiers.MatchString(k) {
+ return &Error{
+ Sender: "checkForValidIdentifiers",
+ OrigError: fmt.Errorf("context-key '%s' (value: '%+v') is not a valid identifier", k, v),
+ }
+ }
+ }
+ return nil
+}
+
+// Update updates this context with the key/value-pairs from another context.
+func (c Context) Update(other Context) Context {
+ for k, v := range other {
+ c[k] = v
+ }
+ return c
+}
+
+// ExecutionContext contains all data important for the current rendering state.
+//
+// If you're writing a custom tag, your tag's Execute()-function will
+// have access to the ExecutionContext. This struct stores anything
+// about the current rendering process's Context including
+// the Context provided by the user (field Public).
+// You can safely use the Private context to provide data to the user's
+// template (like a 'forloop'-information). The Shared-context is used
+// to share data between tags. All ExecutionContexts share this context.
+//
+// Please be careful when accessing the Public data.
+// PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only).
+//
+// To create your own execution context within tags, use the
+// NewChildExecutionContext(parent) function.
+type ExecutionContext struct {
+ template *Template
+
+ Autoescape bool
+ Public Context
+ Private Context
+ Shared Context
+}
+
+var pongo2MetaContext = Context{
+ "version": Version,
+}
+
+func newExecutionContext(tpl *Template, ctx Context) *ExecutionContext {
+ privateCtx := make(Context)
+
+ // Make the pongo2-related funcs/vars available to the context
+ privateCtx["pongo2"] = pongo2MetaContext
+
+ return &ExecutionContext{
+ template: tpl,
+
+ Public: ctx,
+ Private: privateCtx,
+ Autoescape: autoescape,
+ }
+}
+
+func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext {
+ newctx := &ExecutionContext{
+ template: parent.template,
+
+ Public: parent.Public,
+ Private: make(Context),
+ Autoescape: parent.Autoescape,
+ }
+ newctx.Shared = parent.Shared
+
+ // Copy all existing private items
+ newctx.Private.Update(parent.Private)
+
+ return newctx
+}
+
+func (ctx *ExecutionContext) Error(msg string, token *Token) *Error {
+ return ctx.OrigError(errors.New(msg), token)
+}
+
+func (ctx *ExecutionContext) OrigError(err error, token *Token) *Error {
+ filename := ctx.template.name
+ var line, col int
+ if token != nil {
+ // No tokens available
+ // TODO: Add location (from where?)
+ filename = token.Filename
+ line = token.Line
+ col = token.Col
+ }
+ return &Error{
+ Template: ctx.template,
+ Filename: filename,
+ Line: line,
+ Column: col,
+ Token: token,
+ Sender: "execution",
+ OrigError: err,
+ }
+}
+
+func (ctx *ExecutionContext) Logf(format string, args ...interface{}) {
+ ctx.template.set.logf(format, args...)
+}
diff --git a/vendor/github.com/flosch/pongo2/doc.go b/vendor/github.com/flosch/pongo2/doc.go
new file mode 100644
index 0000000..5a23e2b
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/doc.go
@@ -0,0 +1,31 @@
+// A Django-syntax like template-engine
+//
+// Blog posts about pongo2 (including introduction and migration):
+// https://www.florian-schlachter.de/?tag=pongo2
+//
+// Complete documentation on the template language:
+// https://docs.djangoproject.com/en/dev/topics/templates/
+//
+// Try out pongo2 live in the pongo2 playground:
+// https://www.florian-schlachter.de/pongo2/
+//
+// Make sure to read README.md in the repository as well.
+//
+// A tiny example with template strings:
+//
+// (Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277)
+//
+// // Compile the template first (i. e. creating the AST)
+// tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+// if err != nil {
+// panic(err)
+// }
+// // Now you can render the template with the given
+// // pongo2.Context how often you want to.
+// out, err := tpl.Execute(pongo2.Context{"name": "fred"})
+// if err != nil {
+// panic(err)
+// }
+// fmt.Println(out) // Output: Hello Fred!
+//
+package pongo2
diff --git a/vendor/github.com/flosch/pongo2/error.go b/vendor/github.com/flosch/pongo2/error.go
new file mode 100644
index 0000000..8aec8c1
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/error.go
@@ -0,0 +1,91 @@
+package pongo2
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+// The Error type is being used to address an error during lexing, parsing or
+// execution. If you want to return an error object (for example in your own
+// tag or filter) fill this object with as much information as you have.
+// Make sure "Sender" is always given (if you're returning an error within
+// a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag').
+// It's okay if you only fill in ErrorMsg if you don't have any other details at hand.
+type Error struct {
+ Template *Template
+ Filename string
+ Line int
+ Column int
+ Token *Token
+ Sender string
+ OrigError error
+}
+
+func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error {
+ if e.Template == nil {
+ e.Template = template
+ }
+
+ if e.Token == nil {
+ e.Token = t
+ if e.Line <= 0 {
+ e.Line = t.Line
+ e.Column = t.Col
+ }
+ }
+
+ return e
+}
+
+// Returns a nice formatted error string.
+func (e *Error) Error() string {
+ s := "[Error"
+ if e.Sender != "" {
+ s += " (where: " + e.Sender + ")"
+ }
+ if e.Filename != "" {
+ s += " in " + e.Filename
+ }
+ if e.Line > 0 {
+ s += fmt.Sprintf(" | Line %d Col %d", e.Line, e.Column)
+ if e.Token != nil {
+ s += fmt.Sprintf(" near '%s'", e.Token.Val)
+ }
+ }
+ s += "] "
+ s += e.OrigError.Error()
+ return s
+}
+
+// RawLine returns the affected line from the original template, if available.
+func (e *Error) RawLine() (line string, available bool, outErr error) {
+ if e.Line <= 0 || e.Filename == "
+ // Double newline =
")
+ }
+ }
+ }
+
+ if opened {
+ b.WriteString("")
+ }
+
+ return AsValue(b.String()), nil
+}
+
+func filterSplit(in *Value, param *Value) (*Value, *Error) {
+ chunks := strings.Split(in.String(), param.String())
+
+ return AsValue(chunks), nil
+}
+
+func filterLinebreaksbr(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.Replace(in.String(), "\n", "
", -1)), nil
+}
+
+func filterLinenumbers(in *Value, param *Value) (*Value, *Error) {
+ lines := strings.Split(in.String(), "\n")
+ output := make([]string, 0, len(lines))
+ for idx, line := range lines {
+ output = append(output, fmt.Sprintf("%d. %s", idx+1, line))
+ }
+ return AsValue(strings.Join(output, "\n")), nil
+}
+
+func filterLjust(in *Value, param *Value) (*Value, *Error) {
+ times := param.Integer() - in.Len()
+ if times < 0 {
+ times = 0
+ }
+ return AsValue(fmt.Sprintf("%s%s", in.String(), strings.Repeat(" ", times))), nil
+}
+
+func filterUrlencode(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(url.QueryEscape(in.String())), nil
+}
+
+// TODO: This regexp could do some work
+var filterUrlizeURLRegexp = regexp.MustCompile(`((((http|https)://)|www\.|((^|[ ])[0-9A-Za-z_\-]+(\.com|\.net|\.org|\.info|\.biz|\.de))))(?U:.*)([ ]+|$)`)
+var filterUrlizeEmailRegexp = regexp.MustCompile(`(\w+@\w+\.\w{2,4})`)
+
+func filterUrlizeHelper(input string, autoescape bool, trunc int) (string, error) {
+ var soutErr error
+ sout := filterUrlizeURLRegexp.ReplaceAllStringFunc(input, func(raw_url string) string {
+ var prefix string
+ var suffix string
+ if strings.HasPrefix(raw_url, " ") {
+ prefix = " "
+ }
+ if strings.HasSuffix(raw_url, " ") {
+ suffix = " "
+ }
+
+ raw_url = strings.TrimSpace(raw_url)
+
+ t, err := ApplyFilter("iriencode", AsValue(raw_url), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ url := t.String()
+
+ if !strings.HasPrefix(url, "http") {
+ url = fmt.Sprintf("http://%s", url)
+ }
+
+ title := raw_url
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ if autoescape {
+ t, err := ApplyFilter("escape", AsValue(title), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ title = t.String()
+ }
+
+ return fmt.Sprintf(`%s%s%s`, prefix, url, title, suffix)
+ })
+ if soutErr != nil {
+ return "", soutErr
+ }
+
+ sout = filterUrlizeEmailRegexp.ReplaceAllStringFunc(sout, func(mail string) string {
+ title := mail
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ return fmt.Sprintf(`%s`, mail, title)
+ })
+
+ return sout, nil
+}
+
+func filterUrlize(in *Value, param *Value) (*Value, *Error) {
+ autoescape := true
+ if param.IsBool() {
+ autoescape = param.Bool()
+ }
+
+ s, err := filterUrlizeHelper(in.String(), autoescape, -1)
+ if err != nil {
+
+ }
+
+ return AsValue(s), nil
+}
+
+func filterUrlizetrunc(in *Value, param *Value) (*Value, *Error) {
+ s, err := filterUrlizeHelper(in.String(), true, param.Integer())
+ if err != nil {
+ return nil, &Error{
+ Sender: "filter:urlizetrunc",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ return AsValue(s), nil
+}
+
+func filterStringformat(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(param.String(), in.Interface())), nil
+}
+
+var reStriptags = regexp.MustCompile("<[^>]*?>")
+
+func filterStriptags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+
+ // Strip all tags
+ s = reStriptags.ReplaceAllString(s, "")
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+// https://en.wikipedia.org/wiki/Phoneword
+var filterPhone2numericMap = map[string]string{
+ "a": "2", "b": "2", "c": "2", "d": "3", "e": "3", "f": "3", "g": "4", "h": "4", "i": "4", "j": "5", "k": "5",
+ "l": "5", "m": "6", "n": "6", "o": "6", "p": "7", "q": "7", "r": "7", "s": "7", "t": "8", "u": "8", "v": "8",
+ "w": "9", "x": "9", "y": "9", "z": "9",
+}
+
+func filterPhone2numeric(in *Value, param *Value) (*Value, *Error) {
+ sin := in.String()
+ for k, v := range filterPhone2numericMap {
+ sin = strings.Replace(sin, k, v, -1)
+ sin = strings.Replace(sin, strings.ToUpper(k), v, -1)
+ }
+ return AsValue(sin), nil
+}
+
+func filterPluralize(in *Value, param *Value) (*Value, *Error) {
+ if in.IsNumber() {
+ // Works only on numbers
+ if param.Len() > 0 {
+ endings := strings.Split(param.String(), ",")
+ if len(endings) > 2 {
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ if len(endings) == 1 {
+ // 1 argument
+ if in.Integer() != 1 {
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // 2 arguments
+ return AsValue(endings[1]), nil
+ }
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // return default 's'
+ return AsValue("s"), nil
+ }
+ }
+
+ return AsValue(""), nil
+ }
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("filter 'pluralize' does only work on numbers"),
+ }
+}
+
+func filterRandom(in *Value, param *Value) (*Value, *Error) {
+ if !in.CanSlice() || in.Len() <= 0 {
+ return in, nil
+ }
+ i := rand.Intn(in.Len())
+ return in.Index(i), nil
+}
+
+func filterRemovetags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+ tags := strings.Split(param.String(), ",")
+
+ // Strip only specific tags
+ for _, tag := range tags {
+ re := regexp.MustCompile(fmt.Sprintf("?%s/?>", tag))
+ s = re.ReplaceAllString(s, "")
+ }
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+func filterRjust(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(fmt.Sprintf("%%%ds", param.Integer()), in.String())), nil
+}
+
+func filterSlice(in *Value, param *Value) (*Value, *Error) {
+ comp := strings.Split(param.String(), ":")
+ if len(comp) != 2 {
+ return nil, &Error{
+ Sender: "filter:slice",
+ OrigError: errors.New("Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]"),
+ }
+ }
+
+ if !in.CanSlice() {
+ return in, nil
+ }
+
+ from := AsValue(comp[0]).Integer()
+ to := in.Len()
+
+ if from > to {
+ from = to
+ }
+
+ vto := AsValue(comp[1]).Integer()
+ if vto >= from && vto <= in.Len() {
+ to = vto
+ }
+
+ return in.Slice(from, to), nil
+}
+
+func filterTitle(in *Value, param *Value) (*Value, *Error) {
+ if !in.IsString() {
+ return AsValue(""), nil
+ }
+ return AsValue(strings.Title(strings.ToLower(in.String()))), nil
+}
+
+func filterWordcount(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(len(strings.Fields(in.String()))), nil
+}
+
+func filterWordwrap(in *Value, param *Value) (*Value, *Error) {
+ words := strings.Fields(in.String())
+ wordsLen := len(words)
+ wrapAt := param.Integer()
+ if wrapAt <= 0 {
+ return in, nil
+ }
+
+ linecount := wordsLen/wrapAt + wordsLen%wrapAt
+ lines := make([]string, 0, linecount)
+ for i := 0; i < linecount; i++ {
+ lines = append(lines, strings.Join(words[wrapAt*i:min(wrapAt*(i+1), wordsLen)], " "))
+ }
+ return AsValue(strings.Join(lines, "\n")), nil
+}
+
+func filterYesno(in *Value, param *Value) (*Value, *Error) {
+ choices := map[int]string{
+ 0: "yes",
+ 1: "no",
+ 2: "maybe",
+ }
+ paramString := param.String()
+ customChoices := strings.Split(paramString, ",")
+ if len(paramString) > 0 {
+ if len(customChoices) > 3 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+ if len(customChoices) < 2 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+
+ // Map to the options now
+ choices[0] = customChoices[0]
+ choices[1] = customChoices[1]
+ if len(customChoices) == 3 {
+ choices[2] = customChoices[2]
+ }
+ }
+
+ // maybe
+ if in.IsNil() {
+ return AsValue(choices[2]), nil
+ }
+
+ // yes
+ if in.IsTrue() {
+ return AsValue(choices[0]), nil
+ }
+
+ // no
+ return AsValue(choices[1]), nil
+}
diff --git a/vendor/github.com/flosch/pongo2/go.mod b/vendor/github.com/flosch/pongo2/go.mod
new file mode 100644
index 0000000..c9b5531
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/go.mod
@@ -0,0 +1,8 @@
+module github.com/flosch/pongo2
+
+go 1.14
+
+require (
+ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b
+)
diff --git a/vendor/github.com/flosch/pongo2/go.sum b/vendor/github.com/flosch/pongo2/go.sum
new file mode 100644
index 0000000..ee71e85
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/go.sum
@@ -0,0 +1,7 @@
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/vendor/github.com/flosch/pongo2/helpers.go b/vendor/github.com/flosch/pongo2/helpers.go
new file mode 100644
index 0000000..880dbc0
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/helpers.go
@@ -0,0 +1,15 @@
+package pongo2
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/flosch/pongo2/lexer.go b/vendor/github.com/flosch/pongo2/lexer.go
new file mode 100644
index 0000000..f189798
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/lexer.go
@@ -0,0 +1,432 @@
+package pongo2
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+
+ "errors"
+)
+
+const (
+ TokenError = iota
+ EOF
+
+ TokenHTML
+
+ TokenKeyword
+ TokenIdentifier
+ TokenString
+ TokenNumber
+ TokenSymbol
+)
+
+var (
+ tokenSpaceChars = " \n\r\t"
+ tokenIdentifierChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
+ tokenIdentifierCharsWithDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"
+ tokenDigits = "0123456789"
+
+ // Available symbols in pongo2 (within filters/tag)
+ TokenSymbols = []string{
+ // 3-Char symbols
+ "{{-", "-}}", "{%-", "-%}",
+
+ // 2-Char symbols
+ "==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",
+
+ // 1-Char symbol
+ "(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
+ }
+
+ // Available keywords in pongo2
+ TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as", "export"}
+)
+
+type TokenType int
+type Token struct {
+ Filename string
+ Typ TokenType
+ Val string
+ Line int
+ Col int
+ TrimWhitespaces bool
+}
+
+type lexerStateFn func() lexerStateFn
+type lexer struct {
+ name string
+ input string
+ start int // start pos of the item
+ pos int // current pos
+ width int // width of last rune
+ tokens []*Token
+ errored bool
+ startline int
+ startcol int
+ line int
+ col int
+
+ inVerbatim bool
+ verbatimName string
+}
+
+func (t *Token) String() string {
+ val := t.Val
+ if len(val) > 1000 {
+ val = fmt.Sprintf("%s...%s", val[:10], val[len(val)-5:])
+ }
+
+ typ := ""
+ switch t.Typ {
+ case TokenHTML:
+ typ = "HTML"
+ case TokenError:
+ typ = "Error"
+ case TokenIdentifier:
+ typ = "Identifier"
+ case TokenKeyword:
+ typ = "Keyword"
+ case TokenNumber:
+ typ = "Number"
+ case TokenString:
+ typ = "String"
+ case TokenSymbol:
+ typ = "Symbol"
+ default:
+ typ = "Unknown"
+ }
+
+ return fmt.Sprintf("
+ = 40) || (user.karma > calc_avg_karma(userlist)+5) %} class="karma-good"{%
+ endif %}>
+
+
+ {{ user }}
+
+
+
+ Our admins
+ {% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}
+
+ Our members
+ {% for user in userlist %} {{ user_details(user) }} {% endfor %}
+
+
+```
+
+## Features
+
+- Syntax- and feature-set-compatible with [Django 1.7](https://django.readthedocs.io/en/1.7.x/topics/templates.html)
+- [Advanced C-like expressions](https://github.com/flosch/pongo2/blob/master/template_tests/expressions.tpl).
+- [Complex function calls within expressions](https://github.com/flosch/pongo2/blob/master/template_tests/function_calls_wrapper.tpl).
+- [Easy API to create new filters and tags](http://godoc.org/github.com/flosch/pongo2#RegisterFilter) ([including parsing arguments](http://godoc.org/github.com/flosch/pongo2#Parser))
+- Additional features:
+ - Macros including importing macros from other files (see [template_tests/macro.tpl](https://github.com/flosch/pongo2/blob/master/template_tests/macro.tpl))
+ - [Template sandboxing](https://godoc.org/github.com/flosch/pongo2#TemplateSet) ([directory patterns](http://golang.org/pkg/path/filepath/#Match), banned tags/filters)
+
+## Caveats
+
+### Filters
+
+- **date** / **time**: The `date` and `time` filter are taking the Golang specific time- and date-format (not Django's one) currently. [Take a look on the format here](http://golang.org/pkg/time/#Time.Format).
+- **stringformat**: `stringformat` does **not** take Python's string format syntax as a parameter, instead it takes Go's. Essentially `{{ 3.14|stringformat:"pi is %.2f" }}` is `fmt.Sprintf("pi is %.2f", 3.14)`.
+- **escape** / **force_escape**: Unlike Django's behaviour, the `escape`-filter is applied immediately. Therefore there is no need for a `force_escape`-filter yet.
+
+### Tags
+
+- **for**: All the `forloop` fields (like `forloop.counter`) are written with a capital letter at the beginning. For example, the `counter` can be accessed by `forloop.Counter` and the parentloop by `forloop.Parentloop`.
+- **now**: takes Go's time format (see **date** and **time**-filter).
+
+### Misc
+
+- **not in-operator**: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it):
+ `{% if key in map %}Key is in map{% else %}Key not in map{% endif %}` or `{% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}`.
+
+## Add-ons, libraries and helpers
+
+### Official
+
+- [pongo2-addons](https://github.com/flosch/pongo2-addons) - Official additional filters/tags for pongo2 (for example a **markdown**-filter). They are in their own repository because they're relying on 3rd-party-libraries.
+
+### 3rd-party
+
+- [beego-pongo2](https://github.com/oal/beego-pongo2) - A tiny little helper for using Pongo2 with [Beego](https://github.com/astaxie/beego).
+- [beego-pongo2.v2](https://github.com/ipfans/beego-pongo2.v2) - Same as `beego-pongo2`, but for pongo2 v2.
+- [macaron-pongo2](https://github.com/macaron-contrib/pongo2) - pongo2 support for [Macaron](https://github.com/Unknwon/macaron), a modular web framework.
+- [ginpongo2](https://github.com/ngerakines/ginpongo2) - middleware for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [Build'n support for Iris' template engine](https://github.com/kataras/iris)
+- [pongo2gin](https://gitlab.com/go-box/pongo2gin) - alternative renderer for [gin](github.com/gin-gonic/gin) to use pongo2 templates
+- [pongo2-trans](https://github.com/digitalcrab/pongo2trans) - `trans`-tag implementation for internationalization
+- [tpongo2](https://github.com/tango-contrib/tpongo2) - pongo2 support for [Tango](https://github.com/lunny/tango), a micro-kernel & pluggable web framework.
+- [p2cli](https://github.com/wrouesnel/p2cli) - command line templating utility based on pongo2
+- [Pongo2echo](https://github.com/stnc/pongo2echo) - pongo2 echo framework stability renderer [stnc]
+- [Pongo2gin](https://github.com/stnc/pongo2gin) - pongo2 gin minimal framework stability renderer [stnc]
+
+
+Please add your project to this list and send me a pull request when you've developed something nice for pongo2.
+
+## Who's using pongo2
+
+[I'm compiling a list of pongo2 users](https://github.com/flosch/pongo2/issues/241). Add your project or company!
+
+## API-usage examples
+
+Please see the documentation for a full list of provided API methods.
+
+### A tiny example (template string)
+
+```go
+// Compile the template first (i. e. creating the AST)
+tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+if err != nil {
+ panic(err)
+}
+// Now you can render the template with the given
+// pongo2.Context how often you want to.
+out, err := tpl.Execute(pongo2.Context{"name": "florian"})
+if err != nil {
+ panic(err)
+}
+fmt.Println(out) // Output: Hello Florian!
+```
+
+## Example server-usage (template file)
+
+```go
+package main
+
+import (
+ "github.com/flosch/pongo2/v4"
+ "net/http"
+)
+
+// Pre-compiling the templates at application startup using the
+// little Must()-helper function (Must() will panic if FromFile()
+// or FromString() will return with an error - that's it).
+// It's faster to pre-compile it anywhere at startup and only
+// execute the template later.
+var tplExample = pongo2.Must(pongo2.FromFile("example.html"))
+
+func examplePage(w http.ResponseWriter, r *http.Request) {
+ // Execute the template per HTTP request
+ err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func main() {
+ http.HandleFunc("/", examplePage)
+ http.ListenAndServe(":8080", nil)
+}
+```
diff --git a/vendor/github.com/flosch/pongo2/v4/context.go b/vendor/github.com/flosch/pongo2/v4/context.go
new file mode 100644
index 0000000..dbc5e3e
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/context.go
@@ -0,0 +1,137 @@
+package pongo2
+
+import (
+ "fmt"
+ "regexp"
+
+ "errors"
+)
+
+var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$")
+
+var autoescape = true
+
+func SetAutoescape(newValue bool) {
+ autoescape = newValue
+}
+
+// A Context type provides constants, variables, instances or functions to a template.
+//
+// pongo2 automatically provides meta-information or functions through the "pongo2"-key.
+// Currently, context["pongo2"] contains the following keys:
+// 1. version: returns the version string
+//
+// Template examples for accessing items from your context:
+// {{ myconstant }}
+// {{ myfunc("test", 42) }}
+// {{ user.name }}
+// {{ pongo2.version }}
+type Context map[string]interface{}
+
+func (c Context) checkForValidIdentifiers() *Error {
+ for k, v := range c {
+ if !reIdentifiers.MatchString(k) {
+ return &Error{
+ Sender: "checkForValidIdentifiers",
+ OrigError: fmt.Errorf("context-key '%s' (value: '%+v') is not a valid identifier", k, v),
+ }
+ }
+ }
+ return nil
+}
+
+// Update updates this context with the key/value-pairs from another context.
+func (c Context) Update(other Context) Context {
+ for k, v := range other {
+ c[k] = v
+ }
+ return c
+}
+
+// ExecutionContext contains all data important for the current rendering state.
+//
+// If you're writing a custom tag, your tag's Execute()-function will
+// have access to the ExecutionContext. This struct stores anything
+// about the current rendering process's Context including
+// the Context provided by the user (field Public).
+// You can safely use the Private context to provide data to the user's
+// template (like a 'forloop'-information). The Shared-context is used
+// to share data between tags. All ExecutionContexts share this context.
+//
+// Please be careful when accessing the Public data.
+// PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only).
+//
+// To create your own execution context within tags, use the
+// NewChildExecutionContext(parent) function.
+type ExecutionContext struct {
+ template *Template
+
+ Autoescape bool
+ Public Context
+ Private Context
+ Shared Context
+}
+
+var pongo2MetaContext = Context{
+ "version": Version,
+}
+
+func newExecutionContext(tpl *Template, ctx Context) *ExecutionContext {
+ privateCtx := make(Context)
+
+ // Make the pongo2-related funcs/vars available to the context
+ privateCtx["pongo2"] = pongo2MetaContext
+
+ return &ExecutionContext{
+ template: tpl,
+
+ Public: ctx,
+ Private: privateCtx,
+ Autoescape: autoescape,
+ }
+}
+
+func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext {
+ newctx := &ExecutionContext{
+ template: parent.template,
+
+ Public: parent.Public,
+ Private: make(Context),
+ Autoescape: parent.Autoescape,
+ }
+ newctx.Shared = parent.Shared
+
+ // Copy all existing private items
+ newctx.Private.Update(parent.Private)
+
+ return newctx
+}
+
+func (ctx *ExecutionContext) Error(msg string, token *Token) *Error {
+ return ctx.OrigError(errors.New(msg), token)
+}
+
+func (ctx *ExecutionContext) OrigError(err error, token *Token) *Error {
+ filename := ctx.template.name
+ var line, col int
+ if token != nil {
+ // No tokens available
+ // TODO: Add location (from where?)
+ filename = token.Filename
+ line = token.Line
+ col = token.Col
+ }
+ return &Error{
+ Template: ctx.template,
+ Filename: filename,
+ Line: line,
+ Column: col,
+ Token: token,
+ Sender: "execution",
+ OrigError: err,
+ }
+}
+
+func (ctx *ExecutionContext) Logf(format string, args ...interface{}) {
+ ctx.template.set.logf(format, args...)
+}
diff --git a/vendor/github.com/flosch/pongo2/v4/doc.go b/vendor/github.com/flosch/pongo2/v4/doc.go
new file mode 100644
index 0000000..0a161de
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/doc.go
@@ -0,0 +1,31 @@
+// Package pongo2 is a Django-syntax like template-engine
+//
+// Blog posts about pongo2 (including introduction and migration):
+// https://www.florian-schlachter.de/?tag=pongo2
+//
+// Complete documentation on the template language:
+// https://docs.djangoproject.com/en/dev/topics/templates/
+//
+// Try out pongo2 live in the pongo2 playground:
+// https://www.florian-schlachter.de/pongo2/
+//
+// Make sure to read README.md in the repository as well.
+//
+// A tiny example with template strings:
+//
+// (Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277)
+//
+// // Compile the template first (i. e. creating the AST)
+// tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
+// if err != nil {
+// panic(err)
+// }
+// // Now you can render the template with the given
+// // pongo2.Context how often you want to.
+// out, err := tpl.Execute(pongo2.Context{"name": "fred"})
+// if err != nil {
+// panic(err)
+// }
+// fmt.Println(out) // Output: Hello Fred!
+//
+package pongo2
diff --git a/vendor/github.com/flosch/pongo2/v4/error.go b/vendor/github.com/flosch/pongo2/v4/error.go
new file mode 100644
index 0000000..8aec8c1
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/error.go
@@ -0,0 +1,91 @@
+package pongo2
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+// The Error type is being used to address an error during lexing, parsing or
+// execution. If you want to return an error object (for example in your own
+// tag or filter) fill this object with as much information as you have.
+// Make sure "Sender" is always given (if you're returning an error within
+// a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag').
+// It's okay if you only fill in ErrorMsg if you don't have any other details at hand.
+type Error struct {
+ Template *Template
+ Filename string
+ Line int
+ Column int
+ Token *Token
+ Sender string
+ OrigError error
+}
+
+func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error {
+ if e.Template == nil {
+ e.Template = template
+ }
+
+ if e.Token == nil {
+ e.Token = t
+ if e.Line <= 0 {
+ e.Line = t.Line
+ e.Column = t.Col
+ }
+ }
+
+ return e
+}
+
+// Returns a nice formatted error string.
+func (e *Error) Error() string {
+ s := "[Error"
+ if e.Sender != "" {
+ s += " (where: " + e.Sender + ")"
+ }
+ if e.Filename != "" {
+ s += " in " + e.Filename
+ }
+ if e.Line > 0 {
+ s += fmt.Sprintf(" | Line %d Col %d", e.Line, e.Column)
+ if e.Token != nil {
+ s += fmt.Sprintf(" near '%s'", e.Token.Val)
+ }
+ }
+ s += "] "
+ s += e.OrigError.Error()
+ return s
+}
+
+// RawLine returns the affected line from the original template, if available.
+func (e *Error) RawLine() (line string, available bool, outErr error) {
+ if e.Line <= 0 || e.Filename == "
+ // Double newline =
")
+ }
+ }
+ }
+
+ if opened {
+ b.WriteString("")
+ }
+
+ return AsValue(b.String()), nil
+}
+
+func filterSplit(in *Value, param *Value) (*Value, *Error) {
+ chunks := strings.Split(in.String(), param.String())
+
+ return AsValue(chunks), nil
+}
+
+func filterLinebreaksbr(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(strings.Replace(in.String(), "\n", "
", -1)), nil
+}
+
+func filterLinenumbers(in *Value, param *Value) (*Value, *Error) {
+ lines := strings.Split(in.String(), "\n")
+ output := make([]string, 0, len(lines))
+ for idx, line := range lines {
+ output = append(output, fmt.Sprintf("%d. %s", idx+1, line))
+ }
+ return AsValue(strings.Join(output, "\n")), nil
+}
+
+func filterLjust(in *Value, param *Value) (*Value, *Error) {
+ times := param.Integer() - in.Len()
+ if times < 0 {
+ times = 0
+ }
+ return AsValue(fmt.Sprintf("%s%s", in.String(), strings.Repeat(" ", times))), nil
+}
+
+func filterUrlencode(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(url.QueryEscape(in.String())), nil
+}
+
+// TODO: This regexp could do some work
+var filterUrlizeURLRegexp = regexp.MustCompile(`((((http|https)://)|www\.|((^|[ ])[0-9A-Za-z_\-]+(\.com|\.net|\.org|\.info|\.biz|\.de))))(?U:.*)([ ]+|$)`)
+var filterUrlizeEmailRegexp = regexp.MustCompile(`(\w+@\w+\.\w{2,4})`)
+
+func filterUrlizeHelper(input string, autoescape bool, trunc int) (string, error) {
+ var soutErr error
+ sout := filterUrlizeURLRegexp.ReplaceAllStringFunc(input, func(raw_url string) string {
+ var prefix string
+ var suffix string
+ if strings.HasPrefix(raw_url, " ") {
+ prefix = " "
+ }
+ if strings.HasSuffix(raw_url, " ") {
+ suffix = " "
+ }
+
+ raw_url = strings.TrimSpace(raw_url)
+
+ t, err := ApplyFilter("iriencode", AsValue(raw_url), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ url := t.String()
+
+ if !strings.HasPrefix(url, "http") {
+ url = fmt.Sprintf("http://%s", url)
+ }
+
+ title := raw_url
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ if autoescape {
+ t, err := ApplyFilter("escape", AsValue(title), nil)
+ if err != nil {
+ soutErr = err
+ return ""
+ }
+ title = t.String()
+ }
+
+ return fmt.Sprintf(`%s%s%s`, prefix, url, title, suffix)
+ })
+ if soutErr != nil {
+ return "", soutErr
+ }
+
+ sout = filterUrlizeEmailRegexp.ReplaceAllStringFunc(sout, func(mail string) string {
+ title := mail
+
+ if trunc > 3 && len(title) > trunc {
+ title = fmt.Sprintf("%s...", title[:trunc-3])
+ }
+
+ return fmt.Sprintf(`%s`, mail, title)
+ })
+
+ return sout, nil
+}
+
+func filterUrlize(in *Value, param *Value) (*Value, *Error) {
+ autoescape := true
+ if param.IsBool() {
+ autoescape = param.Bool()
+ }
+
+ s, err := filterUrlizeHelper(in.String(), autoescape, -1)
+ if err != nil {
+
+ }
+
+ return AsValue(s), nil
+}
+
+func filterUrlizetrunc(in *Value, param *Value) (*Value, *Error) {
+ s, err := filterUrlizeHelper(in.String(), true, param.Integer())
+ if err != nil {
+ return nil, &Error{
+ Sender: "filter:urlizetrunc",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ return AsValue(s), nil
+}
+
+func filterStringformat(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(param.String(), in.Interface())), nil
+}
+
+var reStriptags = regexp.MustCompile("<[^>]*?>")
+
+func filterStriptags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+
+ // Strip all tags
+ s = reStriptags.ReplaceAllString(s, "")
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+// https://en.wikipedia.org/wiki/Phoneword
+var filterPhone2numericMap = map[string]string{
+ "a": "2", "b": "2", "c": "2", "d": "3", "e": "3", "f": "3", "g": "4", "h": "4", "i": "4", "j": "5", "k": "5",
+ "l": "5", "m": "6", "n": "6", "o": "6", "p": "7", "q": "7", "r": "7", "s": "7", "t": "8", "u": "8", "v": "8",
+ "w": "9", "x": "9", "y": "9", "z": "9",
+}
+
+func filterPhone2numeric(in *Value, param *Value) (*Value, *Error) {
+ sin := in.String()
+ for k, v := range filterPhone2numericMap {
+ sin = strings.Replace(sin, k, v, -1)
+ sin = strings.Replace(sin, strings.ToUpper(k), v, -1)
+ }
+ return AsValue(sin), nil
+}
+
+func filterPluralize(in *Value, param *Value) (*Value, *Error) {
+ if in.IsNumber() {
+ // Works only on numbers
+ if param.Len() > 0 {
+ endings := strings.Split(param.String(), ",")
+ if len(endings) > 2 {
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("you cannot pass more than 2 arguments to filter 'pluralize'"),
+ }
+ }
+ if len(endings) == 1 {
+ // 1 argument
+ if in.Integer() != 1 {
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // 2 arguments
+ return AsValue(endings[1]), nil
+ }
+ return AsValue(endings[0]), nil
+ }
+ } else {
+ if in.Integer() != 1 {
+ // return default 's'
+ return AsValue("s"), nil
+ }
+ }
+
+ return AsValue(""), nil
+ }
+ return nil, &Error{
+ Sender: "filter:pluralize",
+ OrigError: errors.New("filter 'pluralize' does only work on numbers"),
+ }
+}
+
+func filterRandom(in *Value, param *Value) (*Value, *Error) {
+ if !in.CanSlice() || in.Len() <= 0 {
+ return in, nil
+ }
+ i := rand.Intn(in.Len())
+ return in.Index(i), nil
+}
+
+func filterRemovetags(in *Value, param *Value) (*Value, *Error) {
+ s := in.String()
+ tags := strings.Split(param.String(), ",")
+
+ // Strip only specific tags
+ for _, tag := range tags {
+ re := regexp.MustCompile(fmt.Sprintf("?%s/?>", tag))
+ s = re.ReplaceAllString(s, "")
+ }
+
+ return AsValue(strings.TrimSpace(s)), nil
+}
+
+func filterRjust(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(fmt.Sprintf(fmt.Sprintf("%%%ds", param.Integer()), in.String())), nil
+}
+
+func filterSlice(in *Value, param *Value) (*Value, *Error) {
+ comp := strings.Split(param.String(), ":")
+ if len(comp) != 2 {
+ return nil, &Error{
+ Sender: "filter:slice",
+ OrigError: errors.New("Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]"),
+ }
+ }
+
+ if !in.CanSlice() {
+ return in, nil
+ }
+
+ from := AsValue(comp[0]).Integer()
+ to := in.Len()
+
+ if from > to {
+ from = to
+ }
+
+ vto := AsValue(comp[1]).Integer()
+ if vto >= from && vto <= in.Len() {
+ to = vto
+ }
+
+ return in.Slice(from, to), nil
+}
+
+func filterTitle(in *Value, param *Value) (*Value, *Error) {
+ if !in.IsString() {
+ return AsValue(""), nil
+ }
+ return AsValue(strings.Title(strings.ToLower(in.String()))), nil
+}
+
+func filterWordcount(in *Value, param *Value) (*Value, *Error) {
+ return AsValue(len(strings.Fields(in.String()))), nil
+}
+
+func filterWordwrap(in *Value, param *Value) (*Value, *Error) {
+ words := strings.Fields(in.String())
+ wordsLen := len(words)
+ wrapAt := param.Integer()
+ if wrapAt <= 0 {
+ return in, nil
+ }
+
+ linecount := wordsLen/wrapAt + wordsLen%wrapAt
+ lines := make([]string, 0, linecount)
+ for i := 0; i < linecount; i++ {
+ lines = append(lines, strings.Join(words[wrapAt*i:min(wrapAt*(i+1), wordsLen)], " "))
+ }
+ return AsValue(strings.Join(lines, "\n")), nil
+}
+
+func filterYesno(in *Value, param *Value) (*Value, *Error) {
+ choices := map[int]string{
+ 0: "yes",
+ 1: "no",
+ 2: "maybe",
+ }
+ paramString := param.String()
+ customChoices := strings.Split(paramString, ",")
+ if len(paramString) > 0 {
+ if len(customChoices) > 3 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+ if len(customChoices) < 2 {
+ return nil, &Error{
+ Sender: "filter:yesno",
+ OrigError: fmt.Errorf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", paramString),
+ }
+ }
+
+ // Map to the options now
+ choices[0] = customChoices[0]
+ choices[1] = customChoices[1]
+ if len(customChoices) == 3 {
+ choices[2] = customChoices[2]
+ }
+ }
+
+ // maybe
+ if in.IsNil() {
+ return AsValue(choices[2]), nil
+ }
+
+ // yes
+ if in.IsTrue() {
+ return AsValue(choices[0]), nil
+ }
+
+ // no
+ return AsValue(choices[1]), nil
+}
diff --git a/vendor/github.com/flosch/pongo2/v4/go.mod b/vendor/github.com/flosch/pongo2/v4/go.mod
new file mode 100644
index 0000000..e3cb72e
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/go.mod
@@ -0,0 +1,8 @@
+module github.com/flosch/pongo2/v4
+
+go 1.14
+
+require (
+ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b
+)
diff --git a/vendor/github.com/flosch/pongo2/v4/go.sum b/vendor/github.com/flosch/pongo2/v4/go.sum
new file mode 100644
index 0000000..ee71e85
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/go.sum
@@ -0,0 +1,7 @@
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/vendor/github.com/flosch/pongo2/v4/helpers.go b/vendor/github.com/flosch/pongo2/v4/helpers.go
new file mode 100644
index 0000000..880dbc0
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/helpers.go
@@ -0,0 +1,15 @@
+package pongo2
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/flosch/pongo2/v4/lexer.go b/vendor/github.com/flosch/pongo2/v4/lexer.go
new file mode 100644
index 0000000..f189798
--- /dev/null
+++ b/vendor/github.com/flosch/pongo2/v4/lexer.go
@@ -0,0 +1,432 @@
+package pongo2
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+
+ "errors"
+)
+
+const (
+ TokenError = iota
+ EOF
+
+ TokenHTML
+
+ TokenKeyword
+ TokenIdentifier
+ TokenString
+ TokenNumber
+ TokenSymbol
+)
+
+var (
+ tokenSpaceChars = " \n\r\t"
+ tokenIdentifierChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
+ tokenIdentifierCharsWithDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"
+ tokenDigits = "0123456789"
+
+ // Available symbols in pongo2 (within filters/tag)
+ TokenSymbols = []string{
+ // 3-Char symbols
+ "{{-", "-}}", "{%-", "-%}",
+
+ // 2-Char symbols
+ "==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",
+
+ // 1-Char symbol
+ "(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
+ }
+
+ // Available keywords in pongo2
+ TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as", "export"}
+)
+
+type TokenType int
+type Token struct {
+ Filename string
+ Typ TokenType
+ Val string
+ Line int
+ Col int
+ TrimWhitespaces bool
+}
+
+type lexerStateFn func() lexerStateFn
+type lexer struct {
+ name string
+ input string
+ start int // start pos of the item
+ pos int // current pos
+ width int // width of last rune
+ tokens []*Token
+ errored bool
+ startline int
+ startcol int
+ line int
+ col int
+
+ inVerbatim bool
+ verbatimName string
+}
+
+func (t *Token) String() string {
+ val := t.Val
+ if len(val) > 1000 {
+ val = fmt.Sprintf("%s...%s", val[:10], val[len(val)-5:])
+ }
+
+ typ := ""
+ switch t.Typ {
+ case TokenHTML:
+ typ = "HTML"
+ case TokenError:
+ typ = "Error"
+ case TokenIdentifier:
+ typ = "Identifier"
+ case TokenKeyword:
+ typ = "Keyword"
+ case TokenNumber:
+ typ = "Number"
+ case TokenString:
+ typ = "String"
+ case TokenSymbol:
+ typ = "Symbol"
+ default:
+ typ = "Unknown"
+ }
+
+ return fmt.Sprintf("json', 'contT21: RSSloopasiamoonsoulLINEfortcartT14:
80px!--<9px;T04:mike:46ZniceinchYorkricezh:ä'));puremageparatonebond:37Z_of_']);000,zh:çtankyardbowlbush:56ZJava30px
+|}
+%C3%:34ZjeffEXPIcashvisagolfsnowzh:équer.csssickmeatmin.binddellhirepicsrent:36ZHTTP-201fotowolfEND xbox:54ZBODYdick;
+}
+exit:35Zvarsbeat'});diet999;anne}}[i].Langkm²wiretoysaddssealalex;
+ }echonine.org005)tonyjewssandlegsroof000) 200winegeardogsbootgarycutstyletemption.xmlcockgang$('.50pxPh.Dmiscalanloandeskmileryanunixdisc);}
+dustclip).
+
+70px-200DVDs7]>
+_form"leavesstress" />
+.gif" onloadloaderOxfordsistersurvivlistenfemaleDesignsize="appealtext">levelsthankshigherforcedanimalanyoneAfricaagreedrecentPeople
wonderpricesturned|| {};main">inlinesundaywrap">failedcensusminutebeaconquotes150px|estateremoteemail"linkedright;signalformal1.htmlsignupprincefloat:.png" forum.AccesspaperssoundsextendHeightsliderUTF-8"& Before. WithstudioownersmanageprofitjQueryannualparamsboughtfamousgooglelongeri++) {israelsayingdecidehome">headerensurebranchpiecesblock;statedtop">