package yatwiki3 import ( "encoding/json" "html/template" "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\]`), `

$1

`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[b\](.*?)\[/b\]`), `$1`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[i\](.*?)\[/i\]`), `$1`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[u\](.*?)\[/u\]`), `$1`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[s\](.*?)\[/s\]`), `$1`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[spoiler\](.*?)\[/spoiler\]`), `$1`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[img\](.*?)\[/img\]`), ``, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[list\](.*?)\[/list\]`), ``, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[\*\]`), `
  • `, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[url=(.*?)\](.*?)\[/url\]`), `$2`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[article=(.*?)\](.*?)\[/article\]`), "", func(m []string) string { return `` + m[2] + `` }}, pregReplaceRule{regexp.MustCompile(`(?si)\[rev=(.*?)\](.*?)\[/rev\]`), "", func(m []string) string { return `` + m[2] + `` }}, pregReplaceRule{regexp.MustCompile(`(?si)\[imgur\](.*?)\.(...)\[/imgur\]`), ``, nil, }, pregReplaceRule{regexp.MustCompile(`(?si)\[section=(.*?)](.*?)\[/section\]`), "", func(m []string) string { return `
    ` + m[1] + `` + strings.TrimSpace(m[2]) + `
    ` }}, } 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, "") 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, "") } 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 } 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 += `
    ` + this.DynamicContentWarning + `
    ` 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)) }
    "+strings.Join(cols[1:], "")+"