1 package goquery
2
3 import (
4 "bytes"
5 "regexp"
6 "strings"
7
8 "golang.org/x/net/html"
9 )
10
11 var rxClassTrim = regexp.MustCompile("[\t\r\n]")
12
13
14
15
16 func (s *Selection) Attr(attrName string) (val string, exists bool) {
17 if len(s.Nodes) == 0 {
18 return
19 }
20 return getAttributeValue(attrName, s.Nodes[0])
21 }
22
23
24 func (s *Selection) AttrOr(attrName, defaultValue string) string {
25 if len(s.Nodes) == 0 {
26 return defaultValue
27 }
28
29 val, exists := getAttributeValue(attrName, s.Nodes[0])
30 if !exists {
31 return defaultValue
32 }
33
34 return val
35 }
36
37
38 func (s *Selection) RemoveAttr(attrName string) *Selection {
39 for _, n := range s.Nodes {
40 removeAttr(n, attrName)
41 }
42
43 return s
44 }
45
46
47 func (s *Selection) SetAttr(attrName, val string) *Selection {
48 for _, n := range s.Nodes {
49 attr := getAttributePtr(attrName, n)
50 if attr == nil {
51 n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
52 } else {
53 attr.Val = val
54 }
55 }
56
57 return s
58 }
59
60
61
62 func (s *Selection) Text() string {
63 var buf bytes.Buffer
64
65
66 var f func(*html.Node)
67 f = func(n *html.Node) {
68 if n.Type == html.TextNode {
69
70 buf.WriteString(n.Data)
71 }
72 if n.FirstChild != nil {
73 for c := n.FirstChild; c != nil; c = c.NextSibling {
74 f(c)
75 }
76 }
77 }
78 for _, n := range s.Nodes {
79 f(n)
80 }
81
82 return buf.String()
83 }
84
85
86 func (s *Selection) Size() int {
87 return s.Length()
88 }
89
90
91 func (s *Selection) Length() int {
92 return len(s.Nodes)
93 }
94
95
96
97 func (s *Selection) Html() (ret string, e error) {
98
99
100 var buf bytes.Buffer
101
102 if len(s.Nodes) > 0 {
103 for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
104 e = html.Render(&buf, c)
105 if e != nil {
106 return
107 }
108 }
109 ret = buf.String()
110 }
111
112 return
113 }
114
115
116
117 func (s *Selection) AddClass(class ...string) *Selection {
118 classStr := strings.TrimSpace(strings.Join(class, " "))
119
120 if classStr == "" {
121 return s
122 }
123
124 tcls := getClassesSlice(classStr)
125 for _, n := range s.Nodes {
126 curClasses, attr := getClassesAndAttr(n, true)
127 for _, newClass := range tcls {
128 if !strings.Contains(curClasses, " "+newClass+" ") {
129 curClasses += newClass + " "
130 }
131 }
132
133 setClasses(n, attr, curClasses)
134 }
135
136 return s
137 }
138
139
140
141 func (s *Selection) HasClass(class string) bool {
142 class = " " + class + " "
143 for _, n := range s.Nodes {
144 classes, _ := getClassesAndAttr(n, false)
145 if strings.Contains(classes, class) {
146 return true
147 }
148 }
149 return false
150 }
151
152
153
154
155 func (s *Selection) RemoveClass(class ...string) *Selection {
156 var rclasses []string
157
158 classStr := strings.TrimSpace(strings.Join(class, " "))
159 remove := classStr == ""
160
161 if !remove {
162 rclasses = getClassesSlice(classStr)
163 }
164
165 for _, n := range s.Nodes {
166 if remove {
167 removeAttr(n, "class")
168 } else {
169 classes, attr := getClassesAndAttr(n, true)
170 for _, rcl := range rclasses {
171 classes = strings.Replace(classes, " "+rcl+" ", " ", -1)
172 }
173
174 setClasses(n, attr, classes)
175 }
176 }
177
178 return s
179 }
180
181
182
183 func (s *Selection) ToggleClass(class ...string) *Selection {
184 classStr := strings.TrimSpace(strings.Join(class, " "))
185
186 if classStr == "" {
187 return s
188 }
189
190 tcls := getClassesSlice(classStr)
191
192 for _, n := range s.Nodes {
193 classes, attr := getClassesAndAttr(n, true)
194 for _, tcl := range tcls {
195 if strings.Contains(classes, " "+tcl+" ") {
196 classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
197 } else {
198 classes += tcl + " "
199 }
200 }
201
202 setClasses(n, attr, classes)
203 }
204
205 return s
206 }
207
208 func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
209 if n == nil {
210 return nil
211 }
212
213 for i, a := range n.Attr {
214 if a.Key == attrName {
215 return &n.Attr[i]
216 }
217 }
218 return nil
219 }
220
221
222 func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) {
223 if a := getAttributePtr(attrName, n); a != nil {
224 val = a.Val
225 exists = true
226 }
227 return
228 }
229
230
231 func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
232
233 if n.Type == html.ElementNode {
234 attr = getAttributePtr("class", n)
235 if attr == nil && create {
236 n.Attr = append(n.Attr, html.Attribute{
237 Key: "class",
238 Val: "",
239 })
240 attr = &n.Attr[len(n.Attr)-1]
241 }
242 }
243
244 if attr == nil {
245 classes = " "
246 } else {
247 classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ")
248 }
249
250 return
251 }
252
253 func getClassesSlice(classes string) []string {
254 return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ")
255 }
256
257 func removeAttr(n *html.Node, attrName string) {
258 for i, a := range n.Attr {
259 if a.Key == attrName {
260 n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr =
261 n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1]
262 return
263 }
264 }
265 }
266
267 func setClasses(n *html.Node, attr *html.Attribute, classes string) {
268 classes = strings.TrimSpace(classes)
269 if classes == "" {
270 removeAttr(n, "class")
271 return
272 }
273
274 attr.Val = classes
275 }
276
View as plain text