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