bbcode - bbcodehtml/list.go
1 package bbcodehtml
2
3 import (
4 "strings"
5 "unicode"
6
7 "vimagination.zapto.org/bbcode"
8 )
9
10 type list struct{}
11
12 func (list) Name() string {
13 return "list"
14 }
15
16 var (
17 uListOpen = []byte("<ul>")
18 uListClose = []byte("</ul>")
19 liOpen = []byte("<li>")
20 liClose = []byte("</li>")
21 oaList = []byte("<ol type=\"a\">")
22 oAList = []byte("<ol type=\"A\">")
23 oiList = []byte("<ol type=\"i\">")
24 oIList = []byte("<ol type=\"I\">")
25 o1List = []byte("<ol type=\"1\">")
26 oListClose = []byte("</ol>")
27 )
28
29 func (list) Handle(p *bbcode.Processor, attr string) {
30 closer := oListClose
31 switch attr {
32 case "a":
33 p.Write(oaList)
34 case "A":
35 p.Write(oAList)
36 case "i":
37 p.Write(oiList)
38 case "I":
39 p.Write(oIList)
40 case "1":
41 p.Write(o1List)
42 default:
43 p.Write(uListOpen)
44 closer = uListClose
45 }
46
47 t := p.Get()
48
49 var processed bool
50
51 if text, ok := t.(bbcode.Text); ok {
52 if strings.HasPrefix(strings.TrimLeftFunc(text[0], unicode.IsSpace), "*") {
53 p.Write(liOpen)
54 handleLiText(p, text)
55 p.Write(liClose)
56 processed = true
57 }
58 }
59
60 if !processed {
61 Loop:
62 for {
63 switch t := t.(type) {
64 case bbcode.Text:
65 case bbcode.OpenTag:
66 if t.Name == "*" {
67 if handleLi(p) {
68 break Loop
69 }
70 }
71 case bbcode.CloseTag:
72 if strings.EqualFold(t.Name, "list") {
73 break Loop
74 }
75 default:
76 break Loop
77 }
78 t = p.Get()
79 }
80 }
81
82 p.Write(closer)
83 }
84
85 func handleLi(p *bbcode.Processor) bool {
86 p.Write(liOpen)
87 for {
88 switch t := p.Get().(type) {
89 case bbcode.Text:
90 p.Print(t)
91 case bbcode.OpenTag:
92 if t.Name == "*" {
93 p.Write(liClose)
94 p.Write(liOpen)
95 } else {
96 p.ProcessTag(t)
97 }
98 case bbcode.CloseTag:
99 if t.Name == "*" {
100 p.Write(liClose)
101 return false
102 } else if strings.EqualFold(t.Name, "list") {
103 p.Write(liClose)
104 return true
105 }
106 p.Print(t)
107 default:
108 p.Write(liClose)
109 return true
110 }
111 }
112 }
113
114 func handleLiText(p *bbcode.Processor, s interface{}) {
115 var (
116 start = true
117 lastNewLine [2]int
118 firstDone bool
119 )
120 Loop:
121 for {
122 switch t := s.(type) {
123 case bbcode.Text:
124 for m, text := range t {
125 for n, c := range text {
126 if c == '\n' {
127 lastNewLine[0] = m
128 lastNewLine[1] = n
129 start = true
130 } else if start {
131 if c == '*' {
132 if firstDone {
133 p.Print(t[:lastNewLine[0]])
134 p.Print(text[:lastNewLine[1]])
135 p.Write(liClose)
136 p.Write(liOpen)
137 } else {
138 firstDone = true
139 }
140 t = t[lastNewLine[0]:]
141 t[0] = t[0][n+1:]
142 s = t
143 start = false
144 continue Loop
145 } else if !unicode.IsSpace(c) {
146 start = false
147 }
148 }
149 }
150 }
151 p.Print(t)
152 case bbcode.OpenTag:
153 p.ProcessTag(t)
154 case bbcode.CloseTag:
155 if strings.EqualFold(t.Name, "list") {
156 return
157 }
158 p.Print(t)
159 default:
160 return
161 }
162 start = false
163 s = p.Get()
164 }
165 }
166