ref: 73f203ad86a71136a1d35e0aa1bba574edb91a51
parent: 92a3372a3fdc0365a06a75e6d97f24e624db879b
author: spf13 <steve.francia@gmail.com>
date: Thu Nov 20 07:32:21 EST 2014
Move template library into it's own package (tpl). No longer dependent on hugolib. Can be used externally.
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -17,10 +17,6 @@
"bytes"
"errors"
"fmt"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/parser"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
"html/template"
"io"
"net/url"
@@ -29,8 +25,13 @@
"time"
"github.com/spf13/cast"
+ "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/parser"
"github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/tpl"
+ jww "github.com/spf13/jwalterweatherman"
+ "github.com/spf13/viper"
)
type Page struct {@@ -44,7 +45,7 @@
Truncated bool
Draft bool
PublishDate time.Time
- Tmpl Template
+ Tmpl tpl.Template
Markup string
extension string
@@ -528,7 +529,7 @@
curLayout = layout[0]
}
- return ExecuteTemplateToHTML(p, p.Layout(curLayout)...)
+ return tpl.ExecuteTemplateToHTML(p, p.Layout(curLayout)...)
}
func (page *Page) guessMarkupType() string {@@ -629,7 +630,7 @@
return page.SaveSourceAs(page.FullFilePath())
}
-func (p *Page) ProcessShortcodes(t Template) {+func (p *Page) ProcessShortcodes(t tpl.Template) {// these short codes aren't used until after Page render,
// but processed here to avoid coupling
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -16,8 +16,6 @@
import (
"bytes"
"fmt"
- "github.com/spf13/hugo/helpers"
- jww "github.com/spf13/jwalterweatherman"
"html/template"
"reflect"
"regexp"
@@ -24,6 +22,10 @@
"sort"
"strconv"
"strings"
+
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/tpl"
+ jww "github.com/spf13/jwalterweatherman"
)
type ShortcodeFunc func([]string) string
@@ -117,7 +119,7 @@
// all in one go: extract, render and replace
// only used for testing
-func ShortcodesHandle(stringToParse string, page *Page, t Template) string {+func ShortcodesHandle(stringToParse string, page *Page, t tpl.Template) string {tmpContent, tmpShortcodes := extractAndRenderShortcodes(stringToParse, page, t)
@@ -154,7 +156,7 @@
return fmt.Sprintf("<div>%s-%d</div>", shortcodePlaceholderPrefix, id)}
-func renderShortcodes(sc shortcode, p *Page, t Template) string {+func renderShortcodes(sc shortcode, p *Page, t tpl.Template) string {tokenizedRenderedShortcodes := make(map[string](string))
startCount := 0
@@ -169,7 +171,7 @@
return shortcodes
}
-func renderShortcode(sc shortcode, tokenizedShortcodes map[string](string), cnt int, p *Page, t Template) string {+func renderShortcode(sc shortcode, tokenizedShortcodes map[string](string), cnt int, p *Page, t tpl.Template) string { var data = &ShortcodeWithPage{Params: sc.params, Page: p}tmpl := GetTemplate(sc.name, t)
@@ -209,7 +211,7 @@
return ShortcodeRender(tmpl, data)
}
-func extractAndRenderShortcodes(stringToParse string, p *Page, t Template) (string, map[string]string) {+func extractAndRenderShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]string) {content, shortcodes, err := extractShortcodes(stringToParse, p, t)
renderedShortcodes := make(map[string]string)
@@ -235,7 +237,7 @@
// pageTokens state:
// - before: positioned just before the shortcode start
// - after: shortcode(s) consumed (plural when they are nested)
-func extractShortcode(pt *pageTokens, p *Page, t Template) (shortcode, error) {+func extractShortcode(pt *pageTokens, p *Page, t tpl.Template) (shortcode, error) { sc := shortcode{}var isInner = false
@@ -334,7 +336,7 @@
return sc, nil
}
-func extractShortcodes(stringToParse string, p *Page, t Template) (string, map[string]shortcode, error) {+func extractShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]shortcode, error) {shortCodes := make(map[string]shortcode)
@@ -452,7 +454,7 @@
return buff[0:width], nil
}
-func GetTemplate(name string, t Template) *template.Template {+func GetTemplate(name string, t tpl.Template) *template.Template { if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {return x
}
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -2,13 +2,15 @@
import (
"fmt"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/viper"
"reflect"
"regexp"
"sort"
"strings"
"testing"
+
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/tpl"
+ "github.com/spf13/viper"
)
func pageFromString(in, filename string) (*Page, error) {@@ -15,7 +17,7 @@
return NewPageFrom(strings.NewReader(in), filename)
}
-func CheckShortCodeMatch(t *testing.T, input, expected string, template Template) {+func CheckShortCodeMatch(t *testing.T, input, expected string, template tpl.Template) {p, _ := pageFromString(SIMPLE_PAGE, "simple.md")
output := ShortcodesHandle(input, p, template)
@@ -26,13 +28,13 @@
}
func TestNonSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
// notice the syntax diff from 0.12, now comment delims must be added
CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", tem)}
func TestPositionalParamSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`) CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", tem)@@ -43,7 +45,7 @@
}
func TestNamedParamSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`) CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, tem)@@ -55,7 +57,7 @@
}
func TestInnerSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, tem)@@ -64,7 +66,7 @@
}
func TestInnerSCWithMarkdown(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{% inside %}}@@ -76,7 +78,7 @@
}
func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{% inside %}}@@ -98,7 +100,7 @@
}
func TestEmbeddedSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
CheckShortCodeMatch(t, "{{% test %}}", "This is a simple Test", tem) CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" />\n \n \n</figure>\n", tem) CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"This is a caption\" />\n \n \n <figcaption>\n <p>\n This is a caption\n \n \n \n </p> \n </figcaption>\n \n</figure>\n", tem)@@ -105,7 +107,7 @@
}
func TestNestedSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`) tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)@@ -113,7 +115,7 @@
}
func TestNestedComplexSC(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`) tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`) tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`)@@ -127,7 +129,7 @@
}
func TestFigureImgWidth(t *testing.T) {- tem := NewTemplate()
+ tem := tpl.New()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"apple\" width=\"100px\" />\n \n \n</figure>\n", tem)}
@@ -138,7 +140,7 @@
defer viper.Set("PygmentsStyle", viper.Get("PygmentsStyle")) viper.Set("PygmentsStyle", "bw")- tem := NewTemplate()
+ tem := tpl.New()
code := `
{{< highlight java >}}@@ -196,7 +198,7 @@
} {p, _ := pageFromString(SIMPLE_PAGE, "simple.md")
- tem := NewTemplate()
+ tem := tpl.New()
tem.AddInternalShortcode("tag.html", `tag`) tem.AddInternalShortcode("sc1.html", `sc1`) tem.AddInternalShortcode("sc2.html", `sc2`)--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -31,6 +31,7 @@
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
+ "github.com/spf13/hugo/tpl"
"github.com/spf13/hugo/transform"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/nitro"
@@ -61,7 +62,7 @@
type Site struct {Pages Pages
Files []*source.File
- Tmpl Template
+ Tmpl tpl.Template
Taxonomies TaxonomyList
Source source.Input
Sections Taxonomy
@@ -166,7 +167,7 @@
}
func (s *Site) prepTemplates() {- s.Tmpl = NewTemplate()
+ s.Tmpl = tpl.T()
s.Tmpl.LoadTemplates(s.absLayoutDir())
if s.hasTheme() {s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -13,6 +13,7 @@
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
+ "github.com/spf13/hugo/tpl"
"github.com/spf13/viper"
)
@@ -46,6 +47,14 @@
`
)
+func templatePrep(s *Site) {+ s.Tmpl = tpl.New()
+ s.Tmpl.LoadTemplates(s.absLayoutDir())
+ if s.hasTheme() {+ s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
+ }
+}
+
func pageMust(p *Page, err error) *Page { if err != nil {panic(err)
@@ -57,7 +66,7 @@
p, _ := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
p.Convert()
s := new(Site)
- s.prepTemplates()
+ templatePrep(s)
err := s.renderThing(p, "foobar", nil)
if err == nil { t.Errorf("Expected err to be returned when missing the template.")@@ -66,7 +75,7 @@
func TestAddInvalidTemplate(t *testing.T) {s := new(Site)
- s.prepTemplates()
+ templatePrep(s)
err := s.addTemplate("missing", TEMPLATE_MISSING_FUNC) if err == nil { t.Fatalf("Expecting the template to return an error")@@ -108,7 +117,7 @@
}
s := new(Site)
- s.prepTemplates()
+ templatePrep(s)
for i, test := range tests {p, err := NewPageFrom(strings.NewReader(test.content), "content/a/file.md")
@@ -154,7 +163,7 @@
hugofs.DestinationFS = new(afero.MemMapFs)
s := &Site{}- s.prepTemplates()
+ templatePrep(s)
for i, test := range tests {p, err := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
@@ -306,7 +315,7 @@
}
s.initializeSiteInfo()
- s.prepTemplates()
+ templatePrep(s)
must(s.addTemplate("_default/single.html", "{{.Content}}")) must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>"))@@ -366,7 +375,7 @@
}
t.Logf("Rendering with BaseUrl %q and CanonifyUrls set %v", viper.GetString("baseUrl"), canonify)s.initializeSiteInfo()
- s.prepTemplates()
+ templatePrep(s)
must(s.addTemplate("blue/single.html", TEMPLATE_WITH_URL_ABS)) if err := s.CreatePages(); err != nil {--- a/hugolib/template.go
+++ /dev/null
@@ -1,690 +1,0 @@
-package hugolib
-
-import (
- "bytes"
- "errors"
- "html"
- "html/template"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "strconv"
- "strings"
-
- "github.com/eknkc/amber"
- "github.com/spf13/cast"
- "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) {-
- if l1 == nil || l2 == nil {- return make([]interface{}, 0), nil- }
-
- 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 interface{}, seq interface{}) (interface{}, error) {-
- limitv, err := cast.ToIntE(limit)
-
- if err != nil {- return nil, err
- }
-
- if limitv < 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 limitv > seqv.Len() {- limitv = seqv.Len()
- }
- return seqv.Slice(0, limitv).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 {- method := vv.MethodByName(kv.String())
- if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {- vvv = method.Call(nil)[0]
- } else if 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 {- method := vv.MethodByName(kv.String())
- if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {- vvv = method.Call(nil)[0]
- } else if 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()
- }
-
- 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")- }
-}
-
-func Mod(a, b interface{}) (int64, error) {- av := reflect.ValueOf(a)
- bv := reflect.ValueOf(b)
- var ai, bi int64
-
- switch av.Kind() {- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- ai = av.Int()
- default:
- return 0, errors.New("Modulo operator can't be used with non integer value")- }
-
- switch bv.Kind() {- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- default:
- return 0, errors.New("Modulo operator can't be used with non integer value")- }
-
- if bi == 0 {- return 0, errors.New("The number can't be divided by zero at modulo operation")- }
-
- return ai % bi, nil
-}
-
-func ModBool(a, b interface{}) (bool, error) {- res, err := Mod(a, b)
- if err != nil {- return false, err
- }
- return res == int64(0), nil
-}
-
-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": Mod,
- "mul": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },- "modBool": ModBool,
- "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 {- name := layout
-
- if localTemplates.Lookup(name) == nil {- name = layout + ".html"
- }
-
- if localTemplates.Lookup(name) != nil {- err := localTemplates.ExecuteTemplate(buffer, name, context)
- if err != nil {- jww.ERROR.Println(err, "in", name)
- }
- 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, "")
-}
--- a/hugolib/template_embedded.go
+++ /dev/null
@@ -1,97 +1,0 @@
-// Copyright © 2013 Steve Francia <spf@spf13.com>.
-//
-// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 hugolib
-
-type Tmpl struct {- Name string
- Data string
-}
-
-func (t *GoHtmlTemplate) EmbedShortcodes() {- t.AddInternalShortcode("highlight.html", `{{ .Get 0 | highlight .Inner }}`)- t.AddInternalShortcode("test.html", `This is a simple Test`)- t.AddInternalShortcode("figure.html", `<!-- image -->-<figure {{ with .Get "class" }}class="{{.}}"{{ end }}>- {{ with .Get "link"}}<a href="{{.}}">{{ end }}- <img src="{{ .Get "src" }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt"}}{{.}}{{else}}{{ .Get "caption" }}{{ end }}" {{ end }}{{ with .Get "width" }}width="{{.}}" {{ end }}/>- {{ if .Get "link"}}</a>{{ end }}- {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr")}}- <figcaption>{{ if isset .Params "title" }}- <h4>{{ .Get "title" }}</h4>{{ end }}- {{ if or (.Get "caption") (.Get "attr")}}<p>- {{ .Get "caption" }}- {{ with .Get "attrlink"}}<a href="{{.}}"> {{ end }}- {{ .Get "attr" }}- {{ if .Get "attrlink"}}</a> {{ end }}- </p> {{ end }}- </figcaption>
- {{ end }}-</figure>
-<!-- image -->`)
-}
-
-func (t *GoHtmlTemplate) EmbedTemplates() {-
- t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">- <channel>
- <title>{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}</title>- <link>{{ .Permalink }}</link>- <description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>- <generator>Hugo -- gohugo.io</generator>
- {{ with .Site.LanguageCode }}<language>{{.}}</language>{{end}}- {{ with .Site.Author.name }}<author>{{.}}</author>{{end}}- {{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}- <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</lastBuildDate>- <atom:link href="{{.Url}}" rel="self" type="application/rss+xml" />- {{ range first 15 .Data.Pages }}- <item>
- <title>{{ .Title }}</title>- <link>{{ .Permalink }}</link>- <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</pubDate>- {{with .Site.Author.name}}<author>{{.}}</author>{{end}}- <guid>{{ .Permalink }}</guid>- <description>{{ .Content | html }}</description>- </item>
- {{ end }}- </channel>
-</rss>`)
-
- t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">- {{ range .Data.Pages }}- <url>
- <loc>{{ .Permalink }}</loc>- <lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ with .Sitemap.ChangeFreq }}- <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}- <priority>{{ .Sitemap.Priority }}</priority>{{ end }}- </url>
- {{ end }}-</urlset>`)
-
- t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}<div id="disqus_thread"></div>-<script type="text/javascript">
- var disqus_shortname = '{{ .Site.DisqusShortname }}';- var disqus_identifier = '{{with .GetParam "disqus_identifier" }}{{ . }}{{ else }}{{ .Permalink }}{{end}}';- var disqus_title = '{{with .GetParam "disqus_title" }}{{ . }}{{ else }}{{ .Title }}{{end}}';- var disqus_url = '{{with .GetParam "disqus_url" }}{{ . | html }}{{ else }}{{ .Permalink }}{{end}}';-
- (function() {- var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;- dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
- (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);- })();
-</script>
-<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
-<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)-
-}
--- a/hugolib/template_test.go
+++ /dev/null
@@ -1,342 +1,0 @@
-package hugolib
-
-import (
- "github.com/spf13/hugo/source"
- "reflect"
- "testing"
-)
-
-func TestGt(t *testing.T) {- for i, this := range []struct {- left interface{}- right interface{}- leftShouldWin bool
- }{- {5, 8, false},- {8, 5, true},- {5, 5, false},- {-2, 1, false},- {2, -5, true},- {0.0, 1.23, false},- {1.23, 0.0, true},- {"8", "5", true},- {"5", "0001", true},- {[]int{100, 99}, []int{1, 2, 3, 4}, false},- } {- leftIsBigger := Gt(this.left, this.right)
- if leftIsBigger != this.leftShouldWin {- var which string
- if leftIsBigger {- which = "expected right to be bigger, but left was"
- } else {- which = "expected left to be bigger, but right was"
- }
- t.Errorf("[%d] %v compared to %v: %s", i, this.left, this.right, which)- }
- }
-}
-
-func TestDoArithmetic(t *testing.T) {- for i, this := range []struct {- a interface{}- b interface{}- op rune
- expect interface{}- }{- {3, 2, '+', int64(5)},- {3, 2, '-', int64(1)},- {3, 2, '*', int64(6)},- {3, 2, '/', int64(1)},- {3.0, 2, '+', float64(5)},- {3.0, 2, '-', float64(1)},- {3.0, 2, '*', float64(6)},- {3.0, 2, '/', float64(1.5)},- {3, 2.0, '+', float64(5)},- {3, 2.0, '-', float64(1)},- {3, 2.0, '*', float64(6)},- {3, 2.0, '/', float64(1.5)},- {3.0, 2.0, '+', float64(5)},- {3.0, 2.0, '-', float64(1)},- {3.0, 2.0, '*', float64(6)},- {3.0, 2.0, '/', float64(1.5)},- {uint(3), uint(2), '+', uint64(5)},- {uint(3), uint(2), '-', uint64(1)},- {uint(3), uint(2), '*', uint64(6)},- {uint(3), uint(2), '/', uint64(1)},- {uint(3), 2, '+', uint64(5)},- {uint(3), 2, '-', uint64(1)},- {uint(3), 2, '*', uint64(6)},- {uint(3), 2, '/', uint64(1)},- {3, uint(2), '+', uint64(5)},- {3, uint(2), '-', uint64(1)},- {3, uint(2), '*', uint64(6)},- {3, uint(2), '/', uint64(1)},- {uint(3), -2, '+', int64(1)},- {uint(3), -2, '-', int64(5)},- {uint(3), -2, '*', int64(-6)},- {uint(3), -2, '/', int64(-1)},- {-3, uint(2), '+', int64(-1)},- {-3, uint(2), '-', int64(-5)},- {-3, uint(2), '*', int64(-6)},- {-3, uint(2), '/', int64(-1)},- {uint(3), 2.0, '+', float64(5)},- {uint(3), 2.0, '-', float64(1)},- {uint(3), 2.0, '*', float64(6)},- {uint(3), 2.0, '/', float64(1.5)},- {3.0, uint(2), '+', float64(5)},- {3.0, uint(2), '-', float64(1)},- {3.0, uint(2), '*', float64(6)},- {3.0, uint(2), '/', float64(1.5)},- {0, 0, '+', 0},- {0, 0, '-', 0},- {0, 0, '*', 0},- {"foo", "bar", '+', "foobar"},- {3, 0, '/', false},- {3.0, 0, '/', false},- {3, 0.0, '/', false},- {uint(3), uint(0), '/', false},- {3, uint(0), '/', false},- {-3, uint(0), '/', false},- {uint(3), 0, '/', false},- {3.0, uint(0), '/', false},- {uint(3), 0.0, '/', false},- {3, "foo", '+', false},- {3.0, "foo", '+', false},- {uint(3), "foo", '+', false},- {"foo", 3, '+', false},- {"foo", "bar", '-', false},- {3, 2, '%', false},- } {- result, err := doArithmetic(this.a, this.b, this.op)
- if b, ok := this.expect.(bool); ok && !b {- if err == nil {- t.Errorf("[%d] doArithmetic didn't return an expected error")- }
- } else {- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(result, this.expect) {- t.Errorf("[%d] doArithmetic got %v but expected %v", i, result, this.expect)- }
- }
- }
-}
-
-func TestMod(t *testing.T) {- for i, this := range []struct {- a interface{}- b interface{}- expect interface{}- }{- {3, 2, int64(1)},- {3, 1, int64(0)},- {3, 0, false},- {0, 3, int64(0)},- {3.1, 2, false},- {3, 2.1, false},- {3.1, 2.1, false},- {int8(3), int8(2), int64(1)},- {int16(3), int16(2), int64(1)},- {int32(3), int32(2), int64(1)},- {int64(3), int64(2), int64(1)},- } {- result, err := Mod(this.a, this.b)
- if b, ok := this.expect.(bool); ok && !b {- if err == nil {- t.Errorf("[%d] modulo didn't return an expected error")- }
- } else {- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(result, this.expect) {- t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)- }
- }
- }
-}
-
-func TestModBool(t *testing.T) {- for i, this := range []struct {- a interface{}- b interface{}- expect interface{}- }{- {3, 3, true},- {3, 2, false},- {3, 1, true},- {3, 0, nil},- {0, 3, true},- {3.1, 2, nil},- {3, 2.1, nil},- {3.1, 2.1, nil},- {int8(3), int8(3), true},- {int8(3), int8(2), false},- {int16(3), int16(3), true},- {int16(3), int16(2), false},- {int32(3), int32(3), true},- {int32(3), int32(2), false},- {int64(3), int64(3), true},- {int64(3), int64(2), false},- } {- result, err := ModBool(this.a, this.b)
- if this.expect == nil {- if err == nil {- t.Errorf("[%d] modulo didn't return an expected error")- }
- } else {- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(result, this.expect) {- t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)- }
- }
- }
-}
-
-func TestFirst(t *testing.T) {- for i, this := range []struct {- count interface{}- sequence interface{}- expect interface{}- }{- {int(2), []string{"a", "b", "c"}, []string{"a", "b"}},- {int32(3), []string{"a", "b"}, []string{"a", "b"}},- {int64(2), []int{100, 200, 300}, []int{100, 200}},- {100, []int{100, 200}, []int{100, 200}},- {"1", []int{100, 200, 300}, []int{100}},- {int64(-1), []int{100, 200, 300}, false},- {"noint", []int{100, 200, 300}, false},- } {- results, err := First(this.count, this.sequence)
- if b, ok := this.expect.(bool); ok && !b {- if err == nil {- t.Errorf("[%d] First didn't return an expected error")- }
- } else {- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(results, this.expect) {- t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)- }
- }
- }
-}
-
-func TestIn(t *testing.T) {- for i, this := range []struct {- v1 interface{}- v2 interface{}- expect bool
- }{- {[]string{"a", "b", "c"}, "b", true},- {[]string{"a", "b", "c"}, "d", false},- {[]string{"a", "12", "c"}, 12, false},- {[]int{1, 2, 4}, 2, true},- {[]int{1, 2, 4}, 3, false},- {[]float64{1.23, 2.45, 4.67}, 1.23, true},- {[]float64{1.234567, 2.45, 4.67}, 1.234568, false},- {"this substring should be found", "substring", true},- {"this substring should not be found", "subseastring", false},- } {- result := In(this.v1, this.v2)
-
- if result != this.expect {- t.Errorf("[%d] Got %v but expected %v", i, result, this.expect)- }
- }
-}
-
-func TestIntersect(t *testing.T) {- for i, this := range []struct {- sequence1 interface{}- sequence2 interface{}- expect interface{}- }{- {[]string{"a", "b", "c"}, []string{"a", "b"}, []string{"a", "b"}},- {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},- {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},- {[]string{}, []string{}, []string{}},- {[]string{"a", "b"}, nil, make([]interface{}, 0)},- {nil, []string{"a", "b"}, make([]interface{}, 0)},- {nil, nil, make([]interface{}, 0)},- {[]string{"1", "2"}, []int{1, 2}, []string{}},- {[]int{1, 2}, []string{"1", "2"}, []int{}},- {[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},- {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4}},- {[]int{1, 2, 4}, []int{3, 6}, []int{}},- {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4}},- } {- results, err := Intersect(this.sequence1, this.sequence2)
- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(results, this.expect) {- t.Errorf("[%d] Got %v but expected %v", i, results, this.expect)- }
- }
-
- _, err1 := Intersect("not an array or slice", []string{"a"})-
- if err1 == nil {- t.Error("Excpected error for non array as first arg")- }
-
- _, err2 := Intersect([]string{"a"}, "not an array or slice")-
- if err2 == nil {- t.Error("Excpected error for non array as second arg")- }
-}
-
-func (x *TstX) TstRp() string {- return "r" + x.A
-}
-
-func (x TstX) TstRv() string {- return "r" + x.B
-}
-
-type TstX struct {- A, B string
-}
-
-func TestWhere(t *testing.T) {-
- page1 := &Page{contentType: "v", Source: Source{File: *source.NewFile("/x/y/z/source.md")}}- page2 := &Page{contentType: "w", Source: Source{File: *source.NewFile("/y/z/a/source.md")}}-
- for i, this := range []struct {- sequence interface{}- key interface{}- match interface{}- expect interface{}- }{- {[]map[int]string{{1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"}}, 2, "m", []map[int]string{{1: "a", 2: "m"}}},- {[]map[string]int{{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4}}, "b", 4, []map[string]int{{"a": 3, "b": 4}}},- {[]TstX{{"a", "b"}, {"c", "d"}, {"e", "f"}}, "B", "f", []TstX{{"e", "f"}}},- {[]*map[int]string{&map[int]string{1: "a", 2: "m"}, &map[int]string{1: "c", 2: "d"}, &map[int]string{1: "e", 3: "m"}}, 2, "m", []*map[int]string{&map[int]string{1: "a", 2: "m"}}},- {[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "f"}}, "B", "f", []*TstX{&TstX{"e", "f"}}},- {[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "c"}}, "TstRp", "rc", []*TstX{&TstX{"c", "d"}}},- {[]TstX{TstX{"a", "b"}, TstX{"c", "d"}, TstX{"e", "c"}}, "TstRv", "rc", []TstX{TstX{"e", "c"}}},- {[]*Page{page1, page2}, "Type", "v", []*Page{page1}},- {[]*Page{page1, page2}, "Section", "y", []*Page{page2}},- } {- results, err := Where(this.sequence, this.key, this.match)
- if err != nil {- t.Errorf("[%d] failed: %s", i, err)- continue
- }
- if !reflect.DeepEqual(results, this.expect) {- t.Errorf("[%d] Where clause matching %v with %v, got %v but expected %v", i, this.key, this.match, results, this.expect)- }
- }
-}
--- /dev/null
+++ b/tpl/template.go
@@ -1,0 +1,714 @@
+// Copyright © 2013-14 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 tpl
+
+import (
+ "bytes"
+ "errors"
+ "html"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/eknkc/amber"
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/helpers"
+ jww "github.com/spf13/jwalterweatherman"
+)
+
+var localTemplates *template.Template
+var tmpl Template
+
+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
+}
+
+// The "Global" Template System
+func T() Template {+ if tmpl == nil {+ tmpl = New()
+ }
+
+ return tmpl
+}
+
+// Return a new Hugo Template System
+// With all the additional features, templates & functions
+func New() 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": Mod,
+ "mul": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },+ "modBool": ModBool,
+ "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 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) {+
+ if l1 == nil || l2 == nil {+ return make([]interface{}, 0), nil+ }
+
+ 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 interface{}, seq interface{}) (interface{}, error) {+
+ limitv, err := cast.ToIntE(limit)
+
+ if err != nil {+ return nil, err
+ }
+
+ if limitv < 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 limitv > seqv.Len() {+ limitv = seqv.Len()
+ }
+ return seqv.Slice(0, limitv).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 {+ method := vv.MethodByName(kv.String())
+ if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {+ vvv = method.Call(nil)[0]
+ } else if 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 {+ method := vv.MethodByName(kv.String())
+ if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {+ vvv = method.Call(nil)[0]
+ } else if 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()
+ }
+
+ 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")+ }
+}
+
+func Mod(a, b interface{}) (int64, error) {+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+ var ai, bi int64
+
+ switch av.Kind() {+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ ai = av.Int()
+ default:
+ return 0, errors.New("Modulo operator can't be used with non integer value")+ }
+
+ switch bv.Kind() {+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ bi = bv.Int()
+ default:
+ return 0, errors.New("Modulo operator can't be used with non integer value")+ }
+
+ if bi == 0 {+ return 0, errors.New("The number can't be divided by zero at modulo operation")+ }
+
+ return ai % bi, nil
+}
+
+func ModBool(a, b interface{}) (bool, error) {+ res, err := Mod(a, b)
+ if err != nil {+ return false, err
+ }
+ return res == int64(0), nil
+}
+
+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 {+ name := layout
+
+ if localTemplates.Lookup(name) == nil {+ name = layout + ".html"
+ }
+
+ if localTemplates.Lookup(name) != nil {+ err := localTemplates.ExecuteTemplate(buffer, name, context)
+ if err != nil {+ jww.ERROR.Println(err, "in", name)
+ }
+ 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, "")
+}
--- /dev/null
+++ b/tpl/template_embedded.go
@@ -1,0 +1,97 @@
+// Copyright © 2013 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 tpl
+
+type Tmpl struct {+ Name string
+ Data string
+}
+
+func (t *GoHtmlTemplate) EmbedShortcodes() {+ t.AddInternalShortcode("highlight.html", `{{ .Get 0 | highlight .Inner }}`)+ t.AddInternalShortcode("test.html", `This is a simple Test`)+ t.AddInternalShortcode("figure.html", `<!-- image -->+<figure {{ with .Get "class" }}class="{{.}}"{{ end }}>+ {{ with .Get "link"}}<a href="{{.}}">{{ end }}+ <img src="{{ .Get "src" }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt"}}{{.}}{{else}}{{ .Get "caption" }}{{ end }}" {{ end }}{{ with .Get "width" }}width="{{.}}" {{ end }}/>+ {{ if .Get "link"}}</a>{{ end }}+ {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr")}}+ <figcaption>{{ if isset .Params "title" }}+ <h4>{{ .Get "title" }}</h4>{{ end }}+ {{ if or (.Get "caption") (.Get "attr")}}<p>+ {{ .Get "caption" }}+ {{ with .Get "attrlink"}}<a href="{{.}}"> {{ end }}+ {{ .Get "attr" }}+ {{ if .Get "attrlink"}}</a> {{ end }}+ </p> {{ end }}+ </figcaption>
+ {{ end }}+</figure>
+<!-- image -->`)
+}
+
+func (t *GoHtmlTemplate) EmbedTemplates() {+
+ t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">+ <channel>
+ <title>{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}</title>+ <link>{{ .Permalink }}</link>+ <description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>+ <generator>Hugo -- gohugo.io</generator>
+ {{ with .Site.LanguageCode }}<language>{{.}}</language>{{end}}+ {{ with .Site.Author.name }}<author>{{.}}</author>{{end}}+ {{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}+ <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</lastBuildDate>+ <atom:link href="{{.Url}}" rel="self" type="application/rss+xml" />+ {{ range first 15 .Data.Pages }}+ <item>
+ <title>{{ .Title }}</title>+ <link>{{ .Permalink }}</link>+ <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</pubDate>+ {{with .Site.Author.name}}<author>{{.}}</author>{{end}}+ <guid>{{ .Permalink }}</guid>+ <description>{{ .Content | html }}</description>+ </item>
+ {{ end }}+ </channel>
+</rss>`)
+
+ t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">+ {{ range .Data.Pages }}+ <url>
+ <loc>{{ .Permalink }}</loc>+ <lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ with .Sitemap.ChangeFreq }}+ <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}+ <priority>{{ .Sitemap.Priority }}</priority>{{ end }}+ </url>
+ {{ end }}+</urlset>`)
+
+ t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}<div id="disqus_thread"></div>+<script type="text/javascript">
+ var disqus_shortname = '{{ .Site.DisqusShortname }}';+ var disqus_identifier = '{{with .GetParam "disqus_identifier" }}{{ . }}{{ else }}{{ .Permalink }}{{end}}';+ var disqus_title = '{{with .GetParam "disqus_title" }}{{ . }}{{ else }}{{ .Title }}{{end}}';+ var disqus_url = '{{with .GetParam "disqus_url" }}{{ . | html }}{{ else }}{{ .Permalink }}{{end}}';+
+ (function() {+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;+ dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);+ })();
+</script>
+<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)+
+}
--- /dev/null
+++ b/tpl/template_test.go
@@ -1,0 +1,341 @@
+package tpl
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestGt(t *testing.T) {+ for i, this := range []struct {+ left interface{}+ right interface{}+ leftShouldWin bool
+ }{+ {5, 8, false},+ {8, 5, true},+ {5, 5, false},+ {-2, 1, false},+ {2, -5, true},+ {0.0, 1.23, false},+ {1.23, 0.0, true},+ {"8", "5", true},+ {"5", "0001", true},+ {[]int{100, 99}, []int{1, 2, 3, 4}, false},+ } {+ leftIsBigger := Gt(this.left, this.right)
+ if leftIsBigger != this.leftShouldWin {+ var which string
+ if leftIsBigger {+ which = "expected right to be bigger, but left was"
+ } else {+ which = "expected left to be bigger, but right was"
+ }
+ t.Errorf("[%d] %v compared to %v: %s", i, this.left, this.right, which)+ }
+ }
+}
+
+func TestDoArithmetic(t *testing.T) {+ for i, this := range []struct {+ a interface{}+ b interface{}+ op rune
+ expect interface{}+ }{+ {3, 2, '+', int64(5)},+ {3, 2, '-', int64(1)},+ {3, 2, '*', int64(6)},+ {3, 2, '/', int64(1)},+ {3.0, 2, '+', float64(5)},+ {3.0, 2, '-', float64(1)},+ {3.0, 2, '*', float64(6)},+ {3.0, 2, '/', float64(1.5)},+ {3, 2.0, '+', float64(5)},+ {3, 2.0, '-', float64(1)},+ {3, 2.0, '*', float64(6)},+ {3, 2.0, '/', float64(1.5)},+ {3.0, 2.0, '+', float64(5)},+ {3.0, 2.0, '-', float64(1)},+ {3.0, 2.0, '*', float64(6)},+ {3.0, 2.0, '/', float64(1.5)},+ {uint(3), uint(2), '+', uint64(5)},+ {uint(3), uint(2), '-', uint64(1)},+ {uint(3), uint(2), '*', uint64(6)},+ {uint(3), uint(2), '/', uint64(1)},+ {uint(3), 2, '+', uint64(5)},+ {uint(3), 2, '-', uint64(1)},+ {uint(3), 2, '*', uint64(6)},+ {uint(3), 2, '/', uint64(1)},+ {3, uint(2), '+', uint64(5)},+ {3, uint(2), '-', uint64(1)},+ {3, uint(2), '*', uint64(6)},+ {3, uint(2), '/', uint64(1)},+ {uint(3), -2, '+', int64(1)},+ {uint(3), -2, '-', int64(5)},+ {uint(3), -2, '*', int64(-6)},+ {uint(3), -2, '/', int64(-1)},+ {-3, uint(2), '+', int64(-1)},+ {-3, uint(2), '-', int64(-5)},+ {-3, uint(2), '*', int64(-6)},+ {-3, uint(2), '/', int64(-1)},+ {uint(3), 2.0, '+', float64(5)},+ {uint(3), 2.0, '-', float64(1)},+ {uint(3), 2.0, '*', float64(6)},+ {uint(3), 2.0, '/', float64(1.5)},+ {3.0, uint(2), '+', float64(5)},+ {3.0, uint(2), '-', float64(1)},+ {3.0, uint(2), '*', float64(6)},+ {3.0, uint(2), '/', float64(1.5)},+ {0, 0, '+', 0},+ {0, 0, '-', 0},+ {0, 0, '*', 0},+ {"foo", "bar", '+', "foobar"},+ {3, 0, '/', false},+ {3.0, 0, '/', false},+ {3, 0.0, '/', false},+ {uint(3), uint(0), '/', false},+ {3, uint(0), '/', false},+ {-3, uint(0), '/', false},+ {uint(3), 0, '/', false},+ {3.0, uint(0), '/', false},+ {uint(3), 0.0, '/', false},+ {3, "foo", '+', false},+ {3.0, "foo", '+', false},+ {uint(3), "foo", '+', false},+ {"foo", 3, '+', false},+ {"foo", "bar", '-', false},+ {3, 2, '%', false},+ } {+ result, err := doArithmetic(this.a, this.b, this.op)
+ if b, ok := this.expect.(bool); ok && !b {+ if err == nil {+ t.Errorf("[%d] doArithmetic didn't return an expected error")+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {+ t.Errorf("[%d] doArithmetic got %v but expected %v", i, result, this.expect)+ }
+ }
+ }
+}
+
+func TestMod(t *testing.T) {+ for i, this := range []struct {+ a interface{}+ b interface{}+ expect interface{}+ }{+ {3, 2, int64(1)},+ {3, 1, int64(0)},+ {3, 0, false},+ {0, 3, int64(0)},+ {3.1, 2, false},+ {3, 2.1, false},+ {3.1, 2.1, false},+ {int8(3), int8(2), int64(1)},+ {int16(3), int16(2), int64(1)},+ {int32(3), int32(2), int64(1)},+ {int64(3), int64(2), int64(1)},+ } {+ result, err := Mod(this.a, this.b)
+ if b, ok := this.expect.(bool); ok && !b {+ if err == nil {+ t.Errorf("[%d] modulo didn't return an expected error")+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {+ t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)+ }
+ }
+ }
+}
+
+func TestModBool(t *testing.T) {+ for i, this := range []struct {+ a interface{}+ b interface{}+ expect interface{}+ }{+ {3, 3, true},+ {3, 2, false},+ {3, 1, true},+ {3, 0, nil},+ {0, 3, true},+ {3.1, 2, nil},+ {3, 2.1, nil},+ {3.1, 2.1, nil},+ {int8(3), int8(3), true},+ {int8(3), int8(2), false},+ {int16(3), int16(3), true},+ {int16(3), int16(2), false},+ {int32(3), int32(3), true},+ {int32(3), int32(2), false},+ {int64(3), int64(3), true},+ {int64(3), int64(2), false},+ } {+ result, err := ModBool(this.a, this.b)
+ if this.expect == nil {+ if err == nil {+ t.Errorf("[%d] modulo didn't return an expected error")+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {+ t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)+ }
+ }
+ }
+}
+
+func TestFirst(t *testing.T) {+ for i, this := range []struct {+ count interface{}+ sequence interface{}+ expect interface{}+ }{+ {int(2), []string{"a", "b", "c"}, []string{"a", "b"}},+ {int32(3), []string{"a", "b"}, []string{"a", "b"}},+ {int64(2), []int{100, 200, 300}, []int{100, 200}},+ {100, []int{100, 200}, []int{100, 200}},+ {"1", []int{100, 200, 300}, []int{100}},+ {int64(-1), []int{100, 200, 300}, false},+ {"noint", []int{100, 200, 300}, false},+ } {+ results, err := First(this.count, this.sequence)
+ if b, ok := this.expect.(bool); ok && !b {+ if err == nil {+ t.Errorf("[%d] First didn't return an expected error")+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {+ t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)+ }
+ }
+ }
+}
+
+func TestIn(t *testing.T) {+ for i, this := range []struct {+ v1 interface{}+ v2 interface{}+ expect bool
+ }{+ {[]string{"a", "b", "c"}, "b", true},+ {[]string{"a", "b", "c"}, "d", false},+ {[]string{"a", "12", "c"}, 12, false},+ {[]int{1, 2, 4}, 2, true},+ {[]int{1, 2, 4}, 3, false},+ {[]float64{1.23, 2.45, 4.67}, 1.23, true},+ {[]float64{1.234567, 2.45, 4.67}, 1.234568, false},+ {"this substring should be found", "substring", true},+ {"this substring should not be found", "subseastring", false},+ } {+ result := In(this.v1, this.v2)
+
+ if result != this.expect {+ t.Errorf("[%d] Got %v but expected %v", i, result, this.expect)+ }
+ }
+}
+
+func TestIntersect(t *testing.T) {+ for i, this := range []struct {+ sequence1 interface{}+ sequence2 interface{}+ expect interface{}+ }{+ {[]string{"a", "b", "c"}, []string{"a", "b"}, []string{"a", "b"}},+ {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},+ {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},+ {[]string{}, []string{}, []string{}},+ {[]string{"a", "b"}, nil, make([]interface{}, 0)},+ {nil, []string{"a", "b"}, make([]interface{}, 0)},+ {nil, nil, make([]interface{}, 0)},+ {[]string{"1", "2"}, []int{1, 2}, []string{}},+ {[]int{1, 2}, []string{"1", "2"}, []int{}},+ {[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},+ {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4}},+ {[]int{1, 2, 4}, []int{3, 6}, []int{}},+ {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4}},+ } {+ results, err := Intersect(this.sequence1, this.sequence2)
+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {+ t.Errorf("[%d] Got %v but expected %v", i, results, this.expect)+ }
+ }
+
+ _, err1 := Intersect("not an array or slice", []string{"a"})+
+ if err1 == nil {+ t.Error("Excpected error for non array as first arg")+ }
+
+ _, err2 := Intersect([]string{"a"}, "not an array or slice")+
+ if err2 == nil {+ t.Error("Excpected error for non array as second arg")+ }
+}
+
+func (x *TstX) TstRp() string {+ return "r" + x.A
+}
+
+func (x TstX) TstRv() string {+ return "r" + x.B
+}
+
+type TstX struct {+ A, B string
+}
+
+func TestWhere(t *testing.T) {+ // TODO(spf): Put these page tests back in
+ //page1 := &Page{contentType: "v", Source: Source{File: *source.NewFile("/x/y/z/source.md")}}+ //page2 := &Page{contentType: "w", Source: Source{File: *source.NewFile("/y/z/a/source.md")}}+
+ for i, this := range []struct {+ sequence interface{}+ key interface{}+ match interface{}+ expect interface{}+ }{+ {[]map[int]string{{1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"}}, 2, "m", []map[int]string{{1: "a", 2: "m"}}},+ {[]map[string]int{{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4}}, "b", 4, []map[string]int{{"a": 3, "b": 4}}},+ {[]TstX{{"a", "b"}, {"c", "d"}, {"e", "f"}}, "B", "f", []TstX{{"e", "f"}}},+ {[]*map[int]string{&map[int]string{1: "a", 2: "m"}, &map[int]string{1: "c", 2: "d"}, &map[int]string{1: "e", 3: "m"}}, 2, "m", []*map[int]string{&map[int]string{1: "a", 2: "m"}}},+ {[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "f"}}, "B", "f", []*TstX{&TstX{"e", "f"}}},+ {[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "c"}}, "TstRp", "rc", []*TstX{&TstX{"c", "d"}}},+ {[]TstX{TstX{"a", "b"}, TstX{"c", "d"}, TstX{"e", "c"}}, "TstRv", "rc", []TstX{TstX{"e", "c"}}},+ //{[]*Page{page1, page2}, "Type", "v", []*Page{page1}},+ //{[]*Page{page1, page2}, "Section", "y", []*Page{page2}},+ } {+ results, err := Where(this.sequence, this.key, this.match)
+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {+ t.Errorf("[%d] Where clause matching %v with %v, got %v but expected %v", i, this.key, this.match, results, this.expect)+ }
+ }
+}
--
⑨