From 3aaca8f8ea9ea56d38776f53c347a110f3e9b455 Mon Sep 17 00:00:00 2001 From: Martin Angers Date: Tue, 4 Sep 2012 15:18:51 -0400 Subject: [PATCH] refactor of Find() methods to use mapNodes --- README.md | 3 +++ doc.go | 47 ----------------------------------------------- expand.go | 2 +- traversal.go | 52 +++++++++++++++++++++++++++------------------------- 4 files changed, 31 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index f85613e..3f6e86f 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,12 @@ Coming soon... ## TODOs +* Implement NextUntil() and PrevUntil(). +* Check each method, make sure it uses only ElementNodes except when explicitly specified (i.e.: `Contents()`). Cascadia's selectors act only on elements. * Tests to validate that all methods returning a new `*Selection` "rollback" correctly to the previous Selection when calling `.End()`. * 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. ## License diff --git a/doc.go b/doc.go index d109364..81a73e5 100644 --- a/doc.go +++ b/doc.go @@ -94,9 +94,6 @@ The various methods are split into files based on the category of behavior: */ package goquery -// TODO : Test End() on all filtering/expanding/array/traversal functions, make -// sure it returns the same object as the previous selection. - // DONE array.go : Positional Manipulation: First(), Last(), Eq(), Get(), Index(), Slice() // DONE filter.go : Filtering: Filter(), Not(), Has(), End() // DONE expand.go : "Expanding": Add(), AndSelf() @@ -105,47 +102,3 @@ package goquery // traversal.go : Traversal: Contents(), Find(), Children(), Parents...(), Next...(), Prev...(), Siblings() // DONE iteration.go : Iteration: Each(), Map() // DONE type.go : Selection and Document - -// TODO : Benchmarks, examples - -// TODO : Check each method, if it applies to any node or only Element nodes (Cascadia's selectors already make sure of that) - -// TODO : Add the following methods: -// x Add() -// x AndSelf() -// x Attr() -// x Children() -// x Closest() ? -// x Contains() -// x Contents() -// x Each() -// x End() -// x Eq() -// x Filter() -// x Find() : Complete with Selection object and Node object as selectors - Tree Traversal -// x First() -// x Get() -// x Has() -// x HasClass() -// x Html() -// x Index() -// x Is() - Filtering -// x Last() -// x Length() / Size() -// x Map() -// x Next() - Tree traversal -// x NextAll() - Tree traversal -// - NextUntil() - Tree traversal -// x Not() -// x Parent() - Tree traversal -// x Parents() - Tree traversal -// x ParentsUntil() - Tree traversal -// x Prev() - Tree traversal -// x PrevAll() - Tree traversal -// - PrevUntil() - Tree traversal -// x PushStack() -// x Siblings() - Tree traversal -// x Slice() -// x Text() - DOM Manipulation -// x ToArray() -// x Unique() internally only diff --git a/expand.go b/expand.go index 5fae4ad..b1531fe 100644 --- a/expand.go +++ b/expand.go @@ -9,7 +9,7 @@ import ( // The selector string is run in the context of the document of the current // Selection object. func (this *Selection) Add(selector string) *Selection { - return this.AddNodes(findWithContext(selector, this.document.rootNode)...) + return this.AddNodes(findWithSelector([]*html.Node{this.document.rootNode}, selector)...) } // AddSelection() adds the specified Selection object's nodes to those in the diff --git a/traversal.go b/traversal.go index 2771348..3e48cb1 100644 --- a/traversal.go +++ b/traversal.go @@ -7,6 +7,8 @@ import ( type siblingType int +// Sibling type, used internally when iterating over children at the same +// level (siblings) to specify which nodes are requested. const ( siblintPrevAll siblingType = iota - 2 siblingPrev @@ -20,7 +22,7 @@ const ( // elements, filtered by a selector. It returns a new Selection object // containing these matched elements. func (this *Selection) Find(selector string) *Selection { - return pushStack(this, findWithContext(selector, this.Nodes...)) + return pushStack(this, findWithSelector(this.Nodes, selector)) } // FindSelection() gets the descendants of each element in the current @@ -37,14 +39,12 @@ func (this *Selection) FindSelection(sel *Selection) *Selection { // Selection, filtered by some nodes. It returns a new Selection object // containing these matched elements. func (this *Selection) FindNodes(nodes ...*html.Node) *Selection { - var matches []*html.Node - - for _, n := range nodes { + return pushStack(this, mapNodes(nodes, func(i int, n *html.Node) []*html.Node { if sliceContains(this.Nodes, n) { - matches = appendWithoutDuplicates(matches, []*html.Node{n}) + return []*html.Node{n} } - } - return pushStack(this, matches) + return nil + })) } // Contents() gets the children of each element in the Selection, @@ -225,6 +225,22 @@ func filterAndPush(srcSel *Selection, nodes []*html.Node, selector string) *Sele return pushStack(srcSel, winnow(sel, selector, true)) } +// Internal implementation of Find that return raw nodes. +func findWithSelector(nodes []*html.Node, selector string) []*html.Node { + // Compile the selector once + sel := cascadia.MustCompile(selector) + // Map nodes to find the matches within the children of each node + return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) { + // Go down one level, becausejQuery's Find() selects only within descendants + for _, c := range n.Child { + if c.Type == html.ElementNode { + result = append(result, sel.MatchAll(c)...) + } + } + return + }) +} + // Internal implementation to get all parent nodes, stopping at the specified // node (or nil if no stop). func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.Node) []*html.Node { @@ -260,12 +276,16 @@ func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node { }) } +// Gets the children nodes of each node in the specified slice of nodes, +// 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) }) } +// 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) (result []*html.Node) { var prev *html.Node var nFound bool @@ -326,21 +346,3 @@ func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result return } - -// Private internal implementation of the Find() methods -func findWithContext(selector string, nodes ...*html.Node) []*html.Node { - var matches []*html.Node - - // TODO : Refactor to use mapNodes? - sel := cascadia.MustCompile(selector) - // Match the selector on each node - for _, n := range nodes { - // Go down one level, becausejQuery's Find() selects only within descendants - for _, c := range n.Child { - if c.Type == html.ElementNode { - matches = appendWithoutDuplicates(matches, sel.MatchAll(c)) - } - } - } - return matches -}