package yatwiki
import (
"encoding/json"
"html"
"html/template"
"net/url"
"regexp"
"strings"
)
const (
bbcodeIsDeleted string = `[delete]`
)
// An embarassing cascade of half-working hacks follows.
type BBCodeRenderer struct {
baseUrl string
CodePresent bool
DynamicContentWarning string
ContentedURL string
ContentedTag string
}
func NewBBCodeRenderer(baseUrl, ContentedURL, ContentedTag string) *BBCodeRenderer {
return &BBCodeRenderer{
baseUrl: baseUrl,
CodePresent: false,
DynamicContentWarning: `⚠ run dynamic content`,
ContentedURL: ContentedURL,
ContentedTag: ContentedTag,
}
}
func (this *BBCodeRenderer) Reset() {
this.CodePresent = false
}
//
type transformation interface {
MatchString(in string) bool
Apply(in string) string
}
type pregReplaceRule struct {
*regexp.Regexp
replace string
}
func (this pregReplaceRule) Apply(in string) string {
return this.ReplaceAllString(in, this.replace)
}
type pregReplaceFunc struct {
*regexp.Regexp
replaceFunc func([]string) string
}
func (this pregReplaceFunc) Apply(in string) string {
return PregReplaceCallback(this.Regexp, this.replaceFunc, in)
}
// Internal part of BBCode rendering.
// It handles most leaf-level tags.
func (this *BBCodeRenderer) bbcode(data string) string {
s_to_r := []transformation{
pregReplaceRule{regexp.MustCompile(`(?si)\[h\](.*?)\[/h\]`), `
$1
`},
pregReplaceRule{regexp.MustCompile(`(?si)\[b\](.*?)\[/b\]`), `$1`},
pregReplaceRule{regexp.MustCompile(`(?si)\[i\](.*?)\[/i\]`), `$1`},
pregReplaceRule{regexp.MustCompile(`(?si)\[u\](.*?)\[/u\]`), `$1`},
pregReplaceRule{regexp.MustCompile(`(?si)\[s\](.*?)\[/s\]`), `$1`},
pregReplaceRule{regexp.MustCompile(`(?si)\[spoiler\](.*?)\[/spoiler\]`), `$1`},
pregReplaceRule{regexp.MustCompile(`(?si)\[img\](.*?)\[/img\]`), ``},
pregReplaceRule{regexp.MustCompile(`(?si)\[list\](.*?)\[/list\]`), ``},
pregReplaceRule{regexp.MustCompile(`(?si)\[\*\]`), ``},
pregReplaceRule{regexp.MustCompile(`(?si)\[url=(.*?)\](.*?)\[/url\]`), `$2`},
pregReplaceFunc{regexp.MustCompile(`(?si)\[article=(.*?)\](.*?)\[/article\]`), func(m []string) string {
return `` + m[2] + ``
}},
pregReplaceFunc{regexp.MustCompile(`(?si)\[rev=(.*?)\](.*?)\[/rev\]`), func(m []string) string {
return `` + m[2] + ``
}},
pregReplaceRule{regexp.MustCompile(`(?si)\[imgur\](.*?)\.(...)\[/imgur\]`),
``,
},
pregReplaceRule{
regexp.MustCompile(`(?si)\[youtube](.*?)\[/youtube\]`),
``,
},
pregReplaceFunc{regexp.MustCompile(`(?si)\[section=(.*?)](.*?)\[/section\]`), func(m []string) string {
return `` + strings.TrimSpace(m[2]) + `
`
}},
}
if len(this.ContentedTag) > 0 {
s_to_r = append(s_to_r,
pregReplaceRule{regexp.MustCompile(`(?si)\[` + regexp.QuoteMeta(this.ContentedTag) + `\](.*?)\[/` + regexp.QuoteMeta(this.ContentedTag) + `\]`),
``,
},
)
}
for _, prr := range s_to_r {
for prr.MatchString(data) { // repeat until all recursive replacements are consumed
data = prr.Apply(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, "")
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, "")
}
for i := 0; i < len(cols); i += 1 {
cols[i] = inner_formatter(strings.Replace(cols[i], ";", "\n", -1))
}
// Add these columns
tbl = append(tbl, ""+strings.Join(cols[1:], " | ")+" |
")
} 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 "" + strings.Replace(m[1], "
\r\n", "\r\n", -1) + "
"
},
strings.Replace(
strings.Replace(this.bbtables(str), "\r\n", "
", -1), //bbtables(bbcode(h($str)))
"
\r\n
",
"
",
-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
}
spos += hpos
ret += this.bbformat(s[hpos : spos-hpos])
spos += 6
epos := strings.Index(s[spos:], "[/html]")
if epos == -1 {
break // no matching [/html] tag found
}
epos += spos
jsonInnerContent, _ := json.Marshal(s[spos:epos])
ret += ``
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))
}