├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── _examples ├── iris_multi │ ├── main.go │ └── templates │ │ ├── hiAMBER.amber │ │ └── hiHTML.html ├── nethttp_html │ ├── main.go │ └── templates │ │ ├── layouts │ │ └── layout.html │ │ └── mypage.html └── nethttp_multi │ ├── main.go │ └── templates │ ├── hiAMBER.amber │ └── hiHTML.html ├── amber ├── README.md ├── amber.go └── config.go ├── django ├── README.md ├── config.go └── django.go ├── engine.go ├── handlebars ├── README.md ├── config.go └── handlebars.go ├── html ├── README.md ├── config.go └── html.go ├── loader.go ├── logo.jpg ├── markdown ├── README.md ├── config.go └── markdown.go ├── mux.go ├── pug ├── README.md ├── config.go └── pug.go ├── template.go └── vendor └── github.com ├── Joker └── jade │ ├── LICENSE.md │ ├── config.go │ ├── jade_lex.go │ ├── jade_node.go │ ├── jade_parse.go │ ├── lex.go │ ├── node.go │ ├── parse.go │ └── template.go ├── aymerick └── raymond │ ├── LICENSE │ ├── VERSION │ ├── ast │ ├── node.go │ └── print.go │ ├── data_frame.go │ ├── escape.go │ ├── eval.go │ ├── handlebars │ └── doc.go │ ├── helper.go │ ├── lexer │ ├── lexer.go │ └── token.go │ ├── mustache │ ├── Changes │ ├── Rakefile │ ├── TESTING.md │ └── specs │ │ ├── comments.json │ │ ├── comments.yml │ │ ├── delimiters.json │ │ ├── delimiters.yml │ │ ├── interpolation.json │ │ ├── interpolation.yml │ │ ├── inverted.json │ │ ├── inverted.yml │ │ ├── partials.json │ │ ├── partials.yml │ │ ├── sections.json │ │ ├── sections.yml │ │ ├── ~lambdas.json │ │ └── ~lambdas.yml │ ├── parser │ ├── parser.go │ └── whitespace.go │ ├── partial.go │ ├── raymond.go │ ├── string.go │ ├── template.go │ └── utils.go ├── eknkc └── amber │ ├── amberc │ └── cli.go │ ├── compiler.go │ ├── parser │ ├── nodes.go │ ├── parser.go │ └── scanner.go │ └── runtime.go ├── flosch └── pongo2 │ ├── AUTHORS │ ├── LICENSE │ ├── context.go │ ├── doc.go │ ├── error.go │ ├── filters.go │ ├── filters_builtin.go │ ├── helpers.go │ ├── lexer.go │ ├── nodes.go │ ├── nodes_html.go │ ├── nodes_wrapper.go │ ├── parser.go │ ├── parser_document.go │ ├── parser_expression.go │ ├── pongo2.go │ ├── tags.go │ ├── tags_autoescape.go │ ├── tags_block.go │ ├── tags_comment.go │ ├── tags_cycle.go │ ├── tags_extends.go │ ├── tags_filter.go │ ├── tags_firstof.go │ ├── tags_for.go │ ├── tags_if.go │ ├── tags_ifchanged.go │ ├── tags_ifequal.go │ ├── tags_ifnotequal.go │ ├── tags_import.go │ ├── tags_include.go │ ├── tags_lorem.go │ ├── tags_macro.go │ ├── tags_now.go │ ├── tags_set.go │ ├── tags_spaceless.go │ ├── tags_ssi.go │ ├── tags_templatetag.go │ ├── tags_widthratio.go │ ├── tags_with.go │ ├── template.go │ ├── template_loader.go │ ├── template_sets.go │ ├── value.go │ └── variable.go ├── juju └── errors │ ├── LICENSE │ ├── Makefile │ ├── doc.go │ ├── error.go │ ├── errortypes.go │ ├── functions.go │ └── path.go ├── kataras └── go-errors │ ├── LICENSE │ └── errors.go ├── microcosm-cc └── bluemonday │ ├── LICENCE.md │ ├── Makefile │ ├── cmd │ ├── sanitise_html_email │ │ ├── doc.go │ │ └── main.go │ └── sanitise_ugc │ │ ├── doc.go │ │ └── main.go │ ├── doc.go │ ├── helpers.go │ ├── policies.go │ ├── policy.go │ └── sanitize.go ├── russross └── blackfriday │ ├── LICENSE.txt │ ├── README.md │ ├── block.go │ ├── html.go │ ├── inline.go │ ├── latex.go │ ├── markdown.go │ └── smartypants.go ├── shurcooL └── sanitized_anchor_name │ ├── LICENSE │ └── main.go └── valyala └── bytebufferpool ├── LICENSE ├── bytebuffer.go ├── bytebuffer_test.go └── pool.go /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .settings/* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - go1.7 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gerasimos Maropoulos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_examples/iris_multi/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopkg.in/kataras/iris.v6" 5 | "gopkg.in/kataras/iris.v6/adaptors/httprouter" 6 | "gopkg.in/kataras/iris.v6/adaptors/view" 7 | ) 8 | 9 | type mypage struct { 10 | Title string 11 | Message string 12 | } 13 | 14 | func main() { 15 | app := iris.New() 16 | app.Adapt(iris.DevLogger()) 17 | app.Adapt(httprouter.New()) 18 | 19 | // the html engine on ./templates folder compile all *.html files 20 | app.Adapt(view.HTML("./templates", ".html")) 21 | 22 | // add our second template engine with the same directory but with .amber file extension 23 | app.Adapt(view.Amber("./templates", ".amber")) 24 | 25 | app.Get("/render_html", func(ctx *iris.Context) { 26 | ctx.RenderWithStatus(iris.StatusOK, "hiHTML.html", map[string]interface{}{"Name": "You!"}) 27 | }) 28 | 29 | app.Get("/render_amber", func(ctx *iris.Context) { 30 | ctx.MustRender("hiAMBER.amber", map[string]interface{}{"Name": "You!"}) 31 | }) 32 | 33 | println("Open a browser tab & go to localhost:8080/render_html & localhost:8080/render_amber") 34 | app.Listen(":8080") 35 | } 36 | 37 | // More can be found there: 38 | // https://github.com/iris-contrib/examples/tree/master/template_engines 39 | -------------------------------------------------------------------------------- /_examples/iris_multi/templates/hiAMBER.amber: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html 3 | head 4 | title Hello Amber from Go-Template 5 | 6 | meta[name="description"][value="This is a sample"] 7 | 8 | script[type="text/javascript"] 9 | var hw = "Hello #{Name}!" 10 | alert(hw) 11 | 12 | style[type="text/css"] 13 | body { 14 | background: maroon; 15 | color: white 16 | } 17 | 18 | body 19 | header#mainHeader 20 | ul 21 | li.active 22 | a[href="/"] Main Page 23 | [title="Main Page"] 24 | h1 25 | | Hi #{Name} 26 | 27 | footer 28 | | Hey 29 | br 30 | | There 31 | -------------------------------------------------------------------------------- /_examples/iris_multi/templates/hiHTML.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hi 4 | 5 | 6 |

Hi {{.Name}} 7 | 8 | 9 | -------------------------------------------------------------------------------- /_examples/nethttp_html/main.go: -------------------------------------------------------------------------------- 1 | // Package main is a small example for html/template, same for all other 2 | package main 3 | 4 | import ( 5 | "net/http" 6 | 7 | "github.com/kataras/go-template" 8 | "github.com/kataras/go-template/html" 9 | ) 10 | 11 | type mypage struct { 12 | Title string 13 | Message string 14 | } 15 | 16 | func main() { 17 | // create our html template engine, using a Layout 18 | tmplEngine := html.New(html.Config{Layout: "layouts/layout.html"}) 19 | 20 | loader := template.NewLoader() 21 | loader.Directory("./templates", ".html") // defaults 22 | // for binary assets: 23 | // loader.Directory(dir string, ext string).Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) 24 | 25 | // load the templates inside ./templates with extension .html 26 | //using the html template engine 27 | err := loader.LoadEngine(tmplEngine) 28 | if err != nil { 29 | panic("While parsing the template files: " + err.Error()) 30 | } 31 | 32 | http.Handle("/", http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { 33 | // first parameter the writer 34 | // second parameter any page context (look ./templates/mypage.html) and you will understand 35 | // third parameter is optionally, is a map[string]interface{} 36 | // which you can pass a "layout" to change the layout for this specific render action 37 | // or the "charset" to change the defaults which is "utf-8" 38 | err := tmplEngine.ExecuteWriter(res, "mypage.html", mypage{"My Page title", "Hello world!"}) 39 | if err != nil { 40 | res.Write([]byte(err.Error())) 41 | } 42 | })) 43 | 44 | println("Open a browser tab & go to localhost:8080") 45 | http.ListenAndServe(":8080", nil) 46 | } 47 | -------------------------------------------------------------------------------- /_examples/nethttp_html/templates/layouts/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | My Layout 4 | 5 | 6 | 7 |

Body is:

8 | 9 | {{ yield }} 10 | 11 | 12 | -------------------------------------------------------------------------------- /_examples/nethttp_html/templates/mypage.html: -------------------------------------------------------------------------------- 1 |

2 | Title: {{.Title}} 3 |

4 |

Message: {{.Message}}

5 | -------------------------------------------------------------------------------- /_examples/nethttp_multi/main.go: -------------------------------------------------------------------------------- 1 | // Package main uses the template.Mux here to simplify the steps 2 | // and support of loading more than one template engine for a single app 3 | // you can do the same things without the Mux, as you saw on the /html example folder 4 | package main 5 | 6 | import ( 7 | "net/http" 8 | 9 | "github.com/kataras/go-template" 10 | "github.com/kataras/go-template/amber" 11 | "github.com/kataras/go-template/html" 12 | ) 13 | 14 | type mypage struct { 15 | Title string 16 | Message string 17 | } 18 | 19 | func main() { 20 | // templates := template.NewMux() 21 | // templates.AddEngine(tmplEngine).Directory("./templates", ".html") // the defaults 22 | // or just use the default package-level mux 23 | // defaults to dir "./templates" and ".html" as extension, you can change it by .Directory("./mydir", ".html") 24 | template.AddEngine(html.New()) 25 | // set our second template engine with the same directory but with .amber file extension 26 | template.AddEngine(amber.New()).Directory("./templates", ".amber") 27 | 28 | // load all the template files using the correct template engine for each one of the files 29 | err := template.Load() 30 | if err != nil { 31 | panic("While parsing the template files: " + err.Error()) 32 | } 33 | 34 | http.Handle("/render_html", http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { 35 | // first parameter the writer 36 | // second parameter any page context (look ./templates/mypage.html) and you will understand 37 | // third parameter is optionally, is a map[string]interface{} 38 | // which you can pass a "layout" to change the layout for this specific render action 39 | // or the "charset" to change the defaults which is "utf-8" 40 | 41 | // Does the same thing but returns the parsed template file results as string 42 | // useful when you want to send rich e-mails with a template 43 | // template.ExecuteString(name, pageContext, options...) 44 | 45 | err := template.ExecuteWriter(res, "hiHTML.html", map[string]interface{}{"Name": "You!"}) // yes you can pass simple maps instead of structs 46 | if err != nil { 47 | res.Write([]byte(err.Error())) 48 | } 49 | })) 50 | 51 | http.Handle("/render_amber", http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { 52 | template.ExecuteWriter(res, "hiAMBER.amber", map[string]interface{}{"Name": "You!"}) 53 | })) 54 | 55 | println("Open a browser tab & go to localhost:8080/render_html & localhost:8080/render_amber") 56 | http.ListenAndServe(":8080", nil) 57 | } 58 | -------------------------------------------------------------------------------- /_examples/nethttp_multi/templates/hiAMBER.amber: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html 3 | head 4 | title Hello Amber from Go-Template 5 | 6 | meta[name="description"][value="This is a sample"] 7 | 8 | script[type="text/javascript"] 9 | var hw = "Hello #{Name}!" 10 | alert(hw) 11 | 12 | style[type="text/css"] 13 | body { 14 | background: maroon; 15 | color: white 16 | } 17 | 18 | body 19 | header#mainHeader 20 | ul 21 | li.active 22 | a[href="/"] Main Page 23 | [title="Main Page"] 24 | h1 25 | | Hi #{Name} 26 | 27 | footer 28 | | Hey 29 | br 30 | | There 31 | -------------------------------------------------------------------------------- /_examples/nethttp_multi/templates/hiHTML.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hi 4 | 5 | 6 |

Hi {{.Name}} 7 | 8 | 9 | -------------------------------------------------------------------------------- /amber/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the buit'n [amber](https://github.com/eknkc/amber) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /amber/amber.go: -------------------------------------------------------------------------------- 1 | package amber 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "io" 7 | "path/filepath" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/eknkc/amber" 12 | ) 13 | 14 | // Engine the amber template engine 15 | type Engine struct { 16 | Config Config 17 | templateCache map[string]*template.Template 18 | mu sync.Mutex 19 | } 20 | 21 | // New creates and returns a new amber engine 22 | func New(cfg ...Config) *Engine { 23 | c := DefaultConfig() 24 | if len(cfg) > 0 { 25 | c = cfg[0] 26 | } 27 | 28 | // cuz mergo has a little bug on maps 29 | if c.Funcs == nil { 30 | c.Funcs = make(map[string]interface{}, 0) 31 | } 32 | 33 | return &Engine{Config: c} 34 | } 35 | 36 | // Funcs should returns the helper funcs 37 | func (e *Engine) Funcs() map[string]interface{} { 38 | return e.Config.Funcs 39 | } 40 | 41 | // LoadDirectory builds the amber templates 42 | func (e *Engine) LoadDirectory(directory string, extension string) error { 43 | opt := amber.DirOptions{} 44 | opt.Recursive = true 45 | 46 | // prepare the global amber funcs 47 | funcs := template.FuncMap{} 48 | 49 | for k, v := range amber.FuncMap { // add the amber's default funcs 50 | funcs[k] = v 51 | } 52 | 53 | if e.Config.Funcs != nil { // add the config's funcs 54 | for k, v := range e.Config.Funcs { 55 | funcs[k] = v 56 | } 57 | } 58 | 59 | amber.FuncMap = funcs //set the funcs 60 | opt.Ext = extension 61 | 62 | templates, err := amber.CompileDir(directory, opt, amber.DefaultOptions) // this returns the map with stripped extension, we want extension so we copy the map 63 | if err == nil { 64 | e.mu.Lock() 65 | defer e.mu.Unlock() 66 | e.templateCache = make(map[string]*template.Template) 67 | for k, v := range templates { 68 | name := filepath.ToSlash(k + opt.Ext) 69 | e.templateCache[name] = v 70 | delete(templates, k) 71 | } 72 | 73 | } 74 | return err 75 | } 76 | 77 | // LoadAssets builds the templates from binary assets 78 | func (e *Engine) LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error { 79 | e.templateCache = make(map[string]*template.Template) 80 | // prepare the global amber funcs 81 | funcs := template.FuncMap{} 82 | 83 | for k, v := range amber.FuncMap { // add the amber's default funcs 84 | funcs[k] = v 85 | } 86 | 87 | if e.Config.Funcs != nil { // add the config's funcs 88 | for k, v := range e.Config.Funcs { 89 | funcs[k] = v 90 | } 91 | } 92 | if len(virtualDirectory) > 0 { 93 | if virtualDirectory[0] == '.' { // first check for .wrong 94 | virtualDirectory = virtualDirectory[1:] 95 | } 96 | if virtualDirectory[0] == '/' || virtualDirectory[0] == filepath.Separator { // second check for /something, (or ./something if we had dot on 0 it will be removed 97 | virtualDirectory = virtualDirectory[1:] 98 | } 99 | } 100 | amber.FuncMap = funcs //set the funcs 101 | 102 | e.mu.Lock() 103 | defer e.mu.Unlock() 104 | 105 | names := namesFn() 106 | 107 | for _, path := range names { 108 | if !strings.HasPrefix(path, virtualDirectory) { 109 | continue 110 | } 111 | ext := filepath.Ext(path) 112 | if ext == virtualExtension { 113 | 114 | rel, err := filepath.Rel(virtualDirectory, path) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | buf, err := assetFn(path) 120 | if err != nil { 121 | return err 122 | } 123 | 124 | name := filepath.ToSlash(rel) 125 | tmpl, err := amber.CompileData(buf, name, amber.DefaultOptions) 126 | 127 | if err != nil { 128 | return err 129 | } 130 | 131 | e.templateCache[name] = tmpl 132 | } 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func (e *Engine) fromCache(relativeName string) *template.Template { 139 | e.mu.Lock() 140 | tmpl, ok := e.templateCache[relativeName] 141 | if ok { 142 | e.mu.Unlock() 143 | return tmpl 144 | } 145 | e.mu.Unlock() 146 | return nil 147 | } 148 | 149 | // ExecuteWriter executes a templates and write its results to the out writer 150 | func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error { 151 | if tmpl := e.fromCache(name); tmpl != nil { 152 | return tmpl.ExecuteTemplate(out, name, binding) 153 | } 154 | 155 | return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir", name) 156 | } 157 | 158 | // ExecuteRaw receives, parse and executes raw source template contents 159 | // it's super-simple function without options and funcs, it's not used widely 160 | // implements the EngineRawExecutor interface 161 | func (e *Engine) ExecuteRaw(src string, wr io.Writer, binding interface{}) (err error) { 162 | tmpl, err := amber.Compile(src, amber.DefaultOptions) 163 | if err != nil { 164 | return err 165 | } 166 | return tmpl.Execute(wr, binding) 167 | } 168 | -------------------------------------------------------------------------------- /amber/config.go: -------------------------------------------------------------------------------- 1 | package amber 2 | 3 | // Config contains fields useful to configure this template engine 4 | type Config struct { 5 | // Funcs for the html/template result, amber default funcs are not overrided so use it without worries 6 | Funcs map[string]interface{} 7 | } 8 | 9 | // DefaultConfig returns the default configuration 10 | func DefaultConfig() Config { 11 | return Config{Funcs: make(map[string]interface{})} 12 | } 13 | -------------------------------------------------------------------------------- /django/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the buit'n [django/pongo2](https://github.com/flosch/pongo2) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /django/config.go: -------------------------------------------------------------------------------- 1 | package django 2 | 3 | import "github.com/flosch/pongo2" 4 | 5 | type ( 6 | // Value conversion for pongo2.Value 7 | Value pongo2.Value 8 | // Error conversion for pongo2.Error 9 | Error pongo2.Error 10 | // FilterFunction conversion for pongo2.FilterFunction 11 | FilterFunction func(in *Value, param *Value) (out *Value, err *Error) 12 | ) 13 | 14 | // Config for django template engine 15 | type Config struct { 16 | // Filters for pongo2, map[name of the filter] the filter function . The filters are auto register 17 | Filters map[string]FilterFunction 18 | // Globals share context fields between templates. https://github.com/flosch/pongo2/issues/35 19 | Globals map[string]interface{} 20 | // DebugTemplates enables template debugging. 21 | // The verbose error messages will appear in browser instead of quiet passes with error code 22 | DebugTemplates bool 23 | } 24 | 25 | // DefaultConfig returns the default configuration for the django template engine 26 | func DefaultConfig() Config { 27 | return Config{ 28 | Filters: make(map[string]FilterFunction), 29 | Globals: make(map[string]interface{}, 0), 30 | DebugTemplates: false, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /engine.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type ( 8 | // Engine the interface that all template engines must implement 9 | Engine interface { 10 | // LoadDirectory builds the templates, usually by directory and extension but these are engine's decisions 11 | LoadDirectory(directory string, extension string) error 12 | // LoadAssets loads the templates by binary 13 | // assetFn is a func which returns bytes, use it to load the templates by binary 14 | // namesFn returns the template filenames 15 | LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error 16 | 17 | // ExecuteWriter finds, execute a template and write its result to the out writer 18 | // options are the optional runtime options can be passed by user and catched by the template engine when render 19 | // an example of this is the "layout" or "gzip" option 20 | ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error 21 | } 22 | 23 | // EngineFuncs is optional interface for the Engine 24 | // used to insert the helper funcs 25 | EngineFuncs interface { 26 | // Funcs should returns the context or the funcs, 27 | // this property is used in order to register any optional helper funcs 28 | Funcs() map[string]interface{} 29 | } 30 | 31 | // EngineRawExecutor is optional interface for the Engine 32 | // used to receive and parse a raw template string instead of a filename 33 | EngineRawExecutor interface { 34 | // ExecuteRaw is super-simple function without options and funcs, it's not used widely 35 | ExecuteRaw(src string, wr io.Writer, binding interface{}) error 36 | } 37 | ) 38 | 39 | // Below are just helpers for my two web frameworks which you can use also to your web app 40 | 41 | // NoLayout to disable layout for a particular template file 42 | const NoLayout = "@.|.@no_layout@.|.@" // html/html.go. handlebars/handlebars.go. It's the same but the var is separated care for the future here. 43 | 44 | // GetGzipOption receives a default value and the render options map and returns if gzip is enabled for this render action 45 | func GetGzipOption(defaultValue bool, options map[string]interface{}) bool { 46 | gzipOpt := options["gzip"] // we only need that, so don't create new map to keep the options. 47 | if b, isBool := gzipOpt.(bool); isBool { 48 | return b 49 | } 50 | return defaultValue 51 | } 52 | 53 | // GetCharsetOption receives a default value and the render options map and returns the correct charset for this render action 54 | func GetCharsetOption(defaultValue string, options map[string]interface{}) string { 55 | charsetOpt := options["charset"] 56 | if s, isString := charsetOpt.(string); isString { 57 | return s 58 | } 59 | return defaultValue 60 | } 61 | -------------------------------------------------------------------------------- /handlebars/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the buit'n [handlebars/raymond](https://github.com/aymerick/raymond) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /handlebars/config.go: -------------------------------------------------------------------------------- 1 | package handlebars 2 | 3 | // Config for handlebars template engine 4 | type Config struct { 5 | // Helpers for Handlebars, you can register your own by raymond.RegisterHelper(name string, a interface{}) or RegisterHelpers(map[string]interface{}) 6 | // or just fill this method, do not override it it is not nil by default (because of Iris' helpers (url and urlpath) 7 | Helpers map[string]interface{} 8 | Layout string 9 | } 10 | 11 | // DefaultConfig returns the default configs for the handlebars template engine 12 | func DefaultConfig() Config { 13 | return Config{ 14 | Helpers: make(map[string]interface{}, 0), 15 | Layout: "", 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /html/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the buit'n and the default [html/template](https://golang.org/pkg/text/template) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /html/config.go: -------------------------------------------------------------------------------- 1 | package html 2 | 3 | // Config for html template engine 4 | type Config struct { 5 | Left string 6 | Right string 7 | Layout string 8 | Funcs map[string]interface{} 9 | LayoutFuncs map[string]interface{} 10 | } 11 | 12 | // DefaultConfig returns the default configs for the html template engine 13 | func DefaultConfig() Config { 14 | return Config{ 15 | Left: "{{", 16 | Right: "}}", 17 | Layout: "", 18 | Funcs: make(map[string]interface{}, 0), 19 | LayoutFuncs: make(map[string]interface{}, 0), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /loader.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/kataras/go-errors" 8 | ) 9 | 10 | var ( 11 | // DefaultExtension the default file extension if empty setted 12 | DefaultExtension = ".html" 13 | // DefaultDirectory the default directory if empty setted 14 | DefaultDirectory = "." + string(os.PathSeparator) + "templates" 15 | ) 16 | 17 | type ( 18 | // Loader contains the funcs to set the location for the templates by directory or by binary 19 | Loader struct { 20 | Dir string 21 | Extension string 22 | // AssetFn and NamesFn used when files are distributed inside the app executable 23 | AssetFn func(name string) ([]byte, error) 24 | NamesFn func() []string 25 | } 26 | // BinaryLoader optionally, called after EngineLocation's Directory, used when files are distributed inside the app executable 27 | // sets the AssetFn and NamesFn 28 | BinaryLoader struct { 29 | *Loader 30 | } 31 | ) 32 | 33 | // NewLoader returns a default Loader which is used to load template engine(s) 34 | func NewLoader() *Loader { 35 | return &Loader{Dir: DefaultDirectory, Extension: DefaultExtension} 36 | } 37 | 38 | // Directory sets the directory to load from 39 | // returns the Binary location which is optional 40 | func (t *Loader) Directory(dir string, fileExtension string) *BinaryLoader { 41 | if dir == "" { 42 | dir = DefaultDirectory // the default templates dir 43 | } 44 | if fileExtension == "" { 45 | fileExtension = DefaultExtension 46 | } else if fileExtension[0] != '.' { // if missing the start dot 47 | fileExtension = "." + fileExtension 48 | } 49 | 50 | t.Dir = dir 51 | t.Extension = fileExtension 52 | 53 | return &BinaryLoader{Loader: t} 54 | } 55 | 56 | // Binary optionally, called after Loader.Directory, used when files are distributed inside the app executable 57 | // sets the AssetFn and NamesFn 58 | func (t *BinaryLoader) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) { 59 | if assetFn == nil || namesFn == nil { 60 | return 61 | } 62 | 63 | t.AssetFn = assetFn 64 | t.NamesFn = namesFn 65 | // if extension is not static(setted by .Directory) 66 | if t.Extension == "" { 67 | if names := namesFn(); len(names) > 0 { 68 | t.Extension = filepath.Ext(names[0]) // we need the extension to get the correct template engine on the Render method 69 | } 70 | } 71 | } 72 | 73 | // IsBinary returns true if .Binary is called and AssetFn and NamesFn are setted 74 | func (t *Loader) IsBinary() bool { 75 | return t.AssetFn != nil && t.NamesFn != nil 76 | } 77 | 78 | var errMissingDirectoryOrAssets = errors.New("missing Directory or Assets by binary for the template engine") 79 | 80 | // LoadEngine receives a template Engine and calls its LoadAssets or the LoadDirectory with the loader's locations 81 | func (t *Loader) LoadEngine(e Engine) error { 82 | if t.Dir == "" { 83 | return errMissingDirectoryOrAssets 84 | } 85 | 86 | if t.IsBinary() { 87 | // don't try to put abs path here 88 | // fixes: http://support.iris-go.com/d/22-template-binary-problem-in-v6 89 | return e.LoadAssets(t.Dir, t.Extension, t.AssetFn, t.NamesFn) 90 | } 91 | 92 | // fixes when user tries to execute the binary from a temp location while the templates are relatively located 93 | absDir, err := filepath.Abs(t.Dir) 94 | // panic here of course. 95 | if err != nil { 96 | panic("couldn't find the dir in the relative dir: '" + t.Dir + 97 | "' neither as absolute: '" + absDir + "'\n" + err.Error()) 98 | } 99 | return e.LoadDirectory(absDir, t.Extension) 100 | } 101 | -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kataras/go-template/a4a986b413e8d88ebd894b4a3e327314069f2439/logo.jpg -------------------------------------------------------------------------------- /markdown/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the [markdown/blackfriday](https://github.com/russross/blackfriday) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /markdown/config.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | // Config for markdown template engine 4 | type Config struct { 5 | // Sanitize if true then returns safe html, default is false 6 | Sanitize bool 7 | } 8 | 9 | // DefaultConfig returns the default configs for the markdown template engine 10 | func DefaultConfig() Config { 11 | return Config{ 12 | Sanitize: false, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /markdown/markdown.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "sync" 11 | 12 | "github.com/microcosm-cc/bluemonday" 13 | "github.com/russross/blackfriday" 14 | ) 15 | 16 | // Supports RAW markdown only, no context binding or layout, to use dynamic markdown with other template engine use the context.Markdown/MarkdownString 17 | 18 | type ( 19 | // Engine the jade engine 20 | Engine struct { 21 | Config Config 22 | templateCache map[string][]byte 23 | mu sync.Mutex 24 | } 25 | ) 26 | 27 | // New creates and returns a Pongo template engine 28 | func New(cfg ...Config) *Engine { 29 | c := DefaultConfig() 30 | if len(cfg) > 0 { 31 | c = cfg[0] 32 | } 33 | 34 | return &Engine{Config: c, templateCache: make(map[string][]byte)} 35 | } 36 | 37 | // LoadDirectory builds the markdown templates 38 | func (e *Engine) LoadDirectory(dir string, extension string) error { 39 | e.mu.Lock() 40 | defer e.mu.Unlock() 41 | var templateErr error 42 | // Walk the supplied directory and compile any files that match our extension list. 43 | filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 44 | 45 | if info == nil || info.IsDir() { 46 | 47 | } else { 48 | 49 | rel, err := filepath.Rel(dir, path) 50 | if err != nil { 51 | templateErr = err 52 | return err 53 | } 54 | 55 | ext := filepath.Ext(rel) 56 | 57 | if ext == extension { 58 | buf, err := ioutil.ReadFile(path) 59 | if err != nil { 60 | templateErr = err 61 | return err 62 | } 63 | 64 | buf = blackfriday.MarkdownCommon(buf) 65 | if e.Config.Sanitize { 66 | buf = bluemonday.UGCPolicy().SanitizeBytes(buf) 67 | } 68 | 69 | if err != nil { 70 | templateErr = err 71 | return err 72 | } 73 | name := filepath.ToSlash(rel) 74 | e.templateCache[name] = buf 75 | 76 | } 77 | } 78 | return nil 79 | }) 80 | 81 | return templateErr 82 | 83 | } 84 | 85 | // LoadAssets loads the templates by binary 86 | func (e *Engine) LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error { 87 | if len(virtualDirectory) > 0 { 88 | if virtualDirectory[0] == '.' { // first check for .wrong 89 | virtualDirectory = virtualDirectory[1:] 90 | } 91 | if virtualDirectory[0] == '/' || virtualDirectory[0] == os.PathSeparator { // second check for /something, (or ./something if we had dot on 0 it will be removed 92 | virtualDirectory = virtualDirectory[1:] 93 | } 94 | } 95 | 96 | e.mu.Lock() 97 | defer e.mu.Unlock() 98 | 99 | names := namesFn() 100 | for _, path := range names { 101 | if !strings.HasPrefix(path, virtualDirectory) { 102 | continue 103 | } 104 | 105 | rel, err := filepath.Rel(virtualDirectory, path) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | ext := filepath.Ext(rel) 111 | if ext == virtualExtension { 112 | 113 | buf, err := assetFn(path) 114 | if err != nil { 115 | return err 116 | } 117 | b := blackfriday.MarkdownCommon(buf) 118 | if e.Config.Sanitize { 119 | b = bluemonday.UGCPolicy().SanitizeBytes(b) 120 | } 121 | name := filepath.ToSlash(rel) 122 | e.templateCache[name] = b 123 | 124 | } 125 | 126 | } 127 | return nil 128 | } 129 | 130 | func (e *Engine) fromCache(relativeName string) []byte { 131 | e.mu.Lock() 132 | 133 | tmpl, ok := e.templateCache[relativeName] 134 | 135 | if ok { 136 | e.mu.Unlock() // defer is slow 137 | return tmpl 138 | } 139 | e.mu.Unlock() // defer is slow 140 | return nil 141 | } 142 | 143 | // ExecuteWriter executes a templates and write its results to the out writer 144 | // layout here is useless 145 | func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error { 146 | if tmpl := e.fromCache(name); tmpl != nil { 147 | _, err := out.Write(tmpl) 148 | return err 149 | } 150 | 151 | return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir", name) 152 | } 153 | 154 | // ExecuteRaw receives, parse and executes raw source template contents 155 | // it's super-simple function without options and funcs, it's not used widely 156 | // implements the EngineRawExecutor interface 157 | func (e *Engine) ExecuteRaw(src string, wr io.Writer, binding interface{}) (err error) { 158 | parsed := blackfriday.MarkdownCommon([]byte(src)) 159 | if e.Config.Sanitize { 160 | parsed = bluemonday.UGCPolicy().SanitizeBytes(parsed) 161 | } 162 | _, err = wr.Write(parsed) 163 | return 164 | } 165 | -------------------------------------------------------------------------------- /pug/README.md: -------------------------------------------------------------------------------- 1 | ## Folder information 2 | 3 | This folder contains the [pug/jade](https://github.com/Joker/jade) cross-framework template engine support. 4 | -------------------------------------------------------------------------------- /pug/config.go: -------------------------------------------------------------------------------- 1 | package pug 2 | 3 | import "github.com/kataras/go-template/html" 4 | 5 | // Pug is the 'jade', same configs as the html engine 6 | 7 | // Config for pug template engine 8 | type Config html.Config 9 | 10 | // DefaultConfig returns the default configuration for the pug(jade) template engine 11 | func DefaultConfig() Config { 12 | return Config(html.DefaultConfig()) 13 | } 14 | -------------------------------------------------------------------------------- /pug/pug.go: -------------------------------------------------------------------------------- 1 | //Package pug the JadeEngine's functionality lives inside ../html now 2 | package pug 3 | 4 | import ( 5 | "github.com/Joker/jade" 6 | "github.com/kataras/go-template/html" 7 | ) 8 | 9 | // New creates and returns the Pug template engine 10 | func New(cfg ...Config) *html.Engine { 11 | c := DefaultConfig() 12 | if len(cfg) > 0 { 13 | c = cfg[0] 14 | } 15 | // pass the Pug/Jade configs to the html's configuration 16 | 17 | s := html.New(html.Config(c)) 18 | s.Middleware = jade.Parse 19 | return s 20 | } 21 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | // Package template provides the easier way to use templates via template engine 2 | // supports multiple template engines with different file extensions 3 | // 6 template engines supported: 4 | // standard html/template 5 | // amber 6 | // django 7 | // handlebars 8 | // pug(jade) 9 | // markdown 10 | package template 11 | 12 | const ( 13 | // Version current version number 14 | Version = "0.0.5" 15 | ) 16 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Joker 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of jade nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/config.go: -------------------------------------------------------------------------------- 1 | package jade 2 | 3 | var ( 4 | // Pretty print if true 5 | PrettyOutput = true 6 | 7 | // Output indent strring for pretty print 8 | OutputIndent = " " 9 | 10 | // Tabulation size of parse file 11 | TabSize = 4 12 | 13 | // Left go-template delim 14 | LeftDelim = "{{" 15 | 16 | // Right go-template delim 17 | RightDelim = "}}" 18 | ) 19 | 20 | const ( 21 | nestIndent = true 22 | lineIndent = false 23 | 24 | tabComment = "//-" 25 | htmlComment = "//" 26 | 27 | interDelim = "#{" 28 | unEscInterDelim = "!{" 29 | rightInterDelim = "}" 30 | ) 31 | 32 | var itemToStr = map[itemType]string{ 33 | itemError: "itemError", 34 | itemEOF: "itemEOF", 35 | itemEndL: "itemEndL", 36 | itemEndAttr: "itemEndAttr", 37 | itemIdentSpace: "itemIdentSpace", 38 | itemIdentTab: "itemIdentTab", 39 | itemTag: "itemTag", 40 | itemVoidTag: "itemVoidTag", 41 | itemInlineTag: "itemInlineTag", 42 | itemInlineVoidTag: "itemInlineVoidTag", 43 | itemHTMLTag: "itemHTMLTag", 44 | itemDiv: "itemDiv", 45 | itemID: "itemID", 46 | itemClass: "itemClass", 47 | itemAttr: "itemAttr", 48 | itemAttrN: "itemAttrN", 49 | itemAttrName: "itemAttrName", 50 | itemAttrVoid: "itemAttrVoid", 51 | itemAction: "itemAction", 52 | itemInlineAction: "itemInlineAction", 53 | itemInlineText: "itemInlineText", 54 | itemFilter: "itemFilter", 55 | itemDoctype: "itemDoctype", 56 | itemComment: "itemComment", 57 | itemBlank: "itemBlank", 58 | itemParentIdent: "itemParentIdent", 59 | itemChildIdent: "itemChildIdent", 60 | itemText: "itemText", 61 | } 62 | 63 | var key = map[string]itemType{ 64 | "area": itemVoidTag, 65 | "base": itemVoidTag, 66 | "col": itemVoidTag, 67 | "command": itemVoidTag, 68 | "embed": itemVoidTag, 69 | "hr": itemVoidTag, 70 | "input": itemVoidTag, 71 | "keygen": itemVoidTag, 72 | "link": itemVoidTag, 73 | "meta": itemVoidTag, 74 | "param": itemVoidTag, 75 | "source": itemVoidTag, 76 | "track": itemVoidTag, 77 | "wbr": itemVoidTag, 78 | 79 | "template": itemAction, 80 | "end": itemAction, 81 | "include": itemAction, 82 | "extends": itemAction, 83 | 84 | "if": itemActionEnd, 85 | "else": itemActionEnd, 86 | "range": itemActionEnd, 87 | "with": itemActionEnd, 88 | "block": itemActionEnd, 89 | "define": itemActionEnd, 90 | "each": itemActionEnd, 91 | "for": itemActionEnd, 92 | "while": itemActionEnd, 93 | "unless": itemActionEnd, 94 | "case": itemActionEnd, 95 | 96 | // "if": itemIf, 97 | // "else": itemElse, 98 | // "end": itemEnd, 99 | // "range": itemRange, 100 | // "with": itemWith, 101 | // "nil": itemNil, 102 | // "template": itemTemplate, 103 | // "define": itemDefine, 104 | "mixin": itemDefine, 105 | 106 | "a": itemInlineTag, 107 | "abbr": itemInlineTag, 108 | "acronym": itemInlineTag, 109 | "b": itemInlineTag, 110 | "code": itemInlineTag, 111 | "em": itemInlineTag, 112 | "font": itemInlineTag, 113 | "i": itemInlineTag, 114 | "ins": itemInlineTag, 115 | "kbd": itemInlineTag, 116 | "map": itemInlineTag, 117 | "samp": itemInlineTag, 118 | "small": itemInlineTag, 119 | "span": itemInlineTag, 120 | "strong": itemInlineTag, 121 | "sub": itemInlineTag, 122 | "sup": itemInlineTag, 123 | 124 | "br": itemInlineVoidTag, 125 | "img": itemInlineVoidTag, 126 | } 127 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/jade_parse.go: -------------------------------------------------------------------------------- 1 | package jade 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func (t *tree) parse(treeSet map[string]*tree) (next node) { 8 | token := t.next() 9 | t.Root = t.newList(token.pos) 10 | 11 | for token.typ != itemEOF { 12 | 13 | switch token.typ { 14 | case itemError: 15 | t.errorf("%s", token.val) 16 | case itemDoctype: 17 | t.Root.append(t.newDoctype(token.pos, token.val)) 18 | case itemHTMLTag: 19 | t.Root.append(t.newLine(token.pos, token.val, token.typ, 0, 0)) 20 | 21 | case itemTag, itemDiv, itemInlineTag, itemAction, itemActionEnd, itemDefine, itemComment: 22 | nest := t.newNest(token.pos, token.val, token.typ, 0, 0) 23 | if token.typ < itemComment { 24 | t.parseAttr(nest) 25 | } 26 | t.Root.append(nest) 27 | t.parseInside(nest) 28 | 29 | case itemBlank: 30 | nest := t.newNest(token.pos, token.val, token.typ, 0, 0) 31 | t.parseInside(nest) 32 | } 33 | 34 | token = t.next() 35 | 36 | } 37 | return nil 38 | } 39 | 40 | func (t *tree) parseInside(outTag *nestNode) int { 41 | indentCount := 0 42 | token := t.next() 43 | 44 | for token.typ != itemEOF { 45 | switch token.typ { 46 | case itemError: 47 | t.errorf("%s", token.val) 48 | 49 | case itemEndL: 50 | indentCount = 0 51 | case itemIdentSpace: 52 | indentCount++ 53 | case itemIdentTab: 54 | indentCount += TabSize 55 | case itemParentIdent: 56 | indentCount = outTag.Indent + 1 // for "tag: tag: tag" 57 | case itemChildIdent: 58 | return indentCount // for "]" 59 | 60 | case itemDoctype: 61 | t.backup() 62 | return indentCount 63 | case itemInlineText, itemInlineAction: 64 | outTag.append(t.newLine(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1)) 65 | 66 | case itemHTMLTag, itemText: 67 | if indentCount > outTag.Indent { 68 | outTag.append(t.newLine(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1)) 69 | } else { 70 | t.backup() 71 | return indentCount 72 | } 73 | 74 | case itemVoidTag, itemInlineVoidTag: 75 | if indentCount > outTag.Indent { 76 | nest := t.newNest(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1) 77 | t.parseAttr(nest) 78 | outTag.append(nest) 79 | } else { 80 | t.backup() 81 | return indentCount 82 | } 83 | 84 | case itemTag, itemDiv, itemInlineTag: 85 | if indentCount > outTag.Indent { 86 | nest := t.newNest(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1) 87 | if t.parseAttr(nest) { 88 | outTag.append(nest) 89 | indentCount = t.parseInside(nest) 90 | } else { 91 | nest.typ = itemVoidTag 92 | outTag.append(nest) 93 | } 94 | } else { 95 | t.backup() 96 | return indentCount 97 | } 98 | 99 | case itemAction, itemActionEnd, itemTemplate, itemDefine: 100 | if indentCount > outTag.Indent { 101 | nest := t.newNest(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1) 102 | outTag.append(nest) 103 | indentCount = t.parseInside(nest) 104 | if strings.HasPrefix(nest.Tag, "if") || strings.HasPrefix(nest.Tag, "with") { 105 | action := t.next() 106 | if strings.HasPrefix(action.val, "else") { 107 | nest.typ = itemAction 108 | } 109 | t.backup() 110 | } 111 | } else { 112 | t.backup() 113 | return indentCount 114 | } 115 | 116 | case itemBlank, itemComment: 117 | if indentCount > outTag.Indent { 118 | nest := t.newNest(token.pos, token.val, token.typ, indentCount, outTag.Nesting+1) 119 | if token.typ == itemComment { 120 | outTag.append(nest) 121 | } 122 | indentCount = t.parseInside(nest) 123 | } else { 124 | t.backup() 125 | return indentCount 126 | } 127 | 128 | } 129 | token = t.next() 130 | } 131 | t.backup() 132 | return indentCount 133 | } 134 | 135 | func (t *tree) parseAttr(currentTag *nestNode) bool { 136 | for { 137 | attr := t.next() 138 | switch attr.typ { 139 | case itemError: 140 | t.errorf("%s", attr.val) 141 | case itemID: 142 | if len(currentTag.id) > 0 { 143 | t.errorf("unexpected second id \"%s\" ", attr.val) 144 | } 145 | currentTag.id = attr.val 146 | case itemClass: 147 | currentTag.class = append(currentTag.class, attr.val) 148 | case itemAttr, itemAttrN, itemAttrName, itemAttrVoid: 149 | currentTag.append(t.newAttr(attr.pos, attr.val, attr.typ)) 150 | case itemEndTag: 151 | currentTag.append(t.newAttr(attr.pos, "/>", itemEndTag)) 152 | return false 153 | default: 154 | t.backup() 155 | currentTag.append(t.newAttr(attr.pos, ">", itemEndAttr)) 156 | return true 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/lex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package jade 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "unicode" 11 | "unicode/utf8" 12 | ) 13 | 14 | // item represents a token or text string returned from the scanner. 15 | type item struct { 16 | typ itemType // The type of this item. 17 | pos psn // The starting position, in bytes, of this item in the input string. 18 | val string // The value of this item. 19 | } 20 | 21 | func (i item) String() string { 22 | switch { 23 | case i.typ == itemEOF: 24 | return "EOF" 25 | case i.typ == itemError: 26 | return i.val 27 | case len(i.val) > 10: 28 | return fmt.Sprintf("%.10q...", i.val) 29 | } 30 | return fmt.Sprintf("%q", i.val) 31 | } 32 | 33 | const eof = -1 34 | 35 | // stateFn represents the state of the scanner as a function that returns the next state. 36 | type stateFn func(*lexer) stateFn 37 | 38 | // lexer holds the state of the scanner. 39 | type lexer struct { 40 | name string // the name of the input; used only for error reports 41 | input string // the string being scanned 42 | leftDelim string // start of action 43 | rightDelim string // end of action 44 | state stateFn // the next lexing function to enter 45 | pos psn // current position in the input 46 | start psn // start position of this item 47 | width psn // width of last rune read from input 48 | lastPos psn // position of most recent item returned by nextItem 49 | items chan item // channel of scanned items 50 | previous int // previous depth 51 | parenDepth int // nesting depth 52 | env map[mode]int 53 | } 54 | 55 | // next returns the next rune in the input. 56 | func (l *lexer) next() rune { 57 | if int(l.pos) >= len(l.input) { 58 | l.width = 0 59 | return eof 60 | } 61 | r, w := utf8.DecodeRuneInString(l.input[l.pos:]) 62 | l.width = psn(w) 63 | l.pos += l.width 64 | return r 65 | } 66 | 67 | // peek returns but does not consume the next rune in the input. 68 | func (l *lexer) peek() rune { 69 | r := l.next() 70 | l.backup() 71 | return r 72 | } 73 | 74 | // backup steps back one rune. Can only be called once per call of next. 75 | func (l *lexer) backup() { 76 | l.pos -= l.width 77 | } 78 | 79 | // emit passes an item back to the client. 80 | func (l *lexer) emit(t itemType) { 81 | l.items <- item{t, l.start, l.input[l.start:l.pos]} 82 | l.start = l.pos 83 | } 84 | 85 | // ignore skips over the pending input before this point. 86 | func (l *lexer) ignore() { 87 | l.start = l.pos 88 | } 89 | 90 | // accept consumes the next rune if it's from the valid set. 91 | func (l *lexer) accept(valid string) bool { 92 | if strings.IndexRune(valid, l.next()) >= 0 { 93 | return true 94 | } 95 | l.backup() 96 | return false 97 | } 98 | 99 | // acceptRun consumes a run of runes from the valid set. 100 | func (l *lexer) acceptRun(valid string) { 101 | for strings.IndexRune(valid, l.next()) >= 0 { 102 | } 103 | l.backup() 104 | } 105 | 106 | // lineNumber reports which line we're on, based on the position of 107 | // the previous item returned by nextItem. Doing it this way 108 | // means we don't have to worry about peek double counting. 109 | func (l *lexer) lineNumber() int { 110 | return 1 + strings.Count(l.input[:l.lastPos], "\n") 111 | } 112 | 113 | // errorf returns an error token and terminates the scan by passing 114 | // back a nil pointer that will be the next state, terminating l.nextItem. 115 | func (l *lexer) errorf(format string, args ...interface{}) stateFn { 116 | l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} 117 | return nil 118 | } 119 | 120 | // nextItem returns the next item from the input. 121 | func (l *lexer) nextItem() item { 122 | item := <-l.items 123 | l.lastPos = item.pos 124 | return item 125 | } 126 | 127 | // lex creates a new scanner for the input string. 128 | func lex(name, input, left, right string) *lexer { 129 | if left == "" { 130 | left = LeftDelim 131 | } 132 | if right == "" { 133 | right = RightDelim 134 | } 135 | l := &lexer{ 136 | name: name, 137 | input: input, 138 | leftDelim: left, 139 | rightDelim: right, 140 | items: make(chan item), 141 | env: make(map[mode]int), 142 | } 143 | go l.run() 144 | return l 145 | } 146 | 147 | // isSpace reports whether r is a space character. 148 | func isSpace(r rune) bool { 149 | return r == ' ' || r == '\t' 150 | } 151 | 152 | // isEndOfLine reports whether r is an end-of-line character. 153 | func isEndOfLine(r rune) bool { 154 | return r == '\r' || r == '\n' 155 | } 156 | 157 | // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. 158 | func isAlphaNumeric(r rune) bool { 159 | return r == '_' || r == '-' || unicode.IsLetter(r) || unicode.IsDigit(r) 160 | } 161 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package jade 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | ) 11 | 12 | var textFormat = "%s" // Changed to "%q" in tests for better error messages. 13 | 14 | // A Node is an element in the parse tree. The interface is trivial. 15 | // The interface contains an unexported method so that only 16 | // types local to this package can satisfy it. 17 | type node interface { 18 | Type() nodeType 19 | position() psn // byte position of start of node in full original input string 20 | String() string 21 | 22 | // Copy does a deep copy of the Node and all its components. 23 | // To avoid type assertions, some XxxNodes also have specialized 24 | // CopyXxx methods that return *XxxNode. 25 | Copy() node 26 | 27 | // tree returns the containing *Tree. 28 | // It is unexported so all implementations of Node are in this package. 29 | tree() *tree 30 | tp() itemType 31 | } 32 | 33 | // Type returns itself and provides an easy default implementation 34 | // for embedding in a Node. Embedded in all non-trivial Nodes. 35 | func (t nodeType) Type() nodeType { 36 | return t 37 | } 38 | 39 | // Pos represents a byte position in the original input text from which 40 | // this template was parsed. 41 | type psn int 42 | 43 | func (p psn) position() psn { 44 | return p 45 | } 46 | 47 | // listNode holds a sequence of nodes. 48 | type listNode struct { 49 | nodeType 50 | psn 51 | tr *tree 52 | Nodes []node // The element nodes in lexical order. 53 | } 54 | 55 | func (t *tree) newList(pos psn) *listNode { 56 | return &listNode{tr: t, nodeType: nodeList, psn: pos} 57 | } 58 | 59 | func (l *listNode) append(n node) { 60 | l.Nodes = append(l.Nodes, n) 61 | } 62 | 63 | func (l *listNode) tree() *tree { 64 | return l.tr 65 | } 66 | func (l *listNode) tp() itemType { 67 | return 0 68 | } 69 | 70 | func (l *listNode) String() string { 71 | b := new(bytes.Buffer) 72 | for _, n := range l.Nodes { 73 | fmt.Fprint(b, n) 74 | } 75 | return b.String() 76 | } 77 | 78 | func (l *listNode) CopyList() *listNode { 79 | if l == nil { 80 | return l 81 | } 82 | n := l.tr.newList(l.psn) 83 | for _, elem := range l.Nodes { 84 | n.append(elem.Copy()) 85 | } 86 | return n 87 | } 88 | 89 | func (l *listNode) Copy() node { 90 | return l.CopyList() 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/Joker/jade/template.go: -------------------------------------------------------------------------------- 1 | // Jade.go - template engine. Package implements Jade-lang templates for generating Go html/template output. 2 | package jade 3 | 4 | import ( 5 | "io/ioutil" 6 | "path/filepath" 7 | ) 8 | 9 | /* 10 | Parse parses the template definition string to construct a representation of the template for execution. 11 | 12 | Trivial usage: 13 | 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "github.com/Joker/jade" 19 | ) 20 | 21 | func main() { 22 | tpl, err := jade.Parse("tpl_name", "doctype 5: html: body: p Hello world!") 23 | if err != nil { 24 | fmt.Printf("Parse error: %v", err) 25 | return 26 | } 27 | 28 | fmt.Printf( "Output:\n\n%s", tpl ) 29 | } 30 | 31 | Output: 32 | 33 | 34 | 35 | 36 |

Hello world!

37 | 38 | 39 | */ 40 | func Parse(name, text string) (string, error) { 41 | outTpl, err := newTree(name).Parse(text, LeftDelim, RightDelim, make(map[string]*tree)) 42 | if err != nil { 43 | return "", err 44 | } 45 | return outTpl.String(), nil 46 | } 47 | 48 | // ParseFile parse the jade template file in given filename 49 | func ParseFile(filename string) (string, error) { 50 | b, err := ioutil.ReadFile(filename) 51 | if err != nil { 52 | return "", err 53 | } 54 | s := string(b) 55 | name := filepath.Base(filename) 56 | 57 | return Parse(name, s) 58 | } 59 | 60 | func (t *tree) String() string { 61 | return t.Root.String() 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Aymerick JEHANNE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/VERSION: -------------------------------------------------------------------------------- 1 | 2.0.1 2 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/data_frame.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import "reflect" 4 | 5 | // DataFrame represents a private data frame. 6 | // 7 | // Cf. private variables documentation at: http://handlebarsjs.com/block_helpers.html 8 | type DataFrame struct { 9 | parent *DataFrame 10 | data map[string]interface{} 11 | } 12 | 13 | // NewDataFrame instanciates a new private data frame. 14 | func NewDataFrame() *DataFrame { 15 | return &DataFrame{ 16 | data: make(map[string]interface{}), 17 | } 18 | } 19 | 20 | // Copy instanciates a new private data frame with receiver as parent. 21 | func (p *DataFrame) Copy() *DataFrame { 22 | result := NewDataFrame() 23 | 24 | for k, v := range p.data { 25 | result.data[k] = v 26 | } 27 | 28 | result.parent = p 29 | 30 | return result 31 | } 32 | 33 | // newIterDataFrame instanciates a new private data frame with receiver as parent and with iteration data set (@index, @key, @first, @last) 34 | func (p *DataFrame) newIterDataFrame(length int, i int, key interface{}) *DataFrame { 35 | result := p.Copy() 36 | 37 | result.Set("index", i) 38 | result.Set("key", key) 39 | result.Set("first", i == 0) 40 | result.Set("last", i == length-1) 41 | 42 | return result 43 | } 44 | 45 | // Set sets a data value. 46 | func (p *DataFrame) Set(key string, val interface{}) { 47 | p.data[key] = val 48 | } 49 | 50 | // Get gets a data value. 51 | func (p *DataFrame) Get(key string) interface{} { 52 | return p.find([]string{key}) 53 | } 54 | 55 | // find gets a deep data value 56 | // 57 | // @todo This is NOT consistent with the way we resolve data in template (cf. `evalDataPathExpression()`) ! FIX THAT ! 58 | func (p *DataFrame) find(parts []string) interface{} { 59 | data := p.data 60 | 61 | for i, part := range parts { 62 | val := data[part] 63 | if val == nil { 64 | return nil 65 | } 66 | 67 | if i == len(parts)-1 { 68 | // found 69 | return val 70 | } 71 | 72 | valValue := reflect.ValueOf(val) 73 | if valValue.Kind() != reflect.Map { 74 | // not found 75 | return nil 76 | } 77 | 78 | // continue 79 | data = mapStringInterface(valValue) 80 | } 81 | 82 | // not found 83 | return nil 84 | } 85 | 86 | // mapStringInterface converts any `map` to `map[string]interface{}` 87 | func mapStringInterface(value reflect.Value) map[string]interface{} { 88 | result := make(map[string]interface{}) 89 | 90 | for _, key := range value.MapKeys() { 91 | result[strValue(key)] = value.MapIndex(key).Interface() 92 | } 93 | 94 | return result 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/escape.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | ) 7 | 8 | // 9 | // That whole file is borrowed from https://github.com/golang/go/tree/master/src/html/escape.go 10 | // 11 | // With changes: 12 | // ' => ' 13 | // " => " 14 | // 15 | // To stay in sync with JS implementation, and make mustache tests pass. 16 | // 17 | 18 | type writer interface { 19 | WriteString(string) (int, error) 20 | } 21 | 22 | const escapedChars = `&'<>"` 23 | 24 | func escape(w writer, s string) error { 25 | i := strings.IndexAny(s, escapedChars) 26 | for i != -1 { 27 | if _, err := w.WriteString(s[:i]); err != nil { 28 | return err 29 | } 30 | var esc string 31 | switch s[i] { 32 | case '&': 33 | esc = "&" 34 | case '\'': 35 | esc = "'" 36 | case '<': 37 | esc = "<" 38 | case '>': 39 | esc = ">" 40 | case '"': 41 | esc = """ 42 | default: 43 | panic("unrecognized escape character") 44 | } 45 | s = s[i+1:] 46 | if _, err := w.WriteString(esc); err != nil { 47 | return err 48 | } 49 | i = strings.IndexAny(s, escapedChars) 50 | } 51 | _, err := w.WriteString(s) 52 | return err 53 | } 54 | 55 | // Escape escapes special HTML characters. 56 | // 57 | // It can be used by helpers that return a SafeString and that need to escape some content by themselves. 58 | func Escape(s string) string { 59 | if strings.IndexAny(s, escapedChars) == -1 { 60 | return s 61 | } 62 | var buf bytes.Buffer 63 | escape(&buf, s) 64 | return buf.String() 65 | } 66 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/handlebars/doc.go: -------------------------------------------------------------------------------- 1 | // Package handlebars contains all the tests that come from handlebars.js project. 2 | package handlebars 3 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/lexer/token.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "fmt" 4 | 5 | const ( 6 | // TokenError represents an error 7 | TokenError TokenKind = iota 8 | 9 | // TokenEOF represents an End Of File 10 | TokenEOF 11 | 12 | // 13 | // Mustache delimiters 14 | // 15 | 16 | // TokenOpen is the OPEN token 17 | TokenOpen 18 | 19 | // TokenClose is the CLOSE token 20 | TokenClose 21 | 22 | // TokenOpenRawBlock is the OPEN_RAW_BLOCK token 23 | TokenOpenRawBlock 24 | 25 | // TokenCloseRawBlock is the CLOSE_RAW_BLOCK token 26 | TokenCloseRawBlock 27 | 28 | // TokenOpenEndRawBlock is the END_RAW_BLOCK token 29 | TokenOpenEndRawBlock 30 | 31 | // TokenOpenUnescaped is the OPEN_UNESCAPED token 32 | TokenOpenUnescaped 33 | 34 | // TokenCloseUnescaped is the CLOSE_UNESCAPED token 35 | TokenCloseUnescaped 36 | 37 | // TokenOpenBlock is the OPEN_BLOCK token 38 | TokenOpenBlock 39 | 40 | // TokenOpenEndBlock is the OPEN_ENDBLOCK token 41 | TokenOpenEndBlock 42 | 43 | // TokenInverse is the INVERSE token 44 | TokenInverse 45 | 46 | // TokenOpenInverse is the OPEN_INVERSE token 47 | TokenOpenInverse 48 | 49 | // TokenOpenInverseChain is the OPEN_INVERSE_CHAIN token 50 | TokenOpenInverseChain 51 | 52 | // TokenOpenPartial is the OPEN_PARTIAL token 53 | TokenOpenPartial 54 | 55 | // TokenComment is the COMMENT token 56 | TokenComment 57 | 58 | // 59 | // Inside mustaches 60 | // 61 | 62 | // TokenOpenSexpr is the OPEN_SEXPR token 63 | TokenOpenSexpr 64 | 65 | // TokenCloseSexpr is the CLOSE_SEXPR token 66 | TokenCloseSexpr 67 | 68 | // TokenEquals is the EQUALS token 69 | TokenEquals 70 | 71 | // TokenData is the DATA token 72 | TokenData 73 | 74 | // TokenSep is the SEP token 75 | TokenSep 76 | 77 | // TokenOpenBlockParams is the OPEN_BLOCK_PARAMS token 78 | TokenOpenBlockParams 79 | 80 | // TokenCloseBlockParams is the CLOSE_BLOCK_PARAMS token 81 | TokenCloseBlockParams 82 | 83 | // 84 | // Tokens with content 85 | // 86 | 87 | // TokenContent is the CONTENT token 88 | TokenContent 89 | 90 | // TokenID is the ID token 91 | TokenID 92 | 93 | // TokenString is the STRING token 94 | TokenString 95 | 96 | // TokenNumber is the NUMBER token 97 | TokenNumber 98 | 99 | // TokenBoolean is the BOOLEAN token 100 | TokenBoolean 101 | ) 102 | 103 | const ( 104 | // Option to generate token position in its string representation 105 | dumpTokenPos = false 106 | 107 | // Option to generate values for all token kinds for their string representations 108 | dumpAllTokensVal = true 109 | ) 110 | 111 | // TokenKind represents a Token type. 112 | type TokenKind int 113 | 114 | // Token represents a scanned token. 115 | type Token struct { 116 | Kind TokenKind // Token kind 117 | Val string // Token value 118 | 119 | Pos int // Byte position in input string 120 | Line int // Line number in input string 121 | } 122 | 123 | // tokenName permits to display token name given token type 124 | var tokenName = map[TokenKind]string{ 125 | TokenError: "Error", 126 | TokenEOF: "EOF", 127 | TokenContent: "Content", 128 | TokenComment: "Comment", 129 | TokenOpen: "Open", 130 | TokenClose: "Close", 131 | TokenOpenUnescaped: "OpenUnescaped", 132 | TokenCloseUnescaped: "CloseUnescaped", 133 | TokenOpenBlock: "OpenBlock", 134 | TokenOpenEndBlock: "OpenEndBlock", 135 | TokenOpenRawBlock: "OpenRawBlock", 136 | TokenCloseRawBlock: "CloseRawBlock", 137 | TokenOpenEndRawBlock: "OpenEndRawBlock", 138 | TokenOpenBlockParams: "OpenBlockParams", 139 | TokenCloseBlockParams: "CloseBlockParams", 140 | TokenInverse: "Inverse", 141 | TokenOpenInverse: "OpenInverse", 142 | TokenOpenInverseChain: "OpenInverseChain", 143 | TokenOpenPartial: "OpenPartial", 144 | TokenOpenSexpr: "OpenSexpr", 145 | TokenCloseSexpr: "CloseSexpr", 146 | TokenID: "ID", 147 | TokenEquals: "Equals", 148 | TokenString: "String", 149 | TokenNumber: "Number", 150 | TokenBoolean: "Boolean", 151 | TokenData: "Data", 152 | TokenSep: "Sep", 153 | } 154 | 155 | // String returns the token kind string representation for debugging. 156 | func (k TokenKind) String() string { 157 | s := tokenName[k] 158 | if s == "" { 159 | return fmt.Sprintf("Token-%d", int(k)) 160 | } 161 | return s 162 | } 163 | 164 | // String returns the token string representation for debugging. 165 | func (t Token) String() string { 166 | result := "" 167 | 168 | if dumpTokenPos { 169 | result += fmt.Sprintf("%d:", t.Pos) 170 | } 171 | 172 | result += fmt.Sprintf("%s", t.Kind) 173 | 174 | if (dumpAllTokensVal || (t.Kind >= TokenContent)) && len(t.Val) > 0 { 175 | if len(t.Val) > 100 { 176 | result += fmt.Sprintf("{%.20q...}", t.Val) 177 | } else { 178 | result += fmt.Sprintf("{%q}", t.Val) 179 | } 180 | } 181 | 182 | return result 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/Changes: -------------------------------------------------------------------------------- 1 | 2011-03-20: v1.1.2 2 | Added tests for standalone tags at string boundaries. 3 | Added tests for rendering lambda returns after delimiter changes. 4 | 5 | 2011-03-20: v1.0.3 6 | Added tests for standalone tags at string boundaries. 7 | Added tests for rendering lambda returns after delimiter changes. 8 | 9 | 2011-03-05: v1.1.1 10 | Added tests for indented inline sections. 11 | Added tests for Windows-style newlines. 12 | 13 | 2011-03-05: v1.0.2 14 | Added tests for indented inline sections. 15 | Added tests for Windows-style newlines. 16 | 17 | 2011-03-04: v1.1.0 18 | Implicit iterators. 19 | A single period (`.`) may now be used as a name in Interpolation tags, 20 | which represents the top of stack (cast as a String). 21 | Dotted names. 22 | Names containing one or more periods should be resolved as chained 23 | properties; naïvely, this is like nesting section tags, but with some 24 | built-in scoping protections. 25 | 26 | 2011-03-02: v1.0.1 27 | Clarifying a point in the README about version compliance. 28 | Adding high-level documentation to each spec file. 29 | 30 | 2011-02-28: v1.0.0 31 | Initial Release 32 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/Rakefile: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'yaml' 3 | 4 | # Our custom YAML tags must retain their magic. 5 | %w[ code ].each do |tag| 6 | YAML::add_builtin_type(tag) { |_,val| val.merge(:__tag__ => tag) } 7 | end 8 | 9 | desc 'Build all alternate versions of the specs.' 10 | multitask :build => [ 'build:json' ] 11 | 12 | namespace :build do 13 | note = 'Do not edit this file; changes belong in the appropriate YAML file.' 14 | 15 | desc 'Build JSON versions of the specs.' 16 | task :json do 17 | rm(Dir['specs/*.json'], :verbose => false) 18 | Dir.glob('specs/*.yml').each do |filename| 19 | json_file = filename.gsub('.yml', '.json') 20 | 21 | File.open(json_file, 'w') do |file| 22 | doc = YAML.load_file(filename) 23 | file << doc.merge(:__ATTN__ => note).to_json() 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/TESTING.md: -------------------------------------------------------------------------------- 1 | Testing your Mustache implementation against this specification should be 2 | relatively simple. If you have a readily available testing framework on your 3 | platform, your task may be even simpler. 4 | 5 | In general, the process for each `.yml` file is as follows: 6 | 7 | 1. Use a YAML parser to load the file. 8 | 9 | 2. For each test in the 'tests' array: 10 | 11 | 1. Ensure that each element of the 'partials' hash (if it exists) is 12 | stored in a place where the interpreter will look for it. 13 | 14 | 2. If your implementation will not support lambdas, feel free to skip over 15 | the optional '~lambdas.yml' file. 16 | 17 | 2.1. If your implementation will support lambdas, ensure that each member of 18 | 'data' tagged with '!code' is properly processed into a language- 19 | specific lambda reference. 20 | 21 | * e.g. Given this YAML data hash: 22 | 23 | `{ x: !code { ruby: 'proc { "x" }', perl: 'sub { "x" }' } }` 24 | 25 | a Ruby-based Mustache implementation would process it such that it 26 | was equivalent to this Ruby hash: 27 | 28 | `{ 'x' => proc { "x" } }` 29 | 30 | * If your implementation language does not currently have lambda 31 | examples in the spec, feel free to implement them and send a pull 32 | request. 33 | 34 | * The JSON version of the spec represents these tagged values as a hash 35 | with a '`__tag__`' key of 'code'. 36 | 37 | 3. Render the template (stored in the 'template' key) with the given 'data' 38 | hash. 39 | 40 | 4. Compare the results of your rendering against the 'expected' value; any 41 | differences should be reported, along with any useful debugging 42 | information. 43 | 44 | * Of note, the 'desc' key contains a rough one-line description of the 45 | behavior being tested -- this is most useful in conjunction with the 46 | file name and test 'name'. 47 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/comments.json: -------------------------------------------------------------------------------- 1 | {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Comment tags represent content that should never appear in the resulting\noutput.\n\nThe tag's content may contain any substring (including newlines) EXCEPT the\nclosing delimiter.\n\nComment tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Inline","data":{},"expected":"1234567890","template":"12345{{! Comment Block! }}67890","desc":"Comment blocks should be removed from the template."},{"name":"Multiline","data":{},"expected":"1234567890\n","template":"12345{{!\n This is a\n multi-line comment...\n}}67890\n","desc":"Multiline comments should be permitted."},{"name":"Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{! Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{! Indented Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{! Standalone Comment }}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"!","template":" {{! I'm Still Standalone }}\n!","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"!\n","template":"!\n {{! I'm Still Standalone }}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Inline","data":{},"expected":" 12 \n","template":" 12 {{! 34 }}\n","desc":"Inline comments should not strip whitespace"},{"name":"Surrounding Whitespace","data":{},"expected":"12345 67890","template":"12345 {{! Comment Block! }} 67890","desc":"Comment removal should preserve surrounding whitespace."}]} -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/comments.yml: -------------------------------------------------------------------------------- 1 | overview: | 2 | Comment tags represent content that should never appear in the resulting 3 | output. 4 | 5 | The tag's content may contain any substring (including newlines) EXCEPT the 6 | closing delimiter. 7 | 8 | Comment tags SHOULD be treated as standalone when appropriate. 9 | tests: 10 | - name: Inline 11 | desc: Comment blocks should be removed from the template. 12 | data: { } 13 | template: '12345{{! Comment Block! }}67890' 14 | expected: '1234567890' 15 | 16 | - name: Multiline 17 | desc: Multiline comments should be permitted. 18 | data: { } 19 | template: | 20 | 12345{{! 21 | This is a 22 | multi-line comment... 23 | }}67890 24 | expected: | 25 | 1234567890 26 | 27 | - name: Standalone 28 | desc: All standalone comment lines should be removed. 29 | data: { } 30 | template: | 31 | Begin. 32 | {{! Comment Block! }} 33 | End. 34 | expected: | 35 | Begin. 36 | End. 37 | 38 | - name: Indented Standalone 39 | desc: All standalone comment lines should be removed. 40 | data: { } 41 | template: | 42 | Begin. 43 | {{! Indented Comment Block! }} 44 | End. 45 | expected: | 46 | Begin. 47 | End. 48 | 49 | - name: Standalone Line Endings 50 | desc: '"\r\n" should be considered a newline for standalone tags.' 51 | data: { } 52 | template: "|\r\n{{! Standalone Comment }}\r\n|" 53 | expected: "|\r\n|" 54 | 55 | - name: Standalone Without Previous Line 56 | desc: Standalone tags should not require a newline to precede them. 57 | data: { } 58 | template: " {{! I'm Still Standalone }}\n!" 59 | expected: "!" 60 | 61 | - name: Standalone Without Newline 62 | desc: Standalone tags should not require a newline to follow them. 63 | data: { } 64 | template: "!\n {{! I'm Still Standalone }}" 65 | expected: "!\n" 66 | 67 | - name: Multiline Standalone 68 | desc: All standalone comment lines should be removed. 69 | data: { } 70 | template: | 71 | Begin. 72 | {{! 73 | Something's going on here... 74 | }} 75 | End. 76 | expected: | 77 | Begin. 78 | End. 79 | 80 | - name: Indented Multiline Standalone 81 | desc: All standalone comment lines should be removed. 82 | data: { } 83 | template: | 84 | Begin. 85 | {{! 86 | Something's going on here... 87 | }} 88 | End. 89 | expected: | 90 | Begin. 91 | End. 92 | 93 | - name: Indented Inline 94 | desc: Inline comments should not strip whitespace 95 | data: { } 96 | template: " 12 {{! 34 }}\n" 97 | expected: " 12 \n" 98 | 99 | - name: Surrounding Whitespace 100 | desc: Comment removal should preserve surrounding whitespace. 101 | data: { } 102 | template: '12345 {{! Comment Block! }} 67890' 103 | expected: '12345 67890' 104 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/delimiters.json: -------------------------------------------------------------------------------- 1 | {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Set Delimiter tags are used to change the tag delimiters for all content\nfollowing the tag in the current compilation unit.\n\nThe tag's content MUST be any two non-whitespace sequences (separated by\nwhitespace) EXCEPT an equals sign ('=') followed by the current closing\ndelimiter.\n\nSet Delimiter tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Pair Behavior","data":{"text":"Hey!"},"expected":"(Hey!)","template":"{{=<% %>=}}(<%text%>)","desc":"The equals sign (used on both sides) should permit delimiter changes."},{"name":"Special Characters","data":{"text":"It worked!"},"expected":"(It worked!)","template":"({{=[ ]=}}[text])","desc":"Characters with special meaning regexen should be valid delimiters."},{"name":"Sections","data":{"section":true,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside sections should persist."},{"name":"Inverted Sections","data":{"section":false,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside inverted sections should persist."},{"name":"Partial Inheritence","data":{"value":"yes"},"expected":"[ .yes. ]\n[ .yes. ]\n","template":"[ {{>include}} ]\n{{= | | =}}\n[ |>include| ]\n","desc":"Delimiters set in a parent template should not affect a partial.","partials":{"include":".{{value}}."}},{"name":"Post-Partial Behavior","data":{"value":"yes"},"expected":"[ .yes. .yes. ]\n[ .yes. .|value|. ]\n","template":"[ {{>include}} ]\n[ .{{value}}. .|value|. ]\n","desc":"Delimiters set in a partial should not affect the parent template.","partials":{"include":".{{value}}. {{= | | =}} .|value|."}},{"name":"Surrounding Whitespace","data":{},"expected":"| |","template":"| {{=@ @=}} |","desc":"Surrounding whitespace should be left untouched."},{"name":"Outlying Whitespace (Inline)","data":{},"expected":" | \n","template":" | {{=@ @=}}\n","desc":"Whitespace should be left untouched."},{"name":"Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{=@ @=}}\nEnd.\n","desc":"Standalone lines should be removed from the template."},{"name":"Indented Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{=@ @=}}\nEnd.\n","desc":"Indented standalone lines should be removed from the template."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{= @ @ =}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"=","template":" {{=@ @=}}\n=","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"=\n","template":"=\n {{=@ @=}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Pair with Padding","data":{},"expected":"||","template":"|{{= @ @ =}}|","desc":"Superfluous in-tag whitespace should be ignored."}]} -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/delimiters.yml: -------------------------------------------------------------------------------- 1 | overview: | 2 | Set Delimiter tags are used to change the tag delimiters for all content 3 | following the tag in the current compilation unit. 4 | 5 | The tag's content MUST be any two non-whitespace sequences (separated by 6 | whitespace) EXCEPT an equals sign ('=') followed by the current closing 7 | delimiter. 8 | 9 | Set Delimiter tags SHOULD be treated as standalone when appropriate. 10 | tests: 11 | - name: Pair Behavior 12 | desc: The equals sign (used on both sides) should permit delimiter changes. 13 | data: { text: 'Hey!' } 14 | template: '{{=<% %>=}}(<%text%>)' 15 | expected: '(Hey!)' 16 | 17 | - name: Special Characters 18 | desc: Characters with special meaning regexen should be valid delimiters. 19 | data: { text: 'It worked!' } 20 | template: '({{=[ ]=}}[text])' 21 | expected: '(It worked!)' 22 | 23 | - name: Sections 24 | desc: Delimiters set outside sections should persist. 25 | data: { section: true, data: 'I got interpolated.' } 26 | template: | 27 | [ 28 | {{#section}} 29 | {{data}} 30 | |data| 31 | {{/section}} 32 | 33 | {{= | | =}} 34 | |#section| 35 | {{data}} 36 | |data| 37 | |/section| 38 | ] 39 | expected: | 40 | [ 41 | I got interpolated. 42 | |data| 43 | 44 | {{data}} 45 | I got interpolated. 46 | ] 47 | 48 | - name: Inverted Sections 49 | desc: Delimiters set outside inverted sections should persist. 50 | data: { section: false, data: 'I got interpolated.' } 51 | template: | 52 | [ 53 | {{^section}} 54 | {{data}} 55 | |data| 56 | {{/section}} 57 | 58 | {{= | | =}} 59 | |^section| 60 | {{data}} 61 | |data| 62 | |/section| 63 | ] 64 | expected: | 65 | [ 66 | I got interpolated. 67 | |data| 68 | 69 | {{data}} 70 | I got interpolated. 71 | ] 72 | 73 | - name: Partial Inheritence 74 | desc: Delimiters set in a parent template should not affect a partial. 75 | data: { value: 'yes' } 76 | partials: 77 | include: '.{{value}}.' 78 | template: | 79 | [ {{>include}} ] 80 | {{= | | =}} 81 | [ |>include| ] 82 | expected: | 83 | [ .yes. ] 84 | [ .yes. ] 85 | 86 | - name: Post-Partial Behavior 87 | desc: Delimiters set in a partial should not affect the parent template. 88 | data: { value: 'yes' } 89 | partials: 90 | include: '.{{value}}. {{= | | =}} .|value|.' 91 | template: | 92 | [ {{>include}} ] 93 | [ .{{value}}. .|value|. ] 94 | expected: | 95 | [ .yes. .yes. ] 96 | [ .yes. .|value|. ] 97 | 98 | # Whitespace Sensitivity 99 | 100 | - name: Surrounding Whitespace 101 | desc: Surrounding whitespace should be left untouched. 102 | data: { } 103 | template: '| {{=@ @=}} |' 104 | expected: '| |' 105 | 106 | - name: Outlying Whitespace (Inline) 107 | desc: Whitespace should be left untouched. 108 | data: { } 109 | template: " | {{=@ @=}}\n" 110 | expected: " | \n" 111 | 112 | - name: Standalone Tag 113 | desc: Standalone lines should be removed from the template. 114 | data: { } 115 | template: | 116 | Begin. 117 | {{=@ @=}} 118 | End. 119 | expected: | 120 | Begin. 121 | End. 122 | 123 | - name: Indented Standalone Tag 124 | desc: Indented standalone lines should be removed from the template. 125 | data: { } 126 | template: | 127 | Begin. 128 | {{=@ @=}} 129 | End. 130 | expected: | 131 | Begin. 132 | End. 133 | 134 | - name: Standalone Line Endings 135 | desc: '"\r\n" should be considered a newline for standalone tags.' 136 | data: { } 137 | template: "|\r\n{{= @ @ =}}\r\n|" 138 | expected: "|\r\n|" 139 | 140 | - name: Standalone Without Previous Line 141 | desc: Standalone tags should not require a newline to precede them. 142 | data: { } 143 | template: " {{=@ @=}}\n=" 144 | expected: "=" 145 | 146 | - name: Standalone Without Newline 147 | desc: Standalone tags should not require a newline to follow them. 148 | data: { } 149 | template: "=\n {{=@ @=}}" 150 | expected: "=\n" 151 | 152 | # Whitespace Insensitivity 153 | 154 | - name: Pair with Padding 155 | desc: Superfluous in-tag whitespace should be ignored. 156 | data: { } 157 | template: '|{{= @ @ =}}|' 158 | expected: '||' 159 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/partials.json: -------------------------------------------------------------------------------- 1 | {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Partial tags are used to expand an external template into the current\ntemplate.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names the partial to inject. Set Delimiter tags MUST NOT\naffect the parsing of a partial. The partial MUST be rendered against the\ncontext stack local to the tag. If the named partial cannot be found, the\nempty string SHOULD be used instead, as in interpolations.\n\nPartial tags SHOULD be treated as standalone when appropriate. If this tag\nis used standalone, any whitespace preceding the tag should treated as\nindentation, and prepended to each line of the partial before rendering.\n","tests":[{"name":"Basic Behavior","data":{},"expected":"\"from partial\"","template":"\"{{>text}}\"","desc":"The greater-than operator should expand to the named partial.","partials":{"text":"from partial"}},{"name":"Failed Lookup","data":{},"expected":"\"\"","template":"\"{{>text}}\"","desc":"The empty string should be used when the named partial is not found.","partials":{}},{"name":"Context","data":{"text":"content"},"expected":"\"*content*\"","template":"\"{{>partial}}\"","desc":"The greater-than operator should operate within the current context.","partials":{"partial":"*{{text}}*"}},{"name":"Recursion","data":{"content":"X","nodes":[{"content":"Y","nodes":[]}]},"expected":"X>","template":"{{>node}}","desc":"The greater-than operator should properly recurse.","partials":{"node":"{{content}}<{{#nodes}}{{>node}}{{/nodes}}>"}},{"name":"Surrounding Whitespace","data":{},"expected":"| \t|\t |","template":"| {{>partial}} |","desc":"The greater-than operator should not alter surrounding whitespace.","partials":{"partial":"\t|\t"}},{"name":"Inline Indentation","data":{"data":"|"},"expected":" | >\n>\n","template":" {{data}} {{> partial}}\n","desc":"Whitespace should be left untouched.","partials":{"partial":">\n>"}},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n>|","template":"|\r\n{{>partial}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags.","partials":{"partial":">"}},{"name":"Standalone Without Previous Line","data":{},"expected":" >\n >>","template":" {{>partial}}\n>","desc":"Standalone tags should not require a newline to precede them.","partials":{"partial":">\n>"}},{"name":"Standalone Without Newline","data":{},"expected":">\n >\n >","template":">\n {{>partial}}","desc":"Standalone tags should not require a newline to follow them.","partials":{"partial":">\n>"}},{"name":"Standalone Indentation","data":{"content":"<\n->"},"expected":"\\\n |\n <\n->\n |\n/\n","template":"\\\n {{>partial}}\n/\n","desc":"Each line of the partial should be indented before rendering.","partials":{"partial":"|\n{{{content}}}\n|\n"}},{"name":"Padding Whitespace","data":{"boolean":true},"expected":"|[]|","template":"|{{> partial }}|","desc":"Superfluous in-tag whitespace should be ignored.","partials":{"partial":"[]"}}]} -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/partials.yml: -------------------------------------------------------------------------------- 1 | overview: | 2 | Partial tags are used to expand an external template into the current 3 | template. 4 | 5 | The tag's content MUST be a non-whitespace character sequence NOT containing 6 | the current closing delimiter. 7 | 8 | This tag's content names the partial to inject. Set Delimiter tags MUST NOT 9 | affect the parsing of a partial. The partial MUST be rendered against the 10 | context stack local to the tag. If the named partial cannot be found, the 11 | empty string SHOULD be used instead, as in interpolations. 12 | 13 | Partial tags SHOULD be treated as standalone when appropriate. If this tag 14 | is used standalone, any whitespace preceding the tag should treated as 15 | indentation, and prepended to each line of the partial before rendering. 16 | tests: 17 | - name: Basic Behavior 18 | desc: The greater-than operator should expand to the named partial. 19 | data: { } 20 | template: '"{{>text}}"' 21 | partials: { text: 'from partial' } 22 | expected: '"from partial"' 23 | 24 | - name: Failed Lookup 25 | desc: The empty string should be used when the named partial is not found. 26 | data: { } 27 | template: '"{{>text}}"' 28 | partials: { } 29 | expected: '""' 30 | 31 | - name: Context 32 | desc: The greater-than operator should operate within the current context. 33 | data: { text: 'content' } 34 | template: '"{{>partial}}"' 35 | partials: { partial: '*{{text}}*' } 36 | expected: '"*content*"' 37 | 38 | - name: Recursion 39 | desc: The greater-than operator should properly recurse. 40 | data: { content: "X", nodes: [ { content: "Y", nodes: [] } ] } 41 | template: '{{>node}}' 42 | partials: { node: '{{content}}<{{#nodes}}{{>node}}{{/nodes}}>' } 43 | expected: 'X>' 44 | 45 | # Whitespace Sensitivity 46 | 47 | - name: Surrounding Whitespace 48 | desc: The greater-than operator should not alter surrounding whitespace. 49 | data: { } 50 | template: '| {{>partial}} |' 51 | partials: { partial: "\t|\t" } 52 | expected: "| \t|\t |" 53 | 54 | - name: Inline Indentation 55 | desc: Whitespace should be left untouched. 56 | data: { data: '|' } 57 | template: " {{data}} {{> partial}}\n" 58 | partials: { partial: ">\n>" } 59 | expected: " | >\n>\n" 60 | 61 | - name: Standalone Line Endings 62 | desc: '"\r\n" should be considered a newline for standalone tags.' 63 | data: { } 64 | template: "|\r\n{{>partial}}\r\n|" 65 | partials: { partial: ">" } 66 | expected: "|\r\n>|" 67 | 68 | - name: Standalone Without Previous Line 69 | desc: Standalone tags should not require a newline to precede them. 70 | data: { } 71 | template: " {{>partial}}\n>" 72 | partials: { partial: ">\n>"} 73 | expected: " >\n >>" 74 | 75 | - name: Standalone Without Newline 76 | desc: Standalone tags should not require a newline to follow them. 77 | data: { } 78 | template: ">\n {{>partial}}" 79 | partials: { partial: ">\n>" } 80 | expected: ">\n >\n >" 81 | 82 | - name: Standalone Indentation 83 | desc: Each line of the partial should be indented before rendering. 84 | data: { content: "<\n->" } 85 | template: | 86 | \ 87 | {{>partial}} 88 | / 89 | partials: 90 | partial: | 91 | | 92 | {{{content}}} 93 | | 94 | expected: | 95 | \ 96 | | 97 | < 98 | -> 99 | | 100 | / 101 | 102 | # Whitespace Insensitivity 103 | 104 | - name: Padding Whitespace 105 | desc: Superfluous in-tag whitespace should be ignored. 106 | data: { boolean: true } 107 | template: "|{{> partial }}|" 108 | partials: { partial: "[]" } 109 | expected: '|[]|' 110 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/mustache/specs/~lambdas.json: -------------------------------------------------------------------------------- 1 | {"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Lambdas are a special-cased data type for use in interpolations and\nsections.\n\nWhen used as the data value for an Interpolation tag, the lambda MUST be\ntreatable as an arity 0 function, and invoked as such. The returned value\nMUST be rendered against the default delimiters, then interpolated in place\nof the lambda.\n\nWhen used as the data value for a Section tag, the lambda MUST be treatable\nas an arity 1 function, and invoked as such (passing a String containing the\nunprocessed section contents). The returned value MUST be rendered against\nthe current delimiters, then interpolated in place of the section.\n","tests":[{"name":"Interpolation","data":{"lambda":{"php":"return \"world\";","clojure":"(fn [] \"world\")","__tag__":"code","perl":"sub { \"world\" }","python":"lambda: \"world\"","ruby":"proc { \"world\" }","js":"function() { return \"world\" }"}},"expected":"Hello, world!","template":"Hello, {{lambda}}!","desc":"A lambda's return value should be interpolated."},{"name":"Interpolation - Expansion","data":{"planet":"world","lambda":{"php":"return \"{{planet}}\";","clojure":"(fn [] \"{{planet}}\")","__tag__":"code","perl":"sub { \"{{planet}}\" }","python":"lambda: \"{{planet}}\"","ruby":"proc { \"{{planet}}\" }","js":"function() { return \"{{planet}}\" }"}},"expected":"Hello, world!","template":"Hello, {{lambda}}!","desc":"A lambda's return value should be parsed."},{"name":"Interpolation - Alternate Delimiters","data":{"planet":"world","lambda":{"php":"return \"|planet| => {{planet}}\";","clojure":"(fn [] \"|planet| => {{planet}}\")","__tag__":"code","perl":"sub { \"|planet| => {{planet}}\" }","python":"lambda: \"|planet| => {{planet}}\"","ruby":"proc { \"|planet| => {{planet}}\" }","js":"function() { return \"|planet| => {{planet}}\" }"}},"expected":"Hello, (|planet| => world)!","template":"{{= | | =}}\nHello, (|&lambda|)!","desc":"A lambda's return value should parse with the default delimiters."},{"name":"Interpolation - Multiple Calls","data":{"lambda":{"php":"global $calls; return ++$calls;","clojure":"(def g (atom 0)) (fn [] (swap! g inc))","__tag__":"code","perl":"sub { no strict; $calls += 1 }","python":"lambda: globals().update(calls=globals().get(\"calls\",0)+1) or calls","ruby":"proc { $calls ||= 0; $calls += 1 }","js":"function() { return (g=(function(){return this})()).calls=(g.calls||0)+1 }"}},"expected":"1 == 2 == 3","template":"{{lambda}} == {{{lambda}}} == {{lambda}}","desc":"Interpolated lambdas should not be cached."},{"name":"Escaping","data":{"lambda":{"php":"return \">\";","clojure":"(fn [] \">\")","__tag__":"code","perl":"sub { \">\" }","python":"lambda: \">\"","ruby":"proc { \">\" }","js":"function() { return \">\" }"}},"expected":"<>>","template":"<{{lambda}}{{{lambda}}}","desc":"Lambda results should be appropriately escaped."},{"name":"Section","data":{"x":"Error!","lambda":{"php":"return ($text == \"{{x}}\") ? \"yes\" : \"no\";","clojure":"(fn [text] (if (= text \"{{x}}\") \"yes\" \"no\"))","__tag__":"code","perl":"sub { $_[0] eq \"{{x}}\" ? \"yes\" : \"no\" }","python":"lambda text: text == \"{{x}}\" and \"yes\" or \"no\"","ruby":"proc { |text| text == \"{{x}}\" ? \"yes\" : \"no\" }","js":"function(txt) { return (txt == \"{{x}}\" ? \"yes\" : \"no\") }"}},"expected":"","template":"<{{#lambda}}{{x}}{{/lambda}}>","desc":"Lambdas used for sections should receive the raw section string."},{"name":"Section - Expansion","data":{"planet":"Earth","lambda":{"php":"return $text . \"{{planet}}\" . $text;","clojure":"(fn [text] (str text \"{{planet}}\" text))","__tag__":"code","perl":"sub { $_[0] . \"{{planet}}\" . $_[0] }","python":"lambda text: \"%s{{planet}}%s\" % (text, text)","ruby":"proc { |text| \"#{text}{{planet}}#{text}\" }","js":"function(txt) { return txt + \"{{planet}}\" + txt }"}},"expected":"<-Earth->","template":"<{{#lambda}}-{{/lambda}}>","desc":"Lambdas used for sections should have their results parsed."},{"name":"Section - Alternate Delimiters","data":{"planet":"Earth","lambda":{"php":"return $text . \"{{planet}} => |planet|\" . $text;","clojure":"(fn [text] (str text \"{{planet}} => |planet|\" text))","__tag__":"code","perl":"sub { $_[0] . \"{{planet}} => |planet|\" . $_[0] }","python":"lambda text: \"%s{{planet}} => |planet|%s\" % (text, text)","ruby":"proc { |text| \"#{text}{{planet}} => |planet|#{text}\" }","js":"function(txt) { return txt + \"{{planet}} => |planet|\" + txt }"}},"expected":"<-{{planet}} => Earth->","template":"{{= | | =}}<|#lambda|-|/lambda|>","desc":"Lambdas used for sections should parse with the current delimiters."},{"name":"Section - Multiple Calls","data":{"lambda":{"php":"return \"__\" . $text . \"__\";","clojure":"(fn [text] (str \"__\" text \"__\"))","__tag__":"code","perl":"sub { \"__\" . $_[0] . \"__\" }","python":"lambda text: \"__%s__\" % (text)","ruby":"proc { |text| \"__#{text}__\" }","js":"function(txt) { return \"__\" + txt + \"__\" }"}},"expected":"__FILE__ != __LINE__","template":"{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}","desc":"Lambdas used for sections should not be cached."},{"name":"Inverted Section","data":{"static":"static","lambda":{"php":"return false;","clojure":"(fn [text] false)","__tag__":"code","perl":"sub { 0 }","python":"lambda text: 0","ruby":"proc { |text| false }","js":"function(txt) { return false }"}},"expected":"<>","template":"<{{^lambda}}{{static}}{{/lambda}}>","desc":"Lambdas used for inverted sections should be considered truthy."}]} -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/partial.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // partial represents a partial template 9 | type partial struct { 10 | name string 11 | source string 12 | tpl *Template 13 | } 14 | 15 | // partials stores all global partials 16 | var partials map[string]*partial 17 | 18 | // protects global partials 19 | var partialsMutex sync.RWMutex 20 | 21 | func init() { 22 | partials = make(map[string]*partial) 23 | } 24 | 25 | // newPartial instanciates a new partial 26 | func newPartial(name string, source string, tpl *Template) *partial { 27 | return &partial{ 28 | name: name, 29 | source: source, 30 | tpl: tpl, 31 | } 32 | } 33 | 34 | // RegisterPartial registers a global partial. That partial will be available to all templates. 35 | func RegisterPartial(name string, source string) { 36 | partialsMutex.Lock() 37 | defer partialsMutex.Unlock() 38 | 39 | if partials[name] != nil { 40 | panic(fmt.Errorf("Partial already registered: %s", name)) 41 | } 42 | 43 | partials[name] = newPartial(name, source, nil) 44 | } 45 | 46 | // RegisterPartials registers several global partials. Those partials will be available to all templates. 47 | func RegisterPartials(partials map[string]string) { 48 | for name, p := range partials { 49 | RegisterPartial(name, p) 50 | } 51 | } 52 | 53 | // RegisterPartialTemplate registers a global partial with given parsed template. That partial will be available to all templates. 54 | func RegisterPartialTemplate(name string, tpl *Template) { 55 | partialsMutex.Lock() 56 | defer partialsMutex.Unlock() 57 | 58 | if partials[name] != nil { 59 | panic(fmt.Errorf("Partial already registered: %s", name)) 60 | } 61 | 62 | partials[name] = newPartial(name, "", tpl) 63 | } 64 | 65 | // findPartial finds a registered global partial 66 | func findPartial(name string) *partial { 67 | partialsMutex.RLock() 68 | defer partialsMutex.RUnlock() 69 | 70 | return partials[name] 71 | } 72 | 73 | // template returns parsed partial template 74 | func (p *partial) template() (*Template, error) { 75 | if p.tpl == nil { 76 | var err error 77 | 78 | p.tpl, err = Parse(p.source) 79 | if err != nil { 80 | return nil, err 81 | } 82 | } 83 | 84 | return p.tpl, nil 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/raymond.go: -------------------------------------------------------------------------------- 1 | // Package raymond provides handlebars evaluation 2 | package raymond 3 | 4 | // Render parses a template and evaluates it with given context 5 | // 6 | // Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead. 7 | func Render(source string, ctx interface{}) (string, error) { 8 | // parse template 9 | tpl, err := Parse(source) 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | // renders template 15 | str, err := tpl.Exec(ctx) 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | return str, nil 21 | } 22 | 23 | // MustRender parses a template and evaluates it with given context. It panics on error. 24 | // 25 | // Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead. 26 | func MustRender(source string, ctx interface{}) string { 27 | return MustParse(source).MustExec(ctx) 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/string.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | ) 8 | 9 | // SafeString represents a string that must not be escaped. 10 | // 11 | // A SafeString can be returned by helpers to disable escaping. 12 | type SafeString string 13 | 14 | // isSafeString returns true if argument is a SafeString 15 | func isSafeString(value interface{}) bool { 16 | if _, ok := value.(SafeString); ok { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | // Str returns string representation of any basic type value. 23 | func Str(value interface{}) string { 24 | return strValue(reflect.ValueOf(value)) 25 | } 26 | 27 | // strValue returns string representation of a reflect.Value 28 | func strValue(value reflect.Value) string { 29 | result := "" 30 | 31 | ival, ok := printableValue(value) 32 | if !ok { 33 | panic(fmt.Errorf("Can't print value: %q", value)) 34 | } 35 | 36 | val := reflect.ValueOf(ival) 37 | 38 | switch val.Kind() { 39 | case reflect.Array, reflect.Slice: 40 | for i := 0; i < val.Len(); i++ { 41 | result += strValue(val.Index(i)) 42 | } 43 | case reflect.Bool: 44 | result = "false" 45 | if val.Bool() { 46 | result = "true" 47 | } 48 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 49 | result = fmt.Sprintf("%d", ival) 50 | case reflect.Float32, reflect.Float64: 51 | result = strconv.FormatFloat(val.Float(), 'f', -1, 64) 52 | case reflect.Invalid: 53 | result = "" 54 | default: 55 | result = fmt.Sprintf("%s", ival) 56 | } 57 | 58 | return result 59 | } 60 | 61 | // printableValue returns the, possibly indirected, interface value inside v that 62 | // is best for a call to formatted printer. 63 | // 64 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 65 | func printableValue(v reflect.Value) (interface{}, bool) { 66 | if v.Kind() == reflect.Ptr { 67 | v, _ = indirect(v) // fmt.Fprint handles nil. 68 | } 69 | if !v.IsValid() { 70 | return "", true 71 | } 72 | 73 | if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { 74 | if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) { 75 | v = v.Addr() 76 | } else { 77 | switch v.Kind() { 78 | case reflect.Chan, reflect.Func: 79 | return nil, false 80 | } 81 | } 82 | } 83 | return v.Interface(), true 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/utils.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "path" 5 | "reflect" 6 | ) 7 | 8 | // indirect returns the item at the end of indirection, and a bool to indicate if it's nil. 9 | // We indirect through pointers and empty interfaces (only) because 10 | // non-empty interfaces have methods we might need. 11 | // 12 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 13 | func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { 14 | for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { 15 | if v.IsNil() { 16 | return v, true 17 | } 18 | if v.Kind() == reflect.Interface && v.NumMethod() > 0 { 19 | break 20 | } 21 | } 22 | return v, false 23 | } 24 | 25 | // IsTrue returns true if obj is a truthy value. 26 | func IsTrue(obj interface{}) bool { 27 | thruth, ok := isTrueValue(reflect.ValueOf(obj)) 28 | if !ok { 29 | return false 30 | } 31 | return thruth 32 | } 33 | 34 | // isTrueValue reports whether the value is 'true', in the sense of not the zero of its type, 35 | // and whether the value has a meaningful truth value 36 | // 37 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 38 | func isTrueValue(val reflect.Value) (truth, ok bool) { 39 | if !val.IsValid() { 40 | // Something like var x interface{}, never set. It's a form of nil. 41 | return false, true 42 | } 43 | switch val.Kind() { 44 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 45 | truth = val.Len() > 0 46 | case reflect.Bool: 47 | truth = val.Bool() 48 | case reflect.Complex64, reflect.Complex128: 49 | truth = val.Complex() != 0 50 | case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: 51 | truth = !val.IsNil() 52 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 53 | truth = val.Int() != 0 54 | case reflect.Float32, reflect.Float64: 55 | truth = val.Float() != 0 56 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 57 | truth = val.Uint() != 0 58 | case reflect.Struct: 59 | truth = true // Struct values are always true. 60 | default: 61 | return 62 | } 63 | return truth, true 64 | } 65 | 66 | // canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. 67 | // 68 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 69 | func canBeNil(typ reflect.Type) bool { 70 | switch typ.Kind() { 71 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 72 | return true 73 | } 74 | return false 75 | } 76 | 77 | // fileBase returns base file name 78 | // 79 | // example: /foo/bar/baz.png => baz 80 | func fileBase(filePath string) string { 81 | fileName := path.Base(filePath) 82 | fileExt := path.Ext(filePath) 83 | 84 | return fileName[:len(fileName)-len(fileExt)] 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/eknkc/amber/amberc/cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | amber "github.com/eknkc/amber" 7 | "os" 8 | ) 9 | 10 | var prettyPrint bool 11 | var lineNumbers bool 12 | 13 | func init() { 14 | flag.BoolVar(&prettyPrint, "prettyprint", true, "Use pretty indentation in output html.") 15 | flag.BoolVar(&prettyPrint, "pp", true, "Use pretty indentation in output html.") 16 | 17 | flag.BoolVar(&lineNumbers, "linenos", true, "Enable debugging information in output html.") 18 | flag.BoolVar(&lineNumbers, "ln", true, "Enable debugging information in output html.") 19 | 20 | flag.Parse() 21 | } 22 | 23 | func main() { 24 | input := flag.Arg(0) 25 | 26 | if len(input) == 0 { 27 | fmt.Fprintln(os.Stderr, "Please provide an input file. (amberc input.amber)") 28 | os.Exit(1) 29 | } 30 | 31 | cmp := amber.New() 32 | cmp.PrettyPrint = prettyPrint 33 | cmp.LineNumbers = lineNumbers 34 | 35 | err := cmp.ParseFile(input) 36 | 37 | if err != nil { 38 | fmt.Fprintln(os.Stderr, err) 39 | os.Exit(1) 40 | } 41 | 42 | err = cmp.CompileWriter(os.Stdout) 43 | 44 | if err != nil { 45 | fmt.Fprintln(os.Stderr, err) 46 | os.Exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/AUTHORS: -------------------------------------------------------------------------------- 1 | Main author and maintainer of pongo2: 2 | 3 | * Florian Schlachter 4 | 5 | Contributors (in no specific order): 6 | 7 | * @romanoaugusto88 8 | * @vitalbh 9 | 10 | Feel free to add yourself to the list or to modify your entry if you did a contribution. 11 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Florian Schlachter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/context.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | var reIdentifiers = regexp.MustCompile("^[a-zA-Z0-9_]+$") 9 | 10 | // A Context type provides constants, variables, instances or functions to a template. 11 | // 12 | // pongo2 automatically provides meta-information or functions through the "pongo2"-key. 13 | // Currently, context["pongo2"] contains the following keys: 14 | // 1. version: returns the version string 15 | // 16 | // Template examples for accessing items from your context: 17 | // {{ myconstant }} 18 | // {{ myfunc("test", 42) }} 19 | // {{ user.name }} 20 | // {{ pongo2.version }} 21 | type Context map[string]interface{} 22 | 23 | func (c Context) checkForValidIdentifiers() *Error { 24 | for k, v := range c { 25 | if !reIdentifiers.MatchString(k) { 26 | return &Error{ 27 | Sender: "checkForValidIdentifiers", 28 | ErrorMsg: fmt.Sprintf("Context-key '%s' (value: '%+v') is not a valid identifier.", k, v), 29 | } 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | // Update updates this context with the key/value-pairs from another context. 36 | func (c Context) Update(other Context) Context { 37 | for k, v := range other { 38 | c[k] = v 39 | } 40 | return c 41 | } 42 | 43 | // ExecutionContext contains all data important for the current rendering state. 44 | // 45 | // If you're writing a custom tag, your tag's Execute()-function will 46 | // have access to the ExecutionContext. This struct stores anything 47 | // about the current rendering process's Context including 48 | // the Context provided by the user (field Public). 49 | // You can safely use the Private context to provide data to the user's 50 | // template (like a 'forloop'-information). The Shared-context is used 51 | // to share data between tags. All ExecutionContexts share this context. 52 | // 53 | // Please be careful when accessing the Public data. 54 | // PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only). 55 | // 56 | // To create your own execution context within tags, use the 57 | // NewChildExecutionContext(parent) function. 58 | type ExecutionContext struct { 59 | template *Template 60 | 61 | Autoescape bool 62 | Public Context 63 | Private Context 64 | Shared Context 65 | } 66 | 67 | var pongo2MetaContext = Context{ 68 | "version": Version, 69 | } 70 | 71 | func newExecutionContext(tpl *Template, ctx Context) *ExecutionContext { 72 | privateCtx := make(Context) 73 | 74 | // Make the pongo2-related funcs/vars available to the context 75 | privateCtx["pongo2"] = pongo2MetaContext 76 | 77 | return &ExecutionContext{ 78 | template: tpl, 79 | 80 | Public: ctx, 81 | Private: privateCtx, 82 | Autoescape: true, 83 | } 84 | } 85 | 86 | func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext { 87 | newctx := &ExecutionContext{ 88 | template: parent.template, 89 | 90 | Public: parent.Public, 91 | Private: make(Context), 92 | Autoescape: parent.Autoescape, 93 | } 94 | newctx.Shared = parent.Shared 95 | 96 | // Copy all existing private items 97 | newctx.Private.Update(parent.Private) 98 | 99 | return newctx 100 | } 101 | 102 | func (ctx *ExecutionContext) Error(msg string, token *Token) *Error { 103 | filename := ctx.template.name 104 | var line, col int 105 | if token != nil { 106 | // No tokens available 107 | // TODO: Add location (from where?) 108 | filename = token.Filename 109 | line = token.Line 110 | col = token.Col 111 | } 112 | return &Error{ 113 | Template: ctx.template, 114 | Filename: filename, 115 | Line: line, 116 | Column: col, 117 | Token: token, 118 | Sender: "execution", 119 | ErrorMsg: msg, 120 | } 121 | } 122 | 123 | func (ctx *ExecutionContext) Logf(format string, args ...interface{}) { 124 | ctx.template.set.logf(format, args...) 125 | } 126 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/doc.go: -------------------------------------------------------------------------------- 1 | // A Django-syntax like template-engine 2 | // 3 | // Blog posts about pongo2 (including introduction and migration): 4 | // https://www.florian-schlachter.de/?tag=pongo2 5 | // 6 | // Complete documentation on the template language: 7 | // https://docs.djangoproject.com/en/dev/topics/templates/ 8 | // 9 | // Try out pongo2 live in the pongo2 playground: 10 | // https://www.florian-schlachter.de/pongo2/ 11 | // 12 | // Make sure to read README.md in the repository as well. 13 | // 14 | // A tiny example with template strings: 15 | // 16 | // (Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277) 17 | // 18 | // // Compile the template first (i. e. creating the AST) 19 | // tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!") 20 | // if err != nil { 21 | // panic(err) 22 | // } 23 | // // Now you can render the template with the given 24 | // // pongo2.Context how often you want to. 25 | // out, err := tpl.Execute(pongo2.Context{"name": "fred"}) 26 | // if err != nil { 27 | // panic(err) 28 | // } 29 | // fmt.Println(out) // Output: Hello Fred! 30 | // 31 | package pongo2 32 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/error.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | // The Error type is being used to address an error during lexing, parsing or 10 | // execution. If you want to return an error object (for example in your own 11 | // tag or filter) fill this object with as much information as you have. 12 | // Make sure "Sender" is always given (if you're returning an error within 13 | // a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag'). 14 | // It's okay if you only fill in ErrorMsg if you don't have any other details at hand. 15 | type Error struct { 16 | Template *Template 17 | Filename string 18 | Line int 19 | Column int 20 | Token *Token 21 | Sender string 22 | ErrorMsg string 23 | } 24 | 25 | func (e *Error) updateFromTokenIfNeeded(template *Template, t *Token) *Error { 26 | if e.Template == nil { 27 | e.Template = template 28 | } 29 | 30 | if e.Token == nil { 31 | e.Token = t 32 | if e.Line <= 0 { 33 | e.Line = t.Line 34 | e.Column = t.Col 35 | } 36 | } 37 | 38 | return e 39 | } 40 | 41 | // Returns a nice formatted error string. 42 | func (e *Error) Error() string { 43 | s := "[Error" 44 | if e.Sender != "" { 45 | s += " (where: " + e.Sender + ")" 46 | } 47 | if e.Filename != "" { 48 | s += " in " + e.Filename 49 | } 50 | if e.Line > 0 { 51 | s += fmt.Sprintf(" | Line %d Col %d", e.Line, e.Column) 52 | if e.Token != nil { 53 | s += fmt.Sprintf(" near '%s'", e.Token.Val) 54 | } 55 | } 56 | s += "] " 57 | s += e.ErrorMsg 58 | return s 59 | } 60 | 61 | // RawLine returns the affected line from the original template, if available. 62 | func (e *Error) RawLine() (line string, available bool) { 63 | if e.Line <= 0 || e.Filename == "" { 64 | return "", false 65 | } 66 | 67 | filename := e.Filename 68 | if e.Template != nil { 69 | filename = e.Template.set.resolveFilename(e.Template, e.Filename) 70 | } 71 | file, err := os.Open(filename) 72 | if err != nil { 73 | panic(err) 74 | } 75 | defer func() { 76 | err := file.Close() 77 | if err != nil { 78 | panic(err) 79 | } 80 | }() 81 | 82 | scanner := bufio.NewScanner(file) 83 | l := 0 84 | for scanner.Scan() { 85 | l++ 86 | if l == e.Line { 87 | return scanner.Text(), true 88 | } 89 | } 90 | return "", false 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/filters.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type FilterFunction func(in *Value, param *Value) (out *Value, err *Error) 8 | 9 | var filters map[string]FilterFunction 10 | 11 | func init() { 12 | filters = make(map[string]FilterFunction) 13 | } 14 | 15 | // Registers a new filter. If there's already a filter with the same 16 | // name, RegisterFilter will panic. You usually want to call this 17 | // function in the filter's init() function: 18 | // http://golang.org/doc/effective_go.html#init 19 | // 20 | // See http://www.florian-schlachter.de/post/pongo2/ for more about 21 | // writing filters and tags. 22 | func RegisterFilter(name string, fn FilterFunction) { 23 | _, existing := filters[name] 24 | if existing { 25 | panic(fmt.Sprintf("Filter with name '%s' is already registered.", name)) 26 | } 27 | filters[name] = fn 28 | } 29 | 30 | // Replaces an already registered filter with a new implementation. Use this 31 | // function with caution since it allows you to change existing filter behaviour. 32 | func ReplaceFilter(name string, fn FilterFunction) { 33 | _, existing := filters[name] 34 | if !existing { 35 | panic(fmt.Sprintf("Filter with name '%s' does not exist (therefore cannot be overridden).", name)) 36 | } 37 | filters[name] = fn 38 | } 39 | 40 | // Like ApplyFilter, but panics on an error 41 | func MustApplyFilter(name string, value *Value, param *Value) *Value { 42 | val, err := ApplyFilter(name, value, param) 43 | if err != nil { 44 | panic(err) 45 | } 46 | return val 47 | } 48 | 49 | // Applies a filter to a given value using the given parameters. Returns a *pongo2.Value or an error. 50 | func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) { 51 | fn, existing := filters[name] 52 | if !existing { 53 | return nil, &Error{ 54 | Sender: "applyfilter", 55 | ErrorMsg: fmt.Sprintf("Filter with name '%s' not found.", name), 56 | } 57 | } 58 | 59 | // Make sure param is a *Value 60 | if param == nil { 61 | param = AsValue(nil) 62 | } 63 | 64 | return fn(value, param) 65 | } 66 | 67 | type filterCall struct { 68 | token *Token 69 | 70 | name string 71 | parameter IEvaluator 72 | 73 | filterFunc FilterFunction 74 | } 75 | 76 | func (fc *filterCall) Execute(v *Value, ctx *ExecutionContext) (*Value, *Error) { 77 | var param *Value 78 | var err *Error 79 | 80 | if fc.parameter != nil { 81 | param, err = fc.parameter.Evaluate(ctx) 82 | if err != nil { 83 | return nil, err 84 | } 85 | } else { 86 | param = AsValue(nil) 87 | } 88 | 89 | filteredValue, err := fc.filterFunc(v, param) 90 | if err != nil { 91 | return nil, err.updateFromTokenIfNeeded(ctx.template, fc.token) 92 | } 93 | return filteredValue, nil 94 | } 95 | 96 | // Filter = IDENT | IDENT ":" FilterArg | IDENT "|" Filter 97 | func (p *Parser) parseFilter() (*filterCall, *Error) { 98 | identToken := p.MatchType(TokenIdentifier) 99 | 100 | // Check filter ident 101 | if identToken == nil { 102 | return nil, p.Error("Filter name must be an identifier.", nil) 103 | } 104 | 105 | filter := &filterCall{ 106 | token: identToken, 107 | name: identToken.Val, 108 | } 109 | 110 | // Get the appropriate filter function and bind it 111 | filterFn, exists := filters[identToken.Val] 112 | if !exists { 113 | return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", identToken.Val), identToken) 114 | } 115 | 116 | filter.filterFunc = filterFn 117 | 118 | // Check for filter-argument (2 tokens needed: ':' ARG) 119 | if p.Match(TokenSymbol, ":") != nil { 120 | if p.Peek(TokenSymbol, "}}") != nil { 121 | return nil, p.Error("Filter parameter required after ':'.", nil) 122 | } 123 | 124 | // Get filter argument expression 125 | v, err := p.parseVariableOrLiteral() 126 | if err != nil { 127 | return nil, err 128 | } 129 | filter.parameter = v 130 | } 131 | 132 | return filter, nil 133 | } 134 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/helpers.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | func max(a, b int) int { 4 | if a > b { 5 | return a 6 | } 7 | return b 8 | } 9 | 10 | func min(a, b int) int { 11 | if a < b { 12 | return a 13 | } 14 | return b 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/nodes.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | // The root document 4 | type nodeDocument struct { 5 | Nodes []INode 6 | } 7 | 8 | func (doc *nodeDocument) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | for _, n := range doc.Nodes { 10 | err := n.Execute(ctx, writer) 11 | if err != nil { 12 | return err 13 | } 14 | } 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/nodes_html.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type nodeHTML struct { 4 | token *Token 5 | } 6 | 7 | func (n *nodeHTML) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 8 | writer.WriteString(n.token.Val) 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/nodes_wrapper.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type NodeWrapper struct { 4 | Endtag string 5 | nodes []INode 6 | } 7 | 8 | func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | for _, n := range wrapper.nodes { 10 | err := n.Execute(ctx, writer) 11 | if err != nil { 12 | return err 13 | } 14 | } 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/parser_document.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | // Doc = { ( Filter | Tag | HTML ) } 4 | func (p *Parser) parseDocElement() (INode, *Error) { 5 | t := p.Current() 6 | 7 | switch t.Typ { 8 | case TokenHTML: 9 | p.Consume() // consume HTML element 10 | return &nodeHTML{token: t}, nil 11 | case TokenSymbol: 12 | switch t.Val { 13 | case "{{": 14 | // parse variable 15 | variable, err := p.parseVariableElement() 16 | if err != nil { 17 | return nil, err 18 | } 19 | return variable, nil 20 | case "{%": 21 | // parse tag 22 | tag, err := p.parseTagElement() 23 | if err != nil { 24 | return nil, err 25 | } 26 | return tag, nil 27 | } 28 | } 29 | return nil, p.Error("Unexpected token (only HTML/tags/filters in templates allowed)", t) 30 | } 31 | 32 | func (tpl *Template) parse() *Error { 33 | tpl.parser = newParser(tpl.name, tpl.tokens, tpl) 34 | doc, err := tpl.parser.parseDocument() 35 | if err != nil { 36 | return err 37 | } 38 | tpl.root = doc 39 | return nil 40 | } 41 | 42 | func (p *Parser) parseDocument() (*nodeDocument, *Error) { 43 | doc := &nodeDocument{} 44 | 45 | for p.Remaining() > 0 { 46 | node, err := p.parseDocElement() 47 | if err != nil { 48 | return nil, err 49 | } 50 | doc.Nodes = append(doc.Nodes, node) 51 | } 52 | 53 | return doc, nil 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/pongo2.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | // Version string 4 | const Version = "dev" 5 | 6 | // Must panics, if a Template couldn't successfully parsed. This is how you 7 | // would use it: 8 | // var baseTemplate = pongo2.Must(pongo2.FromFile("templates/base.html")) 9 | func Must(tpl *Template, err error) *Template { 10 | if err != nil { 11 | panic(err) 12 | } 13 | return tpl 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | /* Incomplete: 4 | ----------- 5 | 6 | verbatim (only the "name" argument is missing for verbatim) 7 | 8 | Reconsideration: 9 | ---------------- 10 | 11 | debug (reason: not sure what to output yet) 12 | regroup / Grouping on other properties (reason: maybe too python-specific; not sure how useful this would be in Go) 13 | 14 | Following built-in tags wont be added: 15 | -------------------------------------- 16 | 17 | csrf_token (reason: web-framework specific) 18 | load (reason: python-specific) 19 | url (reason: web-framework specific) 20 | */ 21 | 22 | import ( 23 | "fmt" 24 | ) 25 | 26 | type INodeTag interface { 27 | INode 28 | } 29 | 30 | // This is the function signature of the tag's parser you will have 31 | // to implement in order to create a new tag. 32 | // 33 | // 'doc' is providing access to the whole document while 'arguments' 34 | // is providing access to the user's arguments to the tag: 35 | // 36 | // {% your_tag_name some "arguments" 123 %} 37 | // 38 | // start_token will be the *Token with the tag's name in it (here: your_tag_name). 39 | // 40 | // Please see the Parser documentation on how to use the parser. 41 | // See RegisterTag()'s documentation for more information about 42 | // writing a tag as well. 43 | type TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) 44 | 45 | type tag struct { 46 | name string 47 | parser TagParser 48 | } 49 | 50 | var tags map[string]*tag 51 | 52 | func init() { 53 | tags = make(map[string]*tag) 54 | } 55 | 56 | // Registers a new tag. If there's already a tag with the same 57 | // name, RegisterTag will panic. You usually want to call this 58 | // function in the tag's init() function: 59 | // http://golang.org/doc/effective_go.html#init 60 | // 61 | // See http://www.florian-schlachter.de/post/pongo2/ for more about 62 | // writing filters and tags. 63 | func RegisterTag(name string, parserFn TagParser) { 64 | _, existing := tags[name] 65 | if existing { 66 | panic(fmt.Sprintf("Tag with name '%s' is already registered.", name)) 67 | } 68 | tags[name] = &tag{ 69 | name: name, 70 | parser: parserFn, 71 | } 72 | } 73 | 74 | // Replaces an already registered tag with a new implementation. Use this 75 | // function with caution since it allows you to change existing tag behaviour. 76 | func ReplaceTag(name string, parserFn TagParser) { 77 | _, existing := tags[name] 78 | if !existing { 79 | panic(fmt.Sprintf("Tag with name '%s' does not exist (therefore cannot be overridden).", name)) 80 | } 81 | tags[name] = &tag{ 82 | name: name, 83 | parser: parserFn, 84 | } 85 | } 86 | 87 | // Tag = "{%" IDENT ARGS "%}" 88 | func (p *Parser) parseTagElement() (INodeTag, *Error) { 89 | p.Consume() // consume "{%" 90 | tokenName := p.MatchType(TokenIdentifier) 91 | 92 | // Check for identifier 93 | if tokenName == nil { 94 | return nil, p.Error("Tag name must be an identifier.", nil) 95 | } 96 | 97 | // Check for the existing tag 98 | tag, exists := tags[tokenName.Val] 99 | if !exists { 100 | // Does not exists 101 | return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", tokenName.Val), tokenName) 102 | } 103 | 104 | // Check sandbox tag restriction 105 | if _, isBanned := p.template.set.bannedTags[tokenName.Val]; isBanned { 106 | return nil, p.Error(fmt.Sprintf("Usage of tag '%s' is not allowed (sandbox restriction active).", tokenName.Val), tokenName) 107 | } 108 | 109 | var argsToken []*Token 110 | for p.Peek(TokenSymbol, "%}") == nil && p.Remaining() > 0 { 111 | // Add token to args 112 | argsToken = append(argsToken, p.Current()) 113 | p.Consume() // next token 114 | } 115 | 116 | // EOF? 117 | if p.Remaining() == 0 { 118 | return nil, p.Error("Unexpectedly reached EOF, no tag end found.", p.lastToken) 119 | } 120 | 121 | p.Match(TokenSymbol, "%}") 122 | 123 | argParser := newParser(p.name, argsToken, p.template) 124 | if len(argsToken) == 0 { 125 | // This is done to have nice EOF error messages 126 | argParser.lastToken = tokenName 127 | } 128 | 129 | p.template.level++ 130 | defer func() { p.template.level-- }() 131 | return tag.parser(p, tokenName, argParser) 132 | } 133 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_autoescape.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagAutoescapeNode struct { 4 | wrapper *NodeWrapper 5 | autoescape bool 6 | } 7 | 8 | func (node *tagAutoescapeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | old := ctx.Autoescape 10 | ctx.Autoescape = node.autoescape 11 | 12 | err := node.wrapper.Execute(ctx, writer) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | ctx.Autoescape = old 18 | 19 | return nil 20 | } 21 | 22 | func tagAutoescapeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 23 | autoescapeNode := &tagAutoescapeNode{} 24 | 25 | wrapper, _, err := doc.WrapUntilTag("endautoescape") 26 | if err != nil { 27 | return nil, err 28 | } 29 | autoescapeNode.wrapper = wrapper 30 | 31 | modeToken := arguments.MatchType(TokenIdentifier) 32 | if modeToken == nil { 33 | return nil, arguments.Error("A mode is required for autoescape-tag.", nil) 34 | } 35 | if modeToken.Val == "on" { 36 | autoescapeNode.autoescape = true 37 | } else if modeToken.Val == "off" { 38 | autoescapeNode.autoescape = false 39 | } else { 40 | return nil, arguments.Error("Only 'on' or 'off' is valid as an autoescape-mode.", nil) 41 | } 42 | 43 | if arguments.Remaining() > 0 { 44 | return nil, arguments.Error("Malformed autoescape-tag arguments.", nil) 45 | } 46 | 47 | return autoescapeNode, nil 48 | } 49 | 50 | func init() { 51 | RegisterTag("autoescape", tagAutoescapeParser) 52 | } 53 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_block.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type tagBlockNode struct { 8 | name string 9 | } 10 | 11 | func (node *tagBlockNode) getBlockWrapperByName(tpl *Template) *NodeWrapper { 12 | var t *NodeWrapper 13 | if tpl.child != nil { 14 | // First ask the child for the block 15 | t = node.getBlockWrapperByName(tpl.child) 16 | } 17 | if t == nil { 18 | // Child has no block, lets look up here at parent 19 | t = tpl.blocks[node.name] 20 | } 21 | return t 22 | } 23 | 24 | func (node *tagBlockNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 25 | tpl := ctx.template 26 | if tpl == nil { 27 | panic("internal error: tpl == nil") 28 | } 29 | // Determine the block to execute 30 | blockWrapper := node.getBlockWrapperByName(tpl) 31 | if blockWrapper == nil { 32 | // fmt.Printf("could not find: %s\n", node.name) 33 | return ctx.Error("internal error: block_wrapper == nil in tagBlockNode.Execute()", nil) 34 | } 35 | err := blockWrapper.Execute(ctx, writer) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | // TODO: Add support for {{ block.super }} 41 | 42 | return nil 43 | } 44 | 45 | func tagBlockParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 46 | if arguments.Count() == 0 { 47 | return nil, arguments.Error("Tag 'block' requires an identifier.", nil) 48 | } 49 | 50 | nameToken := arguments.MatchType(TokenIdentifier) 51 | if nameToken == nil { 52 | return nil, arguments.Error("First argument for tag 'block' must be an identifier.", nil) 53 | } 54 | 55 | if arguments.Remaining() != 0 { 56 | return nil, arguments.Error("Tag 'block' takes exactly 1 argument (an identifier).", nil) 57 | } 58 | 59 | wrapper, endtagargs, err := doc.WrapUntilTag("endblock") 60 | if err != nil { 61 | return nil, err 62 | } 63 | if endtagargs.Remaining() > 0 { 64 | endtagnameToken := endtagargs.MatchType(TokenIdentifier) 65 | if endtagnameToken != nil { 66 | if endtagnameToken.Val != nameToken.Val { 67 | return nil, endtagargs.Error(fmt.Sprintf("Name for 'endblock' must equal to 'block'-tag's name ('%s' != '%s').", 68 | nameToken.Val, endtagnameToken.Val), nil) 69 | } 70 | } 71 | 72 | if endtagnameToken == nil || endtagargs.Remaining() > 0 { 73 | return nil, endtagargs.Error("Either no or only one argument (identifier) allowed for 'endblock'.", nil) 74 | } 75 | } 76 | 77 | tpl := doc.template 78 | if tpl == nil { 79 | panic("internal error: tpl == nil") 80 | } 81 | _, hasBlock := tpl.blocks[nameToken.Val] 82 | if !hasBlock { 83 | tpl.blocks[nameToken.Val] = wrapper 84 | } else { 85 | return nil, arguments.Error(fmt.Sprintf("Block named '%s' already defined", nameToken.Val), nil) 86 | } 87 | 88 | return &tagBlockNode{name: nameToken.Val}, nil 89 | } 90 | 91 | func init() { 92 | RegisterTag("block", tagBlockParser) 93 | } 94 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_comment.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagCommentNode struct{} 4 | 5 | func (node *tagCommentNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 6 | return nil 7 | } 8 | 9 | func tagCommentParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 10 | commentNode := &tagCommentNode{} 11 | 12 | // TODO: Process the endtag's arguments (see django 'comment'-tag documentation) 13 | _, _, err := doc.WrapUntilTag("endcomment") 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | if arguments.Count() != 0 { 19 | return nil, arguments.Error("Tag 'comment' does not take any argument.", nil) 20 | } 21 | 22 | return commentNode, nil 23 | } 24 | 25 | func init() { 26 | RegisterTag("comment", tagCommentParser) 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_cycle.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagCycleValue struct { 4 | node *tagCycleNode 5 | value *Value 6 | } 7 | 8 | type tagCycleNode struct { 9 | position *Token 10 | args []IEvaluator 11 | idx int 12 | asName string 13 | silent bool 14 | } 15 | 16 | func (cv *tagCycleValue) String() string { 17 | return cv.value.String() 18 | } 19 | 20 | func (node *tagCycleNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 21 | item := node.args[node.idx%len(node.args)] 22 | node.idx++ 23 | 24 | val, err := item.Evaluate(ctx) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if t, ok := val.Interface().(*tagCycleValue); ok { 30 | // {% cycle "test1" "test2" 31 | // {% cycle cycleitem %} 32 | 33 | // Update the cycle value with next value 34 | item := t.node.args[t.node.idx%len(t.node.args)] 35 | t.node.idx++ 36 | 37 | val, err := item.Evaluate(ctx) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | t.value = val 43 | 44 | if !t.node.silent { 45 | writer.WriteString(val.String()) 46 | } 47 | } else { 48 | // Regular call 49 | 50 | cycleValue := &tagCycleValue{ 51 | node: node, 52 | value: val, 53 | } 54 | 55 | if node.asName != "" { 56 | ctx.Private[node.asName] = cycleValue 57 | } 58 | if !node.silent { 59 | writer.WriteString(val.String()) 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | 66 | // HINT: We're not supporting the old comma-seperated list of expresions argument-style 67 | func tagCycleParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 68 | cycleNode := &tagCycleNode{ 69 | position: start, 70 | } 71 | 72 | for arguments.Remaining() > 0 { 73 | node, err := arguments.ParseExpression() 74 | if err != nil { 75 | return nil, err 76 | } 77 | cycleNode.args = append(cycleNode.args, node) 78 | 79 | if arguments.MatchOne(TokenKeyword, "as") != nil { 80 | // as 81 | 82 | nameToken := arguments.MatchType(TokenIdentifier) 83 | if nameToken == nil { 84 | return nil, arguments.Error("Name (identifier) expected after 'as'.", nil) 85 | } 86 | cycleNode.asName = nameToken.Val 87 | 88 | if arguments.MatchOne(TokenIdentifier, "silent") != nil { 89 | cycleNode.silent = true 90 | } 91 | 92 | // Now we're finished 93 | break 94 | } 95 | } 96 | 97 | if arguments.Remaining() > 0 { 98 | return nil, arguments.Error("Malformed cycle-tag.", nil) 99 | } 100 | 101 | return cycleNode, nil 102 | } 103 | 104 | func init() { 105 | RegisterTag("cycle", tagCycleParser) 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_extends.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagExtendsNode struct { 4 | filename string 5 | } 6 | 7 | func (node *tagExtendsNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 8 | return nil 9 | } 10 | 11 | func tagExtendsParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 12 | extendsNode := &tagExtendsNode{} 13 | 14 | if doc.template.level > 1 { 15 | return nil, arguments.Error("The 'extends' tag can only defined on root level.", start) 16 | } 17 | 18 | if doc.template.parent != nil { 19 | // Already one parent 20 | return nil, arguments.Error("This template has already one parent.", start) 21 | } 22 | 23 | if filenameToken := arguments.MatchType(TokenString); filenameToken != nil { 24 | // prepared, static template 25 | 26 | // Get parent's filename 27 | parentFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val) 28 | 29 | // Parse the parent 30 | parentTemplate, err := doc.template.set.FromFile(parentFilename) 31 | if err != nil { 32 | return nil, err.(*Error) 33 | } 34 | 35 | // Keep track of things 36 | parentTemplate.child = doc.template 37 | doc.template.parent = parentTemplate 38 | extendsNode.filename = parentFilename 39 | } else { 40 | return nil, arguments.Error("Tag 'extends' requires a template filename as string.", nil) 41 | } 42 | 43 | if arguments.Remaining() > 0 { 44 | return nil, arguments.Error("Tag 'extends' does only take 1 argument.", nil) 45 | } 46 | 47 | return extendsNode, nil 48 | } 49 | 50 | func init() { 51 | RegisterTag("extends", tagExtendsParser) 52 | } 53 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_filter.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type nodeFilterCall struct { 8 | name string 9 | paramExpr IEvaluator 10 | } 11 | 12 | type tagFilterNode struct { 13 | position *Token 14 | bodyWrapper *NodeWrapper 15 | filterChain []*nodeFilterCall 16 | } 17 | 18 | func (node *tagFilterNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 19 | temp := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB size 20 | 21 | err := node.bodyWrapper.Execute(ctx, temp) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | value := AsValue(temp.String()) 27 | 28 | for _, call := range node.filterChain { 29 | var param *Value 30 | if call.paramExpr != nil { 31 | param, err = call.paramExpr.Evaluate(ctx) 32 | if err != nil { 33 | return err 34 | } 35 | } else { 36 | param = AsValue(nil) 37 | } 38 | value, err = ApplyFilter(call.name, value, param) 39 | if err != nil { 40 | return ctx.Error(err.Error(), node.position) 41 | } 42 | } 43 | 44 | writer.WriteString(value.String()) 45 | 46 | return nil 47 | } 48 | 49 | func tagFilterParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 50 | filterNode := &tagFilterNode{ 51 | position: start, 52 | } 53 | 54 | wrapper, _, err := doc.WrapUntilTag("endfilter") 55 | if err != nil { 56 | return nil, err 57 | } 58 | filterNode.bodyWrapper = wrapper 59 | 60 | for arguments.Remaining() > 0 { 61 | filterCall := &nodeFilterCall{} 62 | 63 | nameToken := arguments.MatchType(TokenIdentifier) 64 | if nameToken == nil { 65 | return nil, arguments.Error("Expected a filter name (identifier).", nil) 66 | } 67 | filterCall.name = nameToken.Val 68 | 69 | if arguments.MatchOne(TokenSymbol, ":") != nil { 70 | // Filter parameter 71 | // NOTICE: we can't use ParseExpression() here, because it would parse the next filter "|..." as well in the argument list 72 | expr, err := arguments.parseVariableOrLiteral() 73 | if err != nil { 74 | return nil, err 75 | } 76 | filterCall.paramExpr = expr 77 | } 78 | 79 | filterNode.filterChain = append(filterNode.filterChain, filterCall) 80 | 81 | if arguments.MatchOne(TokenSymbol, "|") == nil { 82 | break 83 | } 84 | } 85 | 86 | if arguments.Remaining() > 0 { 87 | return nil, arguments.Error("Malformed filter-tag arguments.", nil) 88 | } 89 | 90 | return filterNode, nil 91 | } 92 | 93 | func init() { 94 | RegisterTag("filter", tagFilterParser) 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_firstof.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagFirstofNode struct { 4 | position *Token 5 | args []IEvaluator 6 | } 7 | 8 | func (node *tagFirstofNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | for _, arg := range node.args { 10 | val, err := arg.Evaluate(ctx) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | if val.IsTrue() { 16 | if ctx.Autoescape && !arg.FilterApplied("safe") { 17 | val, err = ApplyFilter("escape", val, nil) 18 | if err != nil { 19 | return err 20 | } 21 | } 22 | 23 | writer.WriteString(val.String()) 24 | return nil 25 | } 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func tagFirstofParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 32 | firstofNode := &tagFirstofNode{ 33 | position: start, 34 | } 35 | 36 | for arguments.Remaining() > 0 { 37 | node, err := arguments.ParseExpression() 38 | if err != nil { 39 | return nil, err 40 | } 41 | firstofNode.args = append(firstofNode.args, node) 42 | } 43 | 44 | return firstofNode, nil 45 | } 46 | 47 | func init() { 48 | RegisterTag("firstof", tagFirstofParser) 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_for.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagForNode struct { 4 | key string 5 | value string // only for maps: for key, value in map 6 | objectEvaluator IEvaluator 7 | reversed bool 8 | sorted bool 9 | 10 | bodyWrapper *NodeWrapper 11 | emptyWrapper *NodeWrapper 12 | } 13 | 14 | type tagForLoopInformation struct { 15 | Counter int 16 | Counter0 int 17 | Revcounter int 18 | Revcounter0 int 19 | First bool 20 | Last bool 21 | Parentloop *tagForLoopInformation 22 | } 23 | 24 | func (node *tagForNode) Execute(ctx *ExecutionContext, writer TemplateWriter) (forError *Error) { 25 | // Backup forloop (as parentloop in public context), key-name and value-name 26 | forCtx := NewChildExecutionContext(ctx) 27 | parentloop := forCtx.Private["forloop"] 28 | 29 | // Create loop struct 30 | loopInfo := &tagForLoopInformation{ 31 | First: true, 32 | } 33 | 34 | // Is it a loop in a loop? 35 | if parentloop != nil { 36 | loopInfo.Parentloop = parentloop.(*tagForLoopInformation) 37 | } 38 | 39 | // Register loopInfo in public context 40 | forCtx.Private["forloop"] = loopInfo 41 | 42 | obj, err := node.objectEvaluator.Evaluate(forCtx) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | obj.IterateOrder(func(idx, count int, key, value *Value) bool { 48 | // There's something to iterate over (correct type and at least 1 item) 49 | 50 | // Update loop infos and public context 51 | forCtx.Private[node.key] = key 52 | if value != nil { 53 | forCtx.Private[node.value] = value 54 | } 55 | loopInfo.Counter = idx + 1 56 | loopInfo.Counter0 = idx 57 | if idx == 1 { 58 | loopInfo.First = false 59 | } 60 | if idx+1 == count { 61 | loopInfo.Last = true 62 | } 63 | loopInfo.Revcounter = count - idx // TODO: Not sure about this, have to look it up 64 | loopInfo.Revcounter0 = count - (idx + 1) // TODO: Not sure about this, have to look it up 65 | 66 | // Render elements with updated context 67 | err := node.bodyWrapper.Execute(forCtx, writer) 68 | if err != nil { 69 | forError = err 70 | return false 71 | } 72 | return true 73 | }, func() { 74 | // Nothing to iterate over (maybe wrong type or no items) 75 | if node.emptyWrapper != nil { 76 | err := node.emptyWrapper.Execute(forCtx, writer) 77 | if err != nil { 78 | forError = err 79 | } 80 | } 81 | }, node.reversed, node.sorted) 82 | 83 | return forError 84 | } 85 | 86 | func tagForParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 87 | forNode := &tagForNode{} 88 | 89 | // Arguments parsing 90 | var valueToken *Token 91 | keyToken := arguments.MatchType(TokenIdentifier) 92 | if keyToken == nil { 93 | return nil, arguments.Error("Expected an key identifier as first argument for 'for'-tag", nil) 94 | } 95 | 96 | if arguments.Match(TokenSymbol, ",") != nil { 97 | // Value name is provided 98 | valueToken = arguments.MatchType(TokenIdentifier) 99 | if valueToken == nil { 100 | return nil, arguments.Error("Value name must be an identifier.", nil) 101 | } 102 | } 103 | 104 | if arguments.Match(TokenKeyword, "in") == nil { 105 | return nil, arguments.Error("Expected keyword 'in'.", nil) 106 | } 107 | 108 | objectEvaluator, err := arguments.ParseExpression() 109 | if err != nil { 110 | return nil, err 111 | } 112 | forNode.objectEvaluator = objectEvaluator 113 | forNode.key = keyToken.Val 114 | if valueToken != nil { 115 | forNode.value = valueToken.Val 116 | } 117 | 118 | if arguments.MatchOne(TokenIdentifier, "reversed") != nil { 119 | forNode.reversed = true 120 | } 121 | 122 | if arguments.MatchOne(TokenIdentifier, "sorted") != nil { 123 | forNode.sorted = true 124 | } 125 | 126 | if arguments.Remaining() > 0 { 127 | return nil, arguments.Error("Malformed for-loop arguments.", nil) 128 | } 129 | 130 | // Body wrapping 131 | wrapper, endargs, err := doc.WrapUntilTag("empty", "endfor") 132 | if err != nil { 133 | return nil, err 134 | } 135 | forNode.bodyWrapper = wrapper 136 | 137 | if endargs.Count() > 0 { 138 | return nil, endargs.Error("Arguments not allowed here.", nil) 139 | } 140 | 141 | if wrapper.Endtag == "empty" { 142 | // if there's an else in the if-statement, we need the else-Block as well 143 | wrapper, endargs, err = doc.WrapUntilTag("endfor") 144 | if err != nil { 145 | return nil, err 146 | } 147 | forNode.emptyWrapper = wrapper 148 | 149 | if endargs.Count() > 0 { 150 | return nil, endargs.Error("Arguments not allowed here.", nil) 151 | } 152 | } 153 | 154 | return forNode, nil 155 | } 156 | 157 | func init() { 158 | RegisterTag("for", tagForParser) 159 | } 160 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_if.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagIfNode struct { 4 | conditions []IEvaluator 5 | wrappers []*NodeWrapper 6 | } 7 | 8 | func (node *tagIfNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | for i, condition := range node.conditions { 10 | result, err := condition.Evaluate(ctx) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | if result.IsTrue() { 16 | return node.wrappers[i].Execute(ctx, writer) 17 | } 18 | // Last condition? 19 | if len(node.conditions) == i+1 && len(node.wrappers) > i+1 { 20 | return node.wrappers[i+1].Execute(ctx, writer) 21 | } 22 | } 23 | return nil 24 | } 25 | 26 | func tagIfParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 27 | ifNode := &tagIfNode{} 28 | 29 | // Parse first and main IF condition 30 | condition, err := arguments.ParseExpression() 31 | if err != nil { 32 | return nil, err 33 | } 34 | ifNode.conditions = append(ifNode.conditions, condition) 35 | 36 | if arguments.Remaining() > 0 { 37 | return nil, arguments.Error("If-condition is malformed.", nil) 38 | } 39 | 40 | // Check the rest 41 | for { 42 | wrapper, tagArgs, err := doc.WrapUntilTag("elif", "else", "endif") 43 | if err != nil { 44 | return nil, err 45 | } 46 | ifNode.wrappers = append(ifNode.wrappers, wrapper) 47 | 48 | if wrapper.Endtag == "elif" { 49 | // elif can take a condition 50 | condition, err = tagArgs.ParseExpression() 51 | if err != nil { 52 | return nil, err 53 | } 54 | ifNode.conditions = append(ifNode.conditions, condition) 55 | 56 | if tagArgs.Remaining() > 0 { 57 | return nil, tagArgs.Error("Elif-condition is malformed.", nil) 58 | } 59 | } else { 60 | if tagArgs.Count() > 0 { 61 | // else/endif can't take any conditions 62 | return nil, tagArgs.Error("Arguments not allowed here.", nil) 63 | } 64 | } 65 | 66 | if wrapper.Endtag == "endif" { 67 | break 68 | } 69 | } 70 | 71 | return ifNode, nil 72 | } 73 | 74 | func init() { 75 | RegisterTag("if", tagIfParser) 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_ifchanged.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type tagIfchangedNode struct { 8 | watchedExpr []IEvaluator 9 | lastValues []*Value 10 | lastContent []byte 11 | thenWrapper *NodeWrapper 12 | elseWrapper *NodeWrapper 13 | } 14 | 15 | func (node *tagIfchangedNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 16 | if len(node.watchedExpr) == 0 { 17 | // Check against own rendered body 18 | 19 | buf := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB 20 | err := node.thenWrapper.Execute(ctx, buf) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | bufBytes := buf.Bytes() 26 | if !bytes.Equal(node.lastContent, bufBytes) { 27 | // Rendered content changed, output it 28 | writer.Write(bufBytes) 29 | node.lastContent = bufBytes 30 | } 31 | } else { 32 | nowValues := make([]*Value, 0, len(node.watchedExpr)) 33 | for _, expr := range node.watchedExpr { 34 | val, err := expr.Evaluate(ctx) 35 | if err != nil { 36 | return err 37 | } 38 | nowValues = append(nowValues, val) 39 | } 40 | 41 | // Compare old to new values now 42 | changed := len(node.lastValues) == 0 43 | 44 | for idx, oldVal := range node.lastValues { 45 | if !oldVal.EqualValueTo(nowValues[idx]) { 46 | changed = true 47 | break // we can stop here because ONE value changed 48 | } 49 | } 50 | 51 | node.lastValues = nowValues 52 | 53 | if changed { 54 | // Render thenWrapper 55 | err := node.thenWrapper.Execute(ctx, writer) 56 | if err != nil { 57 | return err 58 | } 59 | } else { 60 | // Render elseWrapper 61 | err := node.elseWrapper.Execute(ctx, writer) 62 | if err != nil { 63 | return err 64 | } 65 | } 66 | } 67 | 68 | return nil 69 | } 70 | 71 | func tagIfchangedParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 72 | ifchangedNode := &tagIfchangedNode{} 73 | 74 | for arguments.Remaining() > 0 { 75 | // Parse condition 76 | expr, err := arguments.ParseExpression() 77 | if err != nil { 78 | return nil, err 79 | } 80 | ifchangedNode.watchedExpr = append(ifchangedNode.watchedExpr, expr) 81 | } 82 | 83 | if arguments.Remaining() > 0 { 84 | return nil, arguments.Error("Ifchanged-arguments are malformed.", nil) 85 | } 86 | 87 | // Wrap then/else-blocks 88 | wrapper, endargs, err := doc.WrapUntilTag("else", "endifchanged") 89 | if err != nil { 90 | return nil, err 91 | } 92 | ifchangedNode.thenWrapper = wrapper 93 | 94 | if endargs.Count() > 0 { 95 | return nil, endargs.Error("Arguments not allowed here.", nil) 96 | } 97 | 98 | if wrapper.Endtag == "else" { 99 | // if there's an else in the if-statement, we need the else-Block as well 100 | wrapper, endargs, err = doc.WrapUntilTag("endifchanged") 101 | if err != nil { 102 | return nil, err 103 | } 104 | ifchangedNode.elseWrapper = wrapper 105 | 106 | if endargs.Count() > 0 { 107 | return nil, endargs.Error("Arguments not allowed here.", nil) 108 | } 109 | } 110 | 111 | return ifchangedNode, nil 112 | } 113 | 114 | func init() { 115 | RegisterTag("ifchanged", tagIfchangedParser) 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_ifequal.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagIfEqualNode struct { 4 | var1, var2 IEvaluator 5 | thenWrapper *NodeWrapper 6 | elseWrapper *NodeWrapper 7 | } 8 | 9 | func (node *tagIfEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 10 | r1, err := node.var1.Evaluate(ctx) 11 | if err != nil { 12 | return err 13 | } 14 | r2, err := node.var2.Evaluate(ctx) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | result := r1.EqualValueTo(r2) 20 | 21 | if result { 22 | return node.thenWrapper.Execute(ctx, writer) 23 | } 24 | if node.elseWrapper != nil { 25 | return node.elseWrapper.Execute(ctx, writer) 26 | } 27 | return nil 28 | } 29 | 30 | func tagIfEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 31 | ifequalNode := &tagIfEqualNode{} 32 | 33 | // Parse two expressions 34 | var1, err := arguments.ParseExpression() 35 | if err != nil { 36 | return nil, err 37 | } 38 | var2, err := arguments.ParseExpression() 39 | if err != nil { 40 | return nil, err 41 | } 42 | ifequalNode.var1 = var1 43 | ifequalNode.var2 = var2 44 | 45 | if arguments.Remaining() > 0 { 46 | return nil, arguments.Error("ifequal only takes 2 arguments.", nil) 47 | } 48 | 49 | // Wrap then/else-blocks 50 | wrapper, endargs, err := doc.WrapUntilTag("else", "endifequal") 51 | if err != nil { 52 | return nil, err 53 | } 54 | ifequalNode.thenWrapper = wrapper 55 | 56 | if endargs.Count() > 0 { 57 | return nil, endargs.Error("Arguments not allowed here.", nil) 58 | } 59 | 60 | if wrapper.Endtag == "else" { 61 | // if there's an else in the if-statement, we need the else-Block as well 62 | wrapper, endargs, err = doc.WrapUntilTag("endifequal") 63 | if err != nil { 64 | return nil, err 65 | } 66 | ifequalNode.elseWrapper = wrapper 67 | 68 | if endargs.Count() > 0 { 69 | return nil, endargs.Error("Arguments not allowed here.", nil) 70 | } 71 | } 72 | 73 | return ifequalNode, nil 74 | } 75 | 76 | func init() { 77 | RegisterTag("ifequal", tagIfEqualParser) 78 | } 79 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_ifnotequal.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagIfNotEqualNode struct { 4 | var1, var2 IEvaluator 5 | thenWrapper *NodeWrapper 6 | elseWrapper *NodeWrapper 7 | } 8 | 9 | func (node *tagIfNotEqualNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 10 | r1, err := node.var1.Evaluate(ctx) 11 | if err != nil { 12 | return err 13 | } 14 | r2, err := node.var2.Evaluate(ctx) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | result := !r1.EqualValueTo(r2) 20 | 21 | if result { 22 | return node.thenWrapper.Execute(ctx, writer) 23 | } 24 | if node.elseWrapper != nil { 25 | return node.elseWrapper.Execute(ctx, writer) 26 | } 27 | return nil 28 | } 29 | 30 | func tagIfNotEqualParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 31 | ifnotequalNode := &tagIfNotEqualNode{} 32 | 33 | // Parse two expressions 34 | var1, err := arguments.ParseExpression() 35 | if err != nil { 36 | return nil, err 37 | } 38 | var2, err := arguments.ParseExpression() 39 | if err != nil { 40 | return nil, err 41 | } 42 | ifnotequalNode.var1 = var1 43 | ifnotequalNode.var2 = var2 44 | 45 | if arguments.Remaining() > 0 { 46 | return nil, arguments.Error("ifequal only takes 2 arguments.", nil) 47 | } 48 | 49 | // Wrap then/else-blocks 50 | wrapper, endargs, err := doc.WrapUntilTag("else", "endifnotequal") 51 | if err != nil { 52 | return nil, err 53 | } 54 | ifnotequalNode.thenWrapper = wrapper 55 | 56 | if endargs.Count() > 0 { 57 | return nil, endargs.Error("Arguments not allowed here.", nil) 58 | } 59 | 60 | if wrapper.Endtag == "else" { 61 | // if there's an else in the if-statement, we need the else-Block as well 62 | wrapper, endargs, err = doc.WrapUntilTag("endifnotequal") 63 | if err != nil { 64 | return nil, err 65 | } 66 | ifnotequalNode.elseWrapper = wrapper 67 | 68 | if endargs.Count() > 0 { 69 | return nil, endargs.Error("Arguments not allowed here.", nil) 70 | } 71 | } 72 | 73 | return ifnotequalNode, nil 74 | } 75 | 76 | func init() { 77 | RegisterTag("ifnotequal", tagIfNotEqualParser) 78 | } 79 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_import.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type tagImportNode struct { 8 | position *Token 9 | filename string 10 | macros map[string]*tagMacroNode // alias/name -> macro instance 11 | } 12 | 13 | func (node *tagImportNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 14 | for name, macro := range node.macros { 15 | func(name string, macro *tagMacroNode) { 16 | ctx.Private[name] = func(args ...*Value) *Value { 17 | return macro.call(ctx, args...) 18 | } 19 | }(name, macro) 20 | } 21 | return nil 22 | } 23 | 24 | func tagImportParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 25 | importNode := &tagImportNode{ 26 | position: start, 27 | macros: make(map[string]*tagMacroNode), 28 | } 29 | 30 | filenameToken := arguments.MatchType(TokenString) 31 | if filenameToken == nil { 32 | return nil, arguments.Error("Import-tag needs a filename as string.", nil) 33 | } 34 | 35 | importNode.filename = doc.template.set.resolveFilename(doc.template, filenameToken.Val) 36 | 37 | if arguments.Remaining() == 0 { 38 | return nil, arguments.Error("You must at least specify one macro to import.", nil) 39 | } 40 | 41 | // Compile the given template 42 | tpl, err := doc.template.set.FromFile(importNode.filename) 43 | if err != nil { 44 | return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, start) 45 | } 46 | 47 | for arguments.Remaining() > 0 { 48 | macroNameToken := arguments.MatchType(TokenIdentifier) 49 | if macroNameToken == nil { 50 | return nil, arguments.Error("Expected macro name (identifier).", nil) 51 | } 52 | 53 | asName := macroNameToken.Val 54 | if arguments.Match(TokenKeyword, "as") != nil { 55 | aliasToken := arguments.MatchType(TokenIdentifier) 56 | if aliasToken == nil { 57 | return nil, arguments.Error("Expected macro alias name (identifier).", nil) 58 | } 59 | asName = aliasToken.Val 60 | } 61 | 62 | macroInstance, has := tpl.exportedMacros[macroNameToken.Val] 63 | if !has { 64 | return nil, arguments.Error(fmt.Sprintf("Macro '%s' not found (or not exported) in '%s'.", macroNameToken.Val, 65 | importNode.filename), macroNameToken) 66 | } 67 | 68 | importNode.macros[asName] = macroInstance 69 | 70 | if arguments.Remaining() == 0 { 71 | break 72 | } 73 | 74 | if arguments.Match(TokenSymbol, ",") == nil { 75 | return nil, arguments.Error("Expected ','.", nil) 76 | } 77 | } 78 | 79 | return importNode, nil 80 | } 81 | 82 | func init() { 83 | RegisterTag("import", tagImportParser) 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_include.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagIncludeNode struct { 4 | tpl *Template 5 | filenameEvaluator IEvaluator 6 | lazy bool 7 | only bool 8 | filename string 9 | withPairs map[string]IEvaluator 10 | ifExists bool 11 | } 12 | 13 | func (node *tagIncludeNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 14 | // Building the context for the template 15 | includeCtx := make(Context) 16 | 17 | // Fill the context with all data from the parent 18 | if !node.only { 19 | includeCtx.Update(ctx.Public) 20 | includeCtx.Update(ctx.Private) 21 | } 22 | 23 | // Put all custom with-pairs into the context 24 | for key, value := range node.withPairs { 25 | val, err := value.Evaluate(ctx) 26 | if err != nil { 27 | return err 28 | } 29 | includeCtx[key] = val 30 | } 31 | 32 | // Execute the template 33 | if node.lazy { 34 | // Evaluate the filename 35 | filename, err := node.filenameEvaluator.Evaluate(ctx) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | if filename.String() == "" { 41 | return ctx.Error("Filename for 'include'-tag evaluated to an empty string.", nil) 42 | } 43 | 44 | // Get include-filename 45 | includedFilename := ctx.template.set.resolveFilename(ctx.template, filename.String()) 46 | 47 | includedTpl, err2 := ctx.template.set.FromFile(includedFilename) 48 | if err2 != nil { 49 | // if this is ReadFile error, and "if_exists" flag is enabled 50 | if node.ifExists && err2.(*Error).Sender == "fromfile" { 51 | return nil 52 | } 53 | return err2.(*Error) 54 | } 55 | err2 = includedTpl.ExecuteWriter(includeCtx, writer) 56 | if err2 != nil { 57 | return err2.(*Error) 58 | } 59 | return nil 60 | } 61 | // Template is already parsed with static filename 62 | err := node.tpl.ExecuteWriter(includeCtx, writer) 63 | if err != nil { 64 | return err.(*Error) 65 | } 66 | return nil 67 | } 68 | 69 | type tagIncludeEmptyNode struct{} 70 | 71 | func (node *tagIncludeEmptyNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 72 | return nil 73 | } 74 | 75 | func tagIncludeParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 76 | includeNode := &tagIncludeNode{ 77 | withPairs: make(map[string]IEvaluator), 78 | } 79 | 80 | if filenameToken := arguments.MatchType(TokenString); filenameToken != nil { 81 | // prepared, static template 82 | 83 | // "if_exists" flag 84 | ifExists := arguments.Match(TokenIdentifier, "if_exists") != nil 85 | 86 | // Get include-filename 87 | includedFilename := doc.template.set.resolveFilename(doc.template, filenameToken.Val) 88 | 89 | // Parse the parent 90 | includeNode.filename = includedFilename 91 | includedTpl, err := doc.template.set.FromFile(includedFilename) 92 | if err != nil { 93 | // if this is ReadFile error, and "if_exists" token presents we should create and empty node 94 | if err.(*Error).Sender == "fromfile" && ifExists { 95 | return &tagIncludeEmptyNode{}, nil 96 | } 97 | return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, filenameToken) 98 | } 99 | includeNode.tpl = includedTpl 100 | } else { 101 | // No String, then the user wants to use lazy-evaluation (slower, but possible) 102 | filenameEvaluator, err := arguments.ParseExpression() 103 | if err != nil { 104 | return nil, err.updateFromTokenIfNeeded(doc.template, filenameToken) 105 | } 106 | includeNode.filenameEvaluator = filenameEvaluator 107 | includeNode.lazy = true 108 | includeNode.ifExists = arguments.Match(TokenIdentifier, "if_exists") != nil // "if_exists" flag 109 | } 110 | 111 | // After having parsed the filename we're gonna parse the with+only options 112 | if arguments.Match(TokenIdentifier, "with") != nil { 113 | for arguments.Remaining() > 0 { 114 | // We have at least one key=expr pair (because of starting "with") 115 | keyToken := arguments.MatchType(TokenIdentifier) 116 | if keyToken == nil { 117 | return nil, arguments.Error("Expected an identifier", nil) 118 | } 119 | if arguments.Match(TokenSymbol, "=") == nil { 120 | return nil, arguments.Error("Expected '='.", nil) 121 | } 122 | valueExpr, err := arguments.ParseExpression() 123 | if err != nil { 124 | return nil, err.updateFromTokenIfNeeded(doc.template, keyToken) 125 | } 126 | 127 | includeNode.withPairs[keyToken.Val] = valueExpr 128 | 129 | // Only? 130 | if arguments.Match(TokenIdentifier, "only") != nil { 131 | includeNode.only = true 132 | break // stop parsing arguments because it's the last option 133 | } 134 | } 135 | } 136 | 137 | if arguments.Remaining() > 0 { 138 | return nil, arguments.Error("Malformed 'include'-tag arguments.", nil) 139 | } 140 | 141 | return includeNode, nil 142 | } 143 | 144 | func init() { 145 | RegisterTag("include", tagIncludeParser) 146 | } 147 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_macro.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type tagMacroNode struct { 9 | position *Token 10 | name string 11 | argsOrder []string 12 | args map[string]IEvaluator 13 | exported bool 14 | 15 | wrapper *NodeWrapper 16 | } 17 | 18 | func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 19 | ctx.Private[node.name] = func(args ...*Value) *Value { 20 | return node.call(ctx, args...) 21 | } 22 | 23 | return nil 24 | } 25 | 26 | func (node *tagMacroNode) call(ctx *ExecutionContext, args ...*Value) *Value { 27 | argsCtx := make(Context) 28 | 29 | for k, v := range node.args { 30 | if v == nil { 31 | // User did not provided a default value 32 | argsCtx[k] = nil 33 | } else { 34 | // Evaluate the default value 35 | valueExpr, err := v.Evaluate(ctx) 36 | if err != nil { 37 | ctx.Logf(err.Error()) 38 | return AsSafeValue(err.Error()) 39 | } 40 | 41 | argsCtx[k] = valueExpr 42 | } 43 | } 44 | 45 | if len(args) > len(node.argsOrder) { 46 | // Too many arguments, we're ignoring them and just logging into debug mode. 47 | err := ctx.Error(fmt.Sprintf("Macro '%s' called with too many arguments (%d instead of %d).", 48 | node.name, len(args), len(node.argsOrder)), nil).updateFromTokenIfNeeded(ctx.template, node.position) 49 | 50 | ctx.Logf(err.Error()) // TODO: This is a workaround, because the error is not returned yet to the Execution()-methods 51 | return AsSafeValue(err.Error()) 52 | } 53 | 54 | // Make a context for the macro execution 55 | macroCtx := NewChildExecutionContext(ctx) 56 | 57 | // Register all arguments in the private context 58 | macroCtx.Private.Update(argsCtx) 59 | 60 | for idx, argValue := range args { 61 | macroCtx.Private[node.argsOrder[idx]] = argValue.Interface() 62 | } 63 | 64 | var b bytes.Buffer 65 | err := node.wrapper.Execute(macroCtx, &b) 66 | if err != nil { 67 | return AsSafeValue(err.updateFromTokenIfNeeded(ctx.template, node.position).Error()) 68 | } 69 | 70 | return AsSafeValue(b.String()) 71 | } 72 | 73 | func tagMacroParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 74 | macroNode := &tagMacroNode{ 75 | position: start, 76 | args: make(map[string]IEvaluator), 77 | } 78 | 79 | nameToken := arguments.MatchType(TokenIdentifier) 80 | if nameToken == nil { 81 | return nil, arguments.Error("Macro-tag needs at least an identifier as name.", nil) 82 | } 83 | macroNode.name = nameToken.Val 84 | 85 | if arguments.MatchOne(TokenSymbol, "(") == nil { 86 | return nil, arguments.Error("Expected '('.", nil) 87 | } 88 | 89 | for arguments.Match(TokenSymbol, ")") == nil { 90 | argNameToken := arguments.MatchType(TokenIdentifier) 91 | if argNameToken == nil { 92 | return nil, arguments.Error("Expected argument name as identifier.", nil) 93 | } 94 | macroNode.argsOrder = append(macroNode.argsOrder, argNameToken.Val) 95 | 96 | if arguments.Match(TokenSymbol, "=") != nil { 97 | // Default expression follows 98 | argDefaultExpr, err := arguments.ParseExpression() 99 | if err != nil { 100 | return nil, err 101 | } 102 | macroNode.args[argNameToken.Val] = argDefaultExpr 103 | } else { 104 | // No default expression 105 | macroNode.args[argNameToken.Val] = nil 106 | } 107 | 108 | if arguments.Match(TokenSymbol, ")") != nil { 109 | break 110 | } 111 | if arguments.Match(TokenSymbol, ",") == nil { 112 | return nil, arguments.Error("Expected ',' or ')'.", nil) 113 | } 114 | } 115 | 116 | if arguments.Match(TokenKeyword, "export") != nil { 117 | macroNode.exported = true 118 | } 119 | 120 | if arguments.Remaining() > 0 { 121 | return nil, arguments.Error("Malformed macro-tag.", nil) 122 | } 123 | 124 | // Body wrapping 125 | wrapper, endargs, err := doc.WrapUntilTag("endmacro") 126 | if err != nil { 127 | return nil, err 128 | } 129 | macroNode.wrapper = wrapper 130 | 131 | if endargs.Count() > 0 { 132 | return nil, endargs.Error("Arguments not allowed here.", nil) 133 | } 134 | 135 | if macroNode.exported { 136 | // Now register the macro if it wants to be exported 137 | _, has := doc.template.exportedMacros[macroNode.name] 138 | if has { 139 | return nil, doc.Error(fmt.Sprintf("Another macro with name '%s' already exported.", macroNode.name), start) 140 | } 141 | doc.template.exportedMacros[macroNode.name] = macroNode 142 | } 143 | 144 | return macroNode, nil 145 | } 146 | 147 | func init() { 148 | RegisterTag("macro", tagMacroParser) 149 | } 150 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_now.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type tagNowNode struct { 8 | position *Token 9 | format string 10 | fake bool 11 | } 12 | 13 | func (node *tagNowNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 14 | var t time.Time 15 | if node.fake { 16 | t = time.Date(2014, time.February, 05, 18, 31, 45, 00, time.UTC) 17 | } else { 18 | t = time.Now() 19 | } 20 | 21 | writer.WriteString(t.Format(node.format)) 22 | 23 | return nil 24 | } 25 | 26 | func tagNowParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 27 | nowNode := &tagNowNode{ 28 | position: start, 29 | } 30 | 31 | formatToken := arguments.MatchType(TokenString) 32 | if formatToken == nil { 33 | return nil, arguments.Error("Expected a format string.", nil) 34 | } 35 | nowNode.format = formatToken.Val 36 | 37 | if arguments.MatchOne(TokenIdentifier, "fake") != nil { 38 | nowNode.fake = true 39 | } 40 | 41 | if arguments.Remaining() > 0 { 42 | return nil, arguments.Error("Malformed now-tag arguments.", nil) 43 | } 44 | 45 | return nowNode, nil 46 | } 47 | 48 | func init() { 49 | RegisterTag("now", tagNowParser) 50 | } 51 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_set.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagSetNode struct { 4 | name string 5 | expression IEvaluator 6 | } 7 | 8 | func (node *tagSetNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | // Evaluate expression 10 | value, err := node.expression.Evaluate(ctx) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | ctx.Private[node.name] = value 16 | return nil 17 | } 18 | 19 | func tagSetParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 20 | node := &tagSetNode{} 21 | 22 | // Parse variable name 23 | typeToken := arguments.MatchType(TokenIdentifier) 24 | if typeToken == nil { 25 | return nil, arguments.Error("Expected an identifier.", nil) 26 | } 27 | node.name = typeToken.Val 28 | 29 | if arguments.Match(TokenSymbol, "=") == nil { 30 | return nil, arguments.Error("Expected '='.", nil) 31 | } 32 | 33 | // Variable expression 34 | keyExpression, err := arguments.ParseExpression() 35 | if err != nil { 36 | return nil, err 37 | } 38 | node.expression = keyExpression 39 | 40 | // Remaining arguments 41 | if arguments.Remaining() > 0 { 42 | return nil, arguments.Error("Malformed 'set'-tag arguments.", nil) 43 | } 44 | 45 | return node, nil 46 | } 47 | 48 | func init() { 49 | RegisterTag("set", tagSetParser) 50 | } 51 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_spaceless.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | ) 7 | 8 | type tagSpacelessNode struct { 9 | wrapper *NodeWrapper 10 | } 11 | 12 | var tagSpacelessRegexp = regexp.MustCompile(`(?U:(<.*>))([\t\n\v\f\r ]+)(?U:(<.*>))`) 13 | 14 | func (node *tagSpacelessNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 15 | b := bytes.NewBuffer(make([]byte, 0, 1024)) // 1 KiB 16 | 17 | err := node.wrapper.Execute(ctx, b) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | s := b.String() 23 | // Repeat this recursively 24 | changed := true 25 | for changed { 26 | s2 := tagSpacelessRegexp.ReplaceAllString(s, "$1$3") 27 | changed = s != s2 28 | s = s2 29 | } 30 | 31 | writer.WriteString(s) 32 | 33 | return nil 34 | } 35 | 36 | func tagSpacelessParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 37 | spacelessNode := &tagSpacelessNode{} 38 | 39 | wrapper, _, err := doc.WrapUntilTag("endspaceless") 40 | if err != nil { 41 | return nil, err 42 | } 43 | spacelessNode.wrapper = wrapper 44 | 45 | if arguments.Remaining() > 0 { 46 | return nil, arguments.Error("Malformed spaceless-tag arguments.", nil) 47 | } 48 | 49 | return spacelessNode, nil 50 | } 51 | 52 | func init() { 53 | RegisterTag("spaceless", tagSpacelessParser) 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_ssi.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "io/ioutil" 5 | ) 6 | 7 | type tagSSINode struct { 8 | filename string 9 | content string 10 | template *Template 11 | } 12 | 13 | func (node *tagSSINode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 14 | if node.template != nil { 15 | // Execute the template within the current context 16 | includeCtx := make(Context) 17 | includeCtx.Update(ctx.Public) 18 | includeCtx.Update(ctx.Private) 19 | 20 | err := node.template.execute(includeCtx, writer) 21 | if err != nil { 22 | return err.(*Error) 23 | } 24 | } else { 25 | // Just print out the content 26 | writer.WriteString(node.content) 27 | } 28 | return nil 29 | } 30 | 31 | func tagSSIParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 32 | SSINode := &tagSSINode{} 33 | 34 | if fileToken := arguments.MatchType(TokenString); fileToken != nil { 35 | SSINode.filename = fileToken.Val 36 | 37 | if arguments.Match(TokenIdentifier, "parsed") != nil { 38 | // parsed 39 | temporaryTpl, err := doc.template.set.FromFile(doc.template.set.resolveFilename(doc.template, fileToken.Val)) 40 | if err != nil { 41 | return nil, err.(*Error).updateFromTokenIfNeeded(doc.template, fileToken) 42 | } 43 | SSINode.template = temporaryTpl 44 | } else { 45 | // plaintext 46 | buf, err := ioutil.ReadFile(doc.template.set.resolveFilename(doc.template, fileToken.Val)) 47 | if err != nil { 48 | return nil, (&Error{ 49 | Sender: "tag:ssi", 50 | ErrorMsg: err.Error(), 51 | }).updateFromTokenIfNeeded(doc.template, fileToken) 52 | } 53 | SSINode.content = string(buf) 54 | } 55 | } else { 56 | return nil, arguments.Error("First argument must be a string.", nil) 57 | } 58 | 59 | if arguments.Remaining() > 0 { 60 | return nil, arguments.Error("Malformed SSI-tag argument.", nil) 61 | } 62 | 63 | return SSINode, nil 64 | } 65 | 66 | func init() { 67 | RegisterTag("ssi", tagSSIParser) 68 | } 69 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_templatetag.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagTemplateTagNode struct { 4 | content string 5 | } 6 | 7 | var templateTagMapping = map[string]string{ 8 | "openblock": "{%", 9 | "closeblock": "%}", 10 | "openvariable": "{{", 11 | "closevariable": "}}", 12 | "openbrace": "{", 13 | "closebrace": "}", 14 | "opencomment": "{#", 15 | "closecomment": "#}", 16 | } 17 | 18 | func (node *tagTemplateTagNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 19 | writer.WriteString(node.content) 20 | return nil 21 | } 22 | 23 | func tagTemplateTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 24 | ttNode := &tagTemplateTagNode{} 25 | 26 | if argToken := arguments.MatchType(TokenIdentifier); argToken != nil { 27 | output, found := templateTagMapping[argToken.Val] 28 | if !found { 29 | return nil, arguments.Error("Argument not found", argToken) 30 | } 31 | ttNode.content = output 32 | } else { 33 | return nil, arguments.Error("Identifier expected.", nil) 34 | } 35 | 36 | if arguments.Remaining() > 0 { 37 | return nil, arguments.Error("Malformed templatetag-tag argument.", nil) 38 | } 39 | 40 | return ttNode, nil 41 | } 42 | 43 | func init() { 44 | RegisterTag("templatetag", tagTemplateTagParser) 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_widthratio.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type tagWidthratioNode struct { 9 | position *Token 10 | current, max IEvaluator 11 | width IEvaluator 12 | ctxName string 13 | } 14 | 15 | func (node *tagWidthratioNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 16 | current, err := node.current.Evaluate(ctx) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | max, err := node.max.Evaluate(ctx) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | width, err := node.width.Evaluate(ctx) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | value := int(math.Ceil(current.Float()/max.Float()*width.Float() + 0.5)) 32 | 33 | if node.ctxName == "" { 34 | writer.WriteString(fmt.Sprintf("%d", value)) 35 | } else { 36 | ctx.Private[node.ctxName] = value 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func tagWidthratioParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 43 | widthratioNode := &tagWidthratioNode{ 44 | position: start, 45 | } 46 | 47 | current, err := arguments.ParseExpression() 48 | if err != nil { 49 | return nil, err 50 | } 51 | widthratioNode.current = current 52 | 53 | max, err := arguments.ParseExpression() 54 | if err != nil { 55 | return nil, err 56 | } 57 | widthratioNode.max = max 58 | 59 | width, err := arguments.ParseExpression() 60 | if err != nil { 61 | return nil, err 62 | } 63 | widthratioNode.width = width 64 | 65 | if arguments.MatchOne(TokenKeyword, "as") != nil { 66 | // Name follows 67 | nameToken := arguments.MatchType(TokenIdentifier) 68 | if nameToken == nil { 69 | return nil, arguments.Error("Expected name (identifier).", nil) 70 | } 71 | widthratioNode.ctxName = nameToken.Val 72 | } 73 | 74 | if arguments.Remaining() > 0 { 75 | return nil, arguments.Error("Malformed widthratio-tag arguments.", nil) 76 | } 77 | 78 | return widthratioNode, nil 79 | } 80 | 81 | func init() { 82 | RegisterTag("widthratio", tagWidthratioParser) 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/tags_with.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | type tagWithNode struct { 4 | withPairs map[string]IEvaluator 5 | wrapper *NodeWrapper 6 | } 7 | 8 | func (node *tagWithNode) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error { 9 | //new context for block 10 | withctx := NewChildExecutionContext(ctx) 11 | 12 | // Put all custom with-pairs into the context 13 | for key, value := range node.withPairs { 14 | val, err := value.Evaluate(ctx) 15 | if err != nil { 16 | return err 17 | } 18 | withctx.Private[key] = val 19 | } 20 | 21 | return node.wrapper.Execute(withctx, writer) 22 | } 23 | 24 | func tagWithParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 25 | withNode := &tagWithNode{ 26 | withPairs: make(map[string]IEvaluator), 27 | } 28 | 29 | if arguments.Count() == 0 { 30 | return nil, arguments.Error("Tag 'with' requires at least one argument.", nil) 31 | } 32 | 33 | wrapper, endargs, err := doc.WrapUntilTag("endwith") 34 | if err != nil { 35 | return nil, err 36 | } 37 | withNode.wrapper = wrapper 38 | 39 | if endargs.Count() > 0 { 40 | return nil, endargs.Error("Arguments not allowed here.", nil) 41 | } 42 | 43 | // Scan through all arguments to see which style the user uses (old or new style). 44 | // If we find any "as" keyword we will enforce old style; otherwise we will use new style. 45 | oldStyle := false // by default we're using the new_style 46 | for i := 0; i < arguments.Count(); i++ { 47 | if arguments.PeekN(i, TokenKeyword, "as") != nil { 48 | oldStyle = true 49 | break 50 | } 51 | } 52 | 53 | for arguments.Remaining() > 0 { 54 | if oldStyle { 55 | valueExpr, err := arguments.ParseExpression() 56 | if err != nil { 57 | return nil, err 58 | } 59 | if arguments.Match(TokenKeyword, "as") == nil { 60 | return nil, arguments.Error("Expected 'as' keyword.", nil) 61 | } 62 | keyToken := arguments.MatchType(TokenIdentifier) 63 | if keyToken == nil { 64 | return nil, arguments.Error("Expected an identifier", nil) 65 | } 66 | withNode.withPairs[keyToken.Val] = valueExpr 67 | } else { 68 | keyToken := arguments.MatchType(TokenIdentifier) 69 | if keyToken == nil { 70 | return nil, arguments.Error("Expected an identifier", nil) 71 | } 72 | if arguments.Match(TokenSymbol, "=") == nil { 73 | return nil, arguments.Error("Expected '='.", nil) 74 | } 75 | valueExpr, err := arguments.ParseExpression() 76 | if err != nil { 77 | return nil, err 78 | } 79 | withNode.withPairs[keyToken.Val] = valueExpr 80 | } 81 | } 82 | 83 | return withNode, nil 84 | } 85 | 86 | func init() { 87 | RegisterTag("with", tagWithParser) 88 | } 89 | -------------------------------------------------------------------------------- /vendor/github.com/flosch/pongo2/template_loader.go: -------------------------------------------------------------------------------- 1 | package pongo2 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | // LocalFilesystemLoader represents a local filesystem loader with basic 14 | // BaseDirectory capabilities. The access to the local filesystem is unrestricted. 15 | type LocalFilesystemLoader struct { 16 | baseDir string 17 | } 18 | 19 | // MustNewLocalFileSystemLoader creates a new LocalFilesystemLoader instance 20 | // and panics if there's any error during instantiation. The parameters 21 | // are the same like NewLocalFileSystemLoader. 22 | func MustNewLocalFileSystemLoader(baseDir string) *LocalFilesystemLoader { 23 | fs, err := NewLocalFileSystemLoader(baseDir) 24 | if err != nil { 25 | log.Panic(err) 26 | } 27 | return fs 28 | } 29 | 30 | // NewLocalFileSystemLoader creates a new LocalFilesystemLoader and allows 31 | // templatesto be loaded from disk (unrestricted). If any base directory 32 | // is given (or being set using SetBaseDir), this base directory is being used 33 | // for path calculation in template inclusions/imports. Otherwise the path 34 | // is calculated based relatively to the including template's path. 35 | func NewLocalFileSystemLoader(baseDir string) (*LocalFilesystemLoader, error) { 36 | fs := &LocalFilesystemLoader{} 37 | if baseDir != "" { 38 | if err := fs.SetBaseDir(baseDir); err != nil { 39 | return nil, err 40 | } 41 | } 42 | return fs, nil 43 | } 44 | 45 | // SetBaseDir sets the template's base directory. This directory will 46 | // be used for any relative path in filters, tags and From*-functions to determine 47 | // your template. See the comment for NewLocalFileSystemLoader as well. 48 | func (fs *LocalFilesystemLoader) SetBaseDir(path string) error { 49 | // Make the path absolute 50 | if !filepath.IsAbs(path) { 51 | abs, err := filepath.Abs(path) 52 | if err != nil { 53 | return err 54 | } 55 | path = abs 56 | } 57 | 58 | // Check for existence 59 | fi, err := os.Stat(path) 60 | if err != nil { 61 | return err 62 | } 63 | if !fi.IsDir() { 64 | return fmt.Errorf("The given path '%s' is not a directory.", path) 65 | } 66 | 67 | fs.baseDir = path 68 | return nil 69 | } 70 | 71 | // Get reads the path's content from your local filesystem. 72 | func (fs *LocalFilesystemLoader) Get(path string) (io.Reader, error) { 73 | buf, err := ioutil.ReadFile(path) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return bytes.NewReader(buf), nil 78 | } 79 | 80 | // Abs resolves a filename relative to the base directory. Absolute paths are allowed. 81 | // When there's no base dir set, the absolute path to the filename 82 | // will be calculated based on either the provided base directory (which 83 | // might be a path of a template which includes another template) or 84 | // the current working directory. 85 | func (fs *LocalFilesystemLoader) Abs(base, name string) string { 86 | if filepath.IsAbs(name) { 87 | return name 88 | } 89 | 90 | // Our own base dir has always priority; if there's none 91 | // we use the path provided in base. 92 | var err error 93 | if fs.baseDir == "" { 94 | if base == "" { 95 | base, err = os.Getwd() 96 | if err != nil { 97 | panic(err) 98 | } 99 | return filepath.Join(base, name) 100 | } 101 | 102 | return filepath.Join(filepath.Dir(base), name) 103 | } 104 | 105 | return filepath.Join(fs.baseDir, name) 106 | } 107 | 108 | // SandboxedFilesystemLoader is still WIP. 109 | type SandboxedFilesystemLoader struct { 110 | *LocalFilesystemLoader 111 | } 112 | 113 | // NewSandboxedFilesystemLoader creates a new sandboxed local file system instance. 114 | func NewSandboxedFilesystemLoader(baseDir string) (*SandboxedFilesystemLoader, error) { 115 | fs, err := NewLocalFileSystemLoader(baseDir) 116 | if err != nil { 117 | return nil, err 118 | } 119 | return &SandboxedFilesystemLoader{ 120 | LocalFilesystemLoader: fs, 121 | }, nil 122 | } 123 | 124 | // Move sandbox to a virtual fs 125 | 126 | /* 127 | if len(set.SandboxDirectories) > 0 { 128 | defer func() { 129 | // Remove any ".." or other crap 130 | resolvedPath = filepath.Clean(resolvedPath) 131 | 132 | // Make the path absolute 133 | absPath, err := filepath.Abs(resolvedPath) 134 | if err != nil { 135 | panic(err) 136 | } 137 | resolvedPath = absPath 138 | 139 | // Check against the sandbox directories (once one pattern matches, we're done and can allow it) 140 | for _, pattern := range set.SandboxDirectories { 141 | matched, err := filepath.Match(pattern, resolvedPath) 142 | if err != nil { 143 | panic("Wrong sandbox directory match pattern (see http://golang.org/pkg/path/filepath/#Match).") 144 | } 145 | if matched { 146 | // OK! 147 | return 148 | } 149 | } 150 | 151 | // No pattern matched, we have to log+deny the request 152 | set.logf("Access attempt outside of the sandbox directories (blocked): '%s'", resolvedPath) 153 | resolvedPath = "" 154 | }() 155 | } 156 | */ 157 | -------------------------------------------------------------------------------- /vendor/github.com/juju/errors/Makefile: -------------------------------------------------------------------------------- 1 | default: check 2 | 3 | check: 4 | go test && go test -compiler gccgo 5 | 6 | docs: 7 | godoc2md github.com/juju/errors > README.md 8 | sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md 9 | 10 | 11 | .PHONY: default check docs 12 | -------------------------------------------------------------------------------- /vendor/github.com/juju/errors/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, 2014 Canonical Ltd. 2 | // Licensed under the LGPLv3, see LICENCE file for details. 3 | 4 | /* 5 | [godoc-link-here] 6 | 7 | The juju/errors provides an easy way to annotate errors without losing the 8 | orginal error context. 9 | 10 | The exported `New` and `Errorf` functions are designed to replace the 11 | `errors.New` and `fmt.Errorf` functions respectively. The same underlying 12 | error is there, but the package also records the location at which the error 13 | was created. 14 | 15 | A primary use case for this library is to add extra context any time an 16 | error is returned from a function. 17 | 18 | if err := SomeFunc(); err != nil { 19 | return err 20 | } 21 | 22 | This instead becomes: 23 | 24 | if err := SomeFunc(); err != nil { 25 | return errors.Trace(err) 26 | } 27 | 28 | which just records the file and line number of the Trace call, or 29 | 30 | if err := SomeFunc(); err != nil { 31 | return errors.Annotate(err, "more context") 32 | } 33 | 34 | which also adds an annotation to the error. 35 | 36 | When you want to check to see if an error is of a particular type, a helper 37 | function is normally exported by the package that returned the error, like the 38 | `os` package does. The underlying cause of the error is available using the 39 | `Cause` function. 40 | 41 | os.IsNotExist(errors.Cause(err)) 42 | 43 | The result of the `Error()` call on an annotated error is the annotations joined 44 | with colons, then the result of the `Error()` method for the underlying error 45 | that was the cause. 46 | 47 | err := errors.Errorf("original") 48 | err = errors.Annotatef(err, "context") 49 | err = errors.Annotatef(err, "more context") 50 | err.Error() -> "more context: context: original" 51 | 52 | Obviously recording the file, line and functions is not very useful if you 53 | cannot get them back out again. 54 | 55 | errors.ErrorStack(err) 56 | 57 | will return something like: 58 | 59 | first error 60 | github.com/juju/errors/annotation_test.go:193: 61 | github.com/juju/errors/annotation_test.go:194: annotation 62 | github.com/juju/errors/annotation_test.go:195: 63 | github.com/juju/errors/annotation_test.go:196: more context 64 | github.com/juju/errors/annotation_test.go:197: 65 | 66 | The first error was generated by an external system, so there was no location 67 | associated. The second, fourth, and last lines were generated with Trace calls, 68 | and the other two through Annotate. 69 | 70 | Sometimes when responding to an error you want to return a more specific error 71 | for the situation. 72 | 73 | if err := FindField(field); err != nil { 74 | return errors.Wrap(err, errors.NotFoundf(field)) 75 | } 76 | 77 | This returns an error where the complete error stack is still available, and 78 | `errors.Cause()` will return the `NotFound` error. 79 | 80 | */ 81 | package errors 82 | -------------------------------------------------------------------------------- /vendor/github.com/juju/errors/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Canonical Ltd. 2 | // Licensed under the LGPLv3, see LICENCE file for details. 3 | 4 | package errors 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "runtime" 10 | ) 11 | 12 | // Err holds a description of an error along with information about 13 | // where the error was created. 14 | // 15 | // It may be embedded in custom error types to add extra information that 16 | // this errors package can understand. 17 | type Err struct { 18 | // message holds an annotation of the error. 19 | message string 20 | 21 | // cause holds the cause of the error as returned 22 | // by the Cause method. 23 | cause error 24 | 25 | // previous holds the previous error in the error stack, if any. 26 | previous error 27 | 28 | // file and line hold the source code location where the error was 29 | // created. 30 | file string 31 | line int 32 | } 33 | 34 | // NewErr is used to return an Err for the purpose of embedding in other 35 | // structures. The location is not specified, and needs to be set with a call 36 | // to SetLocation. 37 | // 38 | // For example: 39 | // type FooError struct { 40 | // errors.Err 41 | // code int 42 | // } 43 | // 44 | // func NewFooError(code int) error { 45 | // err := &FooError{errors.NewErr("foo"), code} 46 | // err.SetLocation(1) 47 | // return err 48 | // } 49 | func NewErr(format string, args ...interface{}) Err { 50 | return Err{ 51 | message: fmt.Sprintf(format, args...), 52 | } 53 | } 54 | 55 | // NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other 56 | // structures. The location is not specified, and needs to be set with a call 57 | // to SetLocation. 58 | // 59 | // For example: 60 | // type FooError struct { 61 | // errors.Err 62 | // code int 63 | // } 64 | // 65 | // func (e *FooError) Annotate(format string, args ...interface{}) error { 66 | // err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code} 67 | // err.SetLocation(1) 68 | // return err 69 | // }) 70 | func NewErrWithCause(other error, format string, args ...interface{}) Err { 71 | return Err{ 72 | message: fmt.Sprintf(format, args...), 73 | cause: Cause(other), 74 | previous: other, 75 | } 76 | } 77 | 78 | // Location is the file and line of where the error was most recently 79 | // created or annotated. 80 | func (e *Err) Location() (filename string, line int) { 81 | return e.file, e.line 82 | } 83 | 84 | // Underlying returns the previous error in the error stack, if any. A client 85 | // should not ever really call this method. It is used to build the error 86 | // stack and should not be introspected by client calls. Or more 87 | // specifically, clients should not depend on anything but the `Cause` of an 88 | // error. 89 | func (e *Err) Underlying() error { 90 | return e.previous 91 | } 92 | 93 | // The Cause of an error is the most recent error in the error stack that 94 | // meets one of these criteria: the original error that was raised; the new 95 | // error that was passed into the Wrap function; the most recently masked 96 | // error; or nil if the error itself is considered the Cause. Normally this 97 | // method is not invoked directly, but instead through the Cause stand alone 98 | // function. 99 | func (e *Err) Cause() error { 100 | return e.cause 101 | } 102 | 103 | // Message returns the message stored with the most recent location. This is 104 | // the empty string if the most recent call was Trace, or the message stored 105 | // with Annotate or Mask. 106 | func (e *Err) Message() string { 107 | return e.message 108 | } 109 | 110 | // Error implements error.Error. 111 | func (e *Err) Error() string { 112 | // We want to walk up the stack of errors showing the annotations 113 | // as long as the cause is the same. 114 | err := e.previous 115 | if !sameError(Cause(err), e.cause) && e.cause != nil { 116 | err = e.cause 117 | } 118 | switch { 119 | case err == nil: 120 | return e.message 121 | case e.message == "": 122 | return err.Error() 123 | } 124 | return fmt.Sprintf("%s: %v", e.message, err) 125 | } 126 | 127 | // SetLocation records the source location of the error at callDepth stack 128 | // frames above the call. 129 | func (e *Err) SetLocation(callDepth int) { 130 | _, file, line, _ := runtime.Caller(callDepth + 1) 131 | e.file = trimGoPath(file) 132 | e.line = line 133 | } 134 | 135 | // StackTrace returns one string for each location recorded in the stack of 136 | // errors. The first value is the originating error, with a line for each 137 | // other annotation or tracing of the error. 138 | func (e *Err) StackTrace() []string { 139 | return errorStack(e) 140 | } 141 | 142 | // Ideally we'd have a way to check identity, but deep equals will do. 143 | func sameError(e1, e2 error) bool { 144 | return reflect.DeepEqual(e1, e2) 145 | } 146 | -------------------------------------------------------------------------------- /vendor/github.com/juju/errors/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, 2014 Canonical Ltd. 2 | // Licensed under the LGPLv3, see LICENCE file for details. 3 | 4 | package errors 5 | 6 | import ( 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | // prefixSize is used internally to trim the user specific path from the 12 | // front of the returned filenames from the runtime call stack. 13 | var prefixSize int 14 | 15 | // goPath is the deduced path based on the location of this file as compiled. 16 | var goPath string 17 | 18 | func init() { 19 | _, file, _, ok := runtime.Caller(0) 20 | if file == "?" { 21 | return 22 | } 23 | if ok { 24 | // We know that the end of the file should be: 25 | // github.com/juju/errors/path.go 26 | size := len(file) 27 | suffix := len("github.com/juju/errors/path.go") 28 | goPath = file[:size-suffix] 29 | prefixSize = len(goPath) 30 | } 31 | } 32 | 33 | func trimGoPath(filename string) string { 34 | if strings.HasPrefix(filename, goPath) { 35 | return filename[prefixSize:] 36 | } 37 | return filename 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/kataras/go-errors/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gerasimos Maropoulos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/kataras/go-errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors helps you to write and design your own pre-defined errors, useful when you have a known list of errors 2 | package errors 3 | 4 | import ( 5 | "fmt" 6 | "runtime" 7 | ) 8 | 9 | const ( 10 | // Version current version number 11 | Version = "0.0.3" 12 | ) 13 | 14 | var ( 15 | // Prefix the error prefix, applies to each error's message 16 | // defaults to Error:_ 17 | Prefix = "Error: " 18 | // NewLine adds a new line to the end of each error's message 19 | // defaults to true 20 | NewLine = true 21 | ) 22 | 23 | // Error holds the error message, this message never really changes 24 | type Error struct { 25 | message string 26 | appended bool 27 | } 28 | 29 | // New creates and returns an Error with a pre-defined user output message 30 | // all methods below that doesn't accept a pointer receiver because actualy they are not changing the original message 31 | func New(errMsg string) *Error { 32 | if NewLine { 33 | errMsg += "\n" 34 | } 35 | return &Error{message: Prefix + errMsg} 36 | } 37 | 38 | // String returns the error message 39 | func (e Error) String() string { 40 | return e.message 41 | } 42 | 43 | // Error returns the message of the actual error 44 | // implements the error 45 | func (e Error) Error() string { 46 | return e.String() 47 | } 48 | 49 | // Format returns a formatted new error based on the arguments 50 | // it does NOT change the original error's message 51 | func (e Error) Format(a ...interface{}) Error { 52 | e.message = fmt.Sprintf(e.message, a...) 53 | return e 54 | } 55 | 56 | // Append adds a message to the predefined error message and returns a new error 57 | // it does NOT change the original error's message 58 | func (e Error) Append(format string, a ...interface{}) Error { 59 | // eCp := *e 60 | if NewLine { 61 | format += "\n" 62 | } 63 | e.message += fmt.Sprintf(format, a...) 64 | e.appended = true 65 | return e 66 | } 67 | 68 | // AppendErr adds an error's message to the predefined error message and returns a new error 69 | // it does NOT change the original error's message 70 | func (e Error) AppendErr(err error) Error { 71 | return e.Append(err.Error()) 72 | } 73 | 74 | // IsAppended returns true if the Error instance is created using original's Error.Append/AppendErr func 75 | func (e Error) IsAppended() bool { 76 | return e.appended 77 | } 78 | 79 | // With does the same thing as Format but it receives an error type which if it's nil it returns a nil error 80 | func (e Error) With(err error) error { 81 | if err == nil { 82 | return nil 83 | } 84 | 85 | return e.Format(err.Error()) 86 | } 87 | 88 | // Panic output the message and after panics 89 | func (e Error) Panic() { 90 | _, fn, line, _ := runtime.Caller(1) 91 | errMsg := e.message 92 | errMsg = "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line) 93 | panic(errMsg) 94 | } 95 | 96 | // Panicf output the formatted message and after panics 97 | func (e Error) Panicf(args ...interface{}) { 98 | _, fn, line, _ := runtime.Caller(1) 99 | errMsg := e.Format(args...).Error() 100 | errMsg = "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line) 101 | panic(errMsg) 102 | } 103 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, David Kitchen 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the organisation (Microcosm) nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/Makefile: -------------------------------------------------------------------------------- 1 | # Targets: 2 | # 3 | # all: Builds the code locally after testing 4 | # 5 | # fmt: Formats the source files 6 | # build: Builds the code locally 7 | # vet: Vets the code 8 | # lint: Runs lint over the code (you do not need to fix everything) 9 | # test: Runs the tests 10 | # cover: Gives you the URL to a nice test coverage report 11 | # 12 | # install: Builds, tests and installs the code locally 13 | 14 | .PHONY: all fmt build vet lint test cover clean install 15 | 16 | # The first target is always the default action if `make` is called without 17 | # args we build and install into $GOPATH so that it can just be run 18 | 19 | all: fmt vet test install 20 | 21 | fmt: 22 | @gofmt -w ./$* 23 | 24 | build: clean 25 | @go build 26 | 27 | vet: 28 | @vet *.go 29 | 30 | lint: 31 | @golint *.go 32 | 33 | test: 34 | @go test -v ./... 35 | 36 | cover: COVERAGE_FILE := coverage.out 37 | cover: 38 | @go test -coverprofile=$(COVERAGE_FILE) && \ 39 | cover -html=$(COVERAGE_FILE) && rm $(COVERAGE_FILE) 40 | 41 | install: clean 42 | @go install ./... 43 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/cmd/sanitise_html_email/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package main demonstrates a HTML email cleaner. 3 | 4 | It should be noted that this uses bluemonday to sanitize the HTML but as it 5 | preserves the styling of the email this should not be considered a safe or XSS 6 | secure approach. 7 | 8 | It does function as a basic demonstration of how to take HTML emails, which are 9 | notorious for having inconsistent, obselete and poorly formatted HTML, and to 10 | use bluemonday to normalise the output. 11 | */ 12 | package main 13 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/cmd/sanitise_html_email/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "regexp" 9 | 10 | "github.com/microcosm-cc/bluemonday" 11 | ) 12 | 13 | var ( 14 | // Color is a valid hex color or name of a web safe color 15 | Color = regexp.MustCompile(`(?i)^(#[0-9a-fA-F]{1,6}|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua|orange|aliceblue|antiquewhite|aquamarine|azure|beige|bisque|blanchedalmond|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|gainsboro|ghostwhite|gold|goldenrod|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|limegreen|linen|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|oldlace|olivedrab|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|thistle|tomato|turquoise|violet|wheat|whitesmoke|yellowgreen|rebeccapurple)$`) 16 | 17 | // ButtonType is a button type, or a style type, i.e. "submit" 18 | ButtonType = regexp.MustCompile(`(?i)^[a-zA-Z][a-zA-Z-]{1,30}[a-zA-Z]$`) 19 | 20 | // StyleType is the valid type attribute on a style tag in the 21 | StyleType = regexp.MustCompile(`(?i)^text\/css$`) 22 | ) 23 | 24 | func main() { 25 | // Define a policy, we are using the UGC policy as a base. 26 | p := bluemonday.UGCPolicy() 27 | 28 | // HTML email is often displayed in iframes and needs to preserve core 29 | // structure 30 | p.AllowDocType(true) 31 | p.AllowElements("html", "head", "body", "title") 32 | 33 | // There are not safe, and is only being done here to demonstrate how to 34 | // process HTML emails where styling has to be preserved. This is at the 35 | // expense of security. 36 | p.AllowAttrs("type").Matching(StyleType).OnElements("style") 37 | p.AllowAttrs("style").Globally() 38 | 39 | // HTML email frequently contains obselete and basic HTML 40 | p.AllowElements("font", "main", "nav", "header", "footer", "kbd", "legend") 41 | 42 | // Need to permit the style tag, and buttons are often found in emails (why?) 43 | p.AllowAttrs("type").Matching(ButtonType).OnElements("button") 44 | 45 | // HTML email tends to see the use of obselete spacing and styling attributes 46 | p.AllowAttrs("bgcolor", "color").Matching(Color).OnElements("basefont", "font", "hr") 47 | p.AllowAttrs("border").Matching(bluemonday.Integer).OnElements("img", "table") 48 | p.AllowAttrs("cellpadding", "cellspacing").Matching(bluemonday.Integer).OnElements("table") 49 | 50 | // Allow "class" attributes on all elements 51 | p.AllowStyling() 52 | 53 | // Allow images to be embedded via data-uri 54 | p.AllowDataURIImages() 55 | 56 | // Add "rel=nofollow" to links 57 | p.RequireNoFollowOnLinks(true) 58 | p.RequireNoFollowOnFullyQualifiedLinks(true) 59 | 60 | // Open external links in a new window/tab 61 | p.AddTargetBlankToFullyQualifiedLinks(true) 62 | 63 | // Read input from stdin so that this is a nice unix utility and can receive 64 | // piped input 65 | dirty, err := ioutil.ReadAll(os.Stdin) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | 70 | // Apply the policy and write to stdout 71 | fmt.Fprint( 72 | os.Stdout, 73 | p.Sanitize( 74 | string(dirty), 75 | ), 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/cmd/sanitise_ugc/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package main demonstrates a simple user generated content sanitizer. 3 | 4 | This is the configuration I use on the sites that I run, it allows a lot of safe 5 | HTML that in my case comes from the blackfriday markdown package. As markdown 6 | itself allows HTML the UGCPolicy includes most common HTML. 7 | 8 | CSS and JavaScript is excluded (not white-listed), as are form elements and most 9 | embedded media that isn't just an image or image map. 10 | 11 | As I'm paranoid, I also do not allow data-uri images and embeds. 12 | */ 13 | package main 14 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/cmd/sanitise_ugc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | "github.com/microcosm-cc/bluemonday" 10 | ) 11 | 12 | func main() { 13 | // Define a policy, we are using the UGC policy as a base. 14 | p := bluemonday.UGCPolicy() 15 | 16 | // Add "rel=nofollow" to links 17 | p.RequireNoFollowOnLinks(true) 18 | p.RequireNoFollowOnFullyQualifiedLinks(true) 19 | 20 | // Open external links in a new window/tab 21 | p.AddTargetBlankToFullyQualifiedLinks(true) 22 | 23 | // Read input from stdin so that this is a nice unix utility and can receive 24 | // piped input 25 | dirty, err := ioutil.ReadAll(os.Stdin) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // Apply the policy and write to stdout 31 | fmt.Fprint( 32 | os.Stdout, 33 | p.Sanitize( 34 | string(dirty), 35 | ), 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/microcosm-cc/bluemonday/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, David Kitchen 2 | // 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this 9 | // list of conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // 15 | // * Neither the name of the organisation (Microcosm) nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | /* 31 | Package bluemonday provides a way of describing a whitelist of HTML elements 32 | and attributes as a policy, and for that policy to be applied to untrusted 33 | strings from users that may contain markup. All elements and attributes not on 34 | the whitelist will be stripped. 35 | 36 | The default bluemonday.UGCPolicy().Sanitize() turns this: 37 | 38 | Hello World 39 | 40 | Into the more harmless: 41 | 42 | Hello World 43 | 44 | And it turns this: 45 | 46 | XSS 47 | 48 | Into this: 49 | 50 | XSS 51 | 52 | Whilst still allowing this: 53 | 54 | 55 | 56 | 57 | 58 | To pass through mostly unaltered (it gained a rel="nofollow"): 59 | 60 | 61 | 62 | 63 | 64 | The primary purpose of bluemonday is to take potentially unsafe user generated 65 | content (from things like Markdown, HTML WYSIWYG tools, etc) and make it safe 66 | for you to put on your website. 67 | 68 | It protects sites against XSS (http://en.wikipedia.org/wiki/Cross-site_scripting) 69 | and other malicious content that a user interface may deliver. There are many 70 | vectors for an XSS attack (https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet) 71 | and the safest thing to do is to sanitize user input against a known safe list 72 | of HTML elements and attributes. 73 | 74 | Note: You should always run bluemonday after any other processing. 75 | 76 | If you use blackfriday (https://github.com/russross/blackfriday) or 77 | Pandoc (http://johnmacfarlane.net/pandoc/) then bluemonday should be run after 78 | these steps. This ensures that no insecure HTML is introduced later in your 79 | process. 80 | 81 | bluemonday is heavily inspired by both the OWASP Java HTML Sanitizer 82 | (https://code.google.com/p/owasp-java-html-sanitizer/) and the HTML Purifier 83 | (http://htmlpurifier.org/). 84 | 85 | We ship two default policies, one is bluemonday.StrictPolicy() and can be 86 | thought of as equivalent to stripping all HTML elements and their attributes as 87 | it has nothing on it's whitelist. 88 | 89 | The other is bluemonday.UGCPolicy() and allows a broad selection of HTML 90 | elements and attributes that are safe for user generated content. Note that 91 | this policy does not whitelist iframes, object, embed, styles, script, etc. 92 | 93 | The essence of building a policy is to determine which HTML elements and 94 | attributes are considered safe for your scenario. OWASP provide an XSS 95 | prevention cheat sheet ( https://www.google.com/search?q=xss+prevention+cheat+sheet ) 96 | to help explain the risks, but essentially: 97 | 98 | 1. Avoid whitelisting anything other than plain HTML elements 99 | 2. Avoid whitelisting `script`, `style`, `iframe`, `object`, `embed`, `base` 100 | elements 101 | 3. Avoid whitelisting anything other than plain HTML elements with simple 102 | values that you can match to a regexp 103 | */ 104 | package bluemonday 105 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Blackfriday is distributed under the Simplified BSD License: 2 | 3 | > Copyright © 2011 Russ Ross 4 | > All rights reserved. 5 | > 6 | > Redistribution and use in source and binary forms, with or without 7 | > modification, are permitted provided that the following conditions 8 | > are met: 9 | > 10 | > 1. Redistributions of source code must retain the above copyright 11 | > notice, this list of conditions and the following disclaimer. 12 | > 13 | > 2. Redistributions in binary form must reproduce the above 14 | > copyright notice, this list of conditions and the following 15 | > disclaimer in the documentation and/or other materials provided with 16 | > the distribution. 17 | > 18 | > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | > "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | > FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | > COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | > INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | > BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | > CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | > LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | > ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | > POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /vendor/github.com/shurcooL/sanitized_anchor_name/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Dmitri Shuralyov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/shurcooL/sanitized_anchor_name/main.go: -------------------------------------------------------------------------------- 1 | // Package sanitized_anchor_name provides a func to create sanitized anchor names. 2 | // 3 | // Its logic can be reused by multiple packages to create interoperable anchor names 4 | // and links to those anchors. 5 | // 6 | // At this time, it does not try to ensure that generated anchor names 7 | // are unique, that responsibility falls on the caller. 8 | package sanitized_anchor_name // import "github.com/shurcooL/sanitized_anchor_name" 9 | 10 | import "unicode" 11 | 12 | // Create returns a sanitized anchor name for the given text. 13 | func Create(text string) string { 14 | var anchorName []rune 15 | var futureDash = false 16 | for _, r := range []rune(text) { 17 | switch { 18 | case unicode.IsLetter(r) || unicode.IsNumber(r): 19 | if futureDash && len(anchorName) > 0 { 20 | anchorName = append(anchorName, '-') 21 | } 22 | futureDash = false 23 | anchorName = append(anchorName, unicode.ToLower(r)) 24 | default: 25 | futureDash = true 26 | } 27 | } 28 | return string(anchorName) 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/valyala/bytebufferpool/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/valyala/bytebufferpool/bytebuffer.go: -------------------------------------------------------------------------------- 1 | package bytebufferpool 2 | 3 | import "io" 4 | 5 | // ByteBuffer provides byte buffer, which can be used for minimizing 6 | // memory allocations. 7 | // 8 | // ByteBuffer may be used with functions appending data to the given []byte 9 | // slice. See example code for details. 10 | // 11 | // Use Get for obtaining an empty byte buffer. 12 | type ByteBuffer struct { 13 | 14 | // B is a byte buffer to use in append-like workloads. 15 | // See example code for details. 16 | B []byte 17 | } 18 | 19 | // Len returns the size of the byte buffer. 20 | func (b *ByteBuffer) Len() int { 21 | return len(b.B) 22 | } 23 | 24 | // ReadFrom implements io.ReaderFrom. 25 | // 26 | // The function appends all the data read from r to b. 27 | func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) { 28 | p := b.B 29 | nStart := int64(len(p)) 30 | nMax := int64(cap(p)) 31 | n := nStart 32 | if nMax == 0 { 33 | nMax = 64 34 | p = make([]byte, nMax) 35 | } else { 36 | p = p[:nMax] 37 | } 38 | for { 39 | if n == nMax { 40 | nMax *= 2 41 | bNew := make([]byte, nMax) 42 | copy(bNew, p) 43 | p = bNew 44 | } 45 | nn, err := r.Read(p[n:]) 46 | n += int64(nn) 47 | if err != nil { 48 | b.B = p[:n] 49 | n -= nStart 50 | if err == io.EOF { 51 | return n, nil 52 | } 53 | return n, err 54 | } 55 | } 56 | } 57 | 58 | // WriteTo implements io.WriterTo. 59 | func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) { 60 | n, err := w.Write(b.B) 61 | return int64(n), err 62 | } 63 | 64 | // Bytes returns b.B, i.e. all the bytes accumulated in the buffer. 65 | // 66 | // The purpose of this function is bytes.Buffer compatibility. 67 | func (b *ByteBuffer) Bytes() []byte { 68 | return b.B 69 | } 70 | 71 | // Write implements io.Writer - it appends p to ByteBuffer.B 72 | func (b *ByteBuffer) Write(p []byte) (int, error) { 73 | b.B = append(b.B, p...) 74 | return len(p), nil 75 | } 76 | 77 | // WriteByte appends the byte c to the buffer. 78 | // 79 | // The purpose of this function is bytes.Buffer compatibility. 80 | // 81 | // The function always returns nil. 82 | func (b *ByteBuffer) WriteByte(c byte) error { 83 | b.B = append(b.B, c) 84 | return nil 85 | } 86 | 87 | // WriteString appends s to ByteBuffer.B. 88 | func (b *ByteBuffer) WriteString(s string) (int, error) { 89 | b.B = append(b.B, s...) 90 | return len(s), nil 91 | } 92 | 93 | // Set sets ByteBuffer.B to p. 94 | func (b *ByteBuffer) Set(p []byte) { 95 | b.B = append(b.B[:0], p...) 96 | } 97 | 98 | // SetString sets ByteBuffer.B to s. 99 | func (b *ByteBuffer) SetString(s string) { 100 | b.B = append(b.B[:0], s...) 101 | } 102 | 103 | // String returns string representation of ByteBuffer.B. 104 | func (b *ByteBuffer) String() string { 105 | return string(b.B) 106 | } 107 | 108 | // Reset makes ByteBuffer.B empty. 109 | func (b *ByteBuffer) Reset() { 110 | b.B = b.B[:0] 111 | } 112 | -------------------------------------------------------------------------------- /vendor/github.com/valyala/bytebufferpool/bytebuffer_test.go: -------------------------------------------------------------------------------- 1 | package bytebufferpool 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestByteBufferReadFrom(t *testing.T) { 12 | prefix := "foobar" 13 | expectedS := "asadfsdafsadfasdfisdsdfa" 14 | prefixLen := int64(len(prefix)) 15 | expectedN := int64(len(expectedS)) 16 | 17 | var bb ByteBuffer 18 | bb.WriteString(prefix) 19 | 20 | rf := (io.ReaderFrom)(&bb) 21 | for i := 0; i < 20; i++ { 22 | r := bytes.NewBufferString(expectedS) 23 | n, err := rf.ReadFrom(r) 24 | if n != expectedN { 25 | t.Fatalf("unexpected n=%d. Expecting %d. iteration %d", n, expectedN, i) 26 | } 27 | if err != nil { 28 | t.Fatalf("unexpected error: %s", err) 29 | } 30 | bbLen := int64(bb.Len()) 31 | expectedLen := prefixLen + int64(i+1)*expectedN 32 | if bbLen != expectedLen { 33 | t.Fatalf("unexpected byteBuffer length: %d. Expecting %d", bbLen, expectedLen) 34 | } 35 | for j := 0; j < i; j++ { 36 | start := prefixLen + int64(j)*expectedN 37 | b := bb.B[start : start+expectedN] 38 | if string(b) != expectedS { 39 | t.Fatalf("unexpected byteBuffer contents: %q. Expecting %q", b, expectedS) 40 | } 41 | } 42 | } 43 | } 44 | 45 | func TestByteBufferWriteTo(t *testing.T) { 46 | expectedS := "foobarbaz" 47 | var bb ByteBuffer 48 | bb.WriteString(expectedS[:3]) 49 | bb.WriteString(expectedS[3:]) 50 | 51 | wt := (io.WriterTo)(&bb) 52 | var w bytes.Buffer 53 | for i := 0; i < 10; i++ { 54 | n, err := wt.WriteTo(&w) 55 | if n != int64(len(expectedS)) { 56 | t.Fatalf("unexpected n returned from WriteTo: %d. Expecting %d", n, len(expectedS)) 57 | } 58 | if err != nil { 59 | t.Fatalf("unexpected error: %s", err) 60 | } 61 | s := string(w.Bytes()) 62 | if s != expectedS { 63 | t.Fatalf("unexpected string written %q. Expecting %q", s, expectedS) 64 | } 65 | w.Reset() 66 | } 67 | } 68 | 69 | func TestByteBufferGetPutSerial(t *testing.T) { 70 | testByteBufferGetPut(t) 71 | } 72 | 73 | func TestByteBufferGetPutConcurrent(t *testing.T) { 74 | concurrency := 10 75 | ch := make(chan struct{}, concurrency) 76 | for i := 0; i < concurrency; i++ { 77 | go func() { 78 | testByteBufferGetPut(t) 79 | ch <- struct{}{} 80 | }() 81 | } 82 | 83 | for i := 0; i < concurrency; i++ { 84 | select { 85 | case <-ch: 86 | case <-time.After(time.Second): 87 | t.Fatalf("timeout!") 88 | } 89 | } 90 | } 91 | 92 | func testByteBufferGetPut(t *testing.T) { 93 | for i := 0; i < 10; i++ { 94 | expectedS := fmt.Sprintf("num %d", i) 95 | b := Get() 96 | b.B = append(b.B, "num "...) 97 | b.B = append(b.B, fmt.Sprintf("%d", i)...) 98 | if string(b.B) != expectedS { 99 | t.Fatalf("unexpected result: %q. Expecting %q", b.B, expectedS) 100 | } 101 | Put(b) 102 | } 103 | } 104 | 105 | func testByteBufferGetString(t *testing.T) { 106 | for i := 0; i < 10; i++ { 107 | expectedS := fmt.Sprintf("num %d", i) 108 | b := Get() 109 | b.SetString(expectedS) 110 | if b.String() != expectedS { 111 | t.Fatalf("unexpected result: %q. Expecting %q", b.B, expectedS) 112 | } 113 | Put(b) 114 | } 115 | } 116 | 117 | func TestByteBufferGetStringSerial(t *testing.T) { 118 | testByteBufferGetString(t) 119 | } 120 | 121 | func TestByteBufferGetStringConcurrent(t *testing.T) { 122 | concurrency := 10 123 | ch := make(chan struct{}, concurrency) 124 | for i := 0; i < concurrency; i++ { 125 | go func() { 126 | testByteBufferGetString(t) 127 | ch <- struct{}{} 128 | }() 129 | } 130 | 131 | for i := 0; i < concurrency; i++ { 132 | select { 133 | case <-ch: 134 | case <-time.After(time.Second): 135 | t.Fatalf("timeout!") 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /vendor/github.com/valyala/bytebufferpool/pool.go: -------------------------------------------------------------------------------- 1 | package bytebufferpool 2 | 3 | import ( 4 | "sort" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | const ( 10 | minBitSize = 6 // 2**6=64 is a CPU cache line size 11 | steps = 20 12 | 13 | minSize = 1 << minBitSize 14 | maxSize = 1 << (minBitSize + steps - 1) 15 | 16 | calibrateCallsThreshold = 42000 17 | maxPercentile = 0.95 18 | ) 19 | 20 | // Pool represents byte buffer pool. 21 | // 22 | // Distinct pools may be used for distinct types of byte buffers. 23 | // Properly determined byte buffer types with their own pools may help reducing 24 | // memory waste. 25 | type Pool struct { 26 | calls [steps]uint64 27 | calibrating uint64 28 | 29 | defaultSize uint64 30 | maxSize uint64 31 | 32 | pool sync.Pool 33 | } 34 | 35 | var defaultPool Pool 36 | 37 | // Get returns an empty byte buffer from the pool. 38 | // 39 | // Got byte buffer may be returned to the pool via Put call. 40 | // This reduces the number of memory allocations required for byte buffer 41 | // management. 42 | func Get() *ByteBuffer { return defaultPool.Get() } 43 | 44 | // Get returns new byte buffer with zero length. 45 | // 46 | // The byte buffer may be returned to the pool via Put after the use 47 | // in order to minimize GC overhead. 48 | func (p *Pool) Get() *ByteBuffer { 49 | v := p.pool.Get() 50 | if v != nil { 51 | return v.(*ByteBuffer) 52 | } 53 | return &ByteBuffer{ 54 | B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)), 55 | } 56 | } 57 | 58 | // Put returns byte buffer to the pool. 59 | // 60 | // ByteBuffer.B mustn't be touched after returning it to the pool. 61 | // Otherwise data races will occur. 62 | func Put(b *ByteBuffer) { defaultPool.Put(b) } 63 | 64 | // Put releases byte buffer obtained via Get to the pool. 65 | // 66 | // The buffer mustn't be accessed after returning to the pool. 67 | func (p *Pool) Put(b *ByteBuffer) { 68 | idx := index(len(b.B)) 69 | 70 | if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold { 71 | p.calibrate() 72 | } 73 | 74 | maxSize := int(atomic.LoadUint64(&p.maxSize)) 75 | if maxSize == 0 || cap(b.B) <= maxSize { 76 | b.Reset() 77 | p.pool.Put(b) 78 | } 79 | } 80 | 81 | func (p *Pool) calibrate() { 82 | if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) { 83 | return 84 | } 85 | 86 | a := make(callSizes, 0, steps) 87 | var callsSum uint64 88 | for i := uint64(0); i < steps; i++ { 89 | calls := atomic.SwapUint64(&p.calls[i], 0) 90 | callsSum += calls 91 | a = append(a, callSize{ 92 | calls: calls, 93 | size: minSize << i, 94 | }) 95 | } 96 | sort.Sort(a) 97 | 98 | defaultSize := a[0].size 99 | maxSize := defaultSize 100 | 101 | maxSum := uint64(float64(callsSum) * maxPercentile) 102 | callsSum = 0 103 | for i := 0; i < steps; i++ { 104 | if callsSum > maxSum { 105 | break 106 | } 107 | callsSum += a[i].calls 108 | size := a[i].size 109 | if size > maxSize { 110 | maxSize = size 111 | } 112 | } 113 | 114 | atomic.StoreUint64(&p.defaultSize, defaultSize) 115 | atomic.StoreUint64(&p.maxSize, maxSize) 116 | 117 | atomic.StoreUint64(&p.calibrating, 0) 118 | } 119 | 120 | type callSize struct { 121 | calls uint64 122 | size uint64 123 | } 124 | 125 | type callSizes []callSize 126 | 127 | func (ci callSizes) Len() int { 128 | return len(ci) 129 | } 130 | 131 | func (ci callSizes) Less(i, j int) bool { 132 | return ci[i].calls > ci[j].calls 133 | } 134 | 135 | func (ci callSizes) Swap(i, j int) { 136 | ci[i], ci[j] = ci[j], ci[i] 137 | } 138 | 139 | func index(n int) int { 140 | n-- 141 | n >>= minBitSize 142 | idx := 0 143 | for n > 0 { 144 | n >>= 1 145 | idx++ 146 | } 147 | if idx >= steps { 148 | idx = steps - 1 149 | } 150 | return idx 151 | } 152 | --------------------------------------------------------------------------------