2020-07-28 19:38:12 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2020-10-18 10:10:00 +00:00
|
|
|
"strings"
|
|
|
|
|
2020-09-18 11:11:25 +00:00
|
|
|
kemoji "github.com/kyokomi/emoji"
|
2020-07-28 19:38:12 +00:00
|
|
|
"github.com/yuin/goldmark"
|
2020-10-18 10:10:00 +00:00
|
|
|
emoji "github.com/yuin/goldmark-emoji"
|
2020-09-18 10:34:52 +00:00
|
|
|
"github.com/yuin/goldmark-emoji/definition"
|
2020-09-21 14:53:20 +00:00
|
|
|
"github.com/yuin/goldmark/ast"
|
2020-07-28 19:38:12 +00:00
|
|
|
"github.com/yuin/goldmark/extension"
|
|
|
|
"github.com/yuin/goldmark/parser"
|
2020-09-21 14:53:20 +00:00
|
|
|
"github.com/yuin/goldmark/renderer"
|
2020-07-28 19:38:12 +00:00
|
|
|
"github.com/yuin/goldmark/renderer/html"
|
2020-09-21 14:53:20 +00:00
|
|
|
"github.com/yuin/goldmark/util"
|
2020-07-28 19:38:12 +00:00
|
|
|
)
|
|
|
|
|
2020-09-18 11:11:25 +00:00
|
|
|
var emojilib definition.Emojis
|
|
|
|
|
2020-07-28 19:38:12 +00:00
|
|
|
var markdown goldmark.Markdown
|
|
|
|
|
2020-08-24 19:09:30 +00:00
|
|
|
func initMarkdown() {
|
2020-07-28 19:38:12 +00:00
|
|
|
markdown = goldmark.New(
|
|
|
|
goldmark.WithRendererOptions(
|
|
|
|
html.WithUnsafe(),
|
|
|
|
),
|
|
|
|
goldmark.WithParserOptions(
|
|
|
|
parser.WithAutoHeadingID(),
|
|
|
|
),
|
|
|
|
goldmark.WithExtensions(
|
2020-10-18 10:10:00 +00:00
|
|
|
extension.Table,
|
|
|
|
extension.Strikethrough,
|
2020-07-28 19:38:12 +00:00
|
|
|
extension.Footnote,
|
|
|
|
extension.Typographer,
|
2020-11-28 08:02:12 +00:00
|
|
|
extension.Linkify,
|
2020-09-18 10:34:52 +00:00
|
|
|
// Emojis
|
|
|
|
emoji.New(
|
2020-09-21 14:53:20 +00:00
|
|
|
emoji.WithEmojis(emojiGoLib()),
|
2020-09-18 10:34:52 +00:00
|
|
|
),
|
2020-12-14 21:05:54 +00:00
|
|
|
// Custom
|
|
|
|
&customExtension{},
|
2020-07-28 19:38:12 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-12-14 21:05:54 +00:00
|
|
|
func renderMarkdown(source string) ([]byte, error) {
|
2020-07-28 19:38:12 +00:00
|
|
|
var buffer bytes.Buffer
|
2020-12-14 21:05:54 +00:00
|
|
|
err := markdown.Convert([]byte(source), &buffer)
|
|
|
|
return buffer.Bytes(), err
|
2020-07-28 19:38:12 +00:00
|
|
|
}
|
2020-09-18 11:11:25 +00:00
|
|
|
|
2020-09-21 14:53:20 +00:00
|
|
|
// Extensions etc...
|
|
|
|
|
|
|
|
// All emojis from emoji lib
|
|
|
|
func emojiGoLib() definition.Emojis {
|
2020-12-14 21:05:54 +00:00
|
|
|
if emojilib == nil {
|
2020-09-18 11:11:25 +00:00
|
|
|
var emojis []definition.Emoji
|
|
|
|
for shotcode, e := range kemoji.CodeMap() {
|
|
|
|
emojis = append(emojis, definition.NewEmoji(e, []rune(e), strings.ReplaceAll(shotcode, ":", "")))
|
|
|
|
}
|
|
|
|
emojilib = definition.NewEmojis(emojis...)
|
2020-12-14 21:05:54 +00:00
|
|
|
}
|
2020-09-18 11:11:25 +00:00
|
|
|
return emojilib
|
|
|
|
}
|
2020-09-21 14:53:20 +00:00
|
|
|
|
|
|
|
// Links
|
2020-09-21 15:05:50 +00:00
|
|
|
type customExtension struct{}
|
2020-09-21 14:53:20 +00:00
|
|
|
|
2020-09-21 15:05:50 +00:00
|
|
|
func (l *customExtension) Extend(m goldmark.Markdown) {
|
2020-09-21 14:53:20 +00:00
|
|
|
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
2020-12-14 21:05:54 +00:00
|
|
|
util.Prioritized(&customRenderer{}, 500),
|
2020-09-21 14:53:20 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-09-21 15:05:50 +00:00
|
|
|
type customRenderer struct{}
|
2020-09-21 14:53:20 +00:00
|
|
|
|
2020-09-21 15:05:50 +00:00
|
|
|
func (c *customRenderer) RegisterFuncs(r renderer.NodeRendererFuncRegisterer) {
|
|
|
|
r.Register(ast.KindLink, c.renderLink)
|
|
|
|
r.Register(ast.KindImage, c.renderImage)
|
2020-09-21 14:53:20 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 15:05:50 +00:00
|
|
|
func (c *customRenderer) renderLink(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
2020-09-21 14:53:20 +00:00
|
|
|
if entering {
|
2020-12-14 21:05:54 +00:00
|
|
|
n := node.(*ast.Link)
|
|
|
|
_, _ = w.WriteString("<a href=\"")
|
2020-09-21 14:53:20 +00:00
|
|
|
// Make URL absolute if it's relative
|
2020-12-14 21:05:54 +00:00
|
|
|
newDestination := util.URLEscape(n.Destination, true)
|
|
|
|
if bytes.HasPrefix(newDestination, []byte("/")) {
|
|
|
|
_, _ = w.Write(util.EscapeHTML([]byte(appConfig.Server.PublicAddress)))
|
2020-09-21 14:53:20 +00:00
|
|
|
}
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.Write(util.EscapeHTML(newDestination))
|
|
|
|
_, _ = w.WriteRune('"')
|
2020-09-21 14:53:20 +00:00
|
|
|
// Open external links (links that start with "http") in new tab
|
2020-12-14 21:05:54 +00:00
|
|
|
if bytes.HasPrefix(n.Destination, []byte("http")) {
|
2020-09-21 14:53:20 +00:00
|
|
|
_, _ = w.WriteString(` target="_blank" rel="noopener"`)
|
|
|
|
}
|
|
|
|
// Title
|
|
|
|
if n.Title != nil {
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.WriteString(" title=\"")
|
2020-09-21 14:53:20 +00:00
|
|
|
_, _ = w.Write(n.Title)
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.WriteRune('"')
|
2020-09-21 14:53:20 +00:00
|
|
|
}
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.WriteRune('>')
|
2020-09-21 14:53:20 +00:00
|
|
|
} else {
|
|
|
|
_, _ = w.WriteString("</a>")
|
|
|
|
}
|
|
|
|
return ast.WalkContinue, nil
|
|
|
|
}
|
|
|
|
|
2020-09-21 15:05:50 +00:00
|
|
|
func (c *customRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
|
|
|
if !entering {
|
|
|
|
return ast.WalkContinue, nil
|
|
|
|
}
|
|
|
|
n := node.(*ast.Image)
|
|
|
|
// Make URL absolute if it's relative
|
2020-12-14 21:05:54 +00:00
|
|
|
destination := util.URLEscape(n.Destination, true)
|
|
|
|
if bytes.HasPrefix(destination, []byte("/")) {
|
|
|
|
destination = util.EscapeHTML(append([]byte(appConfig.Server.PublicAddress), destination...))
|
|
|
|
} else {
|
|
|
|
destination = util.EscapeHTML(destination)
|
2020-09-21 15:05:50 +00:00
|
|
|
}
|
|
|
|
_, _ = w.WriteString("<a href=\"")
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.Write(destination)
|
2020-09-21 15:05:50 +00:00
|
|
|
_, _ = w.WriteString("\">")
|
|
|
|
_, _ = w.WriteString("<img src=\"")
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.Write(destination)
|
|
|
|
_, _ = w.WriteString("\" alt=\"")
|
2020-09-21 15:05:50 +00:00
|
|
|
_, _ = w.Write(util.EscapeHTML(n.Text(source)))
|
|
|
|
_ = w.WriteByte('"')
|
|
|
|
_, _ = w.WriteString(" loading=\"lazy\"")
|
|
|
|
if n.Title != nil {
|
2020-12-14 21:05:54 +00:00
|
|
|
_, _ = w.WriteString(" title=\"")
|
2020-09-21 15:05:50 +00:00
|
|
|
_, _ = w.Write(n.Title)
|
|
|
|
_ = w.WriteByte('"')
|
|
|
|
}
|
|
|
|
_, _ = w.WriteString("></a>")
|
|
|
|
return ast.WalkSkipChildren, nil
|
|
|
|
}
|