Merge pull request #86 from dimfeld/master
Add absolute link transformation and footnote enhancements
This commit is contained in:
commit
3c2ae1d185
71
html.go
71
html.go
|
@ -41,6 +41,8 @@ const (
|
||||||
HTML_USE_SMARTYPANTS // enable smart punctuation substitutions
|
HTML_USE_SMARTYPANTS // enable smart punctuation substitutions
|
||||||
HTML_SMARTYPANTS_FRACTIONS // enable smart fractions (with HTML_USE_SMARTYPANTS)
|
HTML_SMARTYPANTS_FRACTIONS // enable smart fractions (with HTML_USE_SMARTYPANTS)
|
||||||
HTML_SMARTYPANTS_LATEX_DASHES // enable LaTeX-style dashes (with HTML_USE_SMARTYPANTS)
|
HTML_SMARTYPANTS_LATEX_DASHES // enable LaTeX-style dashes (with HTML_USE_SMARTYPANTS)
|
||||||
|
HTML_ABSOLUTE_LINKS // convert all links to absolute links, using AbsolutePrefix
|
||||||
|
HTML_FOOTNOTE_RETURN_LINKS // generate a link at the end of a footnote to return to the source
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -54,6 +56,17 @@ var (
|
||||||
htmlEntity = regexp.MustCompile(`&[a-z]{2,5};`)
|
htmlEntity = regexp.MustCompile(`&[a-z]{2,5};`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type HtmlRendererParameters struct {
|
||||||
|
// Prepend this text to each URL, if the HTML_ABSOLUTE_LINKS option is enabled.
|
||||||
|
AbsolutePrefix string
|
||||||
|
// Add this text to each footnote anchor, to ensure uniqueness.
|
||||||
|
FootnoteAnchorPrefix string
|
||||||
|
// Show this text inside the <a> tag for a footnote return link, if the
|
||||||
|
// HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string
|
||||||
|
// <sup>[return]</sup> is used.
|
||||||
|
FootnoteReturnLinkContents string
|
||||||
|
}
|
||||||
|
|
||||||
// Html is a type that implements the Renderer interface for HTML output.
|
// Html is a type that implements the Renderer interface for HTML output.
|
||||||
//
|
//
|
||||||
// Do not create this directly, instead use the HtmlRenderer function.
|
// Do not create this directly, instead use the HtmlRenderer function.
|
||||||
|
@ -63,6 +76,8 @@ type Html struct {
|
||||||
title string // document title
|
title string // document title
|
||||||
css string // optional css file url (used with HTML_COMPLETE_PAGE)
|
css string // optional css file url (used with HTML_COMPLETE_PAGE)
|
||||||
|
|
||||||
|
parameters HtmlRendererParameters
|
||||||
|
|
||||||
// table of contents data
|
// table of contents data
|
||||||
tocMarker int
|
tocMarker int
|
||||||
headerCount int
|
headerCount int
|
||||||
|
@ -85,17 +100,27 @@ const (
|
||||||
// stylesheet.
|
// stylesheet.
|
||||||
// title and css are only used when HTML_COMPLETE_PAGE is selected.
|
// title and css are only used when HTML_COMPLETE_PAGE is selected.
|
||||||
func HtmlRenderer(flags int, title string, css string) Renderer {
|
func HtmlRenderer(flags int, title string, css string) Renderer {
|
||||||
|
return HtmlRendererWithParameters(flags, title, css, HtmlRendererParameters{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func HtmlRendererWithParameters(flags int, title string,
|
||||||
|
css string, renderParameters HtmlRendererParameters) Renderer {
|
||||||
// configure the rendering engine
|
// configure the rendering engine
|
||||||
closeTag := htmlClose
|
closeTag := htmlClose
|
||||||
if flags&HTML_USE_XHTML != 0 {
|
if flags&HTML_USE_XHTML != 0 {
|
||||||
closeTag = xhtmlClose
|
closeTag = xhtmlClose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if renderParameters.FootnoteReturnLinkContents == "" {
|
||||||
|
renderParameters.FootnoteReturnLinkContents = `<sup>[return]</sup>`
|
||||||
|
}
|
||||||
|
|
||||||
return &Html{
|
return &Html{
|
||||||
flags: flags,
|
flags: flags,
|
||||||
closeTag: closeTag,
|
closeTag: closeTag,
|
||||||
title: title,
|
title: title,
|
||||||
css: css,
|
css: css,
|
||||||
|
parameters: renderParameters,
|
||||||
|
|
||||||
headerCount: 0,
|
headerCount: 0,
|
||||||
currentLevel: 0,
|
currentLevel: 0,
|
||||||
|
@ -349,10 +374,22 @@ func (options *Html) FootnoteItem(out *bytes.Buffer, name, text []byte, flags in
|
||||||
if flags&LIST_ITEM_CONTAINS_BLOCK != 0 || flags&LIST_ITEM_BEGINNING_OF_LIST != 0 {
|
if flags&LIST_ITEM_CONTAINS_BLOCK != 0 || flags&LIST_ITEM_BEGINNING_OF_LIST != 0 {
|
||||||
doubleSpace(out)
|
doubleSpace(out)
|
||||||
}
|
}
|
||||||
out.WriteString(`<li id="fn:`)
|
slug := slugify(name)
|
||||||
out.Write(slugify(name))
|
out.WriteString(`<li id="`)
|
||||||
|
out.WriteString(`fn:`)
|
||||||
|
out.WriteString(options.parameters.FootnoteAnchorPrefix)
|
||||||
|
out.Write(slug)
|
||||||
out.WriteString(`">`)
|
out.WriteString(`">`)
|
||||||
out.Write(text)
|
out.Write(text)
|
||||||
|
if options.flags&HTML_FOOTNOTE_RETURN_LINKS != 0 {
|
||||||
|
out.WriteString(` <a class="footnote-return" href="#`)
|
||||||
|
out.WriteString(`fnref:`)
|
||||||
|
out.WriteString(options.parameters.FootnoteAnchorPrefix)
|
||||||
|
out.Write(slug)
|
||||||
|
out.WriteString(`">`)
|
||||||
|
out.WriteString(options.parameters.FootnoteReturnLinkContents)
|
||||||
|
out.WriteString(`</a>`)
|
||||||
|
}
|
||||||
out.WriteString("</li>\n")
|
out.WriteString("</li>\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +447,10 @@ func (options *Html) AutoLink(out *bytes.Buffer, link []byte, kind int) {
|
||||||
out.WriteString("<a href=\"")
|
out.WriteString("<a href=\"")
|
||||||
if kind == LINK_TYPE_EMAIL {
|
if kind == LINK_TYPE_EMAIL {
|
||||||
out.WriteString("mailto:")
|
out.WriteString("mailto:")
|
||||||
|
} else {
|
||||||
|
options.maybeWriteAbsolutePrefix(out, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
entityEscapeWithSkip(out, link, skipRanges)
|
entityEscapeWithSkip(out, link, skipRanges)
|
||||||
|
|
||||||
if options.flags&HTML_NOFOLLOW_LINKS != 0 && !isRelativeLink(link) {
|
if options.flags&HTML_NOFOLLOW_LINKS != 0 && !isRelativeLink(link) {
|
||||||
|
@ -459,12 +499,22 @@ func (options *Html) Emphasis(out *bytes.Buffer, text []byte) {
|
||||||
out.WriteString("</em>")
|
out.WriteString("</em>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (options *Html) maybeWriteAbsolutePrefix(out *bytes.Buffer, link []byte) {
|
||||||
|
if options.flags&HTML_ABSOLUTE_LINKS != 0 && isRelativeLink(link) {
|
||||||
|
out.WriteString(options.parameters.AbsolutePrefix)
|
||||||
|
if link[0] != '/' {
|
||||||
|
out.WriteByte('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (options *Html) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
func (options *Html) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
||||||
if options.flags&HTML_SKIP_IMAGES != 0 {
|
if options.flags&HTML_SKIP_IMAGES != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out.WriteString("<img src=\"")
|
out.WriteString("<img src=\"")
|
||||||
|
options.maybeWriteAbsolutePrefix(out, link)
|
||||||
attrEscape(out, link)
|
attrEscape(out, link)
|
||||||
out.WriteString("\" alt=\"")
|
out.WriteString("\" alt=\"")
|
||||||
if len(alt) > 0 {
|
if len(alt) > 0 {
|
||||||
|
@ -503,6 +553,7 @@ func (options *Html) Link(out *bytes.Buffer, link []byte, title []byte, content
|
||||||
}
|
}
|
||||||
|
|
||||||
out.WriteString("<a href=\"")
|
out.WriteString("<a href=\"")
|
||||||
|
options.maybeWriteAbsolutePrefix(out, link)
|
||||||
attrEscape(out, link)
|
attrEscape(out, link)
|
||||||
if len(title) > 0 {
|
if len(title) > 0 {
|
||||||
out.WriteString("\" title=\"")
|
out.WriteString("\" title=\"")
|
||||||
|
@ -552,9 +603,13 @@ func (options *Html) StrikeThrough(out *bytes.Buffer, text []byte) {
|
||||||
|
|
||||||
func (options *Html) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
|
func (options *Html) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
|
||||||
slug := slugify(ref)
|
slug := slugify(ref)
|
||||||
out.WriteString(`<sup class="footnote-ref" id="fnref:`)
|
out.WriteString(`<sup class="footnote-ref" id="`)
|
||||||
|
out.WriteString(`fnref:`)
|
||||||
|
out.WriteString(options.parameters.FootnoteAnchorPrefix)
|
||||||
out.Write(slug)
|
out.Write(slug)
|
||||||
out.WriteString(`"><a rel="footnote" href="#fn:`)
|
out.WriteString(`"><a rel="footnote" href="#`)
|
||||||
|
out.WriteString(`fn:`)
|
||||||
|
out.WriteString(options.parameters.FootnoteAnchorPrefix)
|
||||||
out.Write(slug)
|
out.Write(slug)
|
||||||
out.WriteString(`">`)
|
out.WriteString(`">`)
|
||||||
out.WriteString(strconv.Itoa(id))
|
out.WriteString(strconv.Itoa(id))
|
||||||
|
|
121
inline_test.go
121
inline_test.go
|
@ -14,29 +14,49 @@
|
||||||
package blackfriday
|
package blackfriday
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runMarkdownInline(input string, extensions, htmlFlags int) string {
|
func runMarkdownInline(input string, extensions, htmlFlags int, params HtmlRendererParameters) string {
|
||||||
extensions |= EXTENSION_AUTOLINK
|
extensions |= EXTENSION_AUTOLINK
|
||||||
extensions |= EXTENSION_STRIKETHROUGH
|
extensions |= EXTENSION_STRIKETHROUGH
|
||||||
|
|
||||||
htmlFlags |= HTML_USE_XHTML
|
htmlFlags |= HTML_USE_XHTML
|
||||||
|
|
||||||
renderer := HtmlRenderer(htmlFlags, "", "")
|
renderer := HtmlRendererWithParameters(htmlFlags, "", "", params)
|
||||||
|
|
||||||
return string(Markdown([]byte(input), renderer, extensions))
|
return string(Markdown([]byte(input), renderer, extensions))
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestsInline(t *testing.T, tests []string) {
|
func doTestsInline(t *testing.T, tests []string) {
|
||||||
doTestsInlineParam(t, tests, 0, 0)
|
doTestsInlineParam(t, tests, 0, 0, HtmlRendererParameters{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func doLinkTestsInline(t *testing.T, tests []string) {
|
||||||
|
doTestsInline(t, tests)
|
||||||
|
|
||||||
|
prefix := "http://localhost"
|
||||||
|
params := HtmlRendererParameters{AbsolutePrefix: prefix}
|
||||||
|
transformTests := transformLinks(tests, prefix)
|
||||||
|
doTestsInlineParam(t, transformTests, 0, HTML_ABSOLUTE_LINKS, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSafeTestsInline(t *testing.T, tests []string) {
|
func doSafeTestsInline(t *testing.T, tests []string) {
|
||||||
doTestsInlineParam(t, tests, 0, HTML_SAFELINK)
|
doTestsInlineParam(t, tests, 0, HTML_SAFELINK, HtmlRendererParameters{})
|
||||||
|
|
||||||
|
// All the links in this test should not have the prefix appended, so
|
||||||
|
// just rerun it with different parameters and the same expectations.
|
||||||
|
prefix := "http://localhost"
|
||||||
|
params := HtmlRendererParameters{AbsolutePrefix: prefix}
|
||||||
|
transformTests := transformLinks(tests, prefix)
|
||||||
|
doTestsInlineParam(t, transformTests, 0, HTML_SAFELINK|HTML_ABSOLUTE_LINKS, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int) {
|
func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int,
|
||||||
|
params HtmlRendererParameters) {
|
||||||
// catch and report panics
|
// catch and report panics
|
||||||
var candidate string
|
var candidate string
|
||||||
/*
|
/*
|
||||||
|
@ -51,7 +71,7 @@ func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int)
|
||||||
input := tests[i]
|
input := tests[i]
|
||||||
candidate = input
|
candidate = input
|
||||||
expected := tests[i+1]
|
expected := tests[i+1]
|
||||||
actual := runMarkdownInline(candidate, extensions, htmlFlags)
|
actual := runMarkdownInline(candidate, extensions, htmlFlags, params)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
|
t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
|
||||||
candidate, expected, actual)
|
candidate, expected, actual)
|
||||||
|
@ -62,13 +82,27 @@ func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int)
|
||||||
for start := 0; start < len(input); start++ {
|
for start := 0; start < len(input); start++ {
|
||||||
for end := start + 1; end <= len(input); end++ {
|
for end := start + 1; end <= len(input); end++ {
|
||||||
candidate = input[start:end]
|
candidate = input[start:end]
|
||||||
_ = runMarkdownInline(candidate, extensions, htmlFlags)
|
_ = runMarkdownInline(candidate, extensions, htmlFlags, params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func transformLinks(tests []string, prefix string) []string {
|
||||||
|
newTests := make([]string, len(tests))
|
||||||
|
anchorRe := regexp.MustCompile(`<a href="/(.*?)"`)
|
||||||
|
imgRe := regexp.MustCompile(`<img src="/(.*?)"`)
|
||||||
|
for i, test := range tests {
|
||||||
|
if i%2 == 1 {
|
||||||
|
test = anchorRe.ReplaceAllString(test, `<a href="`+prefix+`/$1"`)
|
||||||
|
test = imgRe.ReplaceAllString(test, `<img src="`+prefix+`/$1"`)
|
||||||
|
}
|
||||||
|
newTests[i] = test
|
||||||
|
}
|
||||||
|
return newTests
|
||||||
|
}
|
||||||
|
|
||||||
func TestEmphasis(t *testing.T) {
|
func TestEmphasis(t *testing.T) {
|
||||||
var tests = []string{
|
var tests = []string{
|
||||||
"nothing inline\n",
|
"nothing inline\n",
|
||||||
|
@ -383,7 +417,8 @@ func TestInlineLink(t *testing.T) {
|
||||||
"[[t]](/t)\n",
|
"[[t]](/t)\n",
|
||||||
"<p><a href=\"/t\">[t]</a></p>\n",
|
"<p><a href=\"/t\">[t]</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInline(t, tests)
|
doLinkTestsInline(t, tests)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNofollowLink(t *testing.T) {
|
func TestNofollowLink(t *testing.T) {
|
||||||
|
@ -391,13 +426,14 @@ func TestNofollowLink(t *testing.T) {
|
||||||
"[foo](http://bar.com/foo/)\n",
|
"[foo](http://bar.com/foo/)\n",
|
||||||
"<p><a href=\"http://bar.com/foo/\" rel=\"nofollow\">foo</a></p>\n",
|
"<p><a href=\"http://bar.com/foo/\" rel=\"nofollow\">foo</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_SANITIZE_OUTPUT)
|
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_SANITIZE_OUTPUT,
|
||||||
|
HtmlRendererParameters{})
|
||||||
// HTML_SANITIZE_OUTPUT won't allow relative links, so test that separately:
|
// HTML_SANITIZE_OUTPUT won't allow relative links, so test that separately:
|
||||||
tests = []string{
|
tests = []string{
|
||||||
"[foo](/bar/)\n",
|
"[foo](/bar/)\n",
|
||||||
"<p><a href=\"/bar/\">foo</a></p>\n",
|
"<p><a href=\"/bar/\">foo</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS)
|
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS, HtmlRendererParameters{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHrefTargetBlank(t *testing.T) {
|
func TestHrefTargetBlank(t *testing.T) {
|
||||||
|
@ -409,7 +445,7 @@ func TestHrefTargetBlank(t *testing.T) {
|
||||||
"[foo](http://example.com)\n",
|
"[foo](http://example.com)\n",
|
||||||
"<p><a href=\"http://example.com\" target=\"_blank\">foo</a></p>\n",
|
"<p><a href=\"http://example.com\" target=\"_blank\">foo</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_HREF_TARGET_BLANK)
|
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_HREF_TARGET_BLANK, HtmlRendererParameters{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSafeInlineLink(t *testing.T) {
|
func TestSafeInlineLink(t *testing.T) {
|
||||||
|
@ -465,7 +501,7 @@ func TestReferenceLink(t *testing.T) {
|
||||||
"[ref]\n [ref]: /url/ \"title\"\n",
|
"[ref]\n [ref]: /url/ \"title\"\n",
|
||||||
"<p><a href=\"/url/\" title=\"title\">ref</a></p>\n",
|
"<p><a href=\"/url/\" title=\"title\">ref</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInline(t, tests)
|
doLinkTestsInline(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTags(t *testing.T) {
|
func TestTags(t *testing.T) {
|
||||||
|
@ -592,13 +628,12 @@ func TestAutoLink(t *testing.T) {
|
||||||
"http://foo.com/viewtopic.php?param="18"",
|
"http://foo.com/viewtopic.php?param="18"",
|
||||||
"<p><a href=\"http://foo.com/viewtopic.php?param="18"\">http://foo.com/viewtopic.php?param="18"</a></p>\n",
|
"<p><a href=\"http://foo.com/viewtopic.php?param="18"\">http://foo.com/viewtopic.php?param="18"</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsInline(t, tests)
|
doLinkTestsInline(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFootnotes(t *testing.T) {
|
var footnoteTests = []string{
|
||||||
tests := []string{
|
"testing footnotes.[^a]\n\n[^a]: This is the note\n",
|
||||||
"testing footnotes.[^a]\n\n[^a]: This is the note\n",
|
`<p>testing footnotes.<sup class="footnote-ref" id="fnref:a"><a rel="footnote" href="#fn:a">1</a></sup></p>
|
||||||
`<p>testing footnotes.<sup class="footnote-ref" id="fnref:a"><a rel="footnote" href="#fn:a">1</a></sup></p>
|
|
||||||
<div class="footnotes">
|
<div class="footnotes">
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -610,7 +645,7 @@ func TestFootnotes(t *testing.T) {
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
`testing long[^b] notes.
|
`testing long[^b] notes.
|
||||||
|
|
||||||
[^b]: Paragraph 1
|
[^b]: Paragraph 1
|
||||||
|
|
||||||
|
@ -622,7 +657,7 @@ func TestFootnotes(t *testing.T) {
|
||||||
|
|
||||||
No longer in the footnote
|
No longer in the footnote
|
||||||
`,
|
`,
|
||||||
`<p>testing long<sup class="footnote-ref" id="fnref:b"><a rel="footnote" href="#fn:b">1</a></sup> notes.</p>
|
`<p>testing long<sup class="footnote-ref" id="fnref:b"><a rel="footnote" href="#fn:b">1</a></sup> notes.</p>
|
||||||
|
|
||||||
<p>No longer in the footnote</p>
|
<p>No longer in the footnote</p>
|
||||||
<div class="footnotes">
|
<div class="footnotes">
|
||||||
|
@ -644,7 +679,7 @@ some code
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
`testing[^c] multiple[^d] notes.
|
`testing[^c] multiple[^d] notes.
|
||||||
|
|
||||||
[^c]: this is [note] c
|
[^c]: this is [note] c
|
||||||
|
|
||||||
|
@ -658,7 +693,7 @@ what happens here
|
||||||
[note]: /link/c
|
[note]: /link/c
|
||||||
|
|
||||||
`,
|
`,
|
||||||
`<p>testing<sup class="footnote-ref" id="fnref:c"><a rel="footnote" href="#fn:c">1</a></sup> multiple<sup class="footnote-ref" id="fnref:d"><a rel="footnote" href="#fn:d">2</a></sup> notes.</p>
|
`<p>testing<sup class="footnote-ref" id="fnref:c"><a rel="footnote" href="#fn:c">1</a></sup> multiple<sup class="footnote-ref" id="fnref:d"><a rel="footnote" href="#fn:d">2</a></sup> notes.</p>
|
||||||
|
|
||||||
<p>omg</p>
|
<p>omg</p>
|
||||||
|
|
||||||
|
@ -676,8 +711,8 @@ what happens here
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
"testing inline^[this is the note] notes.\n",
|
"testing inline^[this is the note] notes.\n",
|
||||||
`<p>testing inline<sup class="footnote-ref" id="fnref:this-is-the-note"><a rel="footnote" href="#fn:this-is-the-note">1</a></sup> notes.</p>
|
`<p>testing inline<sup class="footnote-ref" id="fnref:this-is-the-note"><a rel="footnote" href="#fn:this-is-the-note">1</a></sup> notes.</p>
|
||||||
<div class="footnotes">
|
<div class="footnotes">
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -688,8 +723,8 @@ what happens here
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
"testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n",
|
"testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n",
|
||||||
`<p>testing multiple<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup> types<sup class="footnote-ref" id="fnref:inline-note"><a rel="footnote" href="#fn:inline-note">2</a></sup> of notes<sup class="footnote-ref" id="fnref:2"><a rel="footnote" href="#fn:2">3</a></sup></p>
|
`<p>testing multiple<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup> types<sup class="footnote-ref" id="fnref:inline-note"><a rel="footnote" href="#fn:inline-note">2</a></sup> of notes<sup class="footnote-ref" id="fnref:2"><a rel="footnote" href="#fn:2">3</a></sup></p>
|
||||||
<div class="footnotes">
|
<div class="footnotes">
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -706,13 +741,13 @@ what happens here
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
`This is a footnote[^1]^[and this is an inline footnote]
|
`This is a footnote[^1]^[and this is an inline footnote]
|
||||||
|
|
||||||
[^1]: the footnote text.
|
[^1]: the footnote text.
|
||||||
|
|
||||||
may be multiple paragraphs.
|
may be multiple paragraphs.
|
||||||
`,
|
`,
|
||||||
`<p>This is a footnote<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup><sup class="footnote-ref" id="fnref:and-this-is-an-i"><a rel="footnote" href="#fn:and-this-is-an-i">2</a></sup></p>
|
`<p>This is a footnote<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup><sup class="footnote-ref" id="fnref:and-this-is-an-i"><a rel="footnote" href="#fn:and-this-is-an-i">2</a></sup></p>
|
||||||
<div class="footnotes">
|
<div class="footnotes">
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -727,9 +762,35 @@ what happens here
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
"empty footnote[^]\n\n[^]: fn text",
|
"empty footnote[^]\n\n[^]: fn text",
|
||||||
"<p>empty footnote<sup class=\"footnote-ref\" id=\"fnref:\"><a rel=\"footnote\" href=\"#fn:\">1</a></sup></p>\n<div class=\"footnotes\">\n\n<hr />\n\n<ol>\n<li id=\"fn:\">fn text\n</li>\n</ol>\n</div>\n",
|
"<p>empty footnote<sup class=\"footnote-ref\" id=\"fnref:\"><a rel=\"footnote\" href=\"#fn:\">1</a></sup></p>\n<div class=\"footnotes\">\n\n<hr />\n\n<ol>\n<li id=\"fn:\">fn text\n</li>\n</ol>\n</div>\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFootnotes(t *testing.T) {
|
||||||
|
doTestsInlineParam(t, footnoteTests, EXTENSION_FOOTNOTES, 0, HtmlRendererParameters{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFootnotesWithParameters(t *testing.T) {
|
||||||
|
tests := make([]string, len(footnoteTests))
|
||||||
|
|
||||||
|
prefix := "testPrefix"
|
||||||
|
returnText := "ret"
|
||||||
|
re := regexp.MustCompile(`(?ms)<li id="fn:(\S+?)">(.*?)</li>`)
|
||||||
|
|
||||||
|
// Transform the test expectations to match the parameters we're using.
|
||||||
|
for i, test := range footnoteTests {
|
||||||
|
if i%2 == 1 {
|
||||||
|
test = strings.Replace(test, "fn:", "fn:"+prefix, -1)
|
||||||
|
test = strings.Replace(test, "fnref:", "fnref:"+prefix, -1)
|
||||||
|
test = re.ReplaceAllString(test, `<li id="fn:$1">$2 <a class="footnote-return" href="#fnref:$1">ret</a></li>`)
|
||||||
|
}
|
||||||
|
tests[i] = test
|
||||||
}
|
}
|
||||||
|
|
||||||
doTestsInlineParam(t, tests, EXTENSION_FOOTNOTES, 0)
|
params := HtmlRendererParameters{
|
||||||
|
FootnoteAnchorPrefix: prefix,
|
||||||
|
FootnoteReturnLinkContents: returnText,
|
||||||
|
}
|
||||||
|
|
||||||
|
doTestsInlineParam(t, tests, EXTENSION_FOOTNOTES, HTML_FOOTNOTE_RETURN_LINKS, params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,7 +332,7 @@ func firstPass(p *parser, input []byte) []byte {
|
||||||
// when last line was none blank and a fenced code block comes after
|
// when last line was none blank and a fenced code block comes after
|
||||||
if beg >= lastFencedCodeBlockEnd {
|
if beg >= lastFencedCodeBlockEnd {
|
||||||
// tmp var so we don't modify beyond bounds of `input`
|
// tmp var so we don't modify beyond bounds of `input`
|
||||||
var tmp = make([]byte, len(input[beg:]), len(input[beg:]) + 1)
|
var tmp = make([]byte, len(input[beg:]), len(input[beg:])+1)
|
||||||
copy(tmp, input[beg:])
|
copy(tmp, input[beg:])
|
||||||
if i := p.fencedCode(&out, append(tmp, '\n'), false); i > 0 {
|
if i := p.fencedCode(&out, append(tmp, '\n'), false); i > 0 {
|
||||||
if !lastLineWasBlank {
|
if !lastLineWasBlank {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func doTestsSanitize(t *testing.T, tests []string) {
|
func doTestsSanitize(t *testing.T, tests []string) {
|
||||||
doTestsInlineParam(t, tests, 0, HTML_SKIP_STYLE|HTML_SANITIZE_OUTPUT)
|
doTestsInlineParam(t, tests, 0, HTML_SKIP_STYLE|HTML_SANITIZE_OUTPUT, HtmlRendererParameters{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSanitizeRawHtmlTag(t *testing.T) {
|
func TestSanitizeRawHtmlTag(t *testing.T) {
|
||||||
|
@ -192,8 +192,8 @@ func TestSanitizeInlineLink(t *testing.T) {
|
||||||
tests := []string{
|
tests := []string{
|
||||||
"[link](javascript:evil)",
|
"[link](javascript:evil)",
|
||||||
"<p><a>link</a></p>\n",
|
"<p><a>link</a></p>\n",
|
||||||
"[link](/abc)",
|
"[link](/abc)",
|
||||||
"<p><a href=\"/abc\">link</a></p>\n",
|
"<p><a href=\"/abc\">link</a></p>\n",
|
||||||
}
|
}
|
||||||
doTestsSanitize(t, tests)
|
doTestsSanitize(t, tests)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue