mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			634 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			634 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package hugolib
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"errors"
 | 
						|
	"html"
 | 
						|
	"html/template"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"reflect"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/eknkc/amber"
 | 
						|
	"github.com/spf13/hugo/helpers"
 | 
						|
	jww "github.com/spf13/jwalterweatherman"
 | 
						|
)
 | 
						|
 | 
						|
var localTemplates *template.Template
 | 
						|
 | 
						|
func Eq(x, y interface{}) bool {
 | 
						|
	return reflect.DeepEqual(x, y)
 | 
						|
}
 | 
						|
 | 
						|
func Ne(x, y interface{}) bool {
 | 
						|
	return !Eq(x, y)
 | 
						|
}
 | 
						|
 | 
						|
func Ge(a, b interface{}) bool {
 | 
						|
	left, right := compareGetFloat(a, b)
 | 
						|
	return left >= right
 | 
						|
}
 | 
						|
 | 
						|
func Gt(a, b interface{}) bool {
 | 
						|
	left, right := compareGetFloat(a, b)
 | 
						|
	return left > right
 | 
						|
}
 | 
						|
 | 
						|
func Le(a, b interface{}) bool {
 | 
						|
	left, right := compareGetFloat(a, b)
 | 
						|
	return left <= right
 | 
						|
}
 | 
						|
 | 
						|
func Lt(a, b interface{}) bool {
 | 
						|
	left, right := compareGetFloat(a, b)
 | 
						|
	return left < right
 | 
						|
}
 | 
						|
 | 
						|
func compareGetFloat(a interface{}, b interface{}) (float64, float64) {
 | 
						|
	var left, right float64
 | 
						|
	av := reflect.ValueOf(a)
 | 
						|
 | 
						|
	switch av.Kind() {
 | 
						|
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
 | 
						|
		left = float64(av.Len())
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		left = float64(av.Int())
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		left = av.Float()
 | 
						|
	case reflect.String:
 | 
						|
		left, _ = strconv.ParseFloat(av.String(), 64)
 | 
						|
	}
 | 
						|
 | 
						|
	bv := reflect.ValueOf(b)
 | 
						|
 | 
						|
	switch bv.Kind() {
 | 
						|
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
 | 
						|
		right = float64(bv.Len())
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		right = float64(bv.Int())
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		right = bv.Float()
 | 
						|
	case reflect.String:
 | 
						|
		right, _ = strconv.ParseFloat(bv.String(), 64)
 | 
						|
	}
 | 
						|
 | 
						|
	return left, right
 | 
						|
}
 | 
						|
 | 
						|
func Intersect(l1, l2 interface{}) (interface{}, error) {
 | 
						|
	l1v := reflect.ValueOf(l1)
 | 
						|
	l2v := reflect.ValueOf(l2)
 | 
						|
 | 
						|
	switch l1v.Kind() {
 | 
						|
	case reflect.Array, reflect.Slice:
 | 
						|
		switch l2v.Kind() {
 | 
						|
		case reflect.Array, reflect.Slice:
 | 
						|
			r := reflect.MakeSlice(l1v.Type(), 0, 0)
 | 
						|
			for i := 0; i < l1v.Len(); i++ {
 | 
						|
				l1vv := l1v.Index(i)
 | 
						|
				for j := 0; j < l2v.Len(); j++ {
 | 
						|
					l2vv := l2v.Index(j)
 | 
						|
					switch l1vv.Kind() {
 | 
						|
					case reflect.String:
 | 
						|
						if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !In(r, l2vv) {
 | 
						|
							r = reflect.Append(r, l2vv)
 | 
						|
						}
 | 
						|
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
						switch l2vv.Kind() {
 | 
						|
						case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
							if l1vv.Int() == l2vv.Int() && !In(r, l2vv) {
 | 
						|
								r = reflect.Append(r, l2vv)
 | 
						|
							}
 | 
						|
						}
 | 
						|
					case reflect.Float32, reflect.Float64:
 | 
						|
						switch l2vv.Kind() {
 | 
						|
						case reflect.Float32, reflect.Float64:
 | 
						|
							if l1vv.Float() == l2vv.Float() && !In(r, l2vv) {
 | 
						|
								r = reflect.Append(r, l2vv)
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return r.Interface(), nil
 | 
						|
		default:
 | 
						|
			return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func In(l interface{}, v interface{}) bool {
 | 
						|
	lv := reflect.ValueOf(l)
 | 
						|
	vv := reflect.ValueOf(v)
 | 
						|
 | 
						|
	switch lv.Kind() {
 | 
						|
	case reflect.Array, reflect.Slice:
 | 
						|
		for i := 0; i < lv.Len(); i++ {
 | 
						|
			lvv := lv.Index(i)
 | 
						|
			switch lvv.Kind() {
 | 
						|
			case reflect.String:
 | 
						|
				if vv.Type() == lvv.Type() && vv.String() == lvv.String() {
 | 
						|
					return true
 | 
						|
				}
 | 
						|
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
				switch vv.Kind() {
 | 
						|
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
					if vv.Int() == lvv.Int() {
 | 
						|
						return true
 | 
						|
					}
 | 
						|
				}
 | 
						|
			case reflect.Float32, reflect.Float64:
 | 
						|
				switch vv.Kind() {
 | 
						|
				case reflect.Float32, reflect.Float64:
 | 
						|
					if vv.Float() == lvv.Float() {
 | 
						|
						return true
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case reflect.String:
 | 
						|
		if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// First is exposed to templates, to iterate over the first N items in a
 | 
						|
// rangeable list.
 | 
						|
func First(limit int, seq interface{}) (interface{}, error) {
 | 
						|
	if limit < 1 {
 | 
						|
		return nil, errors.New("can't return negative/empty count of items from sequence")
 | 
						|
	}
 | 
						|
 | 
						|
	seqv := reflect.ValueOf(seq)
 | 
						|
	// this is better than my first pass; ripped from text/template/exec.go indirect():
 | 
						|
	for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
 | 
						|
		if seqv.IsNil() {
 | 
						|
			return nil, errors.New("can't iterate over a nil value")
 | 
						|
		}
 | 
						|
		if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch seqv.Kind() {
 | 
						|
	case reflect.Array, reflect.Slice, reflect.String:
 | 
						|
		// okay
 | 
						|
	default:
 | 
						|
		return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
 | 
						|
	}
 | 
						|
	if limit > seqv.Len() {
 | 
						|
		limit = seqv.Len()
 | 
						|
	}
 | 
						|
	return seqv.Slice(0, limit).Interface(), nil
 | 
						|
}
 | 
						|
 | 
						|
func Where(seq, key, match interface{}) (interface{}, error) {
 | 
						|
	seqv := reflect.ValueOf(seq)
 | 
						|
	kv := reflect.ValueOf(key)
 | 
						|
	mv := reflect.ValueOf(match)
 | 
						|
 | 
						|
	// this is better than my first pass; ripped from text/template/exec.go indirect():
 | 
						|
	for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
 | 
						|
		if seqv.IsNil() {
 | 
						|
			return nil, errors.New("can't iterate over a nil value")
 | 
						|
		}
 | 
						|
		if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch seqv.Kind() {
 | 
						|
	case reflect.Array, reflect.Slice:
 | 
						|
		r := reflect.MakeSlice(seqv.Type(), 0, 0)
 | 
						|
		for i := 0; i < seqv.Len(); i++ {
 | 
						|
			var vvv reflect.Value
 | 
						|
			vv := seqv.Index(i)
 | 
						|
			switch vv.Kind() {
 | 
						|
			case reflect.Map:
 | 
						|
				if kv.Type() == vv.Type().Key() && vv.MapIndex(kv).IsValid() {
 | 
						|
					vvv = vv.MapIndex(kv)
 | 
						|
				}
 | 
						|
			case reflect.Struct:
 | 
						|
				if kv.Kind() == reflect.String && vv.FieldByName(kv.String()).IsValid() {
 | 
						|
					vvv = vv.FieldByName(kv.String())
 | 
						|
				}
 | 
						|
			case reflect.Ptr:
 | 
						|
				if !vv.IsNil() {
 | 
						|
					ev := vv.Elem()
 | 
						|
					switch ev.Kind() {
 | 
						|
					case reflect.Map:
 | 
						|
						if kv.Type() == ev.Type().Key() && ev.MapIndex(kv).IsValid() {
 | 
						|
							vvv = ev.MapIndex(kv)
 | 
						|
						}
 | 
						|
					case reflect.Struct:
 | 
						|
						if kv.Kind() == reflect.String && ev.FieldByName(kv.String()).IsValid() {
 | 
						|
							vvv = ev.FieldByName(kv.String())
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if vvv.IsValid() && mv.Type() == vvv.Type() {
 | 
						|
				switch mv.Kind() {
 | 
						|
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
					if mv.Int() == vvv.Int() {
 | 
						|
						r = reflect.Append(r, vv)
 | 
						|
					}
 | 
						|
				case reflect.String:
 | 
						|
					if mv.String() == vvv.String() {
 | 
						|
						r = reflect.Append(r, vv)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return r.Interface(), nil
 | 
						|
	default:
 | 
						|
		return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func IsSet(a interface{}, key interface{}) bool {
 | 
						|
	av := reflect.ValueOf(a)
 | 
						|
	kv := reflect.ValueOf(key)
 | 
						|
 | 
						|
	switch av.Kind() {
 | 
						|
	case reflect.Array, reflect.Chan, reflect.Slice:
 | 
						|
		if int64(av.Len()) > kv.Int() {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	case reflect.Map:
 | 
						|
		if kv.Type() == av.Type().Key() {
 | 
						|
			return av.MapIndex(kv).IsValid()
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func ReturnWhenSet(a interface{}, index int) interface{} {
 | 
						|
	av := reflect.ValueOf(a)
 | 
						|
 | 
						|
	switch av.Kind() {
 | 
						|
	case reflect.Array, reflect.Slice:
 | 
						|
		if av.Len() > index {
 | 
						|
 | 
						|
			avv := av.Index(index)
 | 
						|
			switch avv.Kind() {
 | 
						|
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
				return avv.Int()
 | 
						|
			case reflect.String:
 | 
						|
				return avv.String()
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func Highlight(in interface{}, lang string) template.HTML {
 | 
						|
	var str string
 | 
						|
	av := reflect.ValueOf(in)
 | 
						|
	switch av.Kind() {
 | 
						|
	case reflect.String:
 | 
						|
		str = av.String()
 | 
						|
	}
 | 
						|
 | 
						|
	if strings.HasPrefix(strings.TrimSpace(str), "<pre><code>") {
 | 
						|
		str = str[strings.Index(str, "<pre><code>")+11:]
 | 
						|
	}
 | 
						|
	if strings.HasSuffix(strings.TrimSpace(str), "</code></pre>") {
 | 
						|
		str = str[:strings.LastIndex(str, "</code></pre>")]
 | 
						|
	}
 | 
						|
	return template.HTML(helpers.Highlight(html.UnescapeString(str), lang))
 | 
						|
}
 | 
						|
 | 
						|
func SafeHtml(text string) template.HTML {
 | 
						|
	return template.HTML(text)
 | 
						|
}
 | 
						|
 | 
						|
func doArithmetic(a, b interface{}, op rune) (interface{}, error) {
 | 
						|
	av := reflect.ValueOf(a)
 | 
						|
	bv := reflect.ValueOf(b)
 | 
						|
	var ai, bi int64
 | 
						|
	var af, bf float64
 | 
						|
	var au, bu uint64
 | 
						|
	switch av.Kind() {
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		ai = av.Int()
 | 
						|
		switch bv.Kind() {
 | 
						|
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
			bi = bv.Int()
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			af = float64(ai) // may overflow
 | 
						|
			ai = 0
 | 
						|
			bf = bv.Float()
 | 
						|
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
			bu = bv.Uint()
 | 
						|
			if ai >= 0 {
 | 
						|
				au = uint64(ai)
 | 
						|
				ai = 0
 | 
						|
			} else {
 | 
						|
				bi = int64(bu) // may overflow
 | 
						|
				bu = 0
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			return nil, errors.New("Can't apply the operator to the values")
 | 
						|
		}
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		af = av.Float()
 | 
						|
		switch bv.Kind() {
 | 
						|
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
			bf = float64(bv.Int()) // may overflow
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			bf = bv.Float()
 | 
						|
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
			bf = float64(bv.Uint()) // may overflow
 | 
						|
		default:
 | 
						|
			return nil, errors.New("Can't apply the operator to the values")
 | 
						|
		}
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		au = av.Uint()
 | 
						|
		switch bv.Kind() {
 | 
						|
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
			bi = bv.Int()
 | 
						|
			if bi >= 0 {
 | 
						|
				bu = uint64(bi)
 | 
						|
				bi = 0
 | 
						|
			} else {
 | 
						|
				ai = int64(au) // may overflow
 | 
						|
				au = 0
 | 
						|
			}
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			af = float64(au) // may overflow
 | 
						|
			au = 0
 | 
						|
			bf = bv.Float()
 | 
						|
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
			bu = bv.Uint()
 | 
						|
		default:
 | 
						|
			return nil, errors.New("Can't apply the operator to the values")
 | 
						|
		}
 | 
						|
	case reflect.String:
 | 
						|
		as := av.String()
 | 
						|
		if bv.Kind() == reflect.String && op == '+' {
 | 
						|
			bs := bv.String()
 | 
						|
			return as + bs, nil
 | 
						|
		} else {
 | 
						|
			return nil, errors.New("Can't apply the operator to the values")
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return nil, errors.New("Can't apply the operator to the values")
 | 
						|
	}
 | 
						|
 | 
						|
	switch op {
 | 
						|
	case '+':
 | 
						|
		if ai != 0 || bi != 0 {
 | 
						|
			return ai + bi, nil
 | 
						|
		} else if af != 0 || bf != 0 {
 | 
						|
			return af + bf, nil
 | 
						|
		} else if au != 0 || bu != 0 {
 | 
						|
			return au + bu, nil
 | 
						|
		} else {
 | 
						|
			return 0, nil
 | 
						|
		}
 | 
						|
	case '-':
 | 
						|
		if ai != 0 || bi != 0 {
 | 
						|
			return ai - bi, nil
 | 
						|
		} else if af != 0 || bf != 0 {
 | 
						|
			return af - bf, nil
 | 
						|
		} else if au != 0 || bu != 0 {
 | 
						|
			return au - bu, nil
 | 
						|
		} else {
 | 
						|
			return 0, nil
 | 
						|
		}
 | 
						|
	case '*':
 | 
						|
		if ai != 0 || bi != 0 {
 | 
						|
			return ai * bi, nil
 | 
						|
		} else if af != 0 || bf != 0 {
 | 
						|
			return af * bf, nil
 | 
						|
		} else if au != 0 || bu != 0 {
 | 
						|
			return au * bu, nil
 | 
						|
		} else {
 | 
						|
			return 0, nil
 | 
						|
		}
 | 
						|
	case '/':
 | 
						|
		if bi != 0 {
 | 
						|
			return ai / bi, nil
 | 
						|
		} else if bf != 0 {
 | 
						|
			return af / bf, nil
 | 
						|
		} else if bu != 0 {
 | 
						|
			return au / bu, nil
 | 
						|
		} else {
 | 
						|
			return nil, errors.New("Can't divide the value by 0")
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return nil, errors.New("There is no such an operation")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type Template interface {
 | 
						|
	ExecuteTemplate(wr io.Writer, name string, data interface{}) error
 | 
						|
	Lookup(name string) *template.Template
 | 
						|
	Templates() []*template.Template
 | 
						|
	New(name string) *template.Template
 | 
						|
	LoadTemplates(absPath string)
 | 
						|
	LoadTemplatesWithPrefix(absPath, prefix string)
 | 
						|
	AddTemplate(name, tpl string) error
 | 
						|
	AddInternalTemplate(prefix, name, tpl string) error
 | 
						|
	AddInternalShortcode(name, tpl string) error
 | 
						|
}
 | 
						|
 | 
						|
type templateErr struct {
 | 
						|
	name string
 | 
						|
	err  error
 | 
						|
}
 | 
						|
 | 
						|
type GoHtmlTemplate struct {
 | 
						|
	template.Template
 | 
						|
	errors []*templateErr
 | 
						|
}
 | 
						|
 | 
						|
func NewTemplate() Template {
 | 
						|
	var templates = &GoHtmlTemplate{
 | 
						|
		Template: *template.New(""),
 | 
						|
		errors:   make([]*templateErr, 0),
 | 
						|
	}
 | 
						|
 | 
						|
	localTemplates = &templates.Template
 | 
						|
 | 
						|
	funcMap := template.FuncMap{
 | 
						|
		"urlize":      helpers.Urlize,
 | 
						|
		"sanitizeurl": helpers.SanitizeUrl,
 | 
						|
		"eq":          Eq,
 | 
						|
		"ne":          Ne,
 | 
						|
		"gt":          Gt,
 | 
						|
		"ge":          Ge,
 | 
						|
		"lt":          Lt,
 | 
						|
		"le":          Le,
 | 
						|
		"in":          In,
 | 
						|
		"intersect":   Intersect,
 | 
						|
		"isset":       IsSet,
 | 
						|
		"echoParam":   ReturnWhenSet,
 | 
						|
		"safeHtml":    SafeHtml,
 | 
						|
		"first":       First,
 | 
						|
		"where":       Where,
 | 
						|
		"highlight":   Highlight,
 | 
						|
		"add":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '+') },
 | 
						|
		"sub":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '-') },
 | 
						|
		"div":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '/') },
 | 
						|
		"mod":         func(a, b int) int { return a % b },
 | 
						|
		"mul":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },
 | 
						|
		"modBool":     func(a, b int) bool { return a%b == 0 },
 | 
						|
		"lower":       func(a string) string { return strings.ToLower(a) },
 | 
						|
		"upper":       func(a string) string { return strings.ToUpper(a) },
 | 
						|
		"title":       func(a string) string { return strings.Title(a) },
 | 
						|
		"partial":     Partial,
 | 
						|
	}
 | 
						|
 | 
						|
	templates.Funcs(funcMap)
 | 
						|
 | 
						|
	templates.LoadEmbedded()
 | 
						|
	return templates
 | 
						|
}
 | 
						|
 | 
						|
func Partial(name string, context_list ...interface{}) template.HTML {
 | 
						|
	if strings.HasPrefix("partials/", name) {
 | 
						|
		name = name[8:]
 | 
						|
	}
 | 
						|
	var context interface{}
 | 
						|
 | 
						|
	if len(context_list) == 0 {
 | 
						|
		context = nil
 | 
						|
	} else {
 | 
						|
		context = context_list[0]
 | 
						|
	}
 | 
						|
	return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
 | 
						|
}
 | 
						|
 | 
						|
func ExecuteTemplate(context interface{}, layouts ...string) *bytes.Buffer {
 | 
						|
	buffer := new(bytes.Buffer)
 | 
						|
	worked := false
 | 
						|
	for _, layout := range layouts {
 | 
						|
		if localTemplates.Lookup(layout) != nil {
 | 
						|
			err := localTemplates.ExecuteTemplate(buffer, layout, context)
 | 
						|
			if err != nil {
 | 
						|
				jww.ERROR.Println(err, "in", layout)
 | 
						|
			}
 | 
						|
			worked = true
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if !worked {
 | 
						|
		jww.ERROR.Println("Unable to render", layouts)
 | 
						|
		jww.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
 | 
						|
	}
 | 
						|
 | 
						|
	return buffer
 | 
						|
}
 | 
						|
 | 
						|
func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
 | 
						|
	b := ExecuteTemplate(context, layouts...)
 | 
						|
	return template.HTML(string(b.Bytes()))
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) LoadEmbedded() {
 | 
						|
	t.EmbedShortcodes()
 | 
						|
	t.EmbedTemplates()
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {
 | 
						|
	if prefix != "" {
 | 
						|
		return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
 | 
						|
	} else {
 | 
						|
		return t.AddTemplate("_internal/"+name, tpl)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) AddInternalShortcode(name, content string) error {
 | 
						|
	return t.AddInternalTemplate("shortcodes", name, content)
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) AddTemplate(name, tpl string) error {
 | 
						|
	_, err := t.New(name).Parse(tpl)
 | 
						|
	if err != nil {
 | 
						|
		t.errors = append(t.errors, &templateErr{name: name, err: err})
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) AddTemplateFile(name, path string) error {
 | 
						|
	// get the suffix and switch on that
 | 
						|
	ext := filepath.Ext(path)
 | 
						|
	switch ext {
 | 
						|
	case ".amber":
 | 
						|
		compiler := amber.New()
 | 
						|
		// Parse the input file
 | 
						|
		if err := compiler.ParseFile(path); err != nil {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if _, err := compiler.CompileWithTemplate(t.New(name)); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		b, err := ioutil.ReadFile(path)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		return t.AddTemplate(name, string(b))
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
 | 
						|
	return filepath.ToSlash(path[len(base)+1:])
 | 
						|
}
 | 
						|
 | 
						|
func ignoreDotFile(path string) bool {
 | 
						|
	return filepath.Base(path)[0] == '.'
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) loadTemplates(absPath string, prefix string) {
 | 
						|
	walker := func(path string, fi os.FileInfo, err error) error {
 | 
						|
		if err != nil {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if !fi.IsDir() {
 | 
						|
			if ignoreDotFile(path) {
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
 | 
						|
			tplName := t.generateTemplateNameFrom(absPath, path)
 | 
						|
 | 
						|
			if prefix != "" {
 | 
						|
				tplName = strings.Trim(prefix, "/") + "/" + tplName
 | 
						|
			}
 | 
						|
 | 
						|
			t.AddTemplateFile(tplName, path)
 | 
						|
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	filepath.Walk(absPath, walker)
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) LoadTemplatesWithPrefix(absPath string, prefix string) {
 | 
						|
	t.loadTemplates(absPath, prefix)
 | 
						|
}
 | 
						|
 | 
						|
func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
 | 
						|
	t.loadTemplates(absPath, "")
 | 
						|
}
 |