pgenr.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. package pgenr
  2. import (
  3. "bytes"
  4. "golang.org/x/net/html/atom"
  5. "html"
  6. "sync"
  7. )
  8. const (
  9. TextThemeSuccess = "success"
  10. TextThemeDanger = "danger"
  11. )
  12. var (
  13. bufferPool sync.Pool
  14. )
  15. func getBuffer() *bytes.Buffer {
  16. if v := bufferPool.Get(); v == nil {
  17. return new(bytes.Buffer)
  18. } else {
  19. return v.(*bytes.Buffer)
  20. }
  21. }
  22. func releaseBuffer(sb *bytes.Buffer) {
  23. sb.Reset()
  24. bufferPool.Put(sb)
  25. }
  26. type (
  27. Element interface {
  28. Html() string
  29. }
  30. Style map[string]string
  31. TextOption func(t *Text)
  32. ButtonOption func(btn *Button)
  33. Text struct {
  34. Tag atom.Atom
  35. Content string
  36. Theme string
  37. Color string
  38. Style Style
  39. }
  40. Button struct {
  41. Url string
  42. Text string
  43. Color string
  44. Style Style
  45. }
  46. Entry struct {
  47. Title Element
  48. Items map[string]Element
  49. }
  50. Action struct {
  51. Instructions Element
  52. Button *Button
  53. InviteCode string
  54. }
  55. Table struct {
  56. Title Element
  57. Header []Element
  58. Body [][]Element
  59. }
  60. Page struct {
  61. Title string
  62. Head string
  63. Intros []Element
  64. Entries []*Entry
  65. Actions []*Action
  66. Tables []*Table
  67. Outros []Element
  68. Copyright string
  69. }
  70. )
  71. func (s Style) Css(name string, value string) {
  72. s[name] = value
  73. }
  74. func (s Style) String() string {
  75. br := getBuffer()
  76. defer releaseBuffer(br)
  77. for k, v := range s {
  78. br.WriteString(k)
  79. br.WriteString(":")
  80. br.WriteString(v)
  81. br.WriteString(";")
  82. }
  83. return br.String()
  84. }
  85. func (a *Action) Html() string {
  86. return a.String()
  87. }
  88. func (a *Action) String() string {
  89. br := getBuffer()
  90. defer releaseBuffer(br)
  91. if a.Instructions != nil {
  92. br.WriteString(a.Instructions.Html())
  93. }
  94. br.WriteString("<div class='action-block'>")
  95. if a.InviteCode != "" {
  96. br.WriteString("<span class='invite-code'>")
  97. br.WriteString(html.EscapeString(a.InviteCode))
  98. br.WriteString("</span>")
  99. }
  100. if a.Button != nil {
  101. br.WriteString(a.Button.String())
  102. }
  103. br.WriteString("</div>")
  104. return br.String()
  105. }
  106. func (e *Entry) Html() string {
  107. return e.String()
  108. }
  109. func (e *Entry) String() string {
  110. br := getBuffer()
  111. defer releaseBuffer(br)
  112. br.WriteString("<div class='grid'>")
  113. if e.Title != nil {
  114. br.WriteString(e.Title.Html())
  115. }
  116. for k, v := range e.Items {
  117. br.WriteString("<div class='row'>")
  118. br.WriteString("<div class='preview-label'>")
  119. br.WriteString(k)
  120. br.WriteString("</div>")
  121. br.WriteString("<div class='preview-value'>")
  122. br.WriteString(v.Html())
  123. br.WriteString("</div>")
  124. br.WriteString("</div>")
  125. }
  126. br.WriteString("</div>")
  127. return br.String()
  128. }
  129. func (e *Entry) AddItem(label string, txt *Text) *Entry {
  130. if e.Items == nil {
  131. e.Items = make(map[string]Element)
  132. }
  133. e.Items[label] = txt
  134. return e
  135. }
  136. func (page *Page) SetHead(s string) *Page {
  137. page.Head = s
  138. return page
  139. }
  140. func (page *Page) SetCopyright(s string) *Page {
  141. page.Copyright = s
  142. return page
  143. }
  144. func (page *Page) AddPlainIntro(s string) *Page {
  145. page.Intros = append(page.Intros, NewText(s, WithTextTag(atom.P)))
  146. return page
  147. }
  148. func (page *Page) AddIntro(t *Text) *Page {
  149. page.Intros = append(page.Intros, t)
  150. return page
  151. }
  152. func (page *Page) AddEntry(e *Entry) *Page {
  153. page.Entries = append(page.Entries, e)
  154. return page
  155. }
  156. func (page *Page) AddTable(t *Table) *Page {
  157. page.Tables = append(page.Tables, t)
  158. return page
  159. }
  160. func (page *Page) AddButtonAction(s string, btn *Button) *Page {
  161. page.Actions = append(page.Actions, &Action{Instructions: NewText(s, WithTextTag(atom.P)), Button: btn})
  162. return page
  163. }
  164. func (page *Page) AddInviteCodeAction(s string, code string) *Page {
  165. page.Actions = append(page.Actions, &Action{Instructions: NewText(s, WithTextTag(atom.P)), InviteCode: code})
  166. return page
  167. }
  168. func (page *Page) AddOutro(t *Text) *Page {
  169. page.Outros = append(page.Outros, t)
  170. return page
  171. }
  172. func (page *Page) AddPlainOutro(s string) *Page {
  173. page.Outros = append(page.Outros, NewText(s, WithTextTag(atom.P)))
  174. return page
  175. }
  176. func (page *Page) Escape() {
  177. page.Head = html.EscapeString(page.Head)
  178. page.Copyright = html.EscapeString(page.Copyright)
  179. }
  180. func NewPage(title string) *Page {
  181. return &Page{
  182. Title: title,
  183. Intros: make([]Element, 0),
  184. Entries: make([]*Entry, 0),
  185. Actions: make([]*Action, 0),
  186. Outros: make([]Element, 0),
  187. Tables: make([]*Table, 0),
  188. }
  189. }
  190. func NewEntry(title string) *Entry {
  191. return &Entry{
  192. Title: NewText(title, WithTextTag(atom.P)),
  193. Items: make(map[string]Element),
  194. }
  195. }
  196. func WithTextTheme(s string) TextOption {
  197. return func(t *Text) {
  198. t.Theme = s
  199. }
  200. }
  201. func WithTextStyle(ms map[string]string) TextOption {
  202. return func(t *Text) {
  203. t.Style = ms
  204. }
  205. }
  206. func WithTextTag(tag atom.Atom) TextOption {
  207. return func(t *Text) {
  208. t.Tag = tag
  209. }
  210. }
  211. func WithTextColor(color string) TextOption {
  212. return func(t *Text) {
  213. t.Color = color
  214. }
  215. }
  216. func (button *Button) Html() string {
  217. return button.String()
  218. }
  219. func (button *Button) String() string {
  220. br := getBuffer()
  221. defer releaseBuffer(br)
  222. if button.Style == nil {
  223. button.Style = make(map[string]string)
  224. }
  225. if button.Color != "" {
  226. button.Style["color"] = button.Color
  227. }
  228. br.WriteString("<a class='button'")
  229. if button.Url != "" {
  230. br.WriteString(" href='" + button.Url + "'")
  231. }
  232. if len(button.Style) > 0 {
  233. br.WriteString(" style='")
  234. br.WriteString(button.Style.String())
  235. br.WriteString("'")
  236. }
  237. br.WriteString(">")
  238. br.WriteString(html.EscapeString(button.Text))
  239. br.WriteString("</a>")
  240. return br.String()
  241. }
  242. func (table *Table) SetHead(elements ...Element) {
  243. table.Header = elements
  244. }
  245. func (table *Table) AddCell(elements ...Element) {
  246. if table.Body == nil {
  247. table.Body = make([][]Element, 0)
  248. }
  249. table.Body = append(table.Body, elements)
  250. }
  251. func (table *Table) String() string {
  252. br := getBuffer()
  253. defer releaseBuffer(br)
  254. br.WriteString("<div class='table-wrapper'>")
  255. if table.Title != nil {
  256. br.WriteString("<div class='table-title'>")
  257. br.WriteString(table.Title.Html())
  258. br.WriteString("</div>")
  259. }
  260. br.WriteString("<table class='table'>")
  261. if len(table.Header) > 0 {
  262. br.WriteString("<thead>")
  263. br.WriteString("<tr>")
  264. for _, text := range table.Header {
  265. br.WriteString("<th>")
  266. br.WriteString(text.Html())
  267. br.WriteString("</th>")
  268. }
  269. br.WriteString("</tr>")
  270. br.WriteString("</thead>")
  271. }
  272. if len(table.Body) > 0 {
  273. br.WriteString("<tbody>")
  274. for _, cell := range table.Body {
  275. br.WriteString("<tr>")
  276. for _, text := range cell {
  277. br.WriteString("<td>")
  278. br.WriteString(text.Html())
  279. br.WriteString("</td>")
  280. }
  281. br.WriteString("</tr>")
  282. }
  283. br.WriteString("</tbody>")
  284. }
  285. br.WriteString("</table>")
  286. br.WriteString("</div>")
  287. return br.String()
  288. }
  289. func (text *Text) Html() string {
  290. return text.String()
  291. }
  292. func (text *Text) String() string {
  293. br := getBuffer()
  294. defer releaseBuffer(br)
  295. if text.Style == nil {
  296. text.Style = make(map[string]string)
  297. }
  298. if text.Color != "" {
  299. text.Style["color"] = text.Color
  300. }
  301. if text.Tag == 0 {
  302. text.Tag = atom.Span
  303. }
  304. br.WriteString("<")
  305. br.WriteString(text.Tag.String())
  306. if text.Theme != "" {
  307. br.WriteString(" class='text-")
  308. br.WriteString(text.Theme)
  309. br.WriteString("'")
  310. }
  311. if len(text.Style) > 0 {
  312. br.WriteString(" style='")
  313. br.WriteString(text.Style.String())
  314. br.WriteString("'")
  315. }
  316. br.WriteString(">")
  317. br.WriteString(html.EscapeString(text.Content))
  318. br.WriteString("</")
  319. br.WriteString(text.Tag.String())
  320. br.WriteString(">")
  321. return br.String()
  322. }
  323. func NewButton(label, link string, opts ...ButtonOption) *Button {
  324. btn := &Button{Text: label, Url: link}
  325. for _, cb := range opts {
  326. cb(btn)
  327. }
  328. return btn
  329. }
  330. func NewText(s string, opts ...TextOption) *Text {
  331. txt := &Text{Content: s, Tag: atom.Span}
  332. for _, cb := range opts {
  333. cb(txt)
  334. }
  335. return txt
  336. }
  337. func NewTable(title *Text) *Table {
  338. table := &Table{
  339. Title: title,
  340. Header: make([]Element, 0),
  341. Body: make([][]Element, 0),
  342. }
  343. return table
  344. }