shithub: hugo

Download patch

ref: d6000a208c7687ca3a3efd6961ac941ce325e199
parent: 4ea4359ac17a3b5304fb0d73773f99a07975ee1e
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Jan 9 20:36:59 EST 2017

all: Refactor to nonglobal template handling

Updates #2701

--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -19,7 +19,6 @@
 	"html/template"
 	"net/url"
 	"os"
-	"path/filepath"
 	"regexp"
 	"strings"
 	"testing"
@@ -26,9 +25,11 @@
 
 	"io/ioutil"
 	"log"
+	"path/filepath"
 
-	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/tpl"
+
+	"github.com/spf13/hugo/helpers"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/require"
@@ -106,9 +107,8 @@
 			"(?s)^\n<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		if err != nil {
 			t.Fatalf("[%d] Handle shortcode error", i)
@@ -150,9 +150,8 @@
 			"(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -175,9 +174,8 @@
 			"(?s)^<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -210,9 +208,8 @@
 			"(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -245,9 +242,8 @@
 			"(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -274,9 +270,8 @@
 			"(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>$",
 		},
 	} {
-		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -313,13 +308,14 @@
 			},
 		}
 
-		templ := tpl.New(logger)
-		templ.Lookup("").Funcs(tweetFuncMap)
+		p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+			templ.Funcs(tweetFuncMap)
+			return nil
+		})
 
-		p, _ := pageFromString(simplePage, "simple.md")
 		cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.twitter.com/1/statuses/oembed.json?id=666616452582129664")
 		defer os.Remove(cacheFileID)
-		output, err := HandleShortcodes(this.in, p, templ)
+		output, err := HandleShortcodes(this.in, p)
 
 		matched, err := regexp.MatchString(this.expected, output)
 
@@ -353,7 +349,7 @@
 8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
-ding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
+ding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 th:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
@@ -365,13 +361,14 @@
 t+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
-jOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
-<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
+Fhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
+<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
+		},
+	} {
 Vh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
-h5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
-; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
+;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 r:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
--- a/hugolib/handler_base.go
+++ b/hugolib/handler_base.go
@@ -15,12 +15,11 @@
 
 import (
 	"github.com/spf13/hugo/source"
-	"github.com/spf13/hugo/tpl"
 )
 
 type Handler interface {
 	FileConvert(*source.File, *Site) HandledResult
-	PageConvert(*Page, tpl.Template) HandledResult
+	PageConvert(*Page) HandledResult
 	Read(*source.File, *Site) HandledResult
 	Extensions() []string
 }
--- a/hugolib/handler_file.go
+++ b/hugolib/handler_file.go
@@ -18,7 +18,6 @@
 
 	"github.com/dchest/cssmin"
 	"github.com/spf13/hugo/source"
-	"github.com/spf13/hugo/tpl"
 )
 
 func init() {
@@ -32,7 +31,7 @@
 	return HandledResult{file: f}
 }
 
-func (h basicFileHandler) PageConvert(*Page, tpl.Template) HandledResult {
+func (h basicFileHandler) PageConvert(*Page) HandledResult {
 	return HandledResult{}
 }
 
--- a/hugolib/handler_meta.go
+++ b/hugolib/handler_meta.go
@@ -74,7 +74,7 @@
 			return
 		}
 
-		results <- h.PageConvert(p, s.owner.tmpl)
+		results <- h.PageConvert(p)
 	}
 }
 
--- a/hugolib/handler_page.go
+++ b/hugolib/handler_page.go
@@ -19,7 +19,6 @@
 
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/source"
-	"github.com/spf13/hugo/tpl"
 	"github.com/spf13/viper"
 )
 
@@ -56,8 +55,8 @@
 }
 
 func (h markdownHandler) Extensions() []string { return []string{"mdown", "markdown", "md"} }
-func (h markdownHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
-	return commonConvert(p, t)
+func (h markdownHandler) PageConvert(p *Page) HandledResult {
+	return commonConvert(p)
 }
 
 type htmlHandler struct {
@@ -65,7 +64,9 @@
 }
 
 func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} }
-func (h htmlHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
+
+// TODO(bep) globals use p.s.t
+func (h htmlHandler) PageConvert(p *Page) HandledResult {
 	if p.rendered {
 		panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName()))
 	}
@@ -73,7 +74,7 @@
 	// Work on a copy of the raw content from now on.
 	p.createWorkContentCopy()
 
-	p.ProcessShortcodes(t)
+	p.ProcessShortcodes()
 
 	return HandledResult{err: nil}
 }
@@ -83,8 +84,8 @@
 }
 
 func (h asciidocHandler) Extensions() []string { return []string{"asciidoc", "adoc", "ad"} }
-func (h asciidocHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
-	return commonConvert(p, t)
+func (h asciidocHandler) PageConvert(p *Page) HandledResult {
+	return commonConvert(p)
 }
 
 type rstHandler struct {
@@ -92,8 +93,8 @@
 }
 
 func (h rstHandler) Extensions() []string { return []string{"rest", "rst"} }
-func (h rstHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
-	return commonConvert(p, t)
+func (h rstHandler) PageConvert(p *Page) HandledResult {
+	return commonConvert(p)
 }
 
 type mmarkHandler struct {
@@ -101,11 +102,11 @@
 }
 
 func (h mmarkHandler) Extensions() []string { return []string{"mmark"} }
-func (h mmarkHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
-	return commonConvert(p, t)
+func (h mmarkHandler) PageConvert(p *Page) HandledResult {
+	return commonConvert(p)
 }
 
-func commonConvert(p *Page, t tpl.Template) HandledResult {
+func commonConvert(p *Page) HandledResult {
 	if p.rendered {
 		panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName()))
 	}
@@ -113,7 +114,7 @@
 	// Work on a copy of the raw content from now on.
 	p.createWorkContentCopy()
 
-	p.ProcessShortcodes(t)
+	p.ProcessShortcodes()
 
 	// TODO(bep) these page handlers need to be re-evaluated, as it is hard to
 	// process a page in isolation. See the new preRender func.
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -34,7 +34,6 @@
 type HugoSites struct {
 	Sites []*Site
 
-	tmpl    tpl.Template
 	runMode runmode
 
 	multilingual *Multilingual
@@ -50,9 +49,16 @@
 	// The logger to use.
 	log *jww.Notepad
 
-	// TODO(bep) next in line: Viper, hugofs, template
+	tmpl *tpl.GoHTMLTemplate
+
+	// TODO(bep) next in line: Viper, hugofs
 }
 
+func (d *deps) refreshTemplates(withTemplate ...func(templ tpl.Template) error) {
+	d.tmpl = tpl.New(d.log, withTemplate...)
+	d.tmpl.PrintErrors() // TODO(bep) globals error handling
+}
+
 func newDeps(cfg DepsCfg) *deps {
 	logger := cfg.Logger
 
@@ -59,11 +65,12 @@
 	if logger == nil {
 		// TODO(bep) globals default log level
 		//logger = jww.NewNotepad(jww.LevelError, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
-		logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+		logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
 	}
 
 	return &deps{
-		log: logger,
+		log:  logger,
+		tmpl: tpl.New(logger, cfg.WithTemplate...),
 	}
 }
 
@@ -76,8 +83,16 @@
 		return nil, err
 	}
 
+	var d *deps
+
+	if sites[0].deps != nil {
+		d = sites[0].deps
+	} else {
+		d = newDeps(cfg)
+	}
+
 	h := &HugoSites{
-		deps:         newDeps(cfg),
+		deps:         d,
 		multilingual: langConfig,
 		Sites:        sites}
 
@@ -91,7 +106,7 @@
 // NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
 // TODO(bep) globals rename this when all the globals are gone.
 func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) {
-	sites, err := createSitesFromConfig()
+	sites, err := createSitesFromConfig(cfg)
 	if err != nil {
 		return nil, err
 	}
@@ -98,11 +113,17 @@
 	return newHugoSites(cfg, sites...)
 }
 
-func createSitesFromConfig() ([]*Site, error) {
+func createSitesFromConfig(cfg DepsCfg) ([]*Site, error) {
+	deps := newDeps(cfg)
+	return createSitesFromDeps(deps)
+}
+
+func createSitesFromDeps(deps *deps) ([]*Site, error) {
 	var sites []*Site
 	multilingual := viper.GetStringMap("languages")
+
 	if len(multilingual) == 0 {
-		sites = append(sites, newSite(helpers.NewDefaultLanguage()))
+		sites = append(sites, newSite(helpers.NewDefaultLanguage(), deps))
 	}
 
 	if len(multilingual) > 0 {
@@ -115,7 +136,7 @@
 		}
 
 		for _, lang := range languages {
-			sites = append(sites, newSite(lang))
+			sites = append(sites, newSite(lang, deps))
 		}
 
 	}
@@ -134,7 +155,7 @@
 
 func (h *HugoSites) createSitesFromConfig() error {
 
-	sites, err := createSitesFromConfig()
+	sites, err := createSitesFromDeps(h.deps)
 
 	if err != nil {
 		return err
@@ -192,6 +213,8 @@
 
 	// The Logger to use.
 	Logger *jww.Notepad
+
+	WithTemplate []func(templ tpl.Template) error
 }
 
 func (h *HugoSites) renderCrossSitesArtifacts() error {
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -40,7 +40,6 @@
 	bp "github.com/spf13/hugo/bufferpool"
 	"github.com/spf13/hugo/hugofs"
 	"github.com/spf13/hugo/source"
-	"github.com/spf13/hugo/tpl"
 	"github.com/spf13/viper"
 )
 
@@ -1284,7 +1283,7 @@
 		l = p.layouts()
 	}
 
-	return tpl.ExecuteTemplateToHTML(p, l...)
+	return p.s.tmpl.ExecuteTemplateToHTML(p, l...)
 }
 
 func (p *Page) determineMarkupType() string {
@@ -1399,8 +1398,8 @@
 	return p.SaveSourceAs(p.FullFilePath())
 }
 
-func (p *Page) ProcessShortcodes(t tpl.Template) {
-	tmpContent, tmpContentShortCodes, _ := extractAndRenderShortcodes(string(p.workContent), p, t)
+func (p *Page) ProcessShortcodes() {
+	tmpContent, tmpContentShortCodes, _ := extractAndRenderShortcodes(string(p.workContent), p)
 	p.workContent = []byte(tmpContent)
 	p.contentShortCodes = tmpContentShortCodes
 }
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -151,8 +151,8 @@
 
 // HandleShortcodes does all in  one go: extract, render and replace
 // only used for testing
-func HandleShortcodes(stringToParse string, page *Page, t tpl.Template) (string, error) {
-	tmpContent, tmpShortcodes, err := extractAndRenderShortcodes(stringToParse, page, t)
+func HandleShortcodes(stringToParse string, page *Page) (string, error) {
+	tmpContent, tmpShortcodes, err := extractAndRenderShortcodes(stringToParse, page)
 
 	if err != nil {
 		return "", err
@@ -210,8 +210,8 @@
 const innerCleanupRegexp = `\A<p>(.*)</p>\n\z`
 const innerCleanupExpand = "$1"
 
-func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Template) string {
-	tmpl := getShortcodeTemplate(sc.name, t)
+func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page) string {
+	tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
 
 	if tmpl == nil {
 		p.s.log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
@@ -230,7 +230,7 @@
 			case string:
 				inner += innerData.(string)
 			case shortcode:
-				inner += renderShortcode(innerData.(shortcode), data, p, t)
+				inner += renderShortcode(innerData.(shortcode), data, p)
 			default:
 				p.s.log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
 					sc.name, p.BaseFileName(), reflect.TypeOf(innerData))
@@ -280,9 +280,9 @@
 	return renderShortcodeWithPage(tmpl, data)
 }
 
-func extractAndRenderShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]func() (string, error), error) {
+func extractAndRenderShortcodes(stringToParse string, p *Page) (string, map[string]func() (string, error), error) {
 
-	content, shortcodes, err := extractShortcodes(stringToParse, p, t)
+	content, shortcodes, err := extractShortcodes(stringToParse, p)
 
 	if err != nil {
 		//  try to render what we have whilst logging the error
@@ -293,7 +293,7 @@
 	// TODO(bep) refactor this
 	p.shortcodes = shortcodes
 
-	renderedShortcodes := renderShortcodes(shortcodes, p, t)
+	renderedShortcodes := renderShortcodes(shortcodes, p)
 
 	return content, renderedShortcodes, err
 
@@ -315,7 +315,7 @@
 	return result, nil
 }
 
-func renderShortcodes(shortcodes map[string]shortcode, p *Page, t tpl.Template) map[string]func() (string, error) {
+func renderShortcodes(shortcodes map[string]shortcode, p *Page) map[string]func() (string, error) {
 	renderedShortcodes := make(map[string]func() (string, error))
 
 	for key, sc := range shortcodes {
@@ -324,7 +324,7 @@
 			renderedShortcodes[key] = emptyShortcodeFn
 		} else {
 			shorctode := sc
-			renderedShortcodes[key] = func() (string, error) { return renderShortcode(shorctode, nil, p, t), nil }
+			renderedShortcodes[key] = func() (string, error) { return renderShortcode(shorctode, nil, p), nil }
 		}
 	}
 
@@ -336,7 +336,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 tpl.Template) (shortcode, error) {
+func extractShortcode(pt *pageTokens, p *Page) (shortcode, error) {
 	sc := shortcode{}
 	var isInner = false
 
@@ -357,7 +357,7 @@
 			if cnt > 0 {
 				// nested shortcode; append it to inner content
 				pt.backup3(currItem, next)
-				nested, err := extractShortcode(pt, p, t)
+				nested, err := extractShortcode(pt, p)
 				if err == nil {
 					sc.inner = append(sc.inner, nested)
 				} else {
@@ -398,7 +398,7 @@
 			sc.inner = append(sc.inner, currItem.val)
 		case tScName:
 			sc.name = currItem.val
-			tmpl := getShortcodeTemplate(sc.name, t)
+			tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
 
 			if tmpl == nil {
 				return sc, fmt.Errorf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
@@ -454,7 +454,7 @@
 	return sc, nil
 }
 
-func extractShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]shortcode, error) {
+func extractShortcodes(stringToParse string, p *Page) (string, map[string]shortcode, error) {
 
 	shortCodes := make(map[string]shortcode)
 
@@ -492,7 +492,7 @@
 		case tLeftDelimScWithMarkup, tLeftDelimScNoMarkup:
 			// let extractShortcode handle left delim (will do so recursively)
 			pt.backup()
-			if currShortcode, err = extractShortcode(pt, p, t); err != nil {
+			if currShortcode, err = extractShortcode(pt, p); err != nil {
 				return result.String(), shortCodes, err
 			}
 
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -32,8 +32,13 @@
 )
 
 // TODO(bep) remove
-func pageFromString(in, filename string) (*Page, error) {
-	return pageTestSite.NewPageFrom(strings.NewReader(in), filename)
+func pageFromString(in, filename string, withTemplate ...func(templ tpl.Template) error) (*Page, error) {
+	s := pageTestSite
+	if len(withTemplate) > 0 {
+		// Have to create a new site
+		s = NewSiteDefaultLang(withTemplate...)
+	}
+	return s.NewPageFrom(strings.NewReader(in), filename)
 }
 
 func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
@@ -83,10 +88,10 @@
 }
 
 func TestShortcodeGoFuzzReports(t *testing.T) {
-	tem := tpl.New(logger)
 
-	tem.AddInternalShortcode("sc.html", `foo`)
-	p, _ := pageFromString(simplePage, "simple.md")
+	p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+		return templ.AddInternalShortcode("sc.html", `foo`)
+	})
 
 	for i, this := range []struct {
 		data      string
@@ -94,7 +99,7 @@
 	}{
 		{"{{</*/", true},
 	} {
-		output, err := HandleShortcodes(this.data, p, tem)
+		output, err := HandleShortcodes(this.data, p)
 
 		if this.expectErr && err == nil {
 			t.Errorf("[%d] should have errored", i)
@@ -304,8 +309,6 @@
 	viper.Set("pygmentsStyle", "bw")
 	viper.Set("pygmentsUseClasses", false)
 
-	templ := tpl.New(logger)
-
 	code := `
 {{< highlight java >}}
 void do();
@@ -312,7 +315,7 @@
 {{< /highlight >}}`
 
 	p, _ := pageFromString(simplePage, "simple.md")
-	output, err := HandleShortcodes(code, p, templ)
+	output, err := HandleShortcodes(code, p)
 
 	if err != nil {
 		t.Fatal("Handle shortcode error", err)
@@ -379,16 +382,17 @@
 			fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""},
 	} {
 
-		p, _ := pageFromString(simplePage, "simple.md")
-		tem := tpl.New(logger)
-		tem.AddInternalShortcode("tag.html", `tag`)
-		tem.AddInternalShortcode("sc1.html", `sc1`)
-		tem.AddInternalShortcode("sc2.html", `sc2`)
-		tem.AddInternalShortcode("inner.html", `{{with .Inner }}{{ . }}{{ end }}`)
-		tem.AddInternalShortcode("inner2.html", `{{.Inner}}`)
-		tem.AddInternalShortcode("inner3.html", `{{.Inner}}`)
+		p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+			templ.AddInternalShortcode("tag.html", `tag`)
+			templ.AddInternalShortcode("sc1.html", `sc1`)
+			templ.AddInternalShortcode("sc2.html", `sc2`)
+			templ.AddInternalShortcode("inner.html", `{{with .Inner }}{{ . }}{{ end }}`)
+			templ.AddInternalShortcode("inner2.html", `{{.Inner}}`)
+			templ.AddInternalShortcode("inner3.html", `{{.Inner}}`)
+			return nil
+		})
 
-		content, shortCodes, err := extractShortcodes(this.input, p, tem)
+		content, shortCodes, err := extractShortcodes(this.input, p)
 
 		if b, ok := this.expect.(bool); ok && !b {
 			if err == nil {
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -115,19 +115,23 @@
 }
 
 // newSite creates a new site in the given language.
-func newSite(lang *helpers.Language) *Site {
+func newSite(lang *helpers.Language, deps *deps, withTemplate ...func(templ tpl.Template) error) *Site {
 	c := newPageCollections()
-	// TODO(bep) globals (also see other Site creation places)
-	deps := newDeps(DepsCfg{})
 	// TODO(bep) globals
 	viper.Set("currentContentLanguage", lang)
+
+	if deps == nil {
+		depsCfg := DepsCfg{WithTemplate: withTemplate}
+		deps = newDeps(depsCfg)
+	}
+
 	return &Site{deps: deps, Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})}
 
 }
 
 // NewSiteDefaultLang creates a new site in the default language.
-func NewSiteDefaultLang() *Site {
-	return newSite(helpers.NewDefaultLanguage())
+func NewSiteDefaultLang(withTemplate ...func(templ tpl.Template) error) *Site {
+	return newSite(helpers.NewDefaultLanguage(), nil, withTemplate...)
 }
 
 // Convenience func used in tests.
@@ -656,24 +660,23 @@
 
 }
 
-func (s *Site) loadTemplates() {
-	s.owner.tmpl = tpl.InitializeT(s.log)
-	s.owner.tmpl.LoadTemplates(s.absLayoutDir())
-	if s.hasTheme() {
-		s.owner.tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
-	}
-}
-
 func (s *Site) prepTemplates(withTemplate func(templ tpl.Template) error) error {
-	s.loadTemplates()
 
-	if withTemplate != nil {
-		if err := withTemplate(s.owner.tmpl); err != nil {
-			return err
+	wt := func(tmpl tpl.Template) error {
+		// TODO(bep) global error handling
+		tmpl.LoadTemplates(s.absLayoutDir())
+		if s.hasTheme() {
+			tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
 		}
+		if withTemplate != nil {
+			if err := withTemplate(tmpl); err != nil {
+				return err
+			}
+		}
+		return nil
 	}
 
-	s.owner.tmpl.MarkReady()
+	s.refreshTemplates(wt)
 
 	return nil
 }
@@ -778,6 +781,7 @@
 	if err = s.initialize(); err != nil {
 		return
 	}
+
 	s.prepTemplates(config.withTemplate)
 	s.owner.tmpl.PrintErrors()
 	s.timerStep("initialize & template prep")
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -109,8 +109,13 @@
 		t.Fatalf("Got build error: %s", err)
 	}
 
-	if s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError) != 1 {
-		t.Fatalf("Expecting the template to log an ERROR")
+	errCount := s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
+
+	// TODO(bep) globals clean up the template error handling
+	// The template errors are stored in a slice etc. so we get 4 log entries
+	// When we should get only 1
+	if errCount == 0 {
+		t.Fatalf("Expecting the template to log 1 ERROR, got %d", errCount)
 	}
 }
 
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -30,11 +30,9 @@
 	"github.com/yosssi/ace"
 )
 
-var localTemplates *template.Template
+// TODO(bep) globals get rid of the rest of the jww.ERR etc.
+//var tmpl *GoHTMLTemplate
 
-// TODO(bep) globals get rid of the reset of the jww.ERR etc.
-var tmpl *GoHTMLTemplate
-
 // TODO(bep) an interface with hundreds of methods ... remove it.
 // And unexport most of these methods.
 type Template interface {
@@ -45,7 +43,6 @@
 	GetClone() *template.Template
 	LoadTemplates(absPath string)
 	LoadTemplatesWithPrefix(absPath, prefix string)
-	MarkReady()
 	AddTemplate(name, tpl string) error
 	AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
 	AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
@@ -52,6 +49,7 @@
 	AddInternalTemplate(prefix, name, tpl string) error
 	AddInternalShortcode(name, tpl string) error
 	PrintErrors()
+	Funcs(funcMap template.FuncMap)
 }
 
 type templateErr struct {
@@ -60,7 +58,8 @@
 }
 
 type GoHTMLTemplate struct {
-	template.Template
+	*template.Template
+
 	clone *template.Template
 
 	// a separate storage for the overlays created from cloned master templates.
@@ -69,41 +68,54 @@
 
 	errors []*templateErr
 
+	funcster *templateFuncster
+
 	// TODO(bep) globals template
 	log *jww.Notepad
 }
 
-// InitializeT resets the internal template state to its initial state
-func InitializeT(logger *jww.Notepad) *GoHTMLTemplate {
-	tmpl = New(logger)
-	return tmpl
-}
-
 // New returns a new Hugo Template System
 // with all the additional features, templates & functions
-func New(logger *jww.Notepad) *GoHTMLTemplate {
-	var templates = &GoHTMLTemplate{
-		Template: *template.New(""),
+func New(logger *jww.Notepad, withTemplate ...func(templ Template) error) *GoHTMLTemplate {
+	tmpl := &GoHTMLTemplate{
+		Template: template.New(""),
 		overlays: make(map[string]*template.Template),
 		errors:   make([]*templateErr, 0),
 		log:      logger,
 	}
 
-	localTemplates = &templates.Template
+	tmpl.funcster = newTemplateFuncster(tmpl)
 
 	// The URL funcs in the funcMap is somewhat language dependent,
 	// so we need to wait until the language and site config is loaded.
-	initFuncMap()
+	// TODO(bep) globals
+	tmpl.funcster.initFuncMap()
 
-	for k, v := range funcMap {
+	// TODO(bep) globals
+	for k, v := range tmpl.funcster.funcMap {
 		amber.FuncMap[k] = v
 	}
-	templates.Funcs(funcMap)
-	templates.LoadEmbedded()
-	return templates
+
+	tmpl.LoadEmbedded()
+
+	for _, wt := range withTemplate {
+		err := wt(tmpl)
+		if err != nil {
+			tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
+		}
+
+	}
+
+	tmpl.markReady()
+
+	return tmpl
 }
 
-func partial(name string, contextList ...interface{}) template.HTML {
+func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
+	t.Template.Funcs(funcMap)
+}
+
+func (t *GoHTMLTemplate) partial(name string, contextList ...interface{}) template.HTML {
 	if strings.HasPrefix("partials/", name) {
 		name = name[8:]
 	}
@@ -114,16 +126,16 @@
 	} else {
 		context = contextList[0]
 	}
-	return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
+	return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
 }
 
-func executeTemplate(context interface{}, w io.Writer, layouts ...string) {
+func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
 	var worked bool
 	for _, layout := range layouts {
-		templ := Lookup(layout)
+		templ := t.Lookup(layout)
 		if templ == nil {
 			layout += ".html"
-			templ = Lookup(layout)
+			templ = t.Lookup(layout)
 		}
 
 		if templ != nil {
@@ -136,28 +148,20 @@
 		}
 	}
 	if !worked {
-		tmpl.log.ERROR.Println("Unable to render", layouts)
-		tmpl.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
+		t.log.ERROR.Println("Unable to render", layouts)
+		t.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
 	}
 }
 
-func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
+func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
 	b := bp.GetBuffer()
 	defer bp.PutBuffer(b)
-	executeTemplate(context, b, layouts...)
+	t.executeTemplate(context, b, layouts...)
 	return template.HTML(b.String())
 }
 
-func Lookup(name string) *template.Template {
-	return tmpl.Lookup(name)
-}
-
 func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
 
-	if templ := localTemplates.Lookup(name); templ != nil {
-		return templ
-	}
-
 	if t.overlays != nil {
 		if templ, ok := t.overlays[name]; ok {
 			return templ
@@ -183,9 +187,9 @@
 	t.EmbedTemplates()
 }
 
-// MarkReady marks the template as "ready for execution". No changes allowed
+// markReady marks the template as "ready for execution". No changes allowed
 // after this is set.
-func (t *GoHTMLTemplate) MarkReady() {
+func (t *GoHTMLTemplate) markReady() {
 	if t.clone == nil {
 		t.clone = template.Must(t.Template.Clone())
 	}
@@ -522,7 +526,7 @@
 }
 
 func (t *GoHTMLTemplate) PrintErrors() {
-	for _, e := range t.errors {
-		t.log.ERROR.Println(e.err)
+	for i, e := range t.errors {
+		t.log.ERROR.Println(i, ":", e.err)
 	}
 }
--- a/tpl/template_ast_transformers_test.go
+++ b/tpl/template_ast_transformers_test.go
@@ -18,7 +18,6 @@
 
 	"html/template"
 
-	jww "github.com/spf13/jwalterweatherman"
 	"github.com/stretchr/testify/require"
 )
 
@@ -264,8 +263,4 @@
 
 	require.Contains(t, result, "P1: P1L")
 	require.Contains(t, result, "P2: P1L")
-}
-
-func init() {
-	jww.SetStdoutThreshold(jww.LevelCritical)
 }
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -54,10 +54,20 @@
 	_ "image/png"
 )
 
-var (
-	funcMap template.FuncMap
-)
+// Some of the template funcs are'nt entirely stateless.
+type templateFuncster struct {
+	t              *GoHTMLTemplate
+	funcMap        template.FuncMap
+	cachedPartials partialCache
+}
 
+func newTemplateFuncster(t *GoHTMLTemplate) *templateFuncster {
+	return &templateFuncster{
+		t:              t,
+		cachedPartials: partialCache{p: make(map[string]template.HTML)},
+	}
+}
+
 // eq returns the boolean truth of arg1 == arg2.
 func eq(x, y interface{}) bool {
 	normalize := func(v interface{}) interface{} {
@@ -1003,7 +1013,7 @@
 }
 
 // apply takes a map, array, or slice and returns a new slice with the function fname applied over it.
-func apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
+func (tf *templateFuncster) apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
 	if seq == nil {
 		return make([]interface{}, 0), nil
 	}
@@ -1018,7 +1028,7 @@
 		return nil, errors.New("can't iterate over a nil value")
 	}
 
-	fn, found := funcMap[fname]
+	fn, found := tf.funcMap[fname]
 	if !found {
 		return nil, errors.New("can't find function " + fname)
 	}
@@ -1518,34 +1528,32 @@
 // Get retrieves partial output from the cache based upon the partial name.
 // If the partial is not found in the cache, the partial is rendered and added
 // to the cache.
-func (c *partialCache) Get(key, name string, context interface{}) (p template.HTML) {
+func (tf *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
 	var ok bool
 
-	c.RLock()
-	p, ok = c.p[key]
-	c.RUnlock()
+	tf.cachedPartials.RLock()
+	p, ok = tf.cachedPartials.p[key]
+	tf.cachedPartials.RUnlock()
 
 	if ok {
 		return p
 	}
 
-	c.Lock()
-	if p, ok = c.p[key]; !ok {
-		p = partial(name, context)
-		c.p[key] = p
+	tf.cachedPartials.Lock()
+	if p, ok = tf.cachedPartials.p[key]; !ok {
+		p = tf.t.partial(name, context)
+		tf.cachedPartials.p[key] = p
 	}
-	c.Unlock()
+	tf.cachedPartials.Unlock()
 
 	return p
 }
 
-var cachedPartials = partialCache{p: make(map[string]template.HTML)}
-
 // partialCached executes and caches partial templates.  An optional variant
 // string parameter (a string slice actually, but be only use a variadic
 // argument to make it optional) can be passed so that a given partial can have
 // multiple uses.  The cache is created with name+variant as the key.
-func partialCached(name string, context interface{}, variant ...string) template.HTML {
+func (tf *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
 	key := name
 	if len(variant) > 0 {
 		for i := 0; i < len(variant); i++ {
@@ -1552,7 +1560,7 @@
 			key += variant[i]
 		}
 	}
-	return cachedPartials.Get(key, name, context)
+	return tf.Get(key, name, context)
 }
 
 // regexpCache represents a cache of regexp objects protected by a mutex.
@@ -2090,8 +2098,8 @@
 	return os.Getenv(skey), nil
 }
 
-func initFuncMap() {
-	funcMap = template.FuncMap{
+func (tf *templateFuncster) initFuncMap() {
+	funcMap := template.FuncMap{
 		"absURL": absURL,
 		"absLangURL": func(i interface{}) (template.HTML, error) {
 			s, err := cast.ToStringE(i)
@@ -2102,7 +2110,7 @@
 		},
 		"add":           func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '+') },
 		"after":         after,
-		"apply":         apply,
+		"apply":         tf.apply,
 		"base64Decode":  base64Decode,
 		"base64Encode":  base64Encode,
 		"chomp":         chomp,
@@ -2147,8 +2155,8 @@
 		"mul":           func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
 		"ne":            ne,
 		"now":           func() time.Time { return time.Now() },
-		"partial":       partial,
-		"partialCached": partialCached,
+		"partial":       tf.t.partial,
+		"partialCached": tf.partialCached,
 		"plainify":      plainify,
 		"pluralize":     pluralize,
 		"querify":       querify,
@@ -2195,4 +2203,7 @@
 		"i18n":         i18nTranslate,
 		"T":            i18nTranslate,
 	}
+
+	tf.funcMap = funcMap
+	tf.t.Funcs(funcMap)
 }
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -1960,40 +1960,43 @@
 }
 
 func TestApply(t *testing.T) {
+
+	f := newTestFuncster()
+
 	strings := []interface{}{"a\n", "b\n"}
 	noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}}
 
-	chomped, _ := apply(strings, "chomp", ".")
+	chomped, _ := f.apply(strings, "chomp", ".")
 	assert.Equal(t, []interface{}{template.HTML("a"), template.HTML("b")}, chomped)
 
-	chomped, _ = apply(strings, "chomp", "c\n")
+	chomped, _ = f.apply(strings, "chomp", "c\n")
 	assert.Equal(t, []interface{}{template.HTML("c"), template.HTML("c")}, chomped)
 
-	chomped, _ = apply(nil, "chomp", ".")
+	chomped, _ = f.apply(nil, "chomp", ".")
 	assert.Equal(t, []interface{}{}, chomped)
 
-	_, err := apply(strings, "apply", ".")
+	_, err := f.apply(strings, "apply", ".")
 	if err == nil {
 		t.Errorf("apply with apply should fail")
 	}
 
 	var nilErr *error
-	_, err = apply(nilErr, "chomp", ".")
+	_, err = f.apply(nilErr, "chomp", ".")
 	if err == nil {
 		t.Errorf("apply with nil in seq should fail")
 	}
 
-	_, err = apply(strings, "dobedobedo", ".")
+	_, err = f.apply(strings, "dobedobedo", ".")
 	if err == nil {
 		t.Errorf("apply with unknown func should fail")
 	}
 
-	_, err = apply(noStringers, "chomp", ".")
+	_, err = f.apply(noStringers, "chomp", ".")
 	if err == nil {
 		t.Errorf("apply when func fails should fail")
 	}
 
-	_, err = apply(tstNoStringer{}, "chomp", ".")
+	_, err = f.apply(tstNoStringer{}, "chomp", ".")
 	if err == nil {
 		t.Errorf("apply with non-sequence should fail")
 	}
@@ -2780,7 +2783,6 @@
 	data.Params = map[string]interface{}{"langCode": "en"}
 
 	tstInitTemplates()
-	InitializeT(logger)
 	for i, tc := range testCases {
 		var tmp string
 		if tc.variant != "" {
@@ -2831,7 +2833,6 @@
 }
 
 func BenchmarkPartial(b *testing.B) {
-	InitializeT(logger)
 	tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
@@ -2851,7 +2852,6 @@
 }
 
 func BenchmarkPartialCached(b *testing.B) {
-	InitializeT(logger)
 	tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
@@ -2871,7 +2871,6 @@
 }
 
 func BenchmarkPartialCachedVariants(b *testing.B) {
-	InitializeT(logger)
 	tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
@@ -2888,4 +2887,8 @@
 		}
 		buf.Reset()
 	}
+}
+
+func newTestFuncster() *templateFuncster {
+	return New(logger).funcster
 }
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -55,8 +55,6 @@
 
 		for _, root := range []string{"", os.TempDir()} {
 
-			templ := New(logger)
-
 			basePath := this.basePath
 			innerPath := this.innerPath
 
@@ -70,17 +68,20 @@
 
 			d := "DATA"
 
-			err := templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
-				[]byte(this.baseContent), []byte(this.innerContent))
+			templ := New(logger, func(templ Template) error {
+				return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
+					[]byte(this.baseContent), []byte(this.innerContent))
 
-			if err != nil && this.expectErr == 0 {
-				t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
-			} else if err == nil && this.expectErr == 1 {
+			})
+
+			if len(templ.errors) > 0 && this.expectErr == 0 {
+				t.Errorf("Test %d with root '%s' errored: %v", i, root, templ.errors)
+			} else if len(templ.errors) == 0 && this.expectErr == 1 {
 				t.Errorf("#1 Test %d with root '%s' should have errored", i, root)
 			}
 
 			var buff bytes.Buffer
-			err = templ.ExecuteTemplate(&buff, "mytemplate.html", d)
+			err := templ.ExecuteTemplate(&buff, "mytemplate.html", d)
 
 			if err != nil && this.expectErr == 0 {
 				t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
@@ -245,7 +246,6 @@
 		// Issue #1095
 		{"{{apply .C \"urlize\" " +
 			"\".\"}}", 2}} {
-		templ := New(logger)
 
 		d := &Data{
 			A: 42,
@@ -258,15 +258,17 @@
 			H: "a,b,c,d,e,f",
 		}
 
-		err := templ.AddTemplate("fuzz", this.data)
+		templ := New(logger, func(templ Template) error {
+			return templ.AddTemplate("fuzz", this.data)
 
-		if err != nil && this.expectErr == 0 {
-			t.Fatalf("Test %d errored: %s", i, err)
-		} else if err == nil && this.expectErr == 1 {
-			t.Fatalf("#1 Test %d should have errored", i)
-		}
+		})
 
-		err = templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
+		if len(templ.errors) > 0 && this.expectErr == 0 {
+			t.Errorf("Test %d errored: %v", i, templ.errors)
+		} else if len(templ.errors) == 0 && this.expectErr == 1 {
+			t.Errorf("#1 Test %d should have errored", i)
+		}
+		err := templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
 
 		if err != nil && this.expectErr == 0 {
 			t.Fatalf("Test %d errored: %s", i, err)
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -281,10 +281,10 @@
 			"revisionTime": "2016-11-30T04:45:28Z"
 		},
 		{
-			"checksumSHA1": "HWDERqbEvvfLwzP7Dvh2fvu+sng=",
+			"checksumSHA1": "9pkkhgKp3mwSreiML3plQlQYdLQ=",
 			"path": "github.com/spf13/jwalterweatherman",
-			"revision": "bccdd23ae5e51bd2b081814db093646c7af3d34d",
-			"revisionTime": "2017-01-05T10:55:09Z"
+			"revision": "fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66",
+			"revisionTime": "2017-01-09T13:33:55Z"
 		},
 		{
 			"checksumSHA1": "zLJY+lsX1e5OO6gRxQd5RfKgdQY=",