add NextAll(), NextAllFiltered(), refactor all filtered methods

This commit is contained in:
Martin Angers
2012-09-04 13:34:52 -04:00
parent 9394a923e0
commit 19c7e27fc0
2 changed files with 64 additions and 83 deletions

4
doc.go
View File

@@ -133,14 +133,14 @@ package goquery
// x Last() // x Last()
// x Length() / Size() // x Length() / Size()
// x Map() // x Map()
// - Next() - Tree traversal // x Next() - Tree traversal
// - NextAll() - Tree traversal // - NextAll() - Tree traversal
// - NextUntil() - Tree traversal // - NextUntil() - Tree traversal
// x Not() // x Not()
// x Parent() - Tree traversal // x Parent() - Tree traversal
// x Parents() - Tree traversal // x Parents() - Tree traversal
// x ParentsUntil() - Tree traversal // x ParentsUntil() - Tree traversal
// - Prev() - Tree traversal // x Prev() - Tree traversal
// - PrevAll() - Tree traversal // - PrevAll() - Tree traversal
// - PrevUntil() - Tree traversal // - PrevUntil() - Tree traversal
// x PushStack() // x PushStack()

View File

@@ -5,6 +5,16 @@ import (
"exp/html" "exp/html"
) )
type siblingType int
const (
siblintPrevAll siblingType = iota - 2
siblingPrev
siblingAll
siblingNext
siblingNextAll
)
// Find() gets the descendants of each element in the current set of matched // Find() gets the descendants of each element in the current set of matched
// elements, filtered by a selector. It returns a new Selection object // elements, filtered by a selector. It returns a new Selection object
// containing these matched elements. // containing these matched elements.
@@ -19,12 +29,7 @@ func (this *Selection) FindSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return pushStack(this, nil) return pushStack(this, nil)
} }
return this.FindNodes(sel.Nodes...)
// Filter the specified Selection to only the current nodes that
// contain one of the Selection.
return sel.FilterFunction(func(i int, s *Selection) bool {
return sliceContains(this.Nodes, s.Nodes[0])
})
} }
// FindNodes() gets the descendants of each element in the current // FindNodes() gets the descendants of each element in the current
@@ -71,14 +76,7 @@ func (this *Selection) Children() *Selection {
// filtered by the specified selector. It returns a new // filtered by the specified selector. It returns a new
// Selection object containing these elements. // Selection object containing these elements.
func (this *Selection) ChildrenFiltered(selector string) *Selection { func (this *Selection) ChildrenFiltered(selector string) *Selection {
// Get the Children() unfiltered return filterAndPush(this, getSelectionChildren(this, true), selector)
nodes := getSelectionChildren(this, true)
// Create a temporary Selection to filter using winnow
sel := &Selection{nodes, this.document, nil}
// Filter based on selector
nodes = winnow(sel, selector, true)
// Push on the stack and return the "real" Selection
return pushStack(this, nodes)
} }
// Parent() gets the parent of each element in the Selection. It returns a // Parent() gets the parent of each element in the Selection. It returns a
@@ -90,13 +88,7 @@ func (this *Selection) Parent() *Selection {
// ParentFiltered() gets the parent of each element in the Selection filtered by a // ParentFiltered() gets the parent of each element in the Selection filtered by a
// selector. It returns a new Selection object containing the matched elements. // selector. It returns a new Selection object containing the matched elements.
func (this *Selection) ParentFiltered(selector string) *Selection { func (this *Selection) ParentFiltered(selector string) *Selection {
// Get the Parent() unfiltered return filterAndPush(this, getParentNodes(this.Nodes), selector)
nodes := getParentNodes(this.Nodes)
// Create a temporary Selection to filter using winnow
sel := &Selection{nodes, this.document, nil}
// Filter based on selector
nodes = winnow(sel, selector, true)
return pushStack(this, nodes)
} }
// Parents() gets the ancestors of each element in the current Selection. It // Parents() gets the ancestors of each element in the current Selection. It
@@ -108,13 +100,7 @@ func (this *Selection) Parents() *Selection {
// ParentsFiltered() gets the ancestors of each element in the current // ParentsFiltered() gets the ancestors of each element in the current
// Selection. It returns a new Selection object with the matched elements. // Selection. It returns a new Selection object with the matched elements.
func (this *Selection) ParentsFiltered(selector string) *Selection { func (this *Selection) ParentsFiltered(selector string) *Selection {
// Get the Parents() unfiltered return filterAndPush(this, getParentsNodes(this.Nodes, "", nil), selector)
nodes := getParentsNodes(this.Nodes, "", nil)
// Create a temporary Selection to filter using winnow
sel := &Selection{nodes, this.document, nil}
// Filter based on selector
nodes = winnow(sel, selector, true)
return pushStack(this, nodes)
} }
// ParentsUntil() gets the ancestors of each element in the Selection, up to but // ParentsUntil() gets the ancestors of each element in the Selection, up to but
@@ -145,14 +131,7 @@ func (this *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
// results based on a selector string. It returns a new Selection // results based on a selector string. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection { func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, untilSelector, nil), filterSelector)
// Get the ParentsUntil() unfiltered
nodes := getParentsNodes(this.Nodes, untilSelector, nil)
// Create a temporary Selection to filter using winnow
sel := &Selection{nodes, this.document, nil}
// Filter based on selector
nodes = winnow(sel, filterSelector, true)
return pushStack(this, nodes)
} }
// ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the // ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the
@@ -169,71 +148,68 @@ func (this *Selection) ParentsFilteredUntilSelection(filterSelector string, sel
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, "", nodes), filterSelector)
// Get the ParentsUntilNodes() unfiltered
n := getParentsNodes(this.Nodes, "", nodes)
// Create a temporary Selection to filter using winnow
sel := &Selection{n, this.document, nil}
// Filter based on selector
n = winnow(sel, filterSelector, true)
return pushStack(this, n)
} }
// Siblings() gets the siblings of each element in the Selection. It returns // Siblings() gets the siblings of each element in the Selection. It returns
// a new Selection object containing the matched elements. // a new Selection object containing the matched elements.
func (this *Selection) Siblings() *Selection { func (this *Selection) Siblings() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, 0)) return pushStack(this, getSiblingNodes(this.Nodes, siblingAll))
} }
// SiblingsFiltered() gets the siblings of each element in the Selection // SiblingsFiltered() gets the siblings of each element in the Selection
// filtered by a selector. It returns a new Selection object containing the // filtered by a selector. It returns a new Selection object containing the
// matched elements. // matched elements.
func (this *Selection) SiblingsFiltered(selector string) *Selection { func (this *Selection) SiblingsFiltered(selector string) *Selection {
// Get the Siblings() unfiltered return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll), selector)
n := getSiblingNodes(this.Nodes, 0)
// Create a temporary Selection to filter using winnow
sel := &Selection{n, this.document, nil}
// Filter based on selector
n = winnow(sel, selector, true)
return pushStack(this, n)
} }
// Next() gets the immediately following sibling of each element in the // Next() gets the immediately following sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Next() *Selection { func (this *Selection) Next() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, 1)) return pushStack(this, getSiblingNodes(this.Nodes, siblingNext))
} }
// NextFiltered() gets the immediately following sibling of each element in the // NextFiltered() gets the immediately following sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) NextFiltered(selector string) *Selection { func (this *Selection) NextFiltered(selector string) *Selection {
// Get the Next() unfiltered return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext), selector)
n := getSiblingNodes(this.Nodes, 1) }
// Create a temporary Selection to filter using winnow
sel := &Selection{n, this.document, nil} // NextAll() gets all the following siblings of each element in the
// Filter based on selector // Selection. It returns a new Selection object containing the matched elements.
n = winnow(sel, selector, true) func (this *Selection) NextAll() *Selection {
return pushStack(this, n) return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll))
}
// NextAllFiltered() gets all the following siblings of each element in the
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (this *Selection) NextAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll), selector)
} }
// Prev() gets the immediately preceding sibling of each element in the // Prev() gets the immediately preceding sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Prev() *Selection { func (this *Selection) Prev() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, -1)) return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev))
} }
// PrevFiltered() gets the immediately preceding sibling of each element in the // PrevFiltered() gets the immediately preceding sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) PrevFiltered(selector string) *Selection { func (this *Selection) PrevFiltered(selector string) *Selection {
// Get the Prev() unfiltered return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev), selector)
n := getSiblingNodes(this.Nodes, -1) }
// Create a temporary Selection to filter using winnow
sel := &Selection{n, this.document, nil} // Filter and push filters the nodes based on a selector, and pushes the results
// Filter based on selector // on the stack, with the srcSel as previous selection.
n = winnow(sel, selector, true) func filterAndPush(srcSel *Selection, nodes []*html.Node, selector string) *Selection {
return pushStack(this, n) // Create a temporary Selection with the specified nodes to filter using winnow
sel := &Selection{nodes, srcSel.document, nil}
// Filter based on selector and push on stack
return pushStack(srcSel, winnow(sel, selector, true))
} }
// Internal implementation to get all parent nodes, stopping at the specified // Internal implementation to get all parent nodes, stopping at the specified
@@ -260,13 +236,10 @@ func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.
} }
// Internal implementation of sibling nodes that return a raw slice of matches. // Internal implementation of sibling nodes that return a raw slice of matches.
func getSiblingNodes(nodes []*html.Node, siblingType int) []*html.Node { func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node {
// Sibling type means:
// -1 : previous node only
// 1 : next node only
// 0 : all but itself
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) { return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
var prev *html.Node var prev *html.Node
var nFound bool
// Get the parent and loop through all children // Get the parent and loop through all children
if p := n.Parent; p != nil { if p := n.Parent; p != nil {
@@ -274,20 +247,28 @@ func getSiblingNodes(nodes []*html.Node, siblingType int) []*html.Node {
// Care only about elements // Care only about elements
if c.Type == html.ElementNode { if c.Type == html.ElementNode {
// Is it the existing node? // Is it the existing node?
if c == n && siblingType == -1 { if c == n {
// We want the previous node only, so append it and return // Found the current node
if prev != nil { nFound = true
result = append(result, prev) if st == siblingPrev {
// We want the previous node only, so append it and return
if prev != nil {
result = append(result, prev)
}
return
} }
return } else if prev == n && st == siblingNext {
} else if prev == n && siblingType == 1 {
// We want only the next node and this is it, so append it and return // We want only the next node and this is it, so append it and return
result = append(result, c) result = append(result, c)
return return
} }
// Keep child as previous
prev = c prev = c
if c != n && siblingType == 0 {
// This is not the original node, so append it // If child is not the current node, check if sibling type requires
// to add it to the result.
if c != n && (st == siblingAll || (st == siblintPrevAll && !nFound) ||
(st == siblingNextAll && nFound)) {
result = append(result, c) result = append(result, c)
} }
} }