refactor of Find() methods to use mapNodes

This commit is contained in:
Martin Angers
2012-09-04 15:18:51 -04:00
parent 6b27ec19a5
commit 3aaca8f8ea
4 changed files with 31 additions and 73 deletions
+3
View File
@@ -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
-47
View File
@@ -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
+1 -1
View File
@@ -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
+27 -25
View File
@@ -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
}