v2 / vlib / net / html / tag.v
203 lines · 187 sloc · 4.85 KB · e158655f4f28a73358b9e24e82963a193d9fc30e
Raw
1module html
2
3import strings
4import datatypes
5
6pub enum CloseTagType {
7 in_name
8 new_tag
9}
10
11// Tag holds the information of an HTML tag.
12@[heap]
13pub struct Tag {
14pub mut:
15 name string
16 content string
17 children []&Tag
18 attributes map[string]string // attributes will be like map[name]value
19 last_attribute string
20 class_set datatypes.Set[string]
21 parent &Tag = unsafe { nil }
22 position_in_parent int
23 closed bool
24 close_type CloseTagType = .in_name
25mut:
26 text_content string
27 content_is_inner_html bool
28}
29
30fn (mut tag Tag) add_parent(t &Tag, position int) {
31 tag.position_in_parent = position
32 tag.parent = t
33}
34
35fn (mut tag Tag) add_child(t &Tag) int {
36 tag.children << t
37 return tag.children.len
38}
39
40// text returns the text contents of the tag.
41pub fn (tag &Tag) text() string {
42 if tag.name == 'text' {
43 return tag.leading_text()
44 }
45 if tag.name.len >= 2 && tag.name[..2] == 'br' {
46 return '\n'
47 }
48 mut text_str := strings.new_builder(200)
49 text_str.write_string(tag.leading_text())
50 for child in tag.children {
51 text_str.write_string(child.text())
52 }
53 return text_str.str()
54}
55
56fn (tag &Tag) leading_text() string {
57 if tag.text_content.len > 0 {
58 return tag.text_content
59 }
60 if !tag.content_is_inner_html || tag.children.len == 0 || tag.name == 'text' {
61 return tag.content
62 }
63 return ''
64}
65
66pub fn (tag &Tag) str() string {
67 if tag.name == 'text' {
68 return tag.leading_text()
69 }
70 mut html_str := strings.new_builder(200)
71 html_str.write_string('<${tag.name}')
72 for key, value in tag.attributes {
73 html_str.write_string(' ${key}')
74 if value != '' {
75 html_str.write_string('="${value}"')
76 }
77 }
78 html_str.write_string(if tag.closed && tag.close_type == .in_name { '/>' } else { '>' })
79 html_str.write_string(tag.content)
80 if !tag.content_is_inner_html && tag.children.len > 0 {
81 for child in tag.children {
82 html_str.write_string(child.str())
83 }
84 }
85 if !tag.closed || tag.close_type == .new_tag {
86 html_str.write_string('</${tag.name}>')
87 }
88 return html_str.str()
89}
90
91// get_tag retrieves the first found child tag in the tag that has the given tag name.
92pub fn (tag &Tag) get_tag(name string) ?&Tag {
93 for child in tag.children {
94 if child.name == name {
95 return child
96 }
97 if c := child.get_tag(name) {
98 return c
99 }
100 }
101 return none
102}
103
104// get_tags retrieves all child tags recursively in the tag that have the given tag name.
105pub fn (tag &Tag) get_tags(name string) []&Tag {
106 mut res := []&Tag{}
107 for child in tag.children {
108 if child.name == name {
109 res << child
110 }
111 res << child.get_tags(name)
112 }
113 return res
114}
115
116// get_tag_by_attribute retrieves the first found child tag in the tag that has the given attribute name.
117pub fn (tag &Tag) get_tag_by_attribute(name string) ?&Tag {
118 for child in tag.children {
119 if child.attributes[name] != '' {
120 return child
121 }
122 if c := child.get_tag_by_attribute(name) {
123 return c
124 }
125 }
126 return none
127}
128
129// get_tags_by_attribute retrieves all child tags recursively in the tag that have the given attribute name.
130pub fn (tag &Tag) get_tags_by_attribute(name string) []&Tag {
131 mut res := []&Tag{}
132 for child in tag.children {
133 if child.attributes[name] != '' {
134 res << child
135 }
136 res << child.get_tags_by_attribute(name)
137 }
138 return res
139}
140
141// get_tag_by_attribute_value retrieves the first found child tag in the tag that has the given attribute name and value.
142pub fn (tag &Tag) get_tag_by_attribute_value(name string, value string) ?&Tag {
143 for child in tag.children {
144 if child.attributes[name] == value {
145 return child
146 }
147 if c := child.get_tag_by_attribute_value(name, value) {
148 return c
149 }
150 }
151 return none
152}
153
154// get_tags_by_attribute_value retrieves all child tags recursively in the tag that have the given attribute name and value.
155pub fn (tag &Tag) get_tags_by_attribute_value(name string, value string) []&Tag {
156 mut res := []&Tag{}
157 for child in tag.children {
158 if child.attributes[name] == value {
159 res << child
160 }
161 res << child.get_tags_by_attribute_value(name, value)
162 }
163 return res
164}
165
166// get_tag_by_class_name retrieves the first found child tag in the tag that has the given class name(s).
167pub fn (tag &Tag) get_tag_by_class_name(names ...string) ?&Tag {
168 for child in tag.children {
169 mut matched := true
170 for name in names {
171 matched = child.class_set.exists(name)
172 if !matched {
173 break
174 }
175 }
176 if matched {
177 return child
178 }
179 if c := child.get_tag_by_class_name(...names) {
180 return c
181 }
182 }
183 return none
184}
185
186// get_tags_by_class_name retrieves all child tags recursively in the tag that have the given class name(s).
187pub fn (tag &Tag) get_tags_by_class_name(names ...string) []&Tag {
188 mut res := []&Tag{}
189 for child in tag.children {
190 mut matched := true
191 for name in names {
192 matched = child.class_set.exists(name)
193 if !matched {
194 break
195 }
196 }
197 if matched {
198 res << child
199 }
200 res << child.get_tags_by_class_name(...names)
201 }
202 return res
203}
204