|
@@ -1,9 +1,10 @@
|
|
|
package pgenr
|
|
|
|
|
|
import (
|
|
|
+ "bytes"
|
|
|
"golang.org/x/net/html/atom"
|
|
|
"html"
|
|
|
- "strings"
|
|
|
+ "sync"
|
|
|
)
|
|
|
|
|
|
const (
|
|
@@ -11,7 +12,28 @@ const (
|
|
|
TextThemeDanger = "danger"
|
|
|
)
|
|
|
|
|
|
+var (
|
|
|
+ bufferPool sync.Pool
|
|
|
+)
|
|
|
+
|
|
|
+func getBuffer() *bytes.Buffer {
|
|
|
+ if v := bufferPool.Get(); v == nil {
|
|
|
+ return new(bytes.Buffer)
|
|
|
+ } else {
|
|
|
+ return v.(*bytes.Buffer)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func releaseBuffer(sb *bytes.Buffer) {
|
|
|
+ sb.Reset()
|
|
|
+ bufferPool.Put(sb)
|
|
|
+}
|
|
|
+
|
|
|
type (
|
|
|
+ Element interface {
|
|
|
+ Html() string
|
|
|
+ }
|
|
|
+
|
|
|
TextOption func(t *Text)
|
|
|
|
|
|
ButtonOption func(btn *Button)
|
|
@@ -32,37 +54,85 @@ type (
|
|
|
}
|
|
|
|
|
|
Entry struct {
|
|
|
- Title *Text
|
|
|
- Items map[string]*Text
|
|
|
+ Title Element
|
|
|
+ Items map[string]Element
|
|
|
}
|
|
|
|
|
|
Action struct {
|
|
|
- Instructions *Text
|
|
|
+ Instructions Element
|
|
|
Button *Button
|
|
|
InviteCode string
|
|
|
}
|
|
|
|
|
|
Table struct {
|
|
|
- Title *Text
|
|
|
- Header []*Text
|
|
|
- Body [][]*Text
|
|
|
+ Title Element
|
|
|
+ Header []Element
|
|
|
+ Body [][]Element
|
|
|
}
|
|
|
|
|
|
Page struct {
|
|
|
Title string
|
|
|
Head string
|
|
|
- Intros []*Text
|
|
|
+ Intros []Element
|
|
|
Entries []*Entry
|
|
|
Actions []*Action
|
|
|
Tables []*Table
|
|
|
- Outros []*Text
|
|
|
+ Outros []Element
|
|
|
Copyright string
|
|
|
}
|
|
|
)
|
|
|
|
|
|
+func (a *Action) Html() string {
|
|
|
+ return a.String()
|
|
|
+}
|
|
|
+
|
|
|
+func (a *Action) String() string {
|
|
|
+ br := getBuffer()
|
|
|
+ defer releaseBuffer(br)
|
|
|
+ if a.Instructions != nil {
|
|
|
+ br.WriteString(a.Instructions.Html())
|
|
|
+ }
|
|
|
+ br.WriteString("<div class='action-block'>")
|
|
|
+ if a.InviteCode != "" {
|
|
|
+ br.WriteString("<span class='invite-code'>")
|
|
|
+ br.WriteString(html.EscapeString(a.InviteCode))
|
|
|
+ br.WriteString("</span>")
|
|
|
+ }
|
|
|
+ if a.Button != nil {
|
|
|
+ br.WriteString(a.Button.String())
|
|
|
+ }
|
|
|
+ br.WriteString("</div>")
|
|
|
+ return br.String()
|
|
|
+}
|
|
|
+
|
|
|
+func (e *Entry) Html() string {
|
|
|
+ return e.String()
|
|
|
+}
|
|
|
+
|
|
|
+func (e *Entry) String() string {
|
|
|
+ br := getBuffer()
|
|
|
+ defer releaseBuffer(br)
|
|
|
+ br.WriteString("<div class='grid'>")
|
|
|
+ if e.Title != nil {
|
|
|
+ br.WriteString(e.Title.Html())
|
|
|
+ }
|
|
|
+ for k, v := range e.Items {
|
|
|
+ br.WriteString("<div class='row'>")
|
|
|
+ br.WriteString("<div class='preview-label'>")
|
|
|
+ br.WriteString(k)
|
|
|
+ br.WriteString("</div>")
|
|
|
+ br.WriteString("<div class='preview-value'>")
|
|
|
+ br.WriteString(v.Html())
|
|
|
+ br.WriteString("</div>")
|
|
|
+ br.WriteString("</div>")
|
|
|
+ }
|
|
|
+ br.WriteString("</div>")
|
|
|
+ return br.String()
|
|
|
+}
|
|
|
+
|
|
|
func (e *Entry) AddItem(label string, txt *Text) *Entry {
|
|
|
if e.Items == nil {
|
|
|
- e.Items = make(map[string]*Text)
|
|
|
+ e.Items = make(map[string]Element)
|
|
|
}
|
|
|
e.Items[label] = txt
|
|
|
return e
|
|
@@ -126,10 +196,10 @@ func (page *Page) Escape() {
|
|
|
func NewPage(title string) *Page {
|
|
|
return &Page{
|
|
|
Title: title,
|
|
|
- Intros: make([]*Text, 0),
|
|
|
+ Intros: make([]Element, 0),
|
|
|
Entries: make([]*Entry, 0),
|
|
|
Actions: make([]*Action, 0),
|
|
|
- Outros: make([]*Text, 0),
|
|
|
+ Outros: make([]Element, 0),
|
|
|
Tables: make([]*Table, 0),
|
|
|
}
|
|
|
}
|
|
@@ -137,7 +207,7 @@ func NewPage(title string) *Page {
|
|
|
func NewEntry(title string) *Entry {
|
|
|
return &Entry{
|
|
|
Title: NewText(title, WithTextTag(atom.P)),
|
|
|
- Items: make(map[string]*Text),
|
|
|
+ Items: make(map[string]Element),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -165,89 +235,94 @@ func WithTextColor(color string) TextOption {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func (button *Button) Html() string {
|
|
|
+ return button.String()
|
|
|
+}
|
|
|
+
|
|
|
func (button *Button) String() string {
|
|
|
- var (
|
|
|
- sb strings.Builder
|
|
|
- )
|
|
|
+ br := getBuffer()
|
|
|
+ defer releaseBuffer(br)
|
|
|
if button.Style == nil {
|
|
|
button.Style = make(map[string]string)
|
|
|
}
|
|
|
if button.Color != "" {
|
|
|
button.Style["color"] = button.Color
|
|
|
}
|
|
|
- sb.WriteString("<a class='button'")
|
|
|
+ br.WriteString("<a class='button'")
|
|
|
if button.Url != "" {
|
|
|
- sb.WriteString(" href='" + button.Url + "'")
|
|
|
+ br.WriteString(" href='" + button.Url + "'")
|
|
|
}
|
|
|
if len(button.Style) > 0 {
|
|
|
- sb.WriteString(" style='")
|
|
|
+ br.WriteString(" style='")
|
|
|
for k, v := range button.Style {
|
|
|
- sb.WriteString(k)
|
|
|
- sb.WriteString(":")
|
|
|
- sb.WriteString(v)
|
|
|
- sb.WriteString(";")
|
|
|
+ br.WriteString(k)
|
|
|
+ br.WriteString(":")
|
|
|
+ br.WriteString(v)
|
|
|
+ br.WriteString(";")
|
|
|
}
|
|
|
- sb.WriteString("'")
|
|
|
+ br.WriteString("'")
|
|
|
}
|
|
|
- sb.WriteString(">")
|
|
|
- sb.WriteString(html.EscapeString(button.Text))
|
|
|
- sb.WriteString("</a>")
|
|
|
- return sb.String()
|
|
|
+ br.WriteString(">")
|
|
|
+ br.WriteString(html.EscapeString(button.Text))
|
|
|
+ br.WriteString("</a>")
|
|
|
+ return br.String()
|
|
|
}
|
|
|
|
|
|
-func (table *Table) SetHead(header ...*Text) {
|
|
|
- table.Header = header
|
|
|
+func (table *Table) SetHead(elements ...Element) {
|
|
|
+ table.Header = elements
|
|
|
}
|
|
|
|
|
|
-func (table *Table) AddCell(texts ...*Text) {
|
|
|
+func (table *Table) AddCell(elements ...Element) {
|
|
|
if table.Body == nil {
|
|
|
- table.Body = make([][]*Text, 0)
|
|
|
+ table.Body = make([][]Element, 0)
|
|
|
}
|
|
|
- table.Body = append(table.Body, texts)
|
|
|
+ table.Body = append(table.Body, elements)
|
|
|
}
|
|
|
|
|
|
func (table *Table) String() string {
|
|
|
- var (
|
|
|
- sb strings.Builder
|
|
|
- )
|
|
|
- sb.WriteString("<div class='table-wrapper'>")
|
|
|
+ br := getBuffer()
|
|
|
+ defer releaseBuffer(br)
|
|
|
+ br.WriteString("<div class='table-wrapper'>")
|
|
|
if table.Title != nil {
|
|
|
- sb.WriteString("<div class='table-title'>")
|
|
|
- sb.WriteString(table.Title.String())
|
|
|
- sb.WriteString("</div>")
|
|
|
+ br.WriteString("<div class='table-title'>")
|
|
|
+ br.WriteString(table.Title.Html())
|
|
|
+ br.WriteString("</div>")
|
|
|
}
|
|
|
- sb.WriteString("<table class='table'>")
|
|
|
+ br.WriteString("<table class='table'>")
|
|
|
|
|
|
if len(table.Header) > 0 {
|
|
|
- sb.WriteString("<thead><tr>")
|
|
|
+ br.WriteString("<thead><tr>")
|
|
|
for _, text := range table.Header {
|
|
|
- sb.WriteString("<th>")
|
|
|
- sb.WriteString(text.String())
|
|
|
- sb.WriteString("</th>")
|
|
|
+ br.WriteString("<th>")
|
|
|
+ br.WriteString(text.Html())
|
|
|
+ br.WriteString("</th>")
|
|
|
}
|
|
|
- sb.WriteString("</tr></thead>")
|
|
|
+ br.WriteString("</tr></thead>")
|
|
|
}
|
|
|
if len(table.Body) > 0 {
|
|
|
- sb.WriteString("<tbody>")
|
|
|
+ br.WriteString("<tbody>")
|
|
|
for _, cell := range table.Body {
|
|
|
- sb.WriteString("<tr>")
|
|
|
+ br.WriteString("<tr>")
|
|
|
for _, text := range cell {
|
|
|
- sb.WriteString("<td>")
|
|
|
- sb.WriteString(text.String())
|
|
|
- sb.WriteString("</td>")
|
|
|
+ br.WriteString("<td>")
|
|
|
+ br.WriteString(text.Html())
|
|
|
+ br.WriteString("</td>")
|
|
|
}
|
|
|
- sb.WriteString("</tr>")
|
|
|
+ br.WriteString("</tr>")
|
|
|
}
|
|
|
- sb.WriteString("</tbody>")
|
|
|
+ br.WriteString("</tbody>")
|
|
|
}
|
|
|
- sb.WriteString("</table></div>")
|
|
|
- return sb.String()
|
|
|
+ br.WriteString("</table></div>")
|
|
|
+ return br.String()
|
|
|
+}
|
|
|
+
|
|
|
+func (text *Text) Html() string {
|
|
|
+ return text.String()
|
|
|
}
|
|
|
|
|
|
func (text *Text) String() string {
|
|
|
- var (
|
|
|
- sb strings.Builder
|
|
|
- )
|
|
|
+ br := getBuffer()
|
|
|
+ defer releaseBuffer(br)
|
|
|
if text.Style == nil {
|
|
|
text.Style = make(map[string]string)
|
|
|
}
|
|
@@ -257,29 +332,29 @@ func (text *Text) String() string {
|
|
|
if text.Tag == 0 {
|
|
|
text.Tag = atom.Span
|
|
|
}
|
|
|
- sb.WriteString("<")
|
|
|
- sb.WriteString(text.Tag.String())
|
|
|
+ br.WriteString("<")
|
|
|
+ br.WriteString(text.Tag.String())
|
|
|
if text.Theme != "" {
|
|
|
- sb.WriteString(" class='text-")
|
|
|
- sb.WriteString(text.Theme)
|
|
|
- sb.WriteString("'")
|
|
|
+ br.WriteString(" class='text-")
|
|
|
+ br.WriteString(text.Theme)
|
|
|
+ br.WriteString("'")
|
|
|
}
|
|
|
if len(text.Style) > 0 {
|
|
|
- sb.WriteString(" style='")
|
|
|
+ br.WriteString(" style='")
|
|
|
for k, v := range text.Style {
|
|
|
- sb.WriteString(k)
|
|
|
- sb.WriteString(":")
|
|
|
- sb.WriteString(v)
|
|
|
- sb.WriteString(";")
|
|
|
+ br.WriteString(k)
|
|
|
+ br.WriteString(":")
|
|
|
+ br.WriteString(v)
|
|
|
+ br.WriteString(";")
|
|
|
}
|
|
|
- sb.WriteString("'")
|
|
|
+ br.WriteString("'")
|
|
|
}
|
|
|
- sb.WriteString(">")
|
|
|
- sb.WriteString(html.EscapeString(text.Content))
|
|
|
- sb.WriteString("</")
|
|
|
- sb.WriteString(text.Tag.String())
|
|
|
- sb.WriteString(">")
|
|
|
- return sb.String()
|
|
|
+ br.WriteString(">")
|
|
|
+ br.WriteString(html.EscapeString(text.Content))
|
|
|
+ br.WriteString("</")
|
|
|
+ br.WriteString(text.Tag.String())
|
|
|
+ br.WriteString(">")
|
|
|
+ return br.String()
|
|
|
}
|
|
|
|
|
|
func NewButton(label, link string, opts ...ButtonOption) *Button {
|
|
@@ -301,8 +376,8 @@ func NewText(s string, opts ...TextOption) *Text {
|
|
|
func NewTable(title *Text) *Table {
|
|
|
table := &Table{
|
|
|
Title: title,
|
|
|
- Header: make([]*Text, 0),
|
|
|
- Body: make([][]*Text, 0),
|
|
|
+ Header: make([]Element, 0),
|
|
|
+ Body: make([][]Element, 0),
|
|
|
}
|
|
|
return table
|
|
|
}
|