start work on PrevUntil() and NextUntil()

This commit is contained in:
Martin Angers
2012-09-06 10:27:42 -04:00
parent bf960f0855
commit a9c4ad4c8f
3 changed files with 61 additions and 22 deletions

View File

@@ -62,7 +62,7 @@ Taken from example_test.go:
title = s.Find("em").Text()
if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil {
// Not a valid float, ignore score
fmt.Printf("Review %d: %s - %s", i, band, title)
fmt.Printf("Review %d: %s - %s.\n", i, band, title)
} else {
// Print all, including score
fmt.Printf("Review %d: %s - %s (%2.1f).\n", i, band, title, score)
@@ -79,7 +79,6 @@ Taken from example_test.go:
## TODOs
* Implement NextUntil() and PrevUntil().
* Fix Prev...() functions to return nodes in the same order as jQuery (starting with the immediately preceding node, up until first child of the parent).
* Benchmarks so that future changes have a baseline to compare to.
* Add jQuery's `Closest()`? Other missing functions?
* Support negative indices in `Slice()`, like jQuery.

View File

@@ -28,7 +28,7 @@ func ExampleScrape_MetalReview() {
title = s.Find("em").Text()
if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil {
// Not a valid float, ignore score
fmt.Printf("Review %d: %s - %s", i, band, title)
fmt.Printf("Review %d: %s - %s.\n", i, band, title)
} else {
// Print all, including score
fmt.Printf("Review %d: %s - %s (%2.1f).\n", i, band, title, score)

View File

@@ -158,66 +158,70 @@ func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ..
// Siblings() gets the siblings of each element in the Selection. It returns
// a new Selection object containing the matched elements.
func (this *Selection) Siblings() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingAll))
return pushStack(this, getSiblingNodes(this.Nodes, siblingAll, "", nil))
}
// SiblingsFiltered() gets the siblings of each element in the Selection
// filtered by a selector. It returns a new Selection object containing the
// matched elements.
func (this *Selection) SiblingsFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll), selector)
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll, "", nil), selector)
}
// Next() gets the immediately following sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Next() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNext))
return pushStack(this, getSiblingNodes(this.Nodes, siblingNext, "", nil))
}
// NextFiltered() gets the immediately following sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (this *Selection) NextFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext), selector)
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext, "", nil), selector)
}
// NextAll() gets all the following siblings of each element in the
// Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) NextAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll))
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil))
}
// 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)
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil), selector)
}
// Prev() gets the immediately preceding sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Prev() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev))
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil))
}
// PrevFiltered() gets the immediately preceding sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (this *Selection) PrevFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev), selector)
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil), selector)
}
// PrevAll() gets all the preceding siblings of each element in the
// Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) PrevAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll))
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil))
}
// PrevAllFiltered() gets all the preceding siblings of each element in the
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (this *Selection) PrevAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll), selector)
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil), selector)
}
func (this *Selection) PrevUntil(selector string) *Selection {
return nil
}
// Filter and push filters the nodes based on a selector, and pushes the results
@@ -269,22 +273,50 @@ func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.
}
// Internal implementation of sibling nodes that return a raw slice of matches.
func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node {
func getSiblingNodes(nodes []*html.Node, st siblingType, untilSelector string,
untilNodes []*html.Node) []*html.Node {
var f func(*html.Node) bool
// If the requested siblings are ...Until(), create the test function to
// determine if the until condition is reached (returns true if it is)
if st == siblingNextUntil || st == siblingPrevUntil {
f = func(n *html.Node) bool {
if untilSelector != "" {
// Selector-based condition
sel := newSingleSelection(n, nil)
return sel.Is(untilSelector)
} else if len(untilNodes) > 0 {
// Nodes-based condition
sel := newSingleSelection(n, nil)
return sel.IsNodes(untilNodes...)
}
return false
}
}
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
// Get the parent and loop through all children
if p := n.Parent; p != nil {
// For Prev() calls that may return more than one node (unlike Prev()),
// start at the current node, and work backwards from there. Since
// Prev() returns only one node, no advantage to find the current node
// first and loop backwards to find the previous one. Better to just loop
// from the start and stop once it is found.
if st == siblingPrevAll || st == siblingPrevUntil {
// Find the index of this node
// Find the index of this node within its parent's children
for i, c := range p.Child {
if c == n {
// Looking for previous nodes, so start at index - 1 upwards
return getChildrenWithSiblingType(p, st, n, i-1, -1)
// Looking for previous nodes, so start at index - 1 backwards
return getChildrenWithSiblingType(p, st, n, i-1, -1, f)
}
}
// Should never get here.
panic(errors.New(fmt.Sprintf("Could not find node %+v in his parent's Child slice.", n)))
} else {
return getChildrenWithSiblingType(p, st, n, 0, 1)
// Standard loop from start moving forwards.
return getChildrenWithSiblingType(p, st, n, 0, 1, f)
}
}
return nil
@@ -295,14 +327,14 @@ func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node {
// based on the sibling type request.
func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
return getChildrenWithSiblingType(n, st, nil, 0, 1)
return getChildrenWithSiblingType(n, st, nil, 0, 1, nil)
})
}
// Gets the children of the specified parent, based on the requested sibling
// type, skipping a specified node if required.
func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
startIndex int, increment int) (result []*html.Node) {
startIndex int, increment int, untilFunc func(*html.Node) bool) (result []*html.Node) {
var prev *html.Node
var nFound bool
@@ -337,8 +369,16 @@ func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *htm
if c != skipNode &&
(st == siblingAll ||
st == siblingAllIncludingNonElements ||
(st == siblingPrevAll && !nFound) ||
(st == siblingNextAll && nFound)) {
((st == siblingPrevAll || st == siblingPrevUntil) && !nFound) ||
((st == siblingNextAll || st == siblingNextUntil) && nFound)) {
// If this is an ...Until() case, test before append (returns true
// if the until condition is reached)
if st == siblingNextUntil || st == siblingPrevUntil {
if untilFunc(c) {
return
}
}
result = append(result, c)
}
}