Blackfriday Markdown Processor Available at http://github.com/russross/blackfriday Copyright © 2011 Russ Ross <russ@russross.com>. Distributed under the Simplified BSD License. See README.md for details.
HTML rendering backend

package blackfriday

import (
	
	
	
	
	
)
HTMLFlags control optional behavior of HTML renderer.
HTML renderer configuration options.
const (
	HTMLFlagsNone           HTMLFlags = 0
	SkipHTML                HTMLFlags = 1 << iota // Skip preformatted HTML blocks
	SkipImages                                    // Skip embedded images
	SkipLinks                                     // Skip all links
	Safelink                                      // Only link to trusted protocols
	NofollowLinks                                 // Only link with rel="nofollow"
	NoreferrerLinks                               // Only link with rel="noreferrer"
	NoopenerLinks                                 // Only link with rel="noopener"
	HrefTargetBlank                               // Add a blank target
	CompletePage                                  // Generate a complete HTML page
	UseXHTML                                      // Generate XHTML output instead of HTML
	FootnoteReturnLinks                           // Generate a link at the end of a footnote to return to the source
	Smartypants                                   // Enable smart punctuation substitutions
	SmartypantsFractions                          // Enable smart fractions (with Smartypants)
	SmartypantsDashes                             // Enable smart dashes (with Smartypants)
	SmartypantsLatexDashes                        // Enable LaTeX-style dashes (with Smartypants)
	SmartypantsAngledQuotes                       // Enable angled double quotes (with Smartypants) for double quotes rendering
	SmartypantsQuotesNBSP                         // Enable « French guillemets » (with Smartypants)
	TOC                                           // Generate a table of contents
)

var (
	htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag)
)

const (
	htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" +
		processingInstruction + "|" + declaration + "|" + cdata + ")"
	closeTag              = "</" + tagName + "\\s*[>]"
	openTag               = "<" + tagName + attribute + "*" + "\\s*/?>"
	attribute             = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)"
	attributeValue        = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")"
	attributeValueSpec    = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")"
	attributeName         = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
	cdata                 = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
	declaration           = "<![A-Z]+" + "\\s+[^>]*>"
	doubleQuotedValue     = "\"[^\"]*\""
	htmlComment           = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
	processingInstruction = "[<][?].*?[?][>]"
	singleQuotedValue     = "'[^']*'"
	tagName               = "[A-Za-z][A-Za-z0-9-]*"
	unquotedValue         = "[^\"'=<>`\\x00-\\x20]+"
)
HTMLRendererParameters is a collection of supplementary parameters tweaking the behavior of various parts of HTML renderer.
Prepend this text to each relative URL.
Add this text to each footnote anchor, to ensure uniqueness.
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.
If set, add this text to the front of each Heading ID, to ensure uniqueness.
If set, add this text to the back of each Heading ID, to ensure uniqueness.
Increase heading levels: if the offset is 1, <h1> becomes <h2> etc. Negative offset is also valid. Resulting levels are clipped between 1 and 6.
	HeadingLevelOffset int

	Title string // Document title (used if CompletePage is set)
	CSS   string // Optional CSS file URL (used if CompletePage is set)
	Icon  string // Optional icon file URL (used if CompletePage is set)

	Flags HTMLFlags // Flags allow customizing this renderer's behavior
}
HTMLRenderer is a type that implements the Renderer interface for HTML output. Do not create this directly, instead use the NewHTMLRenderer function.
type HTMLRenderer struct {
	HTMLRendererParameters

	closeTag string // how to end singleton tags: either " />" or ">"
Track heading IDs to prevent ID collision in a single generation.
NewHTMLRenderer creates and configures an HTMLRenderer object, which satisfies the Renderer interface.
configure the rendering engine
	 := htmlClose
	if .Flags&UseXHTML != 0 {
		 = xhtmlClose
	}

	if .FootnoteReturnLinkContents == "" {
		.FootnoteReturnLinkContents = `<sup>[return]</sup>`
	}

	return &HTMLRenderer{
		HTMLRendererParameters: ,

		closeTag:   ,
		headingIDs: make(map[string]int),

		sr: NewSmartypantsRenderer(.Flags),
	}
}

func ( []byte,  string) bool {
	,  := findHTMLTagPos(, )
	return 
}
Look for a character, but ignore it when it's in any kind of quotes, it might be JavaScript
func ( []byte,  int,  byte) int {
	 := false
	 := false
	 := false
	 := 
	for  < len() {
		switch {
		case [] ==  && ! && ! && !:
			return 
		case [] == '\'':
			 = !
		case [] == '"':
			 = !
		case [] == '`':
			 = !
		}
		++
	}
	return 
}

func ( []byte,  string) (bool, int) {
	 := 0
	if  < len() && [0] != '<' {
		return false, -1
	}
	++
	 = skipSpace(, )

	if  < len() && [] == '/' {
		++
	}

	 = skipSpace(, )
	 := 0
	for ;  < len(); ,  = +1, +1 {
		if  >= len() {
			break
		}

		if strings.ToLower(string([]))[0] != [] {
			return false, -1
		}
	}

	if  == len() {
		return false, -1
	}

	 := skipUntilCharIgnoreQuotes(, , '>')
	if  >=  {
		return true, 
	}

	return false, -1
}

func ( []byte,  int) int {
	for  < len() && isspace([]) {
		++
	}
	return 
}

a tag begin with '#'
	if [0] == '#' {
		return true
	}
link begin with '/' but not '//', the second maybe a protocol relative link
	if len() >= 2 && [0] == '/' && [1] != '/' {
		return true
	}
only the root '/'
	if len() == 1 && [0] == '/' {
		return true
	}
current directory : begin with "./"
	if bytes.HasPrefix(, []byte("./")) {
		return true
	}
parent directory : begin with "../"
	if bytes.HasPrefix(, []byte("../")) {
		return true
	}

	return false
}

func ( *HTMLRenderer) ( string) string {
	for ,  := .headingIDs[]; ; ,  = .headingIDs[] {
		 := fmt.Sprintf("%s-%d", , +1)

		if ,  := .headingIDs[]; ! {
			.headingIDs[] =  + 1
			 = 
		} else {
			 =  + "-1"
		}
	}

	if ,  := .headingIDs[]; ! {
		.headingIDs[] = 0
	}

	return 
}

func ( *HTMLRenderer) ( []byte) []byte {
	if .AbsolutePrefix != "" && isRelativeLink() && [0] != '.' {
		 := .AbsolutePrefix
		if [0] != '/' {
			 += "/"
		}
		 += string()
		return []byte()
	}
	return 
}

func ( []string,  HTMLFlags,  []byte) []string {
	if isRelativeLink() {
		return 
	}
	 := []string{}
	if &NofollowLinks != 0 {
		 = append(, "nofollow")
	}
	if &NoreferrerLinks != 0 {
		 = append(, "noreferrer")
	}
	if &NoopenerLinks != 0 {
		 = append(, "noopener")
	}
	if &HrefTargetBlank != 0 {
		 = append(, "target=\"_blank\"")
	}
	if len() == 0 {
		return 
	}
	 := fmt.Sprintf("rel=%q", strings.Join(, " "))
	return append(, )
}

func ( []byte) bool {
	return bytes.HasPrefix(, []byte("mailto:"))
}

func ( HTMLFlags,  []byte) bool {
	if &SkipLinks != 0 {
		return true
	}
	return &Safelink != 0 && !isSafeLink() && !isMailto()
}

func ( *Node) bool {
	 := .Parent.Type
	return  != Link &&  != CodeBlock &&  != Code
}

func ( []string,  []byte) []string {
	if len() == 0 {
		return 
	}
	 := bytes.IndexAny(, "\t ")
	if  < 0 {
		 = len()
	}
	return append(, fmt.Sprintf("class=\"language-%s\"", [:]))
}

func ( *HTMLRenderer) ( io.Writer,  []byte,  []string) {
	.Write()
	if len() > 0 {
		.Write(spaceBytes)
		.Write([]byte(strings.Join(, " ")))
	}
	.Write(gtBytes)
	.lastOutputLen = 1
}

func ( string,  *Node) []byte {
	 :=  + string(slugify(.Destination))
	 := fmt.Sprintf(`<a href="#fn:%s">%d</a>`, , .NoteID)
	return []byte(fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, , ))
}

func ( string,  []byte) []byte {
	return []byte(fmt.Sprintf(`<li id="fn:%s%s">`, , ))
}

func (,  string,  []byte) []byte {
	const  = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
	return []byte(fmt.Sprintf(, , , ))
}

func ( *Node) bool {
	if .Prev == nil {
		return false
	}
	 := .Parent.ListData
	return !.Tight && .ListFlags&ListTypeDefinition == 0
}

func ( *Node) bool {
	 := .Parent.Parent
	if  == nil || .Type != List {
		return false
	}
	 := .Tight || .Parent.ListFlags&ListTypeTerm != 0
	return .Type == List && 
}

func ( CellAlignFlags) string {
	switch  {
	case TableAlignmentLeft:
		return "left"
	case TableAlignmentRight:
		return "right"
	case TableAlignmentCenter:
		return "center"
	default:
		return ""
	}
}

func ( *HTMLRenderer) ( io.Writer,  []byte) {
	if .disableTags > 0 {
		.Write(htmlTagRe.ReplaceAll(, []byte{}))
	} else {
		.Write()
	}
	.lastOutputLen = len()
}

func ( *HTMLRenderer) ( io.Writer) {
	if .lastOutputLen > 0 {
		.out(, nlBytes)
	}
}

var (
	nlBytes    = []byte{'\n'}
	gtBytes    = []byte{'>'}
	spaceBytes = []byte{' '}
)

var (
	brTag              = []byte("<br>")
	brXHTMLTag         = []byte("<br />")
	emTag              = []byte("<em>")
	emCloseTag         = []byte("</em>")
	strongTag          = []byte("<strong>")
	strongCloseTag     = []byte("</strong>")
	delTag             = []byte("<del>")
	delCloseTag        = []byte("</del>")
	ttTag              = []byte("<tt>")
	ttCloseTag         = []byte("</tt>")
	aTag               = []byte("<a")
	aCloseTag          = []byte("</a>")
	preTag             = []byte("<pre>")
	preCloseTag        = []byte("</pre>")
	codeTag            = []byte("<code>")
	codeCloseTag       = []byte("</code>")
	pTag               = []byte("<p>")
	pCloseTag          = []byte("</p>")
	blockquoteTag      = []byte("<blockquote>")
	blockquoteCloseTag = []byte("</blockquote>")
	hrTag              = []byte("<hr>")
	hrXHTMLTag         = []byte("<hr />")
	ulTag              = []byte("<ul>")
	ulCloseTag         = []byte("</ul>")
	olTag              = []byte("<ol>")
	olCloseTag         = []byte("</ol>")
	dlTag              = []byte("<dl>")
	dlCloseTag         = []byte("</dl>")
	liTag              = []byte("<li>")
	liCloseTag         = []byte("</li>")
	ddTag              = []byte("<dd>")
	ddCloseTag         = []byte("</dd>")
	dtTag              = []byte("<dt>")
	dtCloseTag         = []byte("</dt>")
	tableTag           = []byte("<table>")
	tableCloseTag      = []byte("</table>")
	tdTag              = []byte("<td")
	tdCloseTag         = []byte("</td>")
	thTag              = []byte("<th")
	thCloseTag         = []byte("</th>")
	theadTag           = []byte("<thead>")
	theadCloseTag      = []byte("</thead>")
	tbodyTag           = []byte("<tbody>")
	tbodyCloseTag      = []byte("</tbody>")
	trTag              = []byte("<tr>")
	trCloseTag         = []byte("</tr>")
	h1Tag              = []byte("<h1")
	h1CloseTag         = []byte("</h1>")
	h2Tag              = []byte("<h2")
	h2CloseTag         = []byte("</h2>")
	h3Tag              = []byte("<h3")
	h3CloseTag         = []byte("</h3>")
	h4Tag              = []byte("<h4")
	h4CloseTag         = []byte("</h4>")
	h5Tag              = []byte("<h5")
	h5CloseTag         = []byte("</h5>")
	h6Tag              = []byte("<h6")
	h6CloseTag         = []byte("</h6>")

	footnotesDivBytes      = []byte("\n<div class=\"footnotes\">\n\n")
	footnotesCloseDivBytes = []byte("\n</div>\n")
)

func ( int) ([]byte, []byte) {
	if  <= 1 {
		return h1Tag, h1CloseTag
	}
	switch  {
	case 2:
		return h2Tag, h2CloseTag
	case 3:
		return h3Tag, h3CloseTag
	case 4:
		return h4Tag, h4CloseTag
	case 5:
		return h5Tag, h5CloseTag
	}
	return h6Tag, h6CloseTag
}

func ( *HTMLRenderer) ( io.Writer) {
	if .Flags&UseXHTML == 0 {
		.out(, hrTag)
	} else {
		.out(, hrXHTMLTag)
	}
}
RenderNode is a default renderer of a single node of a syntax tree. For block nodes it will be called twice: first time with entering=true, second time with entering=false, so that it could know when it's working on an open tag and when on close. It writes the result to w. The return value is a way to tell the calling walker to adjust its walk pattern: e.g. it can terminate the traversal by returning Terminate. Or it can ask the walker to skip a subtree of this node by returning SkipChildren. The typical behavior is to return GoToNext, which asks for the usual traversal to the next node.
func ( *HTMLRenderer) ( io.Writer,  *Node,  bool) WalkStatus {
	 := []string{}
	switch .Type {
	case Text:
		if .Flags&Smartypants != 0 {
			var  bytes.Buffer
			escapeHTML(&, .Literal)
			.sr.Process(, .Bytes())
		} else {
			if .Parent.Type == Link {
				escLink(, .Literal)
			} else {
				escapeHTML(, .Literal)
			}
		}
	case Softbreak:
TODO: make it configurable via out(renderer.softbreak)
	case Hardbreak:
		if .Flags&UseXHTML == 0 {
			.out(, brTag)
		} else {
			.out(, brXHTMLTag)
		}
		.cr()
	case Emph:
		if  {
			.out(, emTag)
		} else {
			.out(, emCloseTag)
		}
	case Strong:
		if  {
			.out(, strongTag)
		} else {
			.out(, strongCloseTag)
		}
	case Del:
		if  {
			.out(, delTag)
		} else {
			.out(, delCloseTag)
		}
	case HTMLSpan:
		if .Flags&SkipHTML != 0 {
			break
		}
		.out(, .Literal)
mark it but don't link it if it is not a safe link: no smartypants
		 := .LinkData.Destination
		if needSkipLink(.Flags, ) {
			if  {
				.out(, ttTag)
			} else {
				.out(, ttCloseTag)
			}
		} else {
			if  {
				 = .addAbsPrefix()
				var  bytes.Buffer
				.WriteString("href=\"")
				escLink(&, )
				.WriteByte('"')
				 = append(, .String())
				if .NoteID != 0 {
					.out(, footnoteRef(.FootnoteAnchorPrefix, ))
					break
				}
				 = appendLinkAttrs(, .Flags, )
				if len(.LinkData.Title) > 0 {
					var  bytes.Buffer
					.WriteString("title=\"")
					escapeHTML(&, .LinkData.Title)
					.WriteByte('"')
					 = append(, .String())
				}
				.tag(, aTag, )
			} else {
				if .NoteID != 0 {
					break
				}
				.out(, aCloseTag)
			}
		}
	case Image:
		if .Flags&SkipImages != 0 {
			return SkipChildren
		}
		if  {
			 := .LinkData.Destination
			 = .addAbsPrefix()
if options.safe && potentiallyUnsafe(dest) { out(w, `<img src="" alt="`) } else {
				.out(, []byte(`<img src="`))
				escLink(, )
}
			}
			.disableTags++
		} else {
			.disableTags--
			if .disableTags == 0 {
				if .LinkData.Title != nil {
					.out(, []byte(`" title="`))
					escapeHTML(, .LinkData.Title)
				}
				.out(, []byte(`" />`))
			}
		}
	case Code:
		.out(, codeTag)
		escapeHTML(, .Literal)
		.out(, codeCloseTag)
	case Document:
		break
	case Paragraph:
		if skipParagraphTags() {
			break
		}
TODO: untangle this clusterfuck about when the newlines need to be added and when not.
			if .Prev != nil {
				switch .Prev.Type {
				case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule:
					.cr()
				}
			}
			if .Parent.Type == BlockQuote && .Prev == nil {
				.cr()
			}
			.out(, pTag)
		} else {
			.out(, pCloseTag)
			if !(.Parent.Type == Item && .Next == nil) {
				.cr()
			}
		}
	case BlockQuote:
		if  {
			.cr()
			.out(, blockquoteTag)
		} else {
			.out(, blockquoteCloseTag)
			.cr()
		}
	case HTMLBlock:
		if .Flags&SkipHTML != 0 {
			break
		}
		.cr()
		.out(, .Literal)
		.cr()
	case Heading:
		 := .HTMLRendererParameters.HeadingLevelOffset + .Level
		,  := headingTagsFromLevel()
		if  {
			if .IsTitleblock {
				 = append(, `class="title"`)
			}
			if .HeadingID != "" {
				 := .ensureUniqueHeadingID(.HeadingID)
				if .HeadingIDPrefix != "" {
					 = .HeadingIDPrefix + 
				}
				if .HeadingIDSuffix != "" {
					 =  + .HeadingIDSuffix
				}
				 = append(, fmt.Sprintf(`id="%s"`, ))
			}
			.cr()
			.tag(, , )
		} else {
			.out(, )
			if !(.Parent.Type == Item && .Next == nil) {
				.cr()
			}
		}
	case HorizontalRule:
		.cr()
		.outHRTag()
		.cr()
	case List:
		 := ulTag
		 := ulCloseTag
		if .ListFlags&ListTypeOrdered != 0 {
			 = olTag
			 = olCloseTag
		}
		if .ListFlags&ListTypeDefinition != 0 {
			 = dlTag
			 = dlCloseTag
		}
		if  {
			if .IsFootnotesList {
				.out(, footnotesDivBytes)
				.outHRTag()
				.cr()
			}
			.cr()
			if .Parent.Type == Item && .Parent.Parent.Tight {
				.cr()
			}
			.tag(, [:len()-1], )
			.cr()
		} else {
cr(w) if node.parent.Type != Item { cr(w) }
			if .Parent.Type == Item && .Next != nil {
				.cr()
			}
			if .Parent.Type == Document || .Parent.Type == BlockQuote {
				.cr()
			}
			if .IsFootnotesList {
				.out(, footnotesCloseDivBytes)
			}
		}
	case Item:
		 := liTag
		 := liCloseTag
		if .ListFlags&ListTypeDefinition != 0 {
			 = ddTag
			 = ddCloseTag
		}
		if .ListFlags&ListTypeTerm != 0 {
			 = dtTag
			 = dtCloseTag
		}
		if  {
			if itemOpenCR() {
				.cr()
			}
			if .ListData.RefLink != nil {
				 := slugify(.ListData.RefLink)
				.out(, footnoteItem(.FootnoteAnchorPrefix, ))
				break
			}
			.out(, )
		} else {
			if .ListData.RefLink != nil {
				 := slugify(.ListData.RefLink)
				if .Flags&FootnoteReturnLinks != 0 {
					.out(, footnoteReturnLink(.FootnoteAnchorPrefix, .FootnoteReturnLinkContents, ))
				}
			}
			.out(, )
			.cr()
		}
	case CodeBlock:
		 = appendLanguageAttr(, .Info)
		.cr()
		.out(, preTag)
		.tag(, codeTag[:len(codeTag)-1], )
		escapeHTML(, .Literal)
		.out(, codeCloseTag)
		.out(, preCloseTag)
		if .Parent.Type != Item {
			.cr()
		}
	case Table:
		if  {
			.cr()
			.out(, tableTag)
		} else {
			.out(, tableCloseTag)
			.cr()
		}
	case TableCell:
		 := tdTag
		 := tdCloseTag
		if .IsHeader {
			 = thTag
			 = thCloseTag
		}
		if  {
			 := cellAlignment(.Align)
			if  != "" {
				 = append(, fmt.Sprintf(`align="%s"`, ))
			}
			if .Prev == nil {
				.cr()
			}
			.tag(, , )
		} else {
			.out(, )
			.cr()
		}
	case TableHead:
		if  {
			.cr()
			.out(, theadTag)
		} else {
			.out(, theadCloseTag)
			.cr()
		}
	case TableBody:
		if  {
			.cr()
XXX: this is to adhere to a rather silly test. Should fix test.
			if .FirstChild == nil {
				.cr()
			}
		} else {
			.out(, tbodyCloseTag)
			.cr()
		}
	case TableRow:
		if  {
			.cr()
			.out(, trTag)
		} else {
			.out(, trCloseTag)
			.cr()
		}
	default:
		panic("Unknown node type " + .Type.String())
	}
	return GoToNext
}
RenderHeader writes HTML document preamble and TOC if requested.
func ( *HTMLRenderer) ( io.Writer,  *Node) {
	.writeDocumentHeader()
	if .Flags&TOC != 0 {
		.writeTOC(, )
	}
}
RenderFooter writes HTML document footer.
func ( *HTMLRenderer) ( io.Writer,  *Node) {
	if .Flags&CompletePage == 0 {
		return
	}
	io.WriteString(, "\n</body>\n</html>\n")
}

func ( *HTMLRenderer) ( io.Writer) {
	if .Flags&CompletePage == 0 {
		return
	}
	 := ""
	if .Flags&UseXHTML != 0 {
		io.WriteString(, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
		io.WriteString(, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
		io.WriteString(, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
		 = " /"
	} else {
		io.WriteString(, "<!DOCTYPE html>\n")
		io.WriteString(, "<html>\n")
	}
	io.WriteString(, "<head>\n")
	io.WriteString(, "  <title>")
	if .Flags&Smartypants != 0 {
		.sr.Process(, []byte(.Title))
	} else {
		escapeHTML(, []byte(.Title))
	}
	io.WriteString(, "</title>\n")
	io.WriteString(, "  <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
	io.WriteString(, Version)
	io.WriteString(, "\"")
	io.WriteString(, )
	io.WriteString(, ">\n")
	io.WriteString(, "  <meta charset=\"utf-8\"")
	io.WriteString(, )
	io.WriteString(, ">\n")
	if .CSS != "" {
		io.WriteString(, "  <link rel=\"stylesheet\" type=\"text/css\" href=\"")
		escapeHTML(, []byte(.CSS))
		io.WriteString(, "\"")
		io.WriteString(, )
		io.WriteString(, ">\n")
	}
	if .Icon != "" {
		io.WriteString(, "  <link rel=\"icon\" type=\"image/x-icon\" href=\"")
		escapeHTML(, []byte(.Icon))
		io.WriteString(, "\"")
		io.WriteString(, )
		io.WriteString(, ">\n")
	}
	io.WriteString(, "</head>\n")
	io.WriteString(, "<body>\n\n")
}

func ( *HTMLRenderer) ( io.Writer,  *Node) {
	 := bytes.Buffer{}

	 := false
	 := 0
	 := 0

	.Walk(func( *Node,  bool) WalkStatus {
		if .Type == Heading && !.HeadingData.IsTitleblock {
			 = 
			if  {
				.HeadingID = fmt.Sprintf("toc_%d", )
				if .Level ==  {
					.WriteString("</li>\n\n<li>")
				} else if .Level <  {
					for .Level <  {
						--
						.WriteString("</li>\n</ul>")
					}
					.WriteString("</li>\n\n<li>")
				} else {
					for .Level >  {
						++
						.WriteString("\n<ul>\n<li>")
					}
				}

				fmt.Fprintf(&, `<a href="#toc_%d">`, )
				++
			} else {
				.WriteString("</a>")
			}
			return GoToNext
		}

		if  {
			return .RenderNode(&, , )
		}

		return GoToNext
	})

	for ;  > 0; -- {
		.WriteString("</li>\n</ul>")
	}

	if .Len() > 0 {
		io.WriteString(, "<nav>\n")
		.Write(.Bytes())
		io.WriteString(, "\n\n</nav>\n")
	}
	.lastOutputLen = .Len()