2017-07-09 00:13:16 +00:00
|
|
|
package yatwiki3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"html/template"
|
2017-07-09 01:00:11 +00:00
|
|
|
"net/url"
|
2017-07-09 00:13:16 +00:00
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// An embarassing cascade of half-working hacks follows.
|
|
|
|
type BBCodeRenderer struct {
|
|
|
|
baseUrl string
|
|
|
|
CodePresent bool
|
|
|
|
DynamicContentWarning string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewBBCodeRenderer(baseUrl string) *BBCodeRenderer {
|
|
|
|
return &BBCodeRenderer{
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
CodePresent: false,
|
|
|
|
DynamicContentWarning: `⚠ run dynamic content`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type pregReplaceRule struct {
|
|
|
|
match *regexp.Regexp
|
|
|
|
replace string
|
|
|
|
replaceFunc func([]string) string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal part of BBCode rendering.
|
|
|
|
// It handles most leaf-level tags.
|
|
|
|
func (this *BBCodeRenderer) bbcode(data string) string {
|
|
|
|
|
|
|
|
s_to_r := []pregReplaceRule{
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[h\](.*?)\[/h\]`), `<h2>$1</h2>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[b\](.*?)\[/b\]`), `<b>$1</b>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[i\](.*?)\[/i\]`), `<i>$1</i>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[u\](.*?)\[/u\]`), `<u>$1</u>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[s\](.*?)\[/s\]`), `<span class="s">$1</span>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[spoiler\](.*?)\[/spoiler\]`), `<span class="spoiler">$1</span>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[img\](.*?)\[/img\]`), `<img alt="" src="$1">`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[list\](.*?)\[/list\]`), `<ul><li>$1</li></ul>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[\*\]`), `</li><li>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[url=(.*?)\](.*?)\[/url\]`), `<a rel="noreferrer" href="$1">$2</a>`, nil},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[article=(.*?)\](.*?)\[/article\]`), "", func(m []string) string {
|
2017-07-09 01:00:11 +00:00
|
|
|
return `<a href="` + template.HTMLEscapeString(this.baseUrl+`view/`+url.QueryEscape(m[1])) + `">` + m[2] + `</a>`
|
2017-07-09 00:13:16 +00:00
|
|
|
}},
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[rev=(.*?)\](.*?)\[/rev\]`), "", func(m []string) string {
|
2017-07-09 01:00:11 +00:00
|
|
|
return `<a href="` + template.HTMLEscapeString(this.baseUrl+`archive/`+url.QueryEscape(m[1])) + `">` + m[2] + `</a>`
|
2017-07-09 00:13:16 +00:00
|
|
|
}},
|
|
|
|
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[imgur\](.*?)\.(...)\[/imgur\]`),
|
|
|
|
`<a href="https://i.imgur.com/${1}.${2}"><img class="imgur" alt="" src="https://i.imgur.com/${1}s.${2}" ></a>`,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
|
|
|
pregReplaceRule{regexp.MustCompile(`(?si)\[section=(.*?)](.*?)\[/section\]`), "", func(m []string) string {
|
|
|
|
return `<div class="section"><a class="sectionheader" href="javascript:;" onclick="ts(this);">` + m[1] + `</a><span style="display:none;">` + strings.TrimSpace(m[2]) + `</span></div>`
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, prr := range s_to_r {
|
|
|
|
|
|
|
|
for prr.match.MatchString(data) { // repeat until all recursive replacements are consumed
|
|
|
|
if len(prr.replace) > 0 {
|
|
|
|
data = prr.match.ReplaceAllString(data, prr.replace)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
data = PregReplaceCallback(prr.match, prr.replaceFunc, data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal part of BBCode rendering.
|
|
|
|
// It extracts tables and then passes the remainder to bbcode().
|
|
|
|
func (this *BBCodeRenderer) bbtables(s string) string {
|
|
|
|
lines := strings.Split(s, "\n")
|
|
|
|
lastct := 0
|
|
|
|
|
|
|
|
ret := make([]string, 0)
|
|
|
|
tbl := make([]string, 0)
|
|
|
|
buffer := make([]string, 0)
|
|
|
|
|
|
|
|
maybe_close_previous_table := func() {
|
|
|
|
if lastct > 0 { // Close previous table
|
|
|
|
tbl = append(tbl, "</table>")
|
|
|
|
ret = append(ret, strings.Join(tbl, ""))
|
|
|
|
tbl = []string{}
|
|
|
|
lastct = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inner_formatter := func(s string) string {
|
|
|
|
return this.bbcode(template.HTMLEscapeString(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
flush_buffer := func() {
|
|
|
|
if len(buffer) > 0 {
|
|
|
|
ret = append(ret, inner_formatter(strings.Join(buffer, "\n")))
|
|
|
|
buffer = []string{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
if len(line) > 0 && line[0] == '|' {
|
|
|
|
flush_buffer()
|
|
|
|
|
|
|
|
cols := strings.Split(line, "|")
|
|
|
|
if lastct != len(cols) {
|
|
|
|
maybe_close_previous_table()
|
|
|
|
|
|
|
|
// Start new table
|
|
|
|
lastct = len(cols)
|
|
|
|
tbl = append(tbl, "<table class=ti>")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(cols); i += 1 {
|
|
|
|
cols[i] = inner_formatter(strings.Replace(cols[i], ";", "\n", -1))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add these columns
|
|
|
|
tbl = append(tbl, "<tr><td>"+strings.Join(cols[1:], "</td><td>")+"</td></tr>")
|
|
|
|
|
|
|
|
} else {
|
|
|
|
maybe_close_previous_table()
|
|
|
|
buffer = append(buffer, line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flush_buffer()
|
|
|
|
maybe_close_previous_table()
|
|
|
|
|
|
|
|
return strings.Join(ret, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal part of BBCode rendering.
|
|
|
|
// It extracts [code] sections and then passes the remainder to bbtables().
|
|
|
|
func (this *BBCodeRenderer) bbformat(str string) string {
|
|
|
|
return PregReplaceCallback(
|
|
|
|
regexp.MustCompile(`(?si)\[code\](.*?)\[/code\]`),
|
|
|
|
func(m []string) string {
|
|
|
|
this.CodePresent = true
|
|
|
|
return "<pre><code>" + strings.Replace(m[1], "<br>\r\n", "\r\n", -1) + "</code></pre>"
|
|
|
|
},
|
|
|
|
strings.Replace(
|
|
|
|
strings.Replace(this.bbtables(str), "\r\n", "<br>", -1), //bbtables(bbcode(h($str)))
|
|
|
|
"</ul><br>\r\n<br>",
|
|
|
|
"</ul><br>",
|
|
|
|
-1, // replace all
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal part of BBCode rendering.
|
|
|
|
// It extracts [html] sections and then passes the remainder to bbformat().
|
|
|
|
func (this *BBCodeRenderer) displayfmt(s string) string {
|
|
|
|
hpos := 0
|
|
|
|
ret := ""
|
|
|
|
|
|
|
|
for {
|
|
|
|
spos := strings.Index(s[hpos:], "[html]")
|
|
|
|
if spos == -1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
ret += this.bbformat(s[hpos : spos-hpos])
|
|
|
|
spos += 6
|
|
|
|
|
|
|
|
epos := strings.Index(s[spos:], "[/html]")
|
|
|
|
if epos == -1 {
|
|
|
|
break // no matching [/html] tag found
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonInnerContent, _ := json.Marshal(s[spos : epos-spos])
|
|
|
|
|
|
|
|
ret += `<div class="html"><a href="javascript:;" onclick="` + template.HTMLEscapeString(`els(this, `+string(jsonInnerContent)+`);`) + `">` + this.DynamicContentWarning + `</a></div>`
|
|
|
|
hpos = epos + 7
|
|
|
|
}
|
|
|
|
return ret + this.bbformat(s[hpos:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenderHTML converts the input string to HTML.
|
|
|
|
func (this *BBCodeRenderer) RenderHTML(s string) template.HTML {
|
|
|
|
return template.HTML(this.displayfmt(s))
|
|
|
|
}
|