mirror of
https://github.com/PuerkitoBio/goquery.git
synced 2024-04-21 12:31:36 +00:00
panic on error, instead of empty selection (like jQuery/Sizzle)
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"exp/html"
|
||||
)
|
||||
|
||||
// TODO : Should return a new Selection object, use pushStack()
|
||||
|
||||
// Adds matching nodes to the current selection. Returns the same Selection object.
|
||||
// The new selector string is run in the context of the document of the Selection object.
|
||||
func (this *Selection) Add(selector string) *Selection {
|
||||
|
||||
+1
-5
@@ -32,17 +32,13 @@ func childrenWithContext(selector string, nodes ...*html.Node) []*html.Node {
|
||||
var matches []*html.Node
|
||||
var allChildren bool
|
||||
var sel cascadia.Selector
|
||||
var e error
|
||||
|
||||
selector = strings.TrimSpace(selector)
|
||||
if selector == "*" || selector == "" {
|
||||
// Get all children
|
||||
allChildren = true
|
||||
} else {
|
||||
if sel, e = cascadia.Compile(selector); e != nil {
|
||||
// Selector doesn't compile, empty selection
|
||||
return nil
|
||||
}
|
||||
sel = cascadia.MustCompile(selector)
|
||||
}
|
||||
|
||||
for _, n := range nodes {
|
||||
|
||||
@@ -37,11 +37,14 @@ necessary since multiple return values cannot be used to allow a chainable inter
|
||||
*/
|
||||
package goquery
|
||||
|
||||
// Positional Filtering: First(), Last(), Eq(), Get(), Index()
|
||||
// Positional Manipulation: First(), Last(), Eq(), Get(), Index(), Slice()
|
||||
// Filtering: Filter(), Not(), Has(), End()
|
||||
// "Expanding": Add(), AndSelf()
|
||||
// Reflect (query) node: Is(), Contains(), HasClass()
|
||||
// Inspect node: Contents(), Html(), Text(), Attr()
|
||||
// Inspect node: Contents(), Html(), Text(), Attr(), Val()
|
||||
// Selection "properties": Length(), Size()
|
||||
// Traversal: Find(), Children(), Parents...(), Next...(), Prev...(), Closest(), Siblings()
|
||||
// Iteration: Each(), Map()
|
||||
|
||||
// TODO : Benchmarks
|
||||
|
||||
@@ -73,17 +76,17 @@ package goquery
|
||||
// - Next() - Tree traversal
|
||||
// - NextAll() - Tree traversal
|
||||
// - NextUntil() - Tree traversal
|
||||
// - Not() - Filtering
|
||||
// x Not() - Filtering
|
||||
// - Parent() - Tree traversal
|
||||
// - Parents() - Tree traversal
|
||||
// - ParentsUntil() - Tree traversal
|
||||
// - Prev() - Tree traversal
|
||||
// - PrevAll() - Tree traversal
|
||||
// - PrevUntil() - Tree traversal
|
||||
// - PushStack() ? - Internals
|
||||
// x PushStack() ? - Internals
|
||||
// - Siblings() - Tree traversal
|
||||
// - Slice() - Filtering
|
||||
// - Text() - DOM Manipulation
|
||||
// - ToArray() Is not implemented, is Selection.Nodes
|
||||
// - Unique() ? Or internally only, to remove duplicates and maintain node order? - Utilities
|
||||
// x ToArray() Is not implemented, is Selection.Nodes
|
||||
// x Unique() ? Or internally only, to remove duplicates and maintain node order? - Utilities
|
||||
// - Val() ? - Attributes
|
||||
|
||||
@@ -6,61 +6,60 @@ import (
|
||||
)
|
||||
|
||||
func (this *Selection) Filter(selector string) *Selection {
|
||||
sel, e := cascadia.Compile(selector)
|
||||
if e != nil {
|
||||
// Selector doesn't compile, which means empty selection
|
||||
return newEmptySelection(this.document)
|
||||
}
|
||||
return pushStack(this, winnow(this, selector, true))
|
||||
}
|
||||
|
||||
return &Selection{sel.Filter(this.Nodes), this.document, nil}
|
||||
func (this *Selection) Not(selector string) *Selection {
|
||||
return pushStack(this, winnow(this, selector, false))
|
||||
}
|
||||
|
||||
func (this *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
|
||||
return &Selection{winnowFunction(this, f, true), this.document, nil}
|
||||
return pushStack(this, winnowFunction(this, f, true))
|
||||
}
|
||||
|
||||
func (this *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
|
||||
return &Selection{winnowFunction(this, f, false), this.document, nil}
|
||||
return pushStack(this, winnowFunction(this, f, false))
|
||||
}
|
||||
|
||||
func (this *Selection) FilterNode(node *html.Node) *Selection {
|
||||
if isInSlice(this.Nodes, node) {
|
||||
return newSingleSelection(node, this.document)
|
||||
}
|
||||
return newEmptySelection(this.document)
|
||||
func (this *Selection) FilterNodes(nodes ...*html.Node) *Selection {
|
||||
return pushStack(this, winnowNodes(this, nodes, true))
|
||||
}
|
||||
|
||||
func (this *Selection) NotNodes(nodes ...*html.Node) *Selection {
|
||||
return pushStack(this, winnowNodes(this, nodes, false))
|
||||
}
|
||||
|
||||
func (this *Selection) FilterSelection(s *Selection) *Selection {
|
||||
return pushStack(this, winnowNodes(this, s.Nodes, true))
|
||||
}
|
||||
|
||||
func (this *Selection) NotSelection(s *Selection) *Selection {
|
||||
return pushStack(this, winnowNodes(this, s.Nodes, false))
|
||||
}
|
||||
|
||||
func (this *Selection) Union(s *Selection) *Selection {
|
||||
return this.FilterSelection(s)
|
||||
}
|
||||
|
||||
func (this *Selection) FilterSelection(s *Selection) *Selection {
|
||||
var matches []*html.Node
|
||||
func winnow(sel *Selection, selector string, keep bool) []*html.Node {
|
||||
cs := cascadia.MustCompile(selector)
|
||||
|
||||
if s == nil {
|
||||
return newEmptySelection(this.document)
|
||||
// Optimize if keep is requested
|
||||
if keep {
|
||||
return cs.Filter(sel.Nodes)
|
||||
} else {
|
||||
// Use grep
|
||||
return grep(sel, func(i int, s *Selection) bool {
|
||||
return !cs(s.Get(0))
|
||||
})
|
||||
}
|
||||
|
||||
// Check for a match for each current selection
|
||||
for _, n1 := range this.Nodes {
|
||||
for _, n2 := range s.Nodes {
|
||||
if n1 == n2 && !isInSlice(matches, n2) {
|
||||
matches = append(matches, n1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return &Selection{matches, this.document, nil}
|
||||
return nil
|
||||
}
|
||||
|
||||
func winnow(sel *Selection, selector string) []*html.Node {
|
||||
cs, e := cascadia.Compile(selector)
|
||||
if e != nil {
|
||||
// Selector doesn't compile, which means empty selection
|
||||
return nil
|
||||
}
|
||||
|
||||
return cs.Filter(sel.Nodes)
|
||||
func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
|
||||
return grep(sel, func(i int, s *Selection) bool {
|
||||
return isInSlice(nodes, s.Get(0)) == keep
|
||||
})
|
||||
}
|
||||
|
||||
// Identical functionality for FilterFunction() and NotFunction(), only keep changes.
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ func TestFilterFunction(t *testing.T) {
|
||||
|
||||
func TestFilterNode(t *testing.T) {
|
||||
sel := doc.Find(".pvk-content")
|
||||
sel2 := sel.FilterNode(sel.Nodes[2])
|
||||
sel2 := sel.FilterNodes(sel.Nodes[2])
|
||||
if len(sel2.Nodes) != 1 {
|
||||
t.Errorf("Expected 1 node, found %v.", len(sel2.Nodes))
|
||||
}
|
||||
|
||||
@@ -19,12 +19,7 @@ func (this *Selection) Find(selector string) *Selection {
|
||||
func findWithContext(selector string, nodes ...*html.Node) []*html.Node {
|
||||
var matches []*html.Node
|
||||
|
||||
sel, e := cascadia.Compile(selector)
|
||||
if e != nil {
|
||||
// Selector doesn't compile, which means empty selection
|
||||
return nil
|
||||
}
|
||||
|
||||
sel := cascadia.MustCompile(selector)
|
||||
// Match the selector on each node
|
||||
for _, n := range nodes {
|
||||
matches = append(matches, sel.MatchAll(n)...)
|
||||
|
||||
+11
-4
@@ -12,12 +12,19 @@ func TestFind(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindInvalidSelector(t *testing.T) {
|
||||
defer func() {
|
||||
if e := recover(); e == nil {
|
||||
t.Error("Expected panic due to invalid selector.")
|
||||
}
|
||||
}()
|
||||
|
||||
doc.Find(":+ ^")
|
||||
}
|
||||
|
||||
func TestEachEmptySelection(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := doc.Find(":+ ^")
|
||||
if sel.Nodes != nil {
|
||||
t.Error("Expected a Selection object with Nodes == nil.")
|
||||
}
|
||||
sel := doc.Find("zzzz")
|
||||
sel.Each(func(i int, n *Selection) {
|
||||
cnt++
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user