"},
}
var listItemStatuses = map[string]string{
" ": "unchecked",
"-": "indeterminate",
"X": "checked",
}
var cleanHeadlineTitleForHTMLAnchorRegexp = regexp.MustCompile(`?a[^>]*>`) // nested a tags are not valid HTML
func NewHTMLWriter() *HTMLWriter {
return &HTMLWriter{
htmlEscape: true,
HighlightCodeBlock: func(source, lang string) string {
return fmt.Sprintf("%s\n
\n%s\n
\n", `
`, html.EscapeString(source))
},
}
}
func (w *HTMLWriter) emptyClone() *HTMLWriter {
wcopy := *w
wcopy.stringBuilder = stringBuilder{}
return &wcopy
}
func (w *HTMLWriter) nodesAsString(nodes ...Node) string {
tmp := w.emptyClone()
tmp.writeNodes(nodes...)
return tmp.String()
}
func (w *HTMLWriter) before(d *Document) {
w.document = d
w.log = d.Log
w.writeOutline(d)
}
func (w *HTMLWriter) after(d *Document) {
w.writeFootnotes(d)
}
func (w *HTMLWriter) writeNodes(ns ...Node) {
for _, n := range ns {
switch n := n.(type) {
case Keyword:
w.writeKeyword(n)
case Include:
w.writeInclude(n)
case Comment:
continue
case NodeWithMeta:
w.writeNodeWithMeta(n)
case Headline:
w.writeHeadline(n)
case Block:
w.writeBlock(n)
case Drawer:
w.writeDrawer(n)
case PropertyDrawer:
continue
case FootnoteDefinition:
continue
case List:
w.writeList(n)
case ListItem:
w.writeListItem(n)
case DescriptiveListItem:
w.writeDescriptiveListItem(n)
case Table:
w.writeTable(n)
case Paragraph:
w.writeParagraph(n)
case Example:
w.writeExample(n)
case HorizontalRule:
w.writeHorizontalRule(n)
case Text:
w.writeText(n)
case Emphasis:
w.writeEmphasis(n)
case StatisticToken:
w.writeStatisticToken(n)
case ExplicitLineBreak:
w.writeExplicitLineBreak(n)
case LineBreak:
w.writeLineBreak(n)
case RegularLink:
w.writeRegularLink(n)
case FootnoteLink:
w.writeFootnoteLink(n)
default:
if n != nil {
panic(fmt.Sprintf("bad node %#v", n))
}
}
}
}
func (w *HTMLWriter) writeBlock(b Block) {
content := ""
if isRawTextBlock(b.Name) {
exportWriter := w.emptyClone()
exportWriter.htmlEscape = false
exportWriter.writeNodes(b.Children...)
content = strings.TrimRightFunc(exportWriter.String(), unicode.IsSpace)
} else {
content = w.nodesAsString(b.Children...)
}
switch name := b.Name; {
case name == "SRC":
lang := "text"
if len(b.Parameters) >= 1 {
lang = strings.ToLower(b.Parameters[0])
}
w.WriteString(w.HighlightCodeBlock(content, lang) + "\n")
case name == "EXAMPLE":
w.WriteString(`
` + "\n" + content + "\n
\n")
case name == "EXPORT" && len(b.Parameters) >= 1 && strings.ToLower(b.Parameters[0]) == "html":
w.WriteString(content + "\n")
case name == "QUOTE":
w.WriteString("
` + "\n")
for _, definition := range d.Footnotes.Ordered() {
w.writeFootnoteDefinition(definition)
}
w.WriteString("
\n
\n")
}
func (w *HTMLWriter) writeOutline(d *Document) {
if w.document.GetOption("toc") && len(d.Outline.Children) != 0 {
w.WriteString("\n")
}
}
func (w *HTMLWriter) writeSection(section *Section) {
// NOTE: To satisfy hugo ExtractTOC() check we cannot use `
\n` here. Doesn't really matter, just a note.
w.WriteString("
")
h := section.Headline
title := cleanHeadlineTitleForHTMLAnchorRegexp.ReplaceAllString(w.nodesAsString(h.Title...), "")
w.WriteString(fmt.Sprintf("%s\n", h.ID(), title))
if len(section.Children) != 0 {
w.WriteString("
\n")
for _, section := range section.Children {
w.writeSection(section)
}
w.WriteString("
\n")
}
w.WriteString("
\n")
}
func (w *HTMLWriter) writeHeadline(h Headline) {
for _, excludeTag := range strings.Fields(w.document.Get("EXCLUDE_TAGS")) {
for _, tag := range h.Tags {
if excludeTag == tag {
return
}
}
}
w.WriteString(fmt.Sprintf(``, h.Lvl, h.ID()) + "\n")
if w.document.GetOption("todo") && h.Status != "" {
w.WriteString(fmt.Sprintf(`%s`, h.Status) + "\n")
}
if w.document.GetOption("pri") && h.Priority != "" {
w.WriteString(fmt.Sprintf(`[%s]`, h.Priority) + "\n")
}
w.writeNodes(h.Title...)
if w.document.GetOption("tags") && len(h.Tags) != 0 {
tags := make([]string, len(h.Tags))
for i, tag := range h.Tags {
tags[i] = fmt.Sprintf(`%s`, tag)
}
w.WriteString(" ")
w.WriteString(fmt.Sprintf(`%s`, strings.Join(tags, " ")))
}
w.WriteString(fmt.Sprintf("\n\n", h.Lvl))
w.writeNodes(h.Children...)
}
func (w *HTMLWriter) writeText(t Text) {
if !w.htmlEscape {
w.WriteString(t.Content)
} else if !w.document.GetOption("e") || t.IsRaw {
w.WriteString(html.EscapeString(t.Content))
} else {
w.WriteString(html.EscapeString(htmlEntityReplacer.Replace(t.Content)))
}
}
func (w *HTMLWriter) writeEmphasis(e Emphasis) {
tags, ok := emphasisTags[e.Kind]
if !ok {
panic(fmt.Sprintf("bad emphasis %#v", e))
}
w.WriteString(tags[0])
w.writeNodes(e.Content...)
w.WriteString(tags[1])
}
func (w *HTMLWriter) writeStatisticToken(s StatisticToken) {
w.WriteString(fmt.Sprintf(`[%s]`, s.Content))
}
func (w *HTMLWriter) writeLineBreak(l LineBreak) {
w.WriteString(strings.Repeat("\n", l.Count))
}
func (w *HTMLWriter) writeExplicitLineBreak(l ExplicitLineBreak) {
w.WriteString(" \n")
}
func (w *HTMLWriter) writeFootnoteLink(l FootnoteLink) {
if !w.document.GetOption("f") {
return
}
n := html.EscapeString(l.Name)
w.WriteString(fmt.Sprintf(`%s`, n, n, n))
}
func (w *HTMLWriter) writeRegularLink(l RegularLink) {
url := html.EscapeString(l.URL)
if l.Protocol == "file" {
url = url[len("file:"):]
}
description := url
if l.Description != nil {
description = w.nodesAsString(l.Description...)
}
switch l.Kind() {
case "image":
w.WriteString(fmt.Sprintf(``, url, description, description))
case "video":
w.WriteString(fmt.Sprintf(``, url, description, description))
default:
w.WriteString(fmt.Sprintf(`%s`, url, description))
}
}
func (w *HTMLWriter) writeList(l List) {
tags, ok := listTags[l.Kind]
if !ok {
panic(fmt.Sprintf("bad list kind %#v", l))
}
w.WriteString(tags[0] + "\n")
w.writeNodes(l.Items...)
w.WriteString(tags[1] + "\n")
}
func (w *HTMLWriter) writeListItem(li ListItem) {
if li.Status != "" {
w.WriteString(fmt.Sprintf("