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