all repos

rss-tools @ master

get rss feed from sources that(i need and) dont provide one

rss-tools/vendor/github.com/PuerkitoBio/goquery/property.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
we're vendoring now, 7 days ago
1
package goquery
2
3
import (
4
	"regexp"
5
	"strings"
6
7
	"golang.org/x/net/html"
8
)
9
10
var rxClassTrim = regexp.MustCompile("[\t\r\n]")
11
12
// Attr gets the specified attribute's value for the first element in the
13
// Selection. To get the value for each element individually, use a looping
14
// construct such as Each or Map method.
15
func (s *Selection) Attr(attrName string) (val string, exists bool) {
16
	if len(s.Nodes) == 0 {
17
		return
18
	}
19
	return getAttributeValue(attrName, s.Nodes[0])
20
}
21
22
// AttrOr works like Attr but returns default value if attribute is not present.
23
func (s *Selection) AttrOr(attrName, defaultValue string) string {
24
	if len(s.Nodes) == 0 {
25
		return defaultValue
26
	}
27
28
	val, exists := getAttributeValue(attrName, s.Nodes[0])
29
	if !exists {
30
		return defaultValue
31
	}
32
33
	return val
34
}
35
36
// RemoveAttr removes the named attribute from each element in the set of matched elements.
37
func (s *Selection) RemoveAttr(attrName string) *Selection {
38
	for _, n := range s.Nodes {
39
		removeAttr(n, attrName)
40
	}
41
42
	return s
43
}
44
45
// SetAttr sets the given attribute on each element in the set of matched elements.
46
func (s *Selection) SetAttr(attrName, val string) *Selection {
47
	for _, n := range s.Nodes {
48
		attr := getAttributePtr(attrName, n)
49
		if attr == nil {
50
			n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
51
		} else {
52
			attr.Val = val
53
		}
54
	}
55
56
	return s
57
}
58
59
// Text gets the combined text contents of each element in the set of matched
60
// elements, including their descendants.
61
func (s *Selection) Text() string {
62
	var builder strings.Builder
63
64
	// Slightly optimized vs calling Each: no single selection object created
65
	var f func(*html.Node)
66
	f = func(n *html.Node) {
67
		if n.Type == html.TextNode {
68
			// Keep newlines and spaces, like jQuery
69
			builder.WriteString(n.Data)
70
		}
71
		if n.FirstChild != nil {
72
			for c := n.FirstChild; c != nil; c = c.NextSibling {
73
				f(c)
74
			}
75
		}
76
	}
77
	for _, n := range s.Nodes {
78
		f(n)
79
	}
80
81
	return builder.String()
82
}
83
84
// Size is an alias for Length.
85
func (s *Selection) Size() int {
86
	return s.Length()
87
}
88
89
// Length returns the number of elements in the Selection object.
90
func (s *Selection) Length() int {
91
	return len(s.Nodes)
92
}
93
94
// Html gets the HTML contents of the first element in the set of matched
95
// elements. It includes text and comment nodes.
96
func (s *Selection) Html() (ret string, e error) {
97
	// Since there is no .innerHtml, the HTML content must be re-created from
98
	// the nodes using html.Render.
99
	var builder strings.Builder
100
101
	if len(s.Nodes) > 0 {
102
		for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
103
			e = html.Render(&builder, c)
104
			if e != nil {
105
				return
106
			}
107
		}
108
		ret = builder.String()
109
	}
110
111
	return
112
}
113
114
// AddClass adds the given class(es) to each element in the set of matched elements.
115
// Multiple class names can be specified, separated by a space or via multiple arguments.
116
func (s *Selection) AddClass(class ...string) *Selection {
117
	classStr := strings.TrimSpace(strings.Join(class, " "))
118
119
	if classStr == "" {
120
		return s
121
	}
122
123
	tcls := getClassesSlice(classStr)
124
	for _, n := range s.Nodes {
125
		curClasses, attr := getClassesAndAttr(n, true)
126
		for _, newClass := range tcls {
127
			if !strings.Contains(curClasses, " "+newClass+" ") {
128
				curClasses += newClass + " "
129
			}
130
		}
131
132
		setClasses(n, attr, curClasses)
133
	}
134
135
	return s
136
}
137
138
// HasClass determines whether any of the matched elements are assigned the
139
// given class.
140
func (s *Selection) HasClass(class string) bool {
141
	class = " " + class + " "
142
	for _, n := range s.Nodes {
143
		classes, _ := getClassesAndAttr(n, false)
144
		if strings.Contains(classes, class) {
145
			return true
146
		}
147
	}
148
	return false
149
}
150
151
// RemoveClass removes the given class(es) from each element in the set of matched elements.
152
// Multiple class names can be specified, separated by a space or via multiple arguments.
153
// If no class name is provided, all classes are removed.
154
func (s *Selection) RemoveClass(class ...string) *Selection {
155
	var rclasses []string
156
157
	classStr := strings.TrimSpace(strings.Join(class, " "))
158
	remove := classStr == ""
159
160
	if !remove {
161
		rclasses = getClassesSlice(classStr)
162
	}
163
164
	for _, n := range s.Nodes {
165
		if remove {
166
			removeAttr(n, "class")
167
		} else {
168
			classes, attr := getClassesAndAttr(n, true)
169
			for _, rcl := range rclasses {
170
				classes = strings.ReplaceAll(classes, " "+rcl+" ", " ")
171
			}
172
173
			setClasses(n, attr, classes)
174
		}
175
	}
176
177
	return s
178
}
179
180
// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
181
// Multiple class names can be specified, separated by a space or via multiple arguments.
182
func (s *Selection) ToggleClass(class ...string) *Selection {
183
	classStr := strings.TrimSpace(strings.Join(class, " "))
184
185
	if classStr == "" {
186
		return s
187
	}
188
189
	tcls := getClassesSlice(classStr)
190
191
	for _, n := range s.Nodes {
192
		classes, attr := getClassesAndAttr(n, true)
193
		for _, tcl := range tcls {
194
			spaceAroundTcl := " " + tcl + " "
195
			if strings.Contains(classes, spaceAroundTcl) {
196
				classes = strings.ReplaceAll(classes, spaceAroundTcl, " ")
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
// Private function to get the specified attribute's value from a node.
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
// Get and normalize the "class" attribute from the node.
231
func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
232
	// Applies only to element nodes
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
}