1package goquery
2
3import "golang.org/x/net/html"
4
5type siblingType int
6
7// Sibling type, used internally when iterating over children at the same
8// level (siblings) to specify which nodes are requested.
9const (
10 siblingPrevUntil siblingType = iota - 3
11 siblingPrevAll
12 siblingPrev
13 siblingAll
14 siblingNext
15 siblingNextAll
16 siblingNextUntil
17 siblingAllIncludingNonElements
18)
19
20// Find gets the descendants of each element in the current set of matched
21// elements, filtered by a selector. It returns a new Selection object
22// containing these matched elements.
23//
24// Note that as for all methods accepting a selector string, the selector is
25// compiled and applied by the cascadia package and inherits its behavior and
26// constraints regarding supported selectors. See the note on cascadia in
27// the goquery documentation here:
28// https://github.com/PuerkitoBio/goquery?tab=readme-ov-file#api
29func (s *Selection) Find(selector string) *Selection {
30 return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
31}
32
33// FindMatcher gets the descendants of each element in the current set of matched
34// elements, filtered by the matcher. It returns a new Selection object
35// containing these matched elements.
36func (s *Selection) FindMatcher(m Matcher) *Selection {
37 return pushStack(s, findWithMatcher(s.Nodes, m))
38}
39
40// FindSelection gets the descendants of each element in the current
41// Selection, filtered by a Selection. It returns a new Selection object
42// containing these matched elements.
43func (s *Selection) FindSelection(sel *Selection) *Selection {
44 if sel == nil {
45 return pushStack(s, nil)
46 }
47 return s.FindNodes(sel.Nodes...)
48}
49
50// FindNodes gets the descendants of each element in the current
51// Selection, filtered by some nodes. It returns a new Selection object
52// containing these matched elements.
53func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
54 return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
55 if sliceContains(s.Nodes, n) {
56 return []*html.Node{n}
57 }
58 return nil
59 }))
60}
61
62// Contents gets the children of each element in the Selection,
63// including text and comment nodes. It returns a new Selection object
64// containing these elements.
65func (s *Selection) Contents() *Selection {
66 return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
67}
68
69// ContentsFiltered gets the children of each element in the Selection,
70// filtered by the specified selector. It returns a new Selection
71// object containing these elements. Since selectors only act on Element nodes,
72// this function is an alias to ChildrenFiltered unless the selector is empty,
73// in which case it is an alias to Contents.
74func (s *Selection) ContentsFiltered(selector string) *Selection {
75 if selector != "" {
76 return s.ChildrenFiltered(selector)
77 }
78 return s.Contents()
79}
80
81// ContentsMatcher gets the children of each element in the Selection,
82// filtered by the specified matcher. It returns a new Selection
83// object containing these elements. Since matchers only act on Element nodes,
84// this function is an alias to ChildrenMatcher.
85func (s *Selection) ContentsMatcher(m Matcher) *Selection {
86 return s.ChildrenMatcher(m)
87}
88
89// Children gets the child elements of each element in the Selection.
90// It returns a new Selection object containing these elements.
91func (s *Selection) Children() *Selection {
92 return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
93}
94
95// ChildrenFiltered gets the child elements of each element in the Selection,
96// filtered by the specified selector. It returns a new
97// Selection object containing these elements.
98func (s *Selection) ChildrenFiltered(selector string) *Selection {
99 return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
100}
101
102// ChildrenMatcher gets the child elements of each element in the Selection,
103// filtered by the specified matcher. It returns a new
104// Selection object containing these elements.
105func (s *Selection) ChildrenMatcher(m Matcher) *Selection {
106 return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m)
107}
108
109// Parent gets the parent of each element in the Selection. It returns a
110// new Selection object containing the matched elements.
111func (s *Selection) Parent() *Selection {
112 return pushStack(s, getParentNodes(s.Nodes))
113}
114
115// ParentFiltered gets the parent of each element in the Selection filtered by a
116// selector. It returns a new Selection object containing the matched elements.
117func (s *Selection) ParentFiltered(selector string) *Selection {
118 return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
119}
120
121// ParentMatcher gets the parent of each element in the Selection filtered by a
122// matcher. It returns a new Selection object containing the matched elements.
123func (s *Selection) ParentMatcher(m Matcher) *Selection {
124 return filterAndPush(s, getParentNodes(s.Nodes), m)
125}
126
127// Closest gets the first element that matches the selector by testing the
128// element itself and traversing up through its ancestors in the DOM tree.
129func (s *Selection) Closest(selector string) *Selection {
130 cs := compileMatcher(selector)
131 return s.ClosestMatcher(cs)
132}
133
134// ClosestMatcher gets the first element that matches the matcher by testing the
135// element itself and traversing up through its ancestors in the DOM tree.
136func (s *Selection) ClosestMatcher(m Matcher) *Selection {
137 return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
138 // For each node in the selection, test the node itself, then each parent
139 // until a match is found.
140 for ; n != nil; n = n.Parent {
141 if m.Match(n) {
142 return []*html.Node{n}
143 }
144 }
145 return nil
146 }))
147}
148
149// ClosestNodes gets the first element that matches one of the nodes by testing the
150// element itself and traversing up through its ancestors in the DOM tree.
151func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
152 set := make(map[*html.Node]bool)
153 for _, n := range nodes {
154 set[n] = true
155 }
156 return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
157 // For each node in the selection, test the node itself, then each parent
158 // until a match is found.
159 for ; n != nil; n = n.Parent {
160 if set[n] {
161 return []*html.Node{n}
162 }
163 }
164 return nil
165 }))
166}
167
168// ClosestSelection gets the first element that matches one of the nodes in the
169// Selection by testing the element itself and traversing up through its ancestors
170// in the DOM tree.
171func (s *Selection) ClosestSelection(sel *Selection) *Selection {
172 if sel == nil {
173 return pushStack(s, nil)
174 }
175 return s.ClosestNodes(sel.Nodes...)
176}
177
178// Parents gets the ancestors of each element in the current Selection. It
179// returns a new Selection object with the matched elements.
180func (s *Selection) Parents() *Selection {
181 return pushStack(s, getParentsNodes(s.Nodes, nil, nil))
182}
183
184// ParentsFiltered gets the ancestors of each element in the current
185// Selection. It returns a new Selection object with the matched elements.
186func (s *Selection) ParentsFiltered(selector string) *Selection {
187 return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
188}
189
190// ParentsMatcher gets the ancestors of each element in the current
191// Selection. It returns a new Selection object with the matched elements.
192func (s *Selection) ParentsMatcher(m Matcher) *Selection {
193 return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m)
194}
195
196// ParentsUntil gets the ancestors of each element in the Selection, up to but
197// not including the element matched by the selector. It returns a new Selection
198// object containing the matched elements.
199func (s *Selection) ParentsUntil(selector string) *Selection {
200 return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
201}
202
203// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
204// not including the element matched by the matcher. It returns a new Selection
205// object containing the matched elements.
206func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection {
207 return pushStack(s, getParentsNodes(s.Nodes, m, nil))
208}
209
210// ParentsUntilSelection gets the ancestors of each element in the Selection,
211// up to but not including the elements in the specified Selection. It returns a
212// new Selection object containing the matched elements.
213func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
214 if sel == nil {
215 return s.Parents()
216 }
217 return s.ParentsUntilNodes(sel.Nodes...)
218}
219
220// ParentsUntilNodes gets the ancestors of each element in the Selection,
221// up to but not including the specified nodes. It returns a
222// new Selection object containing the matched elements.
223func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
224 return pushStack(s, getParentsNodes(s.Nodes, nil, nodes))
225}
226
227// ParentsFilteredUntil is like ParentsUntil, with the option to filter the
228// results based on a selector string. It returns a new Selection
229// object containing the matched elements.
230func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
231 return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
232}
233
234// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
235// results based on a matcher. It returns a new Selection object containing the matched elements.
236func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection {
237 return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter)
238}
239
240// ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
241// option to filter the results based on a selector string. It returns a new
242// Selection object containing the matched elements.
243func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
244 return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
245}
246
247// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
248// option to filter the results based on a matcher. It returns a new
249// Selection object containing the matched elements.
250func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
251 if sel == nil {
252 return s.ParentsMatcher(filter)
253 }
254 return s.ParentsMatcherUntilNodes(filter, sel.Nodes...)
255}
256
257// ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
258// option to filter the results based on a selector string. It returns a new
259// Selection object containing the matched elements.
260func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
261 return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
262}
263
264// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
265// option to filter the results based on a matcher. It returns a new
266// Selection object containing the matched elements.
267func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
268 return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter)
269}
270
271// Siblings gets the siblings of each element in the Selection. It returns
272// a new Selection object containing the matched elements.
273func (s *Selection) Siblings() *Selection {
274 return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil))
275}
276
277// SiblingsFiltered gets the siblings of each element in the Selection
278// filtered by a selector. It returns a new Selection object containing the
279// matched elements.
280func (s *Selection) SiblingsFiltered(selector string) *Selection {
281 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
282}
283
284// SiblingsMatcher gets the siblings of each element in the Selection
285// filtered by a matcher. It returns a new Selection object containing the
286// matched elements.
287func (s *Selection) SiblingsMatcher(m Matcher) *Selection {
288 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m)
289}
290
291// Next gets the immediately following sibling of each element in the
292// Selection. It returns a new Selection object containing the matched elements.
293func (s *Selection) Next() *Selection {
294 return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil))
295}
296
297// NextFiltered gets the immediately following sibling of each element in the
298// Selection filtered by a selector. It returns a new Selection object
299// containing the matched elements.
300func (s *Selection) NextFiltered(selector string) *Selection {
301 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
302}
303
304// NextMatcher gets the immediately following sibling of each element in the
305// Selection filtered by a matcher. It returns a new Selection object
306// containing the matched elements.
307func (s *Selection) NextMatcher(m Matcher) *Selection {
308 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m)
309}
310
311// NextAll gets all the following siblings of each element in the
312// Selection. It returns a new Selection object containing the matched elements.
313func (s *Selection) NextAll() *Selection {
314 return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil))
315}
316
317// NextAllFiltered gets all the following siblings of each element in the
318// Selection filtered by a selector. It returns a new Selection object
319// containing the matched elements.
320func (s *Selection) NextAllFiltered(selector string) *Selection {
321 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
322}
323
324// NextAllMatcher gets all the following siblings of each element in the
325// Selection filtered by a matcher. It returns a new Selection object
326// containing the matched elements.
327func (s *Selection) NextAllMatcher(m Matcher) *Selection {
328 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m)
329}
330
331// Prev gets the immediately preceding sibling of each element in the
332// Selection. It returns a new Selection object containing the matched elements.
333func (s *Selection) Prev() *Selection {
334 return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil))
335}
336
337// PrevFiltered gets the immediately preceding sibling of each element in the
338// Selection filtered by a selector. It returns a new Selection object
339// containing the matched elements.
340func (s *Selection) PrevFiltered(selector string) *Selection {
341 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
342}
343
344// PrevMatcher gets the immediately preceding sibling of each element in the
345// Selection filtered by a matcher. It returns a new Selection object
346// containing the matched elements.
347func (s *Selection) PrevMatcher(m Matcher) *Selection {
348 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m)
349}
350
351// PrevAll gets all the preceding siblings of each element in the
352// Selection. It returns a new Selection object containing the matched elements.
353func (s *Selection) PrevAll() *Selection {
354 return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil))
355}
356
357// PrevAllFiltered gets all the preceding siblings of each element in the
358// Selection filtered by a selector. It returns a new Selection object
359// containing the matched elements.
360func (s *Selection) PrevAllFiltered(selector string) *Selection {
361 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
362}
363
364// PrevAllMatcher gets all the preceding siblings of each element in the
365// Selection filtered by a matcher. It returns a new Selection object
366// containing the matched elements.
367func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
368 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m)
369}
370
371// NextUntil gets all following siblings of each element up to but not
372// including the element matched by the selector. It returns a new Selection
373// object containing the matched elements.
374func (s *Selection) NextUntil(selector string) *Selection {
375 return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
376 compileMatcher(selector), nil))
377}
378
379// NextUntilMatcher gets all following siblings of each element up to but not
380// including the element matched by the matcher. It returns a new Selection
381// object containing the matched elements.
382func (s *Selection) NextUntilMatcher(m Matcher) *Selection {
383 return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
384 m, nil))
385}
386
387// NextUntilSelection gets all following siblings of each element up to but not
388// including the element matched by the Selection. It returns a new Selection
389// object containing the matched elements.
390func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
391 if sel == nil {
392 return s.NextAll()
393 }
394 return s.NextUntilNodes(sel.Nodes...)
395}
396
397// NextUntilNodes gets all following siblings of each element up to but not
398// including the element matched by the nodes. It returns a new Selection
399// object containing the matched elements.
400func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
401 return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
402 nil, nodes))
403}
404
405// PrevUntil gets all preceding siblings of each element up to but not
406// including the element matched by the selector. It returns a new Selection
407// object containing the matched elements.
408func (s *Selection) PrevUntil(selector string) *Selection {
409 return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
410 compileMatcher(selector), nil))
411}
412
413// PrevUntilMatcher gets all preceding siblings of each element up to but not
414// including the element matched by the matcher. It returns a new Selection
415// object containing the matched elements.
416func (s *Selection) PrevUntilMatcher(m Matcher) *Selection {
417 return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
418 m, nil))
419}
420
421// PrevUntilSelection gets all preceding siblings of each element up to but not
422// including the element matched by the Selection. It returns a new Selection
423// object containing the matched elements.
424func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
425 if sel == nil {
426 return s.PrevAll()
427 }
428 return s.PrevUntilNodes(sel.Nodes...)
429}
430
431// PrevUntilNodes gets all preceding siblings of each element up to but not
432// including the element matched by the nodes. It returns a new Selection
433// object containing the matched elements.
434func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
435 return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
436 nil, nodes))
437}
438
439// NextFilteredUntil is like NextUntil, with the option to filter
440// the results based on a selector string.
441// It returns a new Selection object containing the matched elements.
442func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
443 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
444 compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
445}
446
447// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
448// the results based on a matcher.
449// It returns a new Selection object containing the matched elements.
450func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
451 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
452 until, nil), filter)
453}
454
455// NextFilteredUntilSelection is like NextUntilSelection, with the
456// option to filter the results based on a selector string. It returns a new
457// Selection object containing the matched elements.
458func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
459 return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
460}
461
462// NextMatcherUntilSelection is like NextUntilSelection, with the
463// option to filter the results based on a matcher. It returns a new
464// Selection object containing the matched elements.
465func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
466 if sel == nil {
467 return s.NextMatcher(filter)
468 }
469 return s.NextMatcherUntilNodes(filter, sel.Nodes...)
470}
471
472// NextFilteredUntilNodes is like NextUntilNodes, with the
473// option to filter the results based on a selector string. It returns a new
474// Selection object containing the matched elements.
475func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
476 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
477 nil, nodes), compileMatcher(filterSelector))
478}
479
480// NextMatcherUntilNodes is like NextUntilNodes, with the
481// option to filter the results based on a matcher. It returns a new
482// Selection object containing the matched elements.
483func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
484 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
485 nil, nodes), filter)
486}
487
488// PrevFilteredUntil is like PrevUntil, with the option to filter
489// the results based on a selector string.
490// It returns a new Selection object containing the matched elements.
491func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
492 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
493 compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
494}
495
496// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
497// the results based on a matcher.
498// It returns a new Selection object containing the matched elements.
499func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
500 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
501 until, nil), filter)
502}
503
504// PrevFilteredUntilSelection is like PrevUntilSelection, with the
505// option to filter the results based on a selector string. It returns a new
506// Selection object containing the matched elements.
507func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
508 return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
509}
510
511// PrevMatcherUntilSelection is like PrevUntilSelection, with the
512// option to filter the results based on a matcher. It returns a new
513// Selection object containing the matched elements.
514func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
515 if sel == nil {
516 return s.PrevMatcher(filter)
517 }
518 return s.PrevMatcherUntilNodes(filter, sel.Nodes...)
519}
520
521// PrevFilteredUntilNodes is like PrevUntilNodes, with the
522// option to filter the results based on a selector string. It returns a new
523// Selection object containing the matched elements.
524func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
525 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
526 nil, nodes), compileMatcher(filterSelector))
527}
528
529// PrevMatcherUntilNodes is like PrevUntilNodes, with the
530// option to filter the results based on a matcher. It returns a new
531// Selection object containing the matched elements.
532func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
533 return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
534 nil, nodes), filter)
535}
536
537// Filter and push filters the nodes based on a matcher, and pushes the results
538// on the stack, with the srcSel as previous selection.
539func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection {
540 // Create a temporary Selection with the specified nodes to filter using winnow
541 sel := &Selection{nodes, srcSel.document, nil}
542 // Filter based on matcher and push on stack
543 return pushStack(srcSel, winnow(sel, m, true))
544}
545
546// Internal implementation of Find that return raw nodes.
547func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node {
548 // Map nodes to find the matches within the children of each node
549 return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
550 // Go down one level, becausejQuery's Find selects only within descendants
551 for c := n.FirstChild; c != nil; c = c.NextSibling {
552 if c.Type == html.ElementNode {
553 result = append(result, m.MatchAll(c)...)
554 }
555 }
556 return
557 })
558}
559
560// Internal implementation to get all parent nodes, stopping at the specified
561// node (or nil if no stop).
562func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node {
563 return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
564 for p := n.Parent; p != nil; p = p.Parent {
565 sel := newSingleSelection(p, nil)
566 if stopm != nil {
567 if sel.IsMatcher(stopm) {
568 break
569 }
570 } else if len(stopNodes) > 0 {
571 if sel.IsNodes(stopNodes...) {
572 break
573 }
574 }
575 if p.Type == html.ElementNode {
576 result = append(result, p)
577 }
578 }
579 return
580 })
581}
582
583// Internal implementation of sibling nodes that return a raw slice of matches.
584func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node {
585 var f func(*html.Node) bool
586
587 // If the requested siblings are ...Until, create the test function to
588 // determine if the until condition is reached (returns true if it is)
589 if st == siblingNextUntil || st == siblingPrevUntil {
590 f = func(n *html.Node) bool {
591 if untilm != nil {
592 // Matcher-based condition
593 sel := newSingleSelection(n, nil)
594 return sel.IsMatcher(untilm)
595 } else if len(untilNodes) > 0 {
596 // Nodes-based condition
597 sel := newSingleSelection(n, nil)
598 return sel.IsNodes(untilNodes...)
599 }
600 return false
601 }
602 }
603
604 return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
605 return getChildrenWithSiblingType(n.Parent, st, n, f)
606 })
607}
608
609// Gets the children nodes of each node in the specified slice of nodes,
610// based on the sibling type request.
611func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
612 return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
613 return getChildrenWithSiblingType(n, st, nil, nil)
614 })
615}
616
617// Gets the children of the specified parent, based on the requested sibling
618// type, skipping a specified node if required.
619func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
620 untilFunc func(*html.Node) bool) (result []*html.Node) {
621
622 // Create the iterator function
623 var iter = func(cur *html.Node) (ret *html.Node) {
624 // Based on the sibling type requested, iterate the right way
625 for {
626 switch st {
627 case siblingAll, siblingAllIncludingNonElements:
628 if cur == nil {
629 // First iteration, start with first child of parent
630 // Skip node if required
631 if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
632 ret = skipNode.NextSibling
633 }
634 } else {
635 // Skip node if required
636 if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
637 ret = skipNode.NextSibling
638 }
639 }
640 case siblingPrev, siblingPrevAll, siblingPrevUntil:
641 if cur == nil {
642 // Start with previous sibling of the skip node
643 ret = skipNode.PrevSibling
644 } else {
645 ret = cur.PrevSibling
646 }
647 case siblingNext, siblingNextAll, siblingNextUntil:
648 if cur == nil {
649 // Start with next sibling of the skip node
650 ret = skipNode.NextSibling
651 } else {
652 ret = cur.NextSibling
653 }
654 default:
655 panic("Invalid sibling type.")
656 }
657 if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
658 return
659 }
660 // Not a valid node, try again from this one
661 cur = ret
662 }
663 }
664
665 for c := iter(nil); c != nil; c = iter(c) {
666 // If this is an ...Until case, test before append (returns true
667 // if the until condition is reached)
668 if st == siblingNextUntil || st == siblingPrevUntil {
669 if untilFunc(c) {
670 return
671 }
672 }
673 result = append(result, c)
674 if st == siblingNext || st == siblingPrev {
675 // Only one node was requested (immediate next or previous), so exit
676 return
677 }
678 }
679 return
680}
681
682// Internal implementation of parent nodes that return a raw slice of Nodes.
683func getParentNodes(nodes []*html.Node) []*html.Node {
684 return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
685 if n.Parent != nil && n.Parent.Type == html.ElementNode {
686 return []*html.Node{n.Parent}
687 }
688 return nil
689 })
690}
691
692// Internal map function used by many traversing methods. Takes the source nodes
693// to iterate on and the mapping function that returns an array of nodes.
694// Returns an array of nodes mapped by calling the callback function once for
695// each node in the source nodes.
696func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
697 set := make(map[*html.Node]bool)
698 for i, n := range nodes {
699 if vals := f(i, n); len(vals) > 0 {
700 result = appendWithoutDuplicates(result, vals, set)
701 }
702 }
703 return result
704}