725 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			725 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2013 Martini Authors
 | 
						|
// Copyright 2014 The Macaron Authors
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
 | 
						|
// not use this file except in compliance with the License. You may obtain
 | 
						|
// a copy of the License at
 | 
						|
//
 | 
						|
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
						|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
						|
// License for the specific language governing permissions and limitations
 | 
						|
// under the License.
 | 
						|
 | 
						|
package macaron
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/json"
 | 
						|
	"encoding/xml"
 | 
						|
	"fmt"
 | 
						|
	"html/template"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"os"
 | 
						|
	"path"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/Unknwon/com"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	_CONTENT_TYPE    = "Content-Type"
 | 
						|
	_CONTENT_LENGTH  = "Content-Length"
 | 
						|
	_CONTENT_BINARY  = "application/octet-stream"
 | 
						|
	_CONTENT_JSON    = "application/json"
 | 
						|
	_CONTENT_HTML    = "text/html"
 | 
						|
	_CONTENT_PLAIN   = "text/plain"
 | 
						|
	_CONTENT_XHTML   = "application/xhtml+xml"
 | 
						|
	_CONTENT_XML     = "text/xml"
 | 
						|
	_DEFAULT_CHARSET = "UTF-8"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// Provides a temporary buffer to execute templates into and catch errors.
 | 
						|
	bufpool = sync.Pool{
 | 
						|
		New: func() interface{} { return new(bytes.Buffer) },
 | 
						|
	}
 | 
						|
 | 
						|
	// Included helper functions for use when rendering html
 | 
						|
	helperFuncs = template.FuncMap{
 | 
						|
		"yield": func() (string, error) {
 | 
						|
			return "", fmt.Errorf("yield called with no layout defined")
 | 
						|
		},
 | 
						|
		"current": func() (string, error) {
 | 
						|
			return "", nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
type (
 | 
						|
	// TemplateFile represents a interface of template file that has name and can be read.
 | 
						|
	TemplateFile interface {
 | 
						|
		Name() string
 | 
						|
		Data() []byte
 | 
						|
		Ext() string
 | 
						|
	}
 | 
						|
	// TemplateFileSystem represents a interface of template file system that able to list all files.
 | 
						|
	TemplateFileSystem interface {
 | 
						|
		ListFiles() []TemplateFile
 | 
						|
		Get(string) (io.Reader, error)
 | 
						|
	}
 | 
						|
 | 
						|
	// Delims represents a set of Left and Right delimiters for HTML template rendering
 | 
						|
	Delims struct {
 | 
						|
		// Left delimiter, defaults to {{
 | 
						|
		Left string
 | 
						|
		// Right delimiter, defaults to }}
 | 
						|
		Right string
 | 
						|
	}
 | 
						|
 | 
						|
	// RenderOptions represents a struct for specifying configuration options for the Render middleware.
 | 
						|
	RenderOptions struct {
 | 
						|
		// Directory to load templates. Default is "templates".
 | 
						|
		Directory string
 | 
						|
		// Addtional directories to overwite templates.
 | 
						|
		AppendDirectories []string
 | 
						|
		// Layout template name. Will not render a layout if "". Default is to "".
 | 
						|
		Layout string
 | 
						|
		// Extensions to parse template files from. Defaults are [".tmpl", ".html"].
 | 
						|
		Extensions []string
 | 
						|
		// Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Default is [].
 | 
						|
		Funcs []template.FuncMap
 | 
						|
		// Delims sets the action delimiters to the specified strings in the Delims struct.
 | 
						|
		Delims Delims
 | 
						|
		// Appends the given charset to the Content-Type header. Default is "UTF-8".
 | 
						|
		Charset string
 | 
						|
		// Outputs human readable JSON.
 | 
						|
		IndentJSON bool
 | 
						|
		// Outputs human readable XML.
 | 
						|
		IndentXML bool
 | 
						|
		// Prefixes the JSON output with the given bytes.
 | 
						|
		PrefixJSON []byte
 | 
						|
		// Prefixes the XML output with the given bytes.
 | 
						|
		PrefixXML []byte
 | 
						|
		// Allows changing of output to XHTML instead of HTML. Default is "text/html"
 | 
						|
		HTMLContentType string
 | 
						|
		// TemplateFileSystem is the interface for supporting any implmentation of template file system.
 | 
						|
		TemplateFileSystem
 | 
						|
	}
 | 
						|
 | 
						|
	// HTMLOptions is a struct for overriding some rendering Options for specific HTML call
 | 
						|
	HTMLOptions struct {
 | 
						|
		// Layout template name. Overrides Options.Layout.
 | 
						|
		Layout string
 | 
						|
	}
 | 
						|
 | 
						|
	Render interface {
 | 
						|
		http.ResponseWriter
 | 
						|
		SetResponseWriter(http.ResponseWriter)
 | 
						|
 | 
						|
		JSON(int, interface{})
 | 
						|
		JSONString(interface{}) (string, error)
 | 
						|
		RawData(int, []byte)   // Serve content as binary
 | 
						|
		PlainText(int, []byte) // Serve content as plain text
 | 
						|
		HTML(int, string, interface{}, ...HTMLOptions)
 | 
						|
		HTMLSet(int, string, string, interface{}, ...HTMLOptions)
 | 
						|
		HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error)
 | 
						|
		HTMLString(string, interface{}, ...HTMLOptions) (string, error)
 | 
						|
		HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error)
 | 
						|
		HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error)
 | 
						|
		XML(int, interface{})
 | 
						|
		Error(int, ...string)
 | 
						|
		Status(int)
 | 
						|
		SetTemplatePath(string, string)
 | 
						|
		HasTemplateSet(string) bool
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
// TplFile implements TemplateFile interface.
 | 
						|
type TplFile struct {
 | 
						|
	name string
 | 
						|
	data []byte
 | 
						|
	ext  string
 | 
						|
}
 | 
						|
 | 
						|
// NewTplFile cerates new template file with given name and data.
 | 
						|
func NewTplFile(name string, data []byte, ext string) *TplFile {
 | 
						|
	return &TplFile{name, data, ext}
 | 
						|
}
 | 
						|
 | 
						|
func (f *TplFile) Name() string {
 | 
						|
	return f.name
 | 
						|
}
 | 
						|
 | 
						|
func (f *TplFile) Data() []byte {
 | 
						|
	return f.data
 | 
						|
}
 | 
						|
 | 
						|
func (f *TplFile) Ext() string {
 | 
						|
	return f.ext
 | 
						|
}
 | 
						|
 | 
						|
// TplFileSystem implements TemplateFileSystem interface.
 | 
						|
type TplFileSystem struct {
 | 
						|
	files []TemplateFile
 | 
						|
}
 | 
						|
 | 
						|
// NewTemplateFileSystem creates new template file system with given options.
 | 
						|
func NewTemplateFileSystem(opt RenderOptions, omitData bool) TplFileSystem {
 | 
						|
	fs := TplFileSystem{}
 | 
						|
	fs.files = make([]TemplateFile, 0, 10)
 | 
						|
 | 
						|
	// Directories are composed in reverse order because later one overwrites previous ones,
 | 
						|
	// so once found, we can directly jump out of the loop.
 | 
						|
	dirs := make([]string, 0, len(opt.AppendDirectories)+1)
 | 
						|
	for i := len(opt.AppendDirectories) - 1; i >= 0; i-- {
 | 
						|
		dirs = append(dirs, opt.AppendDirectories[i])
 | 
						|
	}
 | 
						|
	dirs = append(dirs, opt.Directory)
 | 
						|
 | 
						|
	var err error
 | 
						|
	for i := range dirs {
 | 
						|
		// Skip ones that does not exists for symlink test,
 | 
						|
		// but allow non-symlink ones added after start.
 | 
						|
		if !com.IsExist(dirs[i]) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		dirs[i], err = filepath.EvalSymlinks(dirs[i])
 | 
						|
		if err != nil {
 | 
						|
			panic("EvalSymlinks(" + dirs[i] + "): " + err.Error())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	lastDir := dirs[len(dirs)-1]
 | 
						|
 | 
						|
	// We still walk the last (original) directory because it's non-sense we load templates not exist in original directory.
 | 
						|
	if err = filepath.Walk(lastDir, func(path string, info os.FileInfo, err error) error {
 | 
						|
		r, err := filepath.Rel(lastDir, path)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		ext := GetExt(r)
 | 
						|
 | 
						|
		for _, extension := range opt.Extensions {
 | 
						|
			if ext != extension {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			var data []byte
 | 
						|
			if !omitData {
 | 
						|
				// Loop over candidates of directory, break out once found.
 | 
						|
				// The file always exists because it's inside the walk function,
 | 
						|
				// and read original file is the worst case.
 | 
						|
				for i := range dirs {
 | 
						|
					path = filepath.Join(dirs[i], r)
 | 
						|
					if !com.IsFile(path) {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
 | 
						|
					data, err = ioutil.ReadFile(path)
 | 
						|
					if err != nil {
 | 
						|
						return err
 | 
						|
					}
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			name := filepath.ToSlash((r[0 : len(r)-len(ext)]))
 | 
						|
			fs.files = append(fs.files, NewTplFile(name, data, ext))
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	}); err != nil {
 | 
						|
		panic("NewTemplateFileSystem: " + err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	return fs
 | 
						|
}
 | 
						|
 | 
						|
func (fs TplFileSystem) ListFiles() []TemplateFile {
 | 
						|
	return fs.files
 | 
						|
}
 | 
						|
 | 
						|
func (fs TplFileSystem) Get(name string) (io.Reader, error) {
 | 
						|
	for i := range fs.files {
 | 
						|
		if fs.files[i].Name()+fs.files[i].Ext() == name {
 | 
						|
			return bytes.NewReader(fs.files[i].Data()), nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, fmt.Errorf("file '%s' not found", name)
 | 
						|
}
 | 
						|
 | 
						|
func PrepareCharset(charset string) string {
 | 
						|
	if len(charset) != 0 {
 | 
						|
		return "; charset=" + charset
 | 
						|
	}
 | 
						|
 | 
						|
	return "; charset=" + _DEFAULT_CHARSET
 | 
						|
}
 | 
						|
 | 
						|
func GetExt(s string) string {
 | 
						|
	index := strings.Index(s, ".")
 | 
						|
	if index == -1 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	return s[index:]
 | 
						|
}
 | 
						|
 | 
						|
func compile(opt RenderOptions) *template.Template {
 | 
						|
	t := template.New(opt.Directory)
 | 
						|
	t.Delims(opt.Delims.Left, opt.Delims.Right)
 | 
						|
	// Parse an initial template in case we don't have any.
 | 
						|
	template.Must(t.Parse("Macaron"))
 | 
						|
 | 
						|
	if opt.TemplateFileSystem == nil {
 | 
						|
		opt.TemplateFileSystem = NewTemplateFileSystem(opt, false)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, f := range opt.TemplateFileSystem.ListFiles() {
 | 
						|
		tmpl := t.New(f.Name())
 | 
						|
		for _, funcs := range opt.Funcs {
 | 
						|
			tmpl.Funcs(funcs)
 | 
						|
		}
 | 
						|
		// Bomb out if parse fails. We don't want any silent server starts.
 | 
						|
		template.Must(tmpl.Funcs(helperFuncs).Parse(string(f.Data())))
 | 
						|
	}
 | 
						|
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	DEFAULT_TPL_SET_NAME = "DEFAULT"
 | 
						|
)
 | 
						|
 | 
						|
// TemplateSet represents a template set of type *template.Template.
 | 
						|
type TemplateSet struct {
 | 
						|
	lock sync.RWMutex
 | 
						|
	sets map[string]*template.Template
 | 
						|
	dirs map[string]string
 | 
						|
}
 | 
						|
 | 
						|
// NewTemplateSet initializes a new empty template set.
 | 
						|
func NewTemplateSet() *TemplateSet {
 | 
						|
	return &TemplateSet{
 | 
						|
		sets: make(map[string]*template.Template),
 | 
						|
		dirs: make(map[string]string),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ts *TemplateSet) Set(name string, opt *RenderOptions) *template.Template {
 | 
						|
	t := compile(*opt)
 | 
						|
 | 
						|
	ts.lock.Lock()
 | 
						|
	defer ts.lock.Unlock()
 | 
						|
 | 
						|
	ts.sets[name] = t
 | 
						|
	ts.dirs[name] = opt.Directory
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
func (ts *TemplateSet) Get(name string) *template.Template {
 | 
						|
	ts.lock.RLock()
 | 
						|
	defer ts.lock.RUnlock()
 | 
						|
 | 
						|
	return ts.sets[name]
 | 
						|
}
 | 
						|
 | 
						|
func (ts *TemplateSet) GetDir(name string) string {
 | 
						|
	ts.lock.RLock()
 | 
						|
	defer ts.lock.RUnlock()
 | 
						|
 | 
						|
	return ts.dirs[name]
 | 
						|
}
 | 
						|
 | 
						|
func prepareRenderOptions(options []RenderOptions) RenderOptions {
 | 
						|
	var opt RenderOptions
 | 
						|
	if len(options) > 0 {
 | 
						|
		opt = options[0]
 | 
						|
	}
 | 
						|
 | 
						|
	// Defaults.
 | 
						|
	if len(opt.Directory) == 0 {
 | 
						|
		opt.Directory = "templates"
 | 
						|
	}
 | 
						|
	if len(opt.Extensions) == 0 {
 | 
						|
		opt.Extensions = []string{".tmpl", ".html"}
 | 
						|
	}
 | 
						|
	if len(opt.HTMLContentType) == 0 {
 | 
						|
		opt.HTMLContentType = _CONTENT_HTML
 | 
						|
	}
 | 
						|
 | 
						|
	return opt
 | 
						|
}
 | 
						|
 | 
						|
func ParseTplSet(tplSet string) (tplName string, tplDir string) {
 | 
						|
	tplSet = strings.TrimSpace(tplSet)
 | 
						|
	if len(tplSet) == 0 {
 | 
						|
		panic("empty template set argument")
 | 
						|
	}
 | 
						|
	infos := strings.Split(tplSet, ":")
 | 
						|
	if len(infos) == 1 {
 | 
						|
		tplDir = infos[0]
 | 
						|
		tplName = path.Base(tplDir)
 | 
						|
	} else {
 | 
						|
		tplName = infos[0]
 | 
						|
		tplDir = infos[1]
 | 
						|
	}
 | 
						|
 | 
						|
	if !com.IsDir(tplDir) {
 | 
						|
		panic("template set path does not exist or is not a directory")
 | 
						|
	}
 | 
						|
	return tplName, tplDir
 | 
						|
}
 | 
						|
 | 
						|
func renderHandler(opt RenderOptions, tplSets []string) Handler {
 | 
						|
	cs := PrepareCharset(opt.Charset)
 | 
						|
	ts := NewTemplateSet()
 | 
						|
	ts.Set(DEFAULT_TPL_SET_NAME, &opt)
 | 
						|
 | 
						|
	var tmpOpt RenderOptions
 | 
						|
	for _, tplSet := range tplSets {
 | 
						|
		tplName, tplDir := ParseTplSet(tplSet)
 | 
						|
		tmpOpt = opt
 | 
						|
		tmpOpt.Directory = tplDir
 | 
						|
		ts.Set(tplName, &tmpOpt)
 | 
						|
	}
 | 
						|
 | 
						|
	return func(ctx *Context) {
 | 
						|
		r := &TplRender{
 | 
						|
			ResponseWriter:  ctx.Resp,
 | 
						|
			TemplateSet:     ts,
 | 
						|
			Opt:             &opt,
 | 
						|
			CompiledCharset: cs,
 | 
						|
		}
 | 
						|
		ctx.Data["TmplLoadTimes"] = func() string {
 | 
						|
			if r.startTime.IsZero() {
 | 
						|
				return ""
 | 
						|
			}
 | 
						|
			return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms"
 | 
						|
		}
 | 
						|
 | 
						|
		ctx.Render = r
 | 
						|
		ctx.MapTo(r, (*Render)(nil))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain.
 | 
						|
// An single variadic macaron.RenderOptions struct can be optionally provided to configure
 | 
						|
// HTML rendering. The default directory for templates is "templates" and the default
 | 
						|
// file extension is ".tmpl" and ".html".
 | 
						|
//
 | 
						|
// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the
 | 
						|
// MACARON_ENV environment variable to "production".
 | 
						|
func Renderer(options ...RenderOptions) Handler {
 | 
						|
	return renderHandler(prepareRenderOptions(options), []string{})
 | 
						|
}
 | 
						|
 | 
						|
func Renderers(options RenderOptions, tplSets ...string) Handler {
 | 
						|
	return renderHandler(prepareRenderOptions([]RenderOptions{options}), tplSets)
 | 
						|
}
 | 
						|
 | 
						|
type TplRender struct {
 | 
						|
	http.ResponseWriter
 | 
						|
	*TemplateSet
 | 
						|
	Opt             *RenderOptions
 | 
						|
	CompiledCharset string
 | 
						|
 | 
						|
	startTime time.Time
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) SetResponseWriter(rw http.ResponseWriter) {
 | 
						|
	r.ResponseWriter = rw
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) JSON(status int, v interface{}) {
 | 
						|
	var (
 | 
						|
		result []byte
 | 
						|
		err    error
 | 
						|
	)
 | 
						|
	if r.Opt.IndentJSON {
 | 
						|
		result, err = json.MarshalIndent(v, "", "  ")
 | 
						|
	} else {
 | 
						|
		result, err = json.Marshal(v)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		http.Error(r, err.Error(), 500)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// json rendered fine, write out the result
 | 
						|
	r.Header().Set(_CONTENT_TYPE, _CONTENT_JSON+r.CompiledCharset)
 | 
						|
	r.WriteHeader(status)
 | 
						|
	if len(r.Opt.PrefixJSON) > 0 {
 | 
						|
		r.Write(r.Opt.PrefixJSON)
 | 
						|
	}
 | 
						|
	r.Write(result)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) JSONString(v interface{}) (string, error) {
 | 
						|
	var result []byte
 | 
						|
	var err error
 | 
						|
	if r.Opt.IndentJSON {
 | 
						|
		result, err = json.MarshalIndent(v, "", "  ")
 | 
						|
	} else {
 | 
						|
		result, err = json.Marshal(v)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return string(result), nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) XML(status int, v interface{}) {
 | 
						|
	var result []byte
 | 
						|
	var err error
 | 
						|
	if r.Opt.IndentXML {
 | 
						|
		result, err = xml.MarshalIndent(v, "", "  ")
 | 
						|
	} else {
 | 
						|
		result, err = xml.Marshal(v)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		http.Error(r, err.Error(), 500)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// XML rendered fine, write out the result
 | 
						|
	r.Header().Set(_CONTENT_TYPE, _CONTENT_XML+r.CompiledCharset)
 | 
						|
	r.WriteHeader(status)
 | 
						|
	if len(r.Opt.PrefixXML) > 0 {
 | 
						|
		r.Write(r.Opt.PrefixXML)
 | 
						|
	}
 | 
						|
	r.Write(result)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) data(status int, contentType string, v []byte) {
 | 
						|
	if r.Header().Get(_CONTENT_TYPE) == "" {
 | 
						|
		r.Header().Set(_CONTENT_TYPE, contentType)
 | 
						|
	}
 | 
						|
	r.WriteHeader(status)
 | 
						|
	r.Write(v)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) RawData(status int, v []byte) {
 | 
						|
	r.data(status, _CONTENT_BINARY, v)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) PlainText(status int, v []byte) {
 | 
						|
	r.data(status, _CONTENT_PLAIN, v)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) execute(t *template.Template, name string, data interface{}) (*bytes.Buffer, error) {
 | 
						|
	buf := bufpool.Get().(*bytes.Buffer)
 | 
						|
	return buf, t.ExecuteTemplate(buf, name, data)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) addYield(t *template.Template, tplName string, data interface{}) {
 | 
						|
	funcs := template.FuncMap{
 | 
						|
		"yield": func() (template.HTML, error) {
 | 
						|
			buf, err := r.execute(t, tplName, data)
 | 
						|
			// return safe html here since we are rendering our own template
 | 
						|
			return template.HTML(buf.String()), err
 | 
						|
		},
 | 
						|
		"current": func() (string, error) {
 | 
						|
			return tplName, nil
 | 
						|
		},
 | 
						|
	}
 | 
						|
	t.Funcs(funcs)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) renderBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) {
 | 
						|
	t := r.TemplateSet.Get(setName)
 | 
						|
	if Env == DEV {
 | 
						|
		opt := *r.Opt
 | 
						|
		opt.Directory = r.TemplateSet.GetDir(setName)
 | 
						|
		t = r.TemplateSet.Set(setName, &opt)
 | 
						|
	}
 | 
						|
	if t == nil {
 | 
						|
		return nil, fmt.Errorf("html/template: template \"%s\" is undefined", tplName)
 | 
						|
	}
 | 
						|
 | 
						|
	opt := r.prepareHTMLOptions(htmlOpt)
 | 
						|
 | 
						|
	if len(opt.Layout) > 0 {
 | 
						|
		r.addYield(t, tplName, data)
 | 
						|
		tplName = opt.Layout
 | 
						|
	}
 | 
						|
 | 
						|
	out, err := r.execute(t, tplName, data)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return out, nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) renderHTML(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) {
 | 
						|
	r.startTime = time.Now()
 | 
						|
 | 
						|
	out, err := r.renderBytes(setName, tplName, data, htmlOpt...)
 | 
						|
	if err != nil {
 | 
						|
		http.Error(r, err.Error(), http.StatusInternalServerError)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	r.Header().Set(_CONTENT_TYPE, r.Opt.HTMLContentType+r.CompiledCharset)
 | 
						|
	r.WriteHeader(status)
 | 
						|
 | 
						|
	if _, err := out.WriteTo(r); err != nil {
 | 
						|
		out.Reset()
 | 
						|
	}
 | 
						|
	bufpool.Put(out)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTML(status int, name string, data interface{}, htmlOpt ...HTMLOptions) {
 | 
						|
	r.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data, htmlOpt...)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTMLSet(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) {
 | 
						|
	r.renderHTML(status, setName, tplName, data, htmlOpt...)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTMLSetBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) {
 | 
						|
	out, err := r.renderBytes(setName, tplName, data, htmlOpt...)
 | 
						|
	if err != nil {
 | 
						|
		return []byte(""), err
 | 
						|
	}
 | 
						|
	return out.Bytes(), nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTMLBytes(name string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) {
 | 
						|
	return r.HTMLSetBytes(DEFAULT_TPL_SET_NAME, name, data, htmlOpt...)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTMLSetString(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (string, error) {
 | 
						|
	p, err := r.HTMLSetBytes(setName, tplName, data, htmlOpt...)
 | 
						|
	return string(p), err
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HTMLString(name string, data interface{}, htmlOpt ...HTMLOptions) (string, error) {
 | 
						|
	p, err := r.HTMLBytes(name, data, htmlOpt...)
 | 
						|
	return string(p), err
 | 
						|
}
 | 
						|
 | 
						|
// Error writes the given HTTP status to the current ResponseWriter
 | 
						|
func (r *TplRender) Error(status int, message ...string) {
 | 
						|
	r.WriteHeader(status)
 | 
						|
	if len(message) > 0 {
 | 
						|
		r.Write([]byte(message[0]))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) Status(status int) {
 | 
						|
	r.WriteHeader(status)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {
 | 
						|
	if len(htmlOpt) > 0 {
 | 
						|
		return htmlOpt[0]
 | 
						|
	}
 | 
						|
 | 
						|
	return HTMLOptions{
 | 
						|
		Layout: r.Opt.Layout,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) SetTemplatePath(setName, dir string) {
 | 
						|
	if len(setName) == 0 {
 | 
						|
		setName = DEFAULT_TPL_SET_NAME
 | 
						|
	}
 | 
						|
	opt := *r.Opt
 | 
						|
	opt.Directory = dir
 | 
						|
	r.TemplateSet.Set(setName, &opt)
 | 
						|
}
 | 
						|
 | 
						|
func (r *TplRender) HasTemplateSet(name string) bool {
 | 
						|
	return r.TemplateSet.Get(name) != nil
 | 
						|
}
 | 
						|
 | 
						|
// DummyRender is used when user does not choose any real render to use.
 | 
						|
// This way, we can print out friendly message which asks them to register one,
 | 
						|
// instead of ugly and confusing 'nil pointer' panic.
 | 
						|
type DummyRender struct {
 | 
						|
	http.ResponseWriter
 | 
						|
}
 | 
						|
 | 
						|
func renderNotRegistered() {
 | 
						|
	panic("middleware render hasn't been registered")
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) SetResponseWriter(http.ResponseWriter) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) JSON(int, interface{}) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) JSONString(interface{}) (string, error) {
 | 
						|
	renderNotRegistered()
 | 
						|
	return "", nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) RawData(int, []byte) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) PlainText(int, []byte) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTML(int, string, interface{}, ...HTMLOptions) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTMLSet(int, string, string, interface{}, ...HTMLOptions) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) {
 | 
						|
	renderNotRegistered()
 | 
						|
	return "", nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTMLString(string, interface{}, ...HTMLOptions) (string, error) {
 | 
						|
	renderNotRegistered()
 | 
						|
	return "", nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) {
 | 
						|
	renderNotRegistered()
 | 
						|
	return nil, nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) {
 | 
						|
	renderNotRegistered()
 | 
						|
	return nil, nil
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) XML(int, interface{}) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) Error(int, ...string) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) Status(int) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) SetTemplatePath(string, string) {
 | 
						|
	renderNotRegistered()
 | 
						|
}
 | 
						|
 | 
						|
func (r *DummyRender) HasTemplateSet(string) bool {
 | 
						|
	renderNotRegistered()
 | 
						|
	return false
 | 
						|
}
 |