all repos

rss-tools @ a5ac527

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

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
we're vendoring now, 7 days ago
1
package goquery
2
3
import (
4
	"io"
5
	"strings"
6
7
	"golang.org/x/net/html"
8
)
9
10
// used to determine if a set (map[*html.Node]bool) should be used
11
// instead of iterating over a slice. The set uses more memory and
12
// is slower than slice iteration for small N.
13
const minNodesForSet = 1000
14
15
var nodeNames = []string{
16
	html.ErrorNode:    "#error",
17
	html.TextNode:     "#text",
18
	html.DocumentNode: "#document",
19
	html.CommentNode:  "#comment",
20
}
21
22
// NodeName returns the node name of the first element in the selection.
23
// It tries to behave in a similar way as the DOM's nodeName property
24
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
25
//
26
// Go's net/html package defines the following node types, listed with
27
// the corresponding returned value from this function:
28
//
29
//	ErrorNode : #error
30
//	TextNode : #text
31
//	DocumentNode : #document
32
//	ElementNode : the element's tag name
33
//	CommentNode : #comment
34
//	DoctypeNode : the name of the document type
35
func NodeName(s *Selection) string {
36
	if s.Length() == 0 {
37
		return ""
38
	}
39
	return nodeName(s.Get(0))
40
}
41
42
// nodeName returns the node name of the given html node.
43
// See NodeName for additional details on behaviour.
44
func nodeName(node *html.Node) string {
45
	if node == nil {
46
		return ""
47
	}
48
49
	switch node.Type {
50
	case html.ElementNode, html.DoctypeNode:
51
		return node.Data
52
	default:
53
		if int(node.Type) < len(nodeNames) {
54
			return nodeNames[node.Type]
55
		}
56
		return ""
57
	}
58
}
59
60
// Render renders the HTML of the first item in the selection and writes it to
61
// the writer. It behaves the same as OuterHtml but writes to w instead of
62
// returning the string.
63
func Render(w io.Writer, s *Selection) error {
64
	if s.Length() == 0 {
65
		return nil
66
	}
67
	n := s.Get(0)
68
	return html.Render(w, n)
69
}
70
71
// OuterHtml returns the outer HTML rendering of the first item in
72
// the selection - that is, the HTML including the first element's
73
// tag and attributes.
74
//
75
// Unlike Html, this is a function and not a method on the Selection,
76
// because this is not a jQuery method (in javascript-land, this is
77
// a property provided by the DOM).
78
func OuterHtml(s *Selection) (string, error) {
79
	var builder strings.Builder
80
	if err := Render(&builder, s); err != nil {
81
		return "", err
82
	}
83
	return builder.String(), nil
84
}
85
86
// Loop through all container nodes to search for the target node.
87
func sliceContains(container []*html.Node, contained *html.Node) bool {
88
	for _, n := range container {
89
		if nodeContains(n, contained) {
90
			return true
91
		}
92
	}
93
94
	return false
95
}
96
97
// Checks if the contained node is within the container node.
98
func nodeContains(container *html.Node, contained *html.Node) bool {
99
	// Check if the parent of the contained node is the container node, traversing
100
	// upward until the top is reached, or the container is found.
101
	for contained = contained.Parent; contained != nil; contained = contained.Parent {
102
		if container == contained {
103
			return true
104
		}
105
	}
106
	return false
107
}
108
109
// Checks if the target node is in the slice of nodes.
110
func isInSlice(slice []*html.Node, node *html.Node) bool {
111
	return indexInSlice(slice, node) > -1
112
}
113
114
// Returns the index of the target node in the slice, or -1.
115
func indexInSlice(slice []*html.Node, node *html.Node) int {
116
	if node != nil {
117
		for i, n := range slice {
118
			if n == node {
119
				return i
120
			}
121
		}
122
	}
123
	return -1
124
}
125
126
// Appends the new nodes to the target slice, making sure no duplicate is added.
127
// There is no check to the original state of the target slice, so it may still
128
// contain duplicates. The target slice is returned because append() may create
129
// a new underlying array. If targetSet is nil, a local set is created with the
130
// target if len(target) + len(nodes) is greater than minNodesForSet.
131
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
132
	// if there are not that many nodes, don't use the map, faster to just use nested loops
133
	// (unless a non-nil targetSet is passed, in which case the caller knows better).
134
	if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
135
		for _, n := range nodes {
136
			if !isInSlice(target, n) {
137
				target = append(target, n)
138
			}
139
		}
140
		return target
141
	}
142
143
	// if a targetSet is passed, then assume it is reliable, otherwise create one
144
	// and initialize it with the current target contents.
145
	if targetSet == nil {
146
		targetSet = make(map[*html.Node]bool, len(target))
147
		for _, n := range target {
148
			targetSet[n] = true
149
		}
150
	}
151
	for _, n := range nodes {
152
		if !targetSet[n] {
153
			target = append(target, n)
154
			targetSet[n] = true
155
		}
156
	}
157
158
	return target
159
}
160
161
// Loop through a selection, returning only those nodes that pass the predicate
162
// function.
163
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
164
	for i, n := range sel.Nodes {
165
		if predicate(i, newSingleSelection(n, sel.document)) {
166
			result = append(result, n)
167
		}
168
	}
169
	return result
170
}
171
172
// Creates a new Selection object based on the specified nodes, and keeps the
173
// source Selection object on the stack (linked list).
174
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
175
	result := &Selection{nodes, fromSel.document, fromSel}
176
	return result
177
}