From cfa964cff5dec2926435aba7934fae0474e13ff6 Mon Sep 17 00:00:00 2001 From: Martin Angers Date: Tue, 4 Sep 2012 10:11:00 -0400 Subject: [PATCH] add Parents() and ParentsFiltered(), with tests --- traversal.go | 38 ++++++++++++++++++++++++++++++++++++-- traversal_test.go | 10 ++++++++++ type_test.go | 4 ++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/traversal.go b/traversal.go index ca4bdbe..f81d73d 100644 --- a/traversal.go +++ b/traversal.go @@ -86,7 +86,7 @@ func (this *Selection) Parent() *Selection { return pushStack(this, getParentNodes(this.Nodes)) } -// Parent() 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. func (this *Selection) ParentFiltered(selector string) *Selection { // Get the Parent() unfiltered @@ -98,10 +98,44 @@ func (this *Selection) ParentFiltered(selector string) *Selection { return pushStack(this, nodes) } +// Parents() gets the ancestors of each element in the current Selection. It +// returns a new Selection object with the matched elements. +func (this *Selection) Parents() *Selection { + return pushStack(this, getParentsNodes(this.Nodes, nil)) +} + +// ParentsFiltered() gets the ancestors of each element in the current +// Selection. It returns a new Selection object with the matched elements. +func (this *Selection) ParentsFiltered(selector string) *Selection { + // Get the Parents() unfiltered + 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) +} + +// Internal implementation to get all parent nodes, stopping at the specified +// node (or nil if no stop). +func getParentsNodes(nodes []*html.Node, stopNode *html.Node) []*html.Node { + return mapNodes(nodes, func(i int, n *html.Node, stop *html.Node) (result []*html.Node) { + for p := n.Parent; p != nil; p = p.Parent { + if p == stopNode { + break + } + if p.Type == html.ElementNode { + result = append(result, p) + } + } + return + }, stopNode) +} + // Internal implementation of parent nodes that return a raw slice of Nodes. func getParentNodes(nodes []*html.Node) []*html.Node { return mapNodes(nodes, func(i int, n *html.Node, _ *html.Node) []*html.Node { - if n.Parent != nil { + if n.Parent != nil && n.Parent.Type == html.ElementNode { return []*html.Node{n.Parent} } return nil diff --git a/traversal_test.go b/traversal_test.go index 88d771a..a112d88 100644 --- a/traversal_test.go +++ b/traversal_test.go @@ -64,3 +64,13 @@ func TestParentFiltered(t *testing.T) { AssertLength(t, sel.Nodes, 1) AssertClass(t, sel, "hero-unit") } + +func TestParents(t *testing.T) { + sel := Doc().Root.Find(".container-fluid").Parents() + AssertLength(t, sel.Nodes, 8) +} + +func TestParentsFiltered(t *testing.T) { + sel := Doc().Root.Find(".container-fluid").ParentsFiltered("body") + AssertLength(t, sel.Nodes, 1) +} diff --git a/type_test.go b/type_test.go index 7ebf310..00a7510 100644 --- a/type_test.go +++ b/type_test.go @@ -18,9 +18,9 @@ func Doc() *Document { func AssertLength(t *testing.T, nodes []*html.Node, length int) { if len(nodes) != length { - t.Errorf("Expected %i nodes, found %v.", length, len(nodes)) + t.Errorf("Expected %d nodes, found %d.", length, len(nodes)) for i, n := range nodes { - t.Logf("Node %i: %+v.", i, n) + t.Logf("Node %d: %+v.", i, n) } } }