Continue with TOC integration and page refactor. Updated a few tests to match new generated output.

This commit is contained in:
spf13 2014-01-28 23:11:05 -05:00
parent f45c6bc38a
commit 1da3fd039a
2 changed files with 411 additions and 354 deletions

View file

@ -38,6 +38,7 @@ type Page struct {
rawContent []byte
Content template.HTML
Summary template.HTML
TableOfContents template.HTML
Truncated bool
plain string // TODO should be []byte
Params map[string]interface{}
@ -75,7 +76,7 @@ type Pages []*Page
func (p *Page) Plain() string {
if len(p.plain) == 0 {
p.plain = StripHTML(StripShortcodes(string(p.rawContent)))
p.plain = StripHTML(StripShortcodes(string(p.renderBytes(p.rawContent))))
}
return p.plain
}
@ -96,6 +97,10 @@ func (p *Page) setSummary() {
}
}
func stripEmptyNav(in []byte) []byte {
return bytes.Replace(in, []byte("<nav>\n</nav>\n\n"), []byte(``), -1)
}
func bytesToHTML(b []byte) template.HTML {
return template.HTML(string(b))
}
@ -104,16 +109,27 @@ func (p *Page) renderBytes(content []byte) []byte {
return renderBytes(content, p.guessMarkupType())
}
func (p *Page) renderString(content string) []byte {
return renderBytes([]byte(content), p.guessMarkupType())
func (p *Page) renderContent(content []byte) []byte {
return renderBytesWithTOC(content, p.guessMarkupType())
}
func renderBytesWithTOC(content []byte, pagefmt string) []byte {
switch pagefmt {
default:
return markdownRenderWithTOC(content)
case "markdown":
return markdownRenderWithTOC(content)
case "rst":
return []byte(getRstContent(content))
}
}
func renderBytes(content []byte, pagefmt string) []byte {
switch pagefmt {
default:
return blackfriday.MarkdownCommon(content)
return markdownRender(content)
case "markdown":
return blackfriday.MarkdownCommon(content)
return markdownRender(content)
case "rst":
return []byte(getRstContent(content))
}
@ -553,7 +569,9 @@ func (page *Page) Convert() error {
markupType := page.guessMarkupType()
switch markupType {
case "markdown", "rst":
page.Content = bytesToHTML(page.renderString(string(RemoveSummaryDivider(page.rawContent))))
tmpContent, tmpTableOfContents := extractTOC(page.renderContent(RemoveSummaryDivider(page.rawContent)))
page.Content = bytesToHTML(tmpContent)
page.TableOfContents = bytesToHTML(tmpTableOfContents)
case "html":
page.Content = bytesToHTML(page.rawContent)
default:
@ -562,19 +580,58 @@ func (page *Page) Convert() error {
return nil
}
// Lazily generate the TOC
func (page *Page) TableOfContents() template.HTML {
return tableOfContentsFromBytes([]byte(page.Content))
func markdownRender(content []byte) []byte {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
return blackfriday.Markdown(content, renderer, 0)
}
func tableOfContentsFromBytes(content []byte) template.HTML {
func markdownRenderWithTOC(content []byte) []byte {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
htmlFlags |= blackfriday.HTML_TOC
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
return template.HTML(string(blackfriday.Markdown(RemoveSummaryDivider(content), renderer, 0)))
return blackfriday.Markdown(content, renderer, 0)
}
func extractTOC(content []byte) (newcontent []byte, toc []byte) {
origContent := make([]byte, len(content))
copy(origContent, content)
first := []byte(`<nav>
<ul>`)
last := []byte(`</ul>
</nav>`)
replacement := []byte(`<nav id="TableOfContents">
<ul>`)
startOfTOC := bytes.Index(content, first)
peekEnd := len(content)
if peekEnd > 70+startOfTOC {
peekEnd = 70 + startOfTOC
}
if startOfTOC < 0 {
return stripEmptyNav(content), toc
}
// Need to peek ahead to see if this nav element is actually the right one.
correctNav := bytes.Index(content[startOfTOC:peekEnd], []byte(`#toc_0`))
if correctNav < 0 { // no match found
return content, toc
}
lengthOfTOC := bytes.Index(content[startOfTOC:], last) + len(last)
endOfTOC := startOfTOC + lengthOfTOC
newcontent = append(content[:startOfTOC], content[endOfTOC:]...)
toc = append(replacement, origContent[startOfTOC+len(first):endOfTOC]...)
return
}
func ReaderToBytes(lines io.Reader) []byte {

View file

@ -98,7 +98,7 @@ func TestRenderThing(t *testing.T) {
}{
{PAGE_SIMPLE_TITLE, TEMPLATE_TITLE, "simple template"},
{PAGE_SIMPLE_TITLE, TEMPLATE_FUNC, "simple-template"},
{PAGE_WITH_MD, TEMPLATE_CONTENT, "<h1>heading 1</h1>\n\n<p>text</p>\n\n<h2>heading 2</h2>\n\n<p>more text</p>\n"},
{PAGE_WITH_MD, TEMPLATE_CONTENT, "\n\n<h1 id=\"toc_0\">heading 1</h1>\n\n<p>text</p>\n\n<h2 id=\"toc_1\">heading 2</h2>\n\n<p>more text</p>\n"},
{SIMPLE_PAGE_RFC3339_DATE, TEMPLATE_DATE, "2013-05-17 16:59:30 &#43;0000 UTC"},
}
@ -265,14 +265,14 @@ func TestSkipRender(t *testing.T) {
doc string
expected string
}{
{"sect/doc1.html", "<h1>title</h1>\n\n<p>some <em>content</em></p>\n"},
{"sect/doc1.html", "\n\n<h1 id=\"toc_0\">title</h1>\n\n<p>some <em>content</em></p>\n"},
{"sect/doc2.html", "<!doctype html><html><body>more content</body></html>"},
{"sect/doc3.html", "<h1>doc3</h1>\n\n<p><em>some</em> content</p>\n"},
{"sect/doc4.html", "<h1>doc4</h1>\n\n<p><em>some content</em></p>\n"},
{"sect/doc3.html", "\n\n<h1 id=\"toc_0\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
{"sect/doc4.html", "\n\n<h1 id=\"toc_0\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
{"sect/doc5.html", "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
{"sect/doc6.html", "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
{"doc7.html", "<html><body>doc7 content</body></html>"},
{"sect/doc8.html", "<h1>title</h1>\n\n<p>some <em>content</em></p>\n"},
{"sect/doc8.html", "\n\n<h1 id=\"toc_0\">title</h1>\n\n<p>some <em>content</em></p>\n"},
}
for _, test := range tests {