URI: 
       Merge branch 'master' of github.com:spf13/hugo - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit e1e6aaed2f57d682f99a478c5602117ae27707eb
   DIR parent 8db3c0b0a677a6b122ad5a396ee52b074acb7e87
  HTML Author: spf13 <steve.francia@gmail.com>
       Date:   Sat, 31 Jan 2015 01:04:28 -0500
       
       Merge branch 'master' of github.com:spf13/hugo
       
       Diffstat:
         M commands/hugo.go                    |       2 +-
         M helpers/path.go                     |      12 ++++++++++--
         M hugolib/handler_page.go             |       4 ++--
         M hugolib/page.go                     |       3 +--
         M hugolib/shortcode.go                |      77 ++++++++++++-------------------
         M hugolib/shortcode_test.go           |      33 ++++++++++++++++---------------
         M livereload/livereload.go            |       4 +++-
         M tpl/template_embedded.go            |      34 ++++++++++++++++----------------
       
       8 files changed, 81 insertions(+), 88 deletions(-)
       ---
   DIR diff --git a/commands/hugo.go b/commands/hugo.go
       @@ -358,7 +358,7 @@ func NewWatcher(port int) error {
                                                        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
        
   DIR diff --git a/helpers/path.go b/helpers/path.go
       @@ -174,9 +174,17 @@ func AbsPathify(inPath string) string {
                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)
        }
   DIR diff --git a/hugolib/handler_page.go b/hugolib/handler_page.go
       @@ -60,7 +60,7 @@ func (h markdownHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
                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 @@ func (h rstHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
                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())
   DIR diff --git a/hugolib/page.go b/hugolib/page.go
       @@ -163,10 +163,9 @@ func (p *Page) setSummary() {
                        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 {
   DIR diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
       @@ -20,7 +20,6 @@ import (
                "reflect"
                "regexp"
                "sort"
       -        "strconv"
                "strings"
                "sync"
        
       @@ -133,7 +132,7 @@ func ShortcodesHandle(stringToParse string, page *Page, t tpl.Template) string {
                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 @@ Loop:
        }
        
        // 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
       +                }
       +        } else {
       +                re, err = regexp.Compile(regexp.QuoteMeta(prefix) + `-(\d+)`)
       +                if err != nil {
       +                        return nil, err
                        }
       -                val := []byte(replacements[key])
       -
       -                newLen += (len(val) - len(key))
                }
        
       -        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
       -
       -                k := bytes.Index(source[start:], []byte(oldVal))
       +        }()
       +        b = re.ReplaceAllFunc(source, func(m []byte) []byte {
       +                key := string(m)
        
       -                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 {
   DIR diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
       @@ -281,23 +281,24 @@ func collectAndShortShortcodes(shortcodes map[string]shortcode) []string {
        
        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 @@ func TestReplaceShortcodeTokens(t *testing.T) {
                                        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)
                                }
                        }
   DIR diff --git a/livereload/livereload.go b/livereload/livereload.go
       @@ -15,6 +15,7 @@ package livereload
        
        import (
                "net/http"
       +        "strings"
        
                "github.com/gorilla/websocket"
        )
       @@ -44,7 +45,8 @@ func ForceRefresh() {
        
        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) {
   DIR diff --git a/tpl/template_embedded.go b/tpl/template_embedded.go
       @@ -50,18 +50,18 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
            <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 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
                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 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
        <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 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
          <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 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
        <!-- 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 @@ func (t *GoHtmlTemplate) EmbedTemplates() {
          {{ 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="{{ . }}">