1package goquery
2
3import "golang.org/x/net/html"
4
5// Filter reduces the set of matched elements to those that match the selector string.
6// It returns a new Selection object for this subset of matching elements.
7func (s *Selection) Filter(selector string) *Selection {
8	return s.FilterMatcher(compileMatcher(selector))
9}
10
11// FilterMatcher reduces the set of matched elements to those that match
12// the given matcher. It returns a new Selection object for this subset
13// of matching elements.
14func (s *Selection) FilterMatcher(m Matcher) *Selection {
15	return pushStack(s, winnow(s, m, true))
16}
17
18// Not removes elements from the Selection that match the selector string.
19// It returns a new Selection object with the matching elements removed.
20func (s *Selection) Not(selector string) *Selection {
21	return s.NotMatcher(compileMatcher(selector))
22}
23
24// NotMatcher removes elements from the Selection that match the given matcher.
25// It returns a new Selection object with the matching elements removed.
26func (s *Selection) NotMatcher(m Matcher) *Selection {
27	return pushStack(s, winnow(s, m, false))
28}
29
30// FilterFunction reduces the set of matched elements to those that pass the function's test.
31// It returns a new Selection object for this subset of elements.
32func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
33	return pushStack(s, winnowFunction(s, f, true))
34}
35
36// NotFunction removes elements from the Selection that pass the function's test.
37// It returns a new Selection object with the matching elements removed.
38func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
39	return pushStack(s, winnowFunction(s, f, false))
40}
41
42// FilterNodes reduces the set of matched elements to those that match the specified nodes.
43// It returns a new Selection object for this subset of elements.
44func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
45	return pushStack(s, winnowNodes(s, nodes, true))
46}
47
48// NotNodes removes elements from the Selection that match the specified nodes.
49// It returns a new Selection object with the matching elements removed.
50func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
51	return pushStack(s, winnowNodes(s, nodes, false))
52}
53
54// FilterSelection reduces the set of matched elements to those that match a
55// node in the specified Selection object.
56// It returns a new Selection object for this subset of elements.
57func (s *Selection) FilterSelection(sel *Selection) *Selection {
58	if sel == nil {
59		return pushStack(s, winnowNodes(s, nil, true))
60	}
61	return pushStack(s, winnowNodes(s, sel.Nodes, true))
62}
63
64// NotSelection removes elements from the Selection that match a node in the specified
65// Selection object. It returns a new Selection object with the matching elements removed.
66func (s *Selection) NotSelection(sel *Selection) *Selection {
67	if sel == nil {
68		return pushStack(s, winnowNodes(s, nil, false))
69	}
70	return pushStack(s, winnowNodes(s, sel.Nodes, false))
71}
72
73// Intersection is an alias for FilterSelection.
74func (s *Selection) Intersection(sel *Selection) *Selection {
75	return s.FilterSelection(sel)
76}
77
78// Has reduces the set of matched elements to those that have a descendant
79// that matches the selector.
80// It returns a new Selection object with the matching elements.
81func (s *Selection) Has(selector string) *Selection {
82	return s.HasSelection(s.document.Find(selector))
83}
84
85// HasMatcher reduces the set of matched elements to those that have a descendant
86// that matches the matcher.
87// It returns a new Selection object with the matching elements.
88func (s *Selection) HasMatcher(m Matcher) *Selection {
89	return s.HasSelection(s.document.FindMatcher(m))
90}
91
92// HasNodes reduces the set of matched elements to those that have a
93// descendant that matches one of the nodes.
94// It returns a new Selection object with the matching elements.
95func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
96	return s.FilterFunction(func(_ int, sel *Selection) bool {
97		// Add all nodes that contain one of the specified nodes
98		for _, n := range nodes {
99			if sel.Contains(n) {
100				return true
101			}
102		}
103		return false
104	})
105}
106
107// HasSelection reduces the set of matched elements to those that have a
108// descendant that matches one of the nodes of the specified Selection object.
109// It returns a new Selection object with the matching elements.
110func (s *Selection) HasSelection(sel *Selection) *Selection {
111	if sel == nil {
112		return s.HasNodes()
113	}
114	return s.HasNodes(sel.Nodes...)
115}
116
117// End ends the most recent filtering operation in the current chain and
118// returns the set of matched elements to its previous state.
119func (s *Selection) End() *Selection {
120	if s.prevSel != nil {
121		return s.prevSel
122	}
123	return newEmptySelection(s.document)
124}
125
126// Filter based on the matcher, and the indicator to keep (Filter) or
127// to get rid of (Not) the matching elements.
128func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
129	// Optimize if keep is requested
130	if keep {
131		return m.Filter(sel.Nodes)
132	}
133	// Use grep
134	return grep(sel, func(i int, s *Selection) bool {
135		return !m.Match(s.Get(0))
136	})
137}
138
139// Filter based on an array of nodes, and the indicator to keep (Filter) or
140// to get rid of (Not) the matching elements.
141func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
142	if len(nodes)+len(sel.Nodes) < minNodesForSet {
143		return grep(sel, func(i int, s *Selection) bool {
144			return isInSlice(nodes, s.Get(0)) == keep
145		})
146	}
147
148	set := make(map[*html.Node]bool)
149	for _, n := range nodes {
150		set[n] = true
151	}
152	return grep(sel, func(i int, s *Selection) bool {
153		return set[s.Get(0)] == keep
154	})
155}
156
157// Filter based on a function test, and the indicator to keep (Filter) or
158// to get rid of (Not) the matching elements.
159func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
160	return grep(sel, func(i int, s *Selection) bool {
161		return f(i, s) == keep
162	})
163}
164