forked from chasefleming/elem-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elem.go
106 lines (89 loc) · 2.09 KB
/
elem.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
package elem
import (
"sort"
"strings"
)
// List of HTML5 void elements. Void elements, also known as self-closing or empty elements,
// are elements that don't have a closing tag because they can't contain any content.
// For example, the <img> tag cannot wrap text or other tags, it stands alone, so it doesn't have a closing tag.
var voidElements = map[string]struct{}{
"area": {},
"base": {},
"br": {},
"col": {},
"command": {},
"embed": {},
"hr": {},
"img": {},
"input": {},
"keygen": {},
"link": {},
"meta": {},
"param": {},
"source": {},
"track": {},
"wbr": {},
}
type Attrs map[string]string
type Node interface {
RenderTo(builder *strings.Builder)
Render() string
}
type TextNode string
func (t TextNode) RenderTo(builder *strings.Builder) {
builder.WriteString(string(t))
}
func (t TextNode) Render() string {
return string(t)
}
type Element struct {
Tag string
Attrs Attrs
Children []Node
}
func (e *Element) RenderTo(builder *strings.Builder) {
// Start with opening tag
builder.WriteString("<")
builder.WriteString(e.Tag)
// Sort the keys for consistent order
keys := make([]string, 0, len(e.Attrs))
for k := range e.Attrs {
keys = append(keys, k)
}
sort.Strings(keys)
// Append the attributes to the builder
for _, k := range keys {
builder.WriteString(` `)
builder.WriteString(k)
builder.WriteString(`="`)
builder.WriteString(e.Attrs[k])
builder.WriteString(`"`)
}
// If it's a void element, close it and return
if _, exists := voidElements[e.Tag]; exists {
builder.WriteString(`>`)
return
}
// Close opening tag
builder.WriteString(`>`)
// Build the content
for _, child := range e.Children {
child.RenderTo(builder)
}
// Append closing tag
builder.WriteString(`</`)
builder.WriteString(e.Tag)
builder.WriteString(`>`)
}
func (e *Element) Render() string {
var builder strings.Builder
e.RenderTo(&builder)
return builder.String()
}
func NewElement(tag string, attrs Attrs, children ...Node) *Element {
return &Element{
Tag: tag,
Attrs: attrs,
Children: children,
}
}