116 lines
3.5 KiB
Go
116 lines
3.5 KiB
Go
// markup.go
|
|
// memeposting markup parser
|
|
//
|
|
package srnd
|
|
|
|
import (
|
|
"github.com/mvdan/xurls"
|
|
"html"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// copypasted from https://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url
|
|
// var re_external_link = regexp.MustCompile(`((?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?)`);
|
|
var re_external_link = xurls.Strict
|
|
var re_backlink = regexp.MustCompile(`>> ?([0-9a-f]+)`)
|
|
var re_boardlink = regexp.MustCompile(`>>> ?/([0-9a-zA-Z\.]+)/`)
|
|
var re_nntpboardlink = regexp.MustCompile(`news:([0-9a-zA-Z\.]+)`)
|
|
|
|
// find all backlinks in string
|
|
func findBacklinks(msg string) (cites []string) {
|
|
re := re_backlink.Copy()
|
|
cmap := make(map[string]string)
|
|
for _, cite := range re.FindAllString(msg, -1) {
|
|
cmap[cite] = cite
|
|
}
|
|
for _, c := range cmap {
|
|
cites = append(cites, c)
|
|
}
|
|
return
|
|
}
|
|
|
|
// parse backlink
|
|
func backlink(word, prefix string) (markup string) {
|
|
re := re_backlink.Copy()
|
|
link := re.FindString(word)
|
|
if len(link) > 2 {
|
|
link = strings.Trim(link[2:], " ")
|
|
if len(link) > 2 {
|
|
url := template.findLink(prefix, link)
|
|
if len(url) == 0 {
|
|
return "<span class='memearrows'>>>" + link + "</span>"
|
|
}
|
|
// backlink exists
|
|
parts := strings.Split(url, "#")
|
|
longhash := ""
|
|
if len(parts) > 1 {
|
|
longhash = parts[1]
|
|
}
|
|
return `<a class='backlink' data-backlinkhash="` + longhash + `" href="` + url + `">>>` + link + "</a>"
|
|
} else {
|
|
return escapeline(word)
|
|
}
|
|
}
|
|
return escapeline(word)
|
|
}
|
|
|
|
func boardlink(word, prefix string, r *regexp.Regexp) (markup string) {
|
|
re := r.Copy()
|
|
l := re.FindStringSubmatch(word)
|
|
if len(l[1]) > 2 {
|
|
link := strings.ToLower(l[1])
|
|
markup = `<a class="boardlink" href="` + prefix + "b/" + link + `">>>>/` + link + `/</a>`
|
|
return
|
|
}
|
|
markup = escapeline(word)
|
|
return
|
|
}
|
|
|
|
func escapeline(line string) (markup string) {
|
|
markup = html.EscapeString(line)
|
|
return
|
|
}
|
|
|
|
func formatline(line, prefix string) (markup string) {
|
|
if len(line) > 0 {
|
|
line_nospace := strings.Trim(line, " ")
|
|
if strings.HasPrefix(line_nospace, ">") && !strings.HasPrefix(line_nospace, ">>") {
|
|
// le ebin meme arrows
|
|
markup += "<span class='memearrows'>"
|
|
markup += escapeline(line)
|
|
markup += "</span>"
|
|
} else if strings.HasPrefix(line_nospace, "<") {
|
|
markup += "<span class='fagarrows'>"
|
|
markup += escapeline(line)
|
|
markup += "</span>"
|
|
} else {
|
|
// regular line
|
|
// for each word
|
|
for _, word := range strings.Split(line, " ") {
|
|
if re_boardlink.MatchString(word) {
|
|
markup += boardlink(word, prefix, re_boardlink)
|
|
} else if re_nntpboardlink.MatchString(word) {
|
|
markup += boardlink(word, prefix, re_nntpboardlink)
|
|
} else if re_backlink.MatchString(word) {
|
|
markup += backlink(word, prefix)
|
|
} else {
|
|
// linkify as needed
|
|
word = escapeline(word)
|
|
markup += re_external_link.ReplaceAllString(word, `<a href="$1">$1</a>`)
|
|
}
|
|
markup += " "
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func MEMEPosting(src, prefix string) (markup string) {
|
|
for _, line := range strings.Split(src, "\n") {
|
|
line = strings.Trim(line, "\r")
|
|
markup += formatline(line, prefix) + "\n"
|
|
}
|
|
return extraMemePosting(markup, prefix)
|
|
}
|