ref: e1e6aaed2f57d682f99a478c5602117ae27707eb
parent: 8db3c0b0a677a6b122ad5a396ee52b074acb7e87
parent: 4a9436c116776ad3b776f9c16a816c878bccd4f3
author: spf13 <steve.francia@gmail.com>
date: Fri Jan 30 20:04:28 EST 2015
Merge branch 'master' of github.com:spf13/hugo
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -358,7 +358,7 @@
continue
}
- isstatic := strings.HasPrefix(ev.Name, helpers.AbsPathify(viper.GetString("StaticDir"))) || strings.HasPrefix(ev.Name, helpers.AbsPathify("themes/"+viper.GetString("theme"))+"/static/")+ isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || strings.HasPrefix(ev.Name, helpers.GetThemesDirPath())
static_changed = static_changed || isstatic
dynamic_changed = dynamic_changed || !isstatic
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -174,9 +174,17 @@
return filepath.Clean(filepath.Join(viper.GetString("WorkingDir"), inPath))}
+func GetStaticDirPath() string {+ return AbsPathify(viper.GetString("StaticDir"))+}
+
+func GetThemesDirPath() string {+ return AbsPathify(filepath.Join("themes", viper.GetString("theme"), "static"))+}
+
func MakeStaticPathRelative(inPath string) (string, error) {- staticDir := AbsPathify(viper.GetString("StaticDir"))- themeStaticDir := AbsPathify("themes/"+viper.GetString("theme")) + "/static/"+ staticDir := GetStaticDirPath()
+ themeStaticDir := GetThemesDirPath()
return MakePathRelative(inPath, staticDir, themeStaticDir)
}
--- a/hugolib/handler_page.go
+++ b/hugolib/handler_page.go
@@ -60,7 +60,7 @@
tmpContent, tmpTableOfContents := helpers.ExtractTOC(p.renderContent(helpers.RemoveSummaryDivider(p.rawContent)))
if len(p.contentShortCodes) > 0 {- tmpContentWithTokensReplaced, err := replaceShortcodeTokens(tmpContent, shortcodePlaceholderPrefix, -1, true, p.contentShortCodes)
+ tmpContentWithTokensReplaced, err := replaceShortcodeTokens(tmpContent, shortcodePlaceholderPrefix, true, p.contentShortCodes)
if err != nil { jww.FATAL.Printf("Fail to replace short code tokens in %s:\n%s", p.BaseFileName(), err.Error())@@ -113,7 +113,7 @@
tmpContent, tmpTableOfContents := helpers.ExtractTOC(p.renderContent(helpers.RemoveSummaryDivider(p.rawContent)))
if len(p.contentShortCodes) > 0 {- tmpContentWithTokensReplaced, err := replaceShortcodeTokens(tmpContent, shortcodePlaceholderPrefix, -1, true, p.contentShortCodes)
+ tmpContentWithTokensReplaced, err := replaceShortcodeTokens(tmpContent, shortcodePlaceholderPrefix, true, p.contentShortCodes)
if err != nil { jww.FATAL.Printf("Fail to replace short code tokens in %s:\n%s", p.BaseFileName(), err.Error())--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -163,10 +163,9 @@
p.Truncated = true // by definition
header := bytes.Split(p.rawContent, helpers.SummaryDivider)[0]
renderedHeader := p.renderBytes(header)
- numShortcodesInHeader := bytes.Count(header, []byte(shortcodePlaceholderPrefix))
if len(p.contentShortCodes) > 0 {tmpContentWithTokensReplaced, err :=
- replaceShortcodeTokens(renderedHeader, shortcodePlaceholderPrefix, numShortcodesInHeader, true, p.contentShortCodes)
+ replaceShortcodeTokens(renderedHeader, shortcodePlaceholderPrefix, true, p.contentShortCodes)
if err != nil { jww.FATAL.Printf("Failed to replace short code tokens in Summary for %s:\n%s", p.BaseFileName(), err.Error()) } else {--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -20,7 +20,6 @@
"reflect"
"regexp"
"sort"
- "strconv"
"strings"
"sync"
@@ -133,7 +132,7 @@
tmpContent, tmpShortcodes := extractAndRenderShortcodes(stringToParse, page, t)
if len(tmpShortcodes) > 0 {- tmpContentWithTokensReplaced, err := replaceShortcodeTokens([]byte(tmpContent), shortcodePlaceholderPrefix, -1, true, tmpShortcodes)
+ tmpContentWithTokensReplaced, err := replaceShortcodeTokens([]byte(tmpContent), shortcodePlaceholderPrefix, true, tmpShortcodes)
if err != nil { jww.ERROR.Printf("Fail to replace short code tokens in %s:\n%s", page.BaseFileName(), err.Error())@@ -432,60 +431,44 @@
}
// Replace prefixed shortcode tokens (HUGOSHORTCODE-1, HUGOSHORTCODE-2) with the real content.
-// This assumes that all tokens exist in the input string and that they are in order.
-// numReplacements = -1 will do len(replacements), and it will always start from the beginning (1)
// wrapped = true means that the token has been wrapped in {@{@/@}@}-func replaceShortcodeTokens(source []byte, prefix string, numReplacements int, wrapped bool, replacements map[string]string) ([]byte, error) {+func replaceShortcodeTokens(source []byte, prefix string, wrapped bool, replacements map[string]string) (b []byte, err error) {+ var re *regexp.Regexp
- if numReplacements < 0 {- numReplacements = len(replacements)
- }
-
- if numReplacements == 0 {- return source, nil
- }
-
- newLen := len(source)
-
- for i := 1; i <= numReplacements; i++ {- key := prefix + "-" + strconv.Itoa(i)
-
- if wrapped {- key = "{@{@" + key + "@}@}"+ if wrapped {+ re, err = regexp.Compile(`\{@\{@` + regexp.QuoteMeta(prefix) + `-\d+@\}@\}`)+ if err != nil {+ return nil, err
}
- val := []byte(replacements[key])
-
- newLen += (len(val) - len(key))
+ } else {+ re, err = regexp.Compile(regexp.QuoteMeta(prefix) + `-(\d+)`)
+ if err != nil {+ return nil, err
+ }
}
- buff := make([]byte, newLen)
-
- width := 0
- start := 0
-
- for i := 0; i < numReplacements; i++ {- tokenNum := i + 1
- oldVal := prefix + "-" + strconv.Itoa(tokenNum)
- if wrapped {- oldVal = "{@{@" + oldVal + "@}@}"+ // use panic/recover for reporting if an unknown
+ defer func() {+ if r := recover(); r != nil {+ var ok bool
+ b = nil
+ err, ok = r.(error)
+ if !ok {+ err = fmt.Errorf("unexpected panic during replaceShortcodeTokens: %v", r)+ }
}
- newVal := []byte(replacements[oldVal])
- j := start
+ }()
+ b = re.ReplaceAllFunc(source, func(m []byte) []byte {+ key := string(m)
- k := bytes.Index(source[start:], []byte(oldVal))
-
- if k < 0 {- // this should never happen, but let the caller decide to panic or not
- return nil, fmt.Errorf("illegal state in content; shortcode token #%d is missing or out of order (%q)", tokenNum, source)+ if val, ok := replacements[key]; ok {+ return []byte(val)
+ } else {+ panic(fmt.Errorf("unknown shortcode token %q", key))}
- j += k
+ })
- width += copy(buff[width:], source[start:j])
- width += copy(buff[width:], newVal)
- start = j + len(oldVal)
- }
- width += copy(buff[width:], source[start:])
- return buff[0:width], nil
+ return b, err
}
func GetTemplate(name string, t tpl.Template) *template.Template {--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -281,23 +281,24 @@
func TestReplaceShortcodeTokens(t *testing.T) { for i, this := range []struct {- input []byte
- prefix string
- replacements map[string]string
- numReplacements int
- wrappedInDiv bool
- expect interface{}+ input string
+ prefix string
+ replacements map[string]string
+ wrappedInDiv bool
+ expect interface{} }{- {[]byte("Hello PREFIX-1."), "PREFIX", map[string]string{"PREFIX-1": "World"}, -1, false, []byte("Hello World.")},- {[]byte("A {@{@A-1@}@} asdf {@{@A-2@}@}."), "A", map[string]string{"{@{@A-1@}@}": "v1", "{@{@A-2@}@}": "v2"}, -1, true, []byte("A v1 asdf v2.")},- {[]byte("Hello PREFIX2-1. Go PREFIX2-2, Go, Go PREFIX2-3 Go Go!."), "PREFIX2", map[string]string{"PREFIX2-1": "Europe", "PREFIX2-2": "Jonny", "PREFIX2-3": "Johnny"}, -1, false, []byte("Hello Europe. Go Jonny, Go, Go Johnny Go Go!.")},- {[]byte("A PREFIX-2 PREFIX-1."), "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, -1, false, false},- {[]byte("A PREFIX-1 PREFIX-2"), "PREFIX", map[string]string{"PREFIX-1": "A"}, -1, false, []byte("A A PREFIX-2")},- {[]byte("A PREFIX-1 but not the second."), "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, -1, false, false},- {[]byte("An PREFIX-1."), "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, 1, false, []byte("An A.")},- {[]byte("An PREFIX-1 PREFIX-2."), "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, 1, false, []byte("An A PREFIX-2.")},+ {"Hello PREFIX-1.", "PREFIX", map[string]string{"PREFIX-1": "World"}, false, "Hello World."},+ {"A {@{@A-1@}@} asdf {@{@A-2@}@}.", "A", map[string]string{"{@{@A-1@}@}": "v1", "{@{@A-2@}@}": "v2"}, true, "A v1 asdf v2."},+ {"Hello PREFIX2-1. Go PREFIX2-2, Go, Go PREFIX2-3 Go Go!.", "PREFIX2", map[string]string{"PREFIX2-1": "Europe", "PREFIX2-2": "Jonny", "PREFIX2-3": "Johnny"}, false, "Hello Europe. Go Jonny, Go, Go Johnny Go Go!."},+ {"A PREFIX-2 PREFIX-1.", "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, false, "A B A."},+ {"A PREFIX-1 PREFIX-2", "PREFIX", map[string]string{"PREFIX-1": "A"}, false, false},+ {"A PREFIX-1 but not the second.", "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, false, "A A but not the second."},+ {"An PREFIX-1.", "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, false, "An A."},+ {"An PREFIX-1 PREFIX-2.", "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B"}, false, "An A B."},+ {"A PREFIX-1 PREFIX-2 PREFIX-3 PREFIX-1 PREFIX-3.", "PREFIX", map[string]string{"PREFIX-1": "A", "PREFIX-2": "B", "PREFIX-3": "C"}, false, "A A B C A C."},+ {"A {@{@PREFIX-1@}@} {@{@PREFIX-2@}@} {@{@PREFIX-3@}@} {@{@PREFIX-1@}@} {@{@PREFIX-3@}@}.", "PREFIX", map[string]string{"{@{@PREFIX-1@}@}": "A", "{@{@PREFIX-2@}@}": "B", "{@{@PREFIX-3@}@}": "C"}, true, "A A B C A C."}, } {- results, err := replaceShortcodeTokens(this.input, this.prefix, this.numReplacements, this.wrappedInDiv, this.replacements)
+ results, err := replaceShortcodeTokens([]byte(this.input), this.prefix, this.wrappedInDiv, this.replacements)
if b, ok := this.expect.(bool); ok && !b { if err == nil {@@ -308,7 +309,7 @@
t.Errorf("[%d] failed: %s", i, err)continue
}
- if !reflect.DeepEqual(results, this.expect) {+ if !reflect.DeepEqual(results, []byte(this.expect.(string))) { t.Errorf("[%d] replaceShortcodeTokens, got %q but expected %q", i, results, this.expect)}
}
--- a/livereload/livereload.go
+++ b/livereload/livereload.go
@@ -15,6 +15,7 @@
import (
"net/http"
+ "strings"
"github.com/gorilla/websocket"
)
@@ -44,7 +45,8 @@
func RefreshPath(s string) {// Tell livereload a file has changed - will force a hard refresh if not CSS or an image
- wsHub.broadcast <- []byte(`{"command":"reload","path":"` + s + "\"" + `,"originalPath":"","liveCSS":true,"liveImg":true}`)+ urlPath := strings.Replace(s, "\\", "/", -1) // If path has backslashes on Windows, make path work for URL
+ wsHub.broadcast <- []byte(`{"command":"reload","path":"` + urlPath + "\"" + `,"originalPath":"","liveCSS":true,"liveImg":true}`)}
func ServeJS(w http.ResponseWriter, r *http.Request) {--- a/tpl/template_embedded.go
+++ b/tpl/template_embedded.go
@@ -50,18 +50,18 @@
<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.email }}<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}- {{ with .Site.Author.email }}<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}- {{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}- <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</lastBuildDate>+ <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}+ <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}+ <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}+ <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}+ <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}+ <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</lastBuildDate>{{ end }} <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 -0700" }}</pubDate>+ <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHtml }}</pubDate> {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}} <guid>{{ .Permalink }}</guid> <description>{{ .Content | html }}</description>@@ -73,8 +73,8 @@
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 }}+ <loc>{{ .Permalink }}</loc>{{ if not .Date.IsZero }}+ <lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }} <changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }} <priority>{{ .Sitemap.Priority }}</priority>{{ end }}</url>
@@ -126,7 +126,7 @@
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)// Add SEO & Social metadata
- t.AddInternalTemplate("_default", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />+ t.AddInternalTemplate("", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" /> <meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}" /> <meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" /> <meta property="og:url" content="{{ .Permalink }}" />@@ -134,7 +134,7 @@
<meta property="og:image" content="{{ . }}" /> {{ end }}{{ end }}-<meta property="og:updated_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" }}"/>{{ with .Params.audio }}+{{ if not .Date.IsZero }}<meta property="og:updated_time" content="{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHtml }}"/>{{ end }}{{ with .Params.audio }} <meta property="og:audio" content="{{ . }}" />{{ end }}{{ with .Params.locale }} <meta property="og:locale" content="{{ . }}" />{{ end }}{{ with .Site.Params.title }} <meta property="og:site_name" content="{{ . }}" />{{ end }}{{ with .Params.videos }}@@ -166,7 +166,7 @@
<!-- Facebook Page Admin ID for Domain Insights -->
{{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}`)- t.AddInternalTemplate("_default", "twitter_cards.html", `{{ if .IsPage }}+ t.AddInternalTemplate("", "twitter_cards.html", `{{ if .IsPage }} {{ with .Params.images }}<!-- Twitter summary card with large image must be at least 280x150px -->
<meta name="twitter:card" content="summary_large_image"/>
@@ -184,17 +184,17 @@
{{ with .twitter }}<meta name="twitter:creator" content="@{{ . }}"/>{{ end }} {{ end }}{{ end }}`)- t.AddInternalTemplate("_default", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}+ t.AddInternalTemplate("", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }} <meta name="news_keywords" content="{{ range $i, $kw := first 10 . }}{{ if $i }},{{ end }}{{ $kw }}{{ end }}" /> {{ end }}{{ end }}`)- t.AddInternalTemplate("_default", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}+ t.AddInternalTemplate("", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }} <meta itemprop="name" content="{{ .Title }}"> <meta itemprop="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ end }}{{ end }}">-{{if .IsPage}}{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}{{ if ne (.PublishDate.Format $ISO8601) "0001-01-01T00:00:00+00:00" }}-<meta itemprop="datePublished" content="{{ .PublishDate.Format $ISO8601 }}" />{{ end }}-<meta itemprop="dateModified" content="{{ .Date.Format $ISO8601 }}" />+{{if .IsPage}}{{ $ISO8601 := "2006-01-02T15:04:05-07:00" }}{{ if not .PublishDate.IsZero }}+<meta itemprop="datePublished" content="{{ .PublishDate.Format $ISO8601 | safeHtml }}" />{{ end }}+{{ if not .Date.IsZero }}<meta itemprop="dateModified" content="{{ .Date.Format $ISO8601 | safeHtml }}" />{{ end }} <meta itemprop="wordCount" content="{{ .WordCount }}"> {{ with .Params.images }}{{ range first 6 . }} <meta itemprop="image" content="{{ . }}">--
⑨