ref: 4a8de8ea46f45776518c7086222be0c94c893764
parent: 41adafbc3e8c53475ba9a8bc8031dd4e0bf59664
author: spf13 <[email protected]>
date: Tue Apr 22 22:52:01 EDT 2014
Add Disqus support out of the box. Move template/bundle into hugolib.
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -28,7 +28,6 @@
"github.com/spf13/cast"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/parser"
- "github.com/spf13/hugo/template/bundle"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"github.com/theplant/blackfriday"
@@ -49,7 +48,7 @@
contentType string
Draft bool
Aliases []string
- Tmpl bundle.Template
+ Tmpl Template
Markup string
renderable bool
layout string
@@ -519,7 +518,7 @@
return nil
}
-func (p *Page) ProcessShortcodes(t bundle.Template) {
+func (p *Page) ProcessShortcodes(t Template) {
p.rawContent = []byte(ShortcodesHandle(string(p.rawContent), p, t))
p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t))
}
--- /dev/null
+++ b/hugolib/path_seperators_windows_test.go
@@ -1,0 +1,17 @@
+package hugolib
+
+import (
+ "testing"
+)
+
+const (
+ win_base = "c:\\a\\windows\\path\\layout"
+ win_path = "c:\\a\\windows\\path\\layout\\sub1\\index.html"
+)
+
+func TestTemplatePathSeperator(t *testing.T) {
+ tmpl := new(GoHtmlTemplate)
+ if name := tmpl.generateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
+ t.Fatalf("Template name incorrect. Expected: %s, Got: %s", "sub1/index.html", name)
+ }
+}
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -20,7 +20,6 @@
"strings"
"unicode"
- "github.com/spf13/hugo/template/bundle"
jww "github.com/spf13/jwalterweatherman"
)
@@ -78,7 +77,7 @@
type Shortcodes map[string]ShortcodeFunc
-func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
+func ShortcodesHandle(stringToParse string, p *Page, t Template) string {
leadStart := strings.Index(stringToParse, `{{%`)
if leadStart >= 0 {
leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart
@@ -147,7 +146,7 @@
return startPos, endPos
}
-func GetTemplate(name string, t bundle.Template) *template.Template {
+func GetTemplate(name string, t Template) *template.Template {
if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
return x
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -27,7 +27,6 @@
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
- "github.com/spf13/hugo/template/bundle"
"github.com/spf13/hugo/transform"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/nitro"
@@ -57,7 +56,7 @@
// 5. The entire collection of files is written to disk.
type Site struct {
Pages Pages
- Tmpl bundle.Template
+ Tmpl Template
Taxonomies TaxonomyList
Source source.Input
Sections Taxonomy
@@ -72,20 +71,21 @@
}
type SiteInfo struct {
- BaseUrl template.URL
- Taxonomies TaxonomyList
- Indexes *TaxonomyList // legacy, should be identical to Taxonomies
- Recent *Pages
- Title string
- Author string
- AuthorEmail string
- LanguageCode string
- Copyright string
- LastChange time.Time
- ConfigGet func(key string) interface{}
- Permalinks PermalinkOverrides
- Params map[string]interface{}
+ BaseUrl template.URL
+ Taxonomies TaxonomyList
+ Indexes *TaxonomyList // legacy, should be identical to Taxonomies
+ Recent *Pages
+ Title string
+ Author map[string]string
+ LanguageCode string
+ DisqusShortname string
+ Copyright string
+ LastChange time.Time
+ ConfigGet func(key string) interface{}
+ Permalinks PermalinkOverrides
+ Params map[string]interface{}
}
+}
type runmode struct {
Watching bool
@@ -130,7 +130,7 @@
}
func (s *Site) prepTemplates() {
- s.Tmpl = bundle.NewTemplate()
+ s.Tmpl = NewTemplate()
s.Tmpl.LoadTemplates(s.absLayoutDir())
if s.hasTheme() {
s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
@@ -234,16 +234,16 @@
permalinks = make(PermalinkOverrides)
}
- s.Info = SiteInfo{
- BaseUrl: template.URL(helpers.SanitizeUrl(viper.GetString("BaseUrl"))),
- Title: viper.GetString("Title"),
- Author: viper.GetString("author"),
- AuthorEmail: viper.GetString("authoremail"),
- LanguageCode: viper.GetString("languagecode"),
- Copyright: viper.GetString("copyright"),
- Recent: &s.Pages,
- Params: params,
- Permalinks: permalinks,
+ s.Info = &SiteInfo{
+ BaseUrl: template.URL(helpers.SanitizeUrl(viper.GetString("BaseUrl"))),
+ Title: viper.GetString("Title"),
+ Author: viper.GetStringMapString("author"),
+ LanguageCode: viper.GetString("languagecode"),
+ Copyright: viper.GetString("copyright"),
+ DisqusShortname: viper.GetString("DisqusShortname"),
+ Recent: &s.Pages,
+ Params: params,
+ Permalinks: permalinks,
}
}
--- /dev/null
+++ b/hugolib/template.go
@@ -1,0 +1,275 @@
+package hugolib
+
+import (
+ "errors"
+ "html"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/eknkc/amber"
+ "github.com/spf13/hugo/helpers"
+)
+
+func Gt(a interface{}, b interface{}) bool {
+ var left, right int64
+ av := reflect.ValueOf(a)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ left = int64(av.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ left = av.Int()
+ case reflect.String:
+ left, _ = strconv.ParseInt(av.String(), 10, 64)
+ }
+
+ bv := reflect.ValueOf(b)
+
+ switch bv.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ right = int64(bv.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ right = bv.Int()
+ case reflect.String:
+ right, _ = strconv.ParseInt(bv.String(), 10, 64)
+ }
+
+ return left > right
+}
+
+// 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 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)
+}
+
+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),
+ }
+
+ funcMap := template.FuncMap{
+ "urlize": helpers.Urlize,
+ "sanitizeurl": helpers.SanitizeUrl,
+ "gt": Gt,
+ "isset": IsSet,
+ "echoParam": ReturnWhenSet,
+ "safeHtml": SafeHtml,
+ "first": First,
+ "highlight": Highlight,
+ "add": func(a, b int) int { return a + b },
+ "sub": func(a, b int) int { return a - b },
+ "div": func(a, b int) int { return a / b },
+ "mod": func(a, b int) int { return a % b },
+ "mul": func(a, b int) int { return 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) },
+ }
+
+ templates.Funcs(funcMap)
+
+ templates.LoadEmbedded()
+ return templates
+}
+
+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 {
+ b, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ return t.AddTemplate(name, string(b))
+}
+
+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
+ }
+
+ // TODO move this into the AddTemplateFile function
+ if strings.HasSuffix(path, ".amber") {
+ compiler := amber.New()
+ // Parse the input file
+ if err := compiler.ParseFile(path); err != nil {
+ return nil
+ }
+
+ if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil {
+ return err
+ }
+
+ } else {
+ 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/hugolib/template_embedded.go
@@ -1,0 +1,84 @@
+// Copyright © 2013 Steve Francia <[email protected]>.
+//
+// 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 }} />
+ {{ 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>{{ .Title }} on {{ .Site.Title }} </title>
+ <generator uri="https://hugo.spf13.com">Hugo</generator>
+ <link>{{ .Permalink }}</link>
+ {{ with .Site.LanguageCode }}<language>{{.}}</language>{{end}}
+ {{ with .Site.Author.name }}<author>{{.}}</author>{{end}}
+ {{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}
+ <updated>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</updated>
+ {{ 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("", "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/hugolib/template_test.go
@@ -1,0 +1,55 @@
+package hugolib
+
+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},
+ {"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 TestFirst(t *testing.T) {
+ for i, this := range []struct {
+ count int
+ sequence interface{}
+ expect interface{}
+ }{
+ {2, []string{"a", "b", "c"}, []string{"a", "b"}},
+ {3, []string{"a", "b"}, []string{"a", "b"}},
+ {2, []int{100, 200, 300}, []int{100, 200}},
+ } {
+ results, err := First(this.count, this.sequence)
+ 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)
+ }
+ }
+}
--- a/template/bundle/embedded.go
+++ /dev/null
@@ -1,68 +1,0 @@
-// Copyright © 2013 Steve Francia <[email protected]>.
-//
-// 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 bundle
-
-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 }} />
- {{ 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>{{ .Title }} on {{ .Site.Title }} </title>
- <generator uri="https://hugo.spf13.com">Hugo</generator>
- <link>{{ .Permalink }}</link>
- {{ with .Site.LanguageCode }}<language>{{.}}</language>{{end}}
- {{ with .Site.Author }}<author>{{.}}</author>{{end}}
- {{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}
- <updated>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</updated>
- {{ 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}}<author>{{.}}</author>{{end}}
- <guid>{{ .Permalink }}</guid>
- <description>{{ .Content | html }}</description>
- </item>
- {{ end }}
- </channel>
-</rss>`)
-
-}
--- a/template/bundle/path_seperators_windows_test.go
+++ /dev/null
@@ -1,17 +1,0 @@
-package bundle
-
-import (
- "testing"
-)
-
-const (
- win_base = "c:\\a\\windows\\path\\layout"
- win_path = "c:\\a\\windows\\path\\layout\\sub1\\index.html"
-)
-
-func TestTemplatePathSeperator(t *testing.T) {
- tmpl := new(GoHtmlTemplate)
- if name := tmpl.generateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
- t.Fatalf("Template name incorrect. Expected: %s, Got: %s", "sub1/index.html", name)
- }
-}
--- a/template/bundle/template.go
+++ /dev/null
@@ -1,271 +1,0 @@
-package bundle
-
-import (
- "errors"
- "html"
- "html/template"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "strconv"
- "strings"
-
- "github.com/eknkc/amber"
- "github.com/spf13/hugo/helpers"
-)
-
-func Gt(a interface{}, b interface{}) bool {
- var left, right int64
- av := reflect.ValueOf(a)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- left = int64(av.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- left = av.Int()
- case reflect.String:
- left, _ = strconv.ParseInt(av.String(), 10, 64)
- }
-
- bv := reflect.ValueOf(b)
-
- switch bv.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- right = int64(bv.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- right = bv.Int()
- case reflect.String:
- right, _ = strconv.ParseInt(bv.String(), 10, 64)
- }
-
- return left > right
-}
-
-// 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 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)
-}
-
-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),
- }
-
- funcMap := template.FuncMap{
- "urlize": helpers.Urlize,
- "sanitizeurl": helpers.SanitizeUrl,
- "gt": Gt,
- "isset": IsSet,
- "echoParam": ReturnWhenSet,
- "safeHtml": SafeHtml,
- "first": First,
- "highlight": Highlight,
- "add": func(a, b int) int { return a + b },
- "sub": func(a, b int) int { return a - b },
- "div": func(a, b int) int { return a / b },
- "mod": func(a, b int) int { return a % b },
- "mul": func(a, b int) int { return 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) },
- }
-
- templates.Funcs(funcMap)
-
- templates.LoadEmbedded()
- return templates
-}
-
-func (t *GoHtmlTemplate) LoadEmbedded() {
- t.EmbedShortcodes()
- t.EmbedTemplates()
-}
-
-func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {
- return t.AddTemplate("_internal/"+prefix+"/"+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 {
- b, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- return t.AddTemplate(name, string(b))
-}
-
-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
- }
-
- // TODO move this into the AddTemplateFile function
- if strings.HasSuffix(path, ".amber") {
- compiler := amber.New()
- // Parse the input file
- if err := compiler.ParseFile(path); err != nil {
- return nil
- }
-
- if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil {
- return err
- }
-
- } else {
- 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/template/bundle/template_test.go
+++ /dev/null
@@ -1,55 +1,0 @@
-package bundle
-
-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},
- {"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 TestFirst(t *testing.T) {
- for i, this := range []struct {
- count int
- sequence interface{}
- expect interface{}
- }{
- {2, []string{"a", "b", "c"}, []string{"a", "b"}},
- {3, []string{"a", "b"}, []string{"a", "b"}},
- {2, []int{100, 200, 300}, []int{100, 200}},
- } {
- results, err := First(this.count, this.sequence)
- 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)
- }
- }
-}