iris/render/template/engine/html/html.go
Makis Maropoulos 5060cf4f5e linting
2016-06-18 01:11:03 +03:00

246 lines
5.7 KiB
Go

package html
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/Joker/jade"
"github.com/kataras/iris/config"
)
type (
// Engine the html/template engine & Jade
Engine struct {
Config *config.Template
Templates *template.Template
}
)
var emptyFuncs = template.FuncMap{
"yield": func() (string, error) {
return "", fmt.Errorf("yield was called, yet no layout defined")
},
"partial": func() (string, error) {
return "", fmt.Errorf("block was called, yet no layout defined")
},
"current": func() (string, error) {
return "", nil
}, "render": func() (string, error) {
return "", nil
},
}
// New creates and returns the HTMLTemplate template engine
func New(c config.Template) *Engine {
s := &Engine{Config: &c}
return s
}
// BuildTemplates builds the templates
func (s *Engine) BuildTemplates() error {
if s.Config.Asset == nil || s.Config.AssetNames == nil {
return s.buildFromDir()
}
return s.buildFromAsset()
}
func (s *Engine) buildFromDir() error {
if s.Config.Directory == "" {
return nil //we don't return fill error here(yet)
}
var templateErr error
/*var minifier *minify.M
if s.Config.Minify {
minifier = minify.New()
minifier.AddFunc("text/html", htmlMinifier.Minify)
} // Note: minifier has bugs, I complety remove this from Iris.
*/
dir := s.Config.Directory
s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
// Walk the supplied directory and compile any files that match our extension list.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
return nil
}
rel, err := filepath.Rel(dir, path)
if err != nil {
return err
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = filepath.Ext(rel)
}
for _, extension := range s.Config.Extensions {
if ext == extension {
buf, err := ioutil.ReadFile(path)
contents := string(buf)
/*if s.Config.Minify {
buf, err = minifier.Bytes("text/html", buf)
}*/
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
if s.Config.Engine == config.JadeEngine {
contents, err = jade.Parse(name, contents)
}
if err != nil {
templateErr = err
break
}
// Add our funcmaps.
if s.Config.HTMLTemplate.Funcs != nil {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
return nil
})
return templateErr
}
func (s *Engine) buildFromAsset() error {
var templateErr error
dir := s.Config.Directory
s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
for _, path := range s.Config.AssetNames() {
if !strings.HasPrefix(path, dir) {
continue
}
rel, err := filepath.Rel(dir, path)
if err != nil {
panic(err)
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
}
for _, extension := range s.Config.Extensions {
if ext == extension {
buf, err := s.Config.Asset(path)
if err != nil {
panic(err)
}
contents := string(buf)
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
if s.Config.Engine == config.JadeEngine {
contents, err = jade.Parse(name, contents)
}
if err != nil {
return err
}
// Add our funcmaps.
if s.Config.HTMLTemplate.Funcs != nil {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
}
return templateErr
}
func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
err := s.Templates.ExecuteTemplate(buf, name, binding)
return buf, err
}
func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{
"yield": func() (template.HTML, error) {
buf, err := s.executeTemplateBuf(name, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
"current": func() (string, error) {
return name, nil
},
"partial": func(partialName string) (template.HTML, error) {
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
}
return "", nil
},
"render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
},
}
_userLayoutFuncs := s.Config.HTMLTemplate.LayoutFuncs
if _userLayoutFuncs != nil && len(_userLayoutFuncs) > 0 {
for k, v := range _userLayoutFuncs {
funcs[k] = v
}
}
if tpl := s.Templates.Lookup(name); tpl != nil {
tpl.Funcs(funcs)
}
}
func (s *Engine) runtimeFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{
"render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
},
}
if tpl := s.Templates.Lookup(name); tpl != nil {
tpl.Funcs(funcs)
}
}
// ExecuteWriter executes a templates and write its results to the out writer
func (s *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if layout != "" && layout != config.NoLayout {
s.layoutFuncsFor(name, binding)
name = layout
} else {
s.runtimeFuncsFor(name, binding)
}
return s.Templates.ExecuteTemplate(out, name, binding)
}