-
Notifications
You must be signed in to change notification settings - Fork 8
/
element.go
121 lines (109 loc) · 2.85 KB
/
element.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package htmlutil
import (
"errors"
"fmt"
"html"
"strings"
"github.com/grokify/mogo/type/maputil"
"github.com/grokify/mogo/type/stringsutil"
)
var (
ErrAttributeNameIsRequired = errors.New("attribute name is required")
ErrTagNameIsRequired = errors.New("tag name is required")
)
type Element struct {
TagName string
Attrs map[string][]string
SelfClose bool
InnerHTML []stringsutil.StringableWithErr
}
func NewElement() *Element {
return &Element{
Attrs: map[string][]string{},
InnerHTML: []stringsutil.StringableWithErr{}}
}
func (el *Element) AddAttribute(key string, values ...string) error {
key = strings.TrimSpace(key)
if len(key) == 0 {
return ErrAttributeNameIsRequired
}
if el.Attrs == nil {
el.Attrs = map[string][]string{}
}
if _, ok := el.Attrs[key]; !ok {
el.Attrs[key] = []string{}
}
if len(values) > 0 {
el.Attrs[key] = append(el.Attrs[key], values...)
}
return nil
}
func (el *Element) AddInnerHTML(innerHTML stringsutil.StringableWithErr) {
el.InnerHTML = append(el.InnerHTML, innerHTML)
}
func (el *Element) AddInnerHTMLText(text string, escaped bool) {
el.InnerHTML = append(el.InnerHTML, &Text{
Text: text,
Escaped: escaped})
}
func BuildAttributeHTML(key string, values []string, delimiter string, htmlEscape bool) string {
vals2 := []string{}
if htmlEscape {
for _, val := range values {
vals2 = append(vals2, html.EscapeString(val))
}
} else {
vals2 = values
}
valStr := strings.Join(vals2, delimiter)
return fmt.Sprintf(`%s="%s"`, key, valStr)
}
func (el *Element) String() (string, error) {
el.TagName = strings.TrimSpace(el.TagName)
if len(el.TagName) == 0 {
return "", ErrTagNameIsRequired
}
attrs := []string{}
keysSorted := maputil.StringKeys(el.Attrs, nil)
for _, key := range keysSorted {
vals, ok := el.Attrs[key]
if !ok {
panic("key not found")
}
vals = stringsutil.SliceTrimSpace(vals, true)
if len(vals) == 0 {
attrs = append(attrs, key)
} else if key == AttributeClass {
attrs = append(attrs, BuildAttributeHTML(
key,
stringsutil.SliceCondenseSpace(vals, true, false),
DelimitSpace, true))
} else if key == AttributeOnclick {
attrs = append(attrs, BuildAttributeHTML(key, vals, DelimitSemicolon, false))
} else {
attrs = append(attrs, BuildAttributeHTML(key, vals, DelimitSemicolon, true))
}
}
attrsStr := strings.Join(attrs, " ")
if len(attrsStr) > 0 {
attrsStr = " " + attrsStr
}
openingTagClose := ">"
if el.SelfClose && len(el.InnerHTML) == 0 {
openingTagClose = " />"
}
openingTag := "<" + el.TagName + attrsStr + openingTagClose
var innerHTML string
for _, child := range el.InnerHTML {
childStr, err := child.String()
if err != nil {
return "", err
}
innerHTML += childStr
}
closingTag := ""
if openingTagClose == ">" {
closingTag = "</" + el.TagName + ">"
}
return openingTag + innerHTML + closingTag, nil
}