-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserializer.go
More file actions
96 lines (85 loc) · 2.2 KB
/
serializer.go
File metadata and controls
96 lines (85 loc) · 2.2 KB
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
package GoHtml
import (
"fmt"
"io"
"strings"
"github.com/emirpasic/gods/stacks/linkedliststack"
"golang.org/x/net/html"
)
func encodeListAttributes(node *Node) string {
w := strings.Builder{}
node.IterateAttributes(func(attribute, value string) {
if strings.TrimSpace(value) == "" {
w.Write(fmt.Appendf(nil, " %s", attribute))
} else {
w.Write(fmt.Appendf(nil, " %s=%s", attribute, `"`+html.EscapeString(value)+`"`))
}
})
return w.String()
}
// Encode writes to w encoding of the node tree from rootNode.
// w must not be nil
func Encode(w io.Writer, rootNode *Node) error {
if rootNode == nil {
return NoNodesFound
}
type stackFrame struct {
node *Node
isClosingTag bool
}
stack := linkedliststack.New()
stack.Push(stackFrame{
node: rootNode,
})
for stack.Size() > 0 {
v, _ := stack.Pop()
currentStackFrame := v.(stackFrame)
if currentStackFrame.isClosingTag {
_, err := fmt.Fprintf(w, "</%s>", currentStackFrame.node.GetTagName())
if err != nil {
return err
}
continue
} else if currentStackFrame.node.IsTextNode() {
_, err := fmt.Fprint(w, html.EscapeString(currentStackFrame.node.GetText()))
if err != nil {
return err
}
} else {
_, err := fmt.Fprintf(w, "<%s%s>", func() string {
tagName := currentStackFrame.node.GetTagName()
tagNameUpperCased := strings.ToUpper(tagName)
if tagNameUpperCased == DOCTYPEDTD {
tagName = tagNameUpperCased
}
return tagName
}(), encodeListAttributes(currentStackFrame.node))
if err != nil {
return err
}
}
if currentStackFrame.node.GetNextNode() != nil {
stack.Push(stackFrame{
node: currentStackFrame.node.GetNextNode(),
})
}
if !IsVoidTag(currentStackFrame.node.GetTagName()) && !currentStackFrame.node.IsTextNode() {
stack.Push(stackFrame{
node: currentStackFrame.node,
isClosingTag: true,
})
}
if currentStackFrame.node.GetChildNode() != nil {
stack.Push(stackFrame{
node: currentStackFrame.node.GetChildNode(),
})
}
}
return nil
}
// NodeTreeToHTML returns encoding of node-tree as a string.
func NodeTreeToHTML(rootNode *Node) string {
builder := &strings.Builder{}
Encode(builder, rootNode)
return builder.String()
}