This commit is contained in:
Martin Angers
2014-04-11 12:00:17 -04:00
parent bc0c11b086
commit 2c6687f6d3
10 changed files with 346 additions and 334 deletions

View File

@@ -4,89 +4,89 @@ import (
"code.google.com/p/go.net/html" "code.google.com/p/go.net/html"
) )
// First() reduces the set of matched elements to the first in the set. // First reduces the set of matched elements to the first in the set.
// It returns a new Selection object, and an empty Selection object if the // It returns a new Selection object, and an empty Selection object if the
// the selection is empty. // the selection is empty.
func (this *Selection) First() *Selection { func (s *Selection) First() *Selection {
return this.Eq(0) return s.Eq(0)
} }
// Last() reduces the set of matched elements to the last in the set. // Last reduces the set of matched elements to the last in the set.
// It returns a new Selection object, and an empty Selection object if // It returns a new Selection object, and an empty Selection object if
// the selection is empty. // the selection is empty.
func (this *Selection) Last() *Selection { func (s *Selection) Last() *Selection {
return this.Eq(-1) return s.Eq(-1)
} }
// Eq() reduces the set of matched elements to the one at the specified index. // Eq reduces the set of matched elements to the one at the specified index.
// If a negative index is given, it counts backwards starting at the end of the // If a negative index is given, it counts backwards starting at the end of the
// set. It returns a new Selection object, and an empty Selection object if the // set. It returns a new Selection object, and an empty Selection object if the
// index is invalid. // index is invalid.
func (this *Selection) Eq(index int) *Selection { func (s *Selection) Eq(index int) *Selection {
if index < 0 { if index < 0 {
index += len(this.Nodes) index += len(s.Nodes)
} }
if index >= len(this.Nodes) || index < 0 { if index >= len(s.Nodes) || index < 0 {
return newEmptySelection(this.document) return newEmptySelection(s.document)
} }
return this.Slice(index, index+1) return s.Slice(index, index+1)
} }
// Slice() reduces the set of matched elements to a subset specified by a range // Slice reduces the set of matched elements to a subset specified by a range
// of indices. // of indices.
func (this *Selection) Slice(start int, end int) *Selection { func (s *Selection) Slice(start int, end int) *Selection {
if start < 0 { if start < 0 {
start += len(this.Nodes) start += len(s.Nodes)
} }
if end < 0 { if end < 0 {
end += len(this.Nodes) end += len(s.Nodes)
} }
return pushStack(this, this.Nodes[start:end]) return pushStack(s, s.Nodes[start:end])
} }
// Get() retrieves the underlying node at the specified index. // Get retrieves the underlying node at the specified index.
// Get() without parameter is not implemented, since the node array is available // Get without parameter is not implemented, since the node array is available
// on the Selection object. // on the Selection object.
func (this *Selection) Get(index int) *html.Node { func (s *Selection) Get(index int) *html.Node {
if index < 0 { if index < 0 {
index += len(this.Nodes) // Negative index gets from the end index += len(s.Nodes) // Negative index gets from the end
} }
return this.Nodes[index] return s.Nodes[index]
} }
// Index() returns the position of the first element within the Selection object // Index returns the position of the first element within the Selection object
// relative to its sibling elements. // relative to its sibling elements.
func (this *Selection) Index() int { func (s *Selection) Index() int {
if len(this.Nodes) > 0 { if len(s.Nodes) > 0 {
return newSingleSelection(this.Nodes[0], this.document).PrevAll().Length() return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
} }
return -1 return -1
} }
// IndexSelector() returns the position of the first element within the // IndexSelector returns the position of the first element within the
// Selection object relative to the elements matched by the selector, or -1 if // Selection object relative to the elements matched by the selector, or -1 if
// not found. // not found.
func (this *Selection) IndexSelector(selector string) int { func (s *Selection) IndexSelector(selector string) int {
if len(this.Nodes) > 0 { if len(s.Nodes) > 0 {
sel := this.document.Find(selector) sel := s.document.Find(selector)
return indexInSlice(sel.Nodes, this.Nodes[0]) return indexInSlice(sel.Nodes, s.Nodes[0])
} }
return -1 return -1
} }
// IndexOfNode() returns the position of the specified node within the Selection // IndexOfNode returns the position of the specified node within the Selection
// object, or -1 if not found. // object, or -1 if not found.
func (this *Selection) IndexOfNode(node *html.Node) int { func (s *Selection) IndexOfNode(node *html.Node) int {
return indexInSlice(this.Nodes, node) return indexInSlice(s.Nodes, node)
} }
// IndexOfSelection() returns the position of the first node in the specified // IndexOfSelection returns the position of the first node in the specified
// Selection object within this Selection object, or -1 if not found. // Selection object within this Selection object, or -1 if not found.
func (this *Selection) IndexOfSelection(s *Selection) int { func (s *Selection) IndexOfSelection(sel *Selection) int {
if s != nil && len(s.Nodes) > 0 { if sel != nil && len(sel.Nodes) > 0 {
return indexInSlice(this.Nodes, s.Nodes[0]) return indexInSlice(s.Nodes, sel.Nodes[0])
} }
return -1 return -1
} }

26
doc.go
View File

@@ -23,15 +23,27 @@
/* /*
Package goquery implements features similar to jQuery, including the chainable Package goquery implements features similar to jQuery, including the chainable
syntax, to manipulate and query an HTML document (the modification functions of jQuery are not included). syntax, to manipulate and query an HTML document.
It uses Cascadia as CSS selector (similar to Sizzle for jQuery). GoQuery brings a syntax and a set of features similar to jQuery to the Go language.
It is based on Go's net/html package and the CSS Selector library cascadia. Since
the net/html parser returns tokens (nodes), and not a full-featured DOM object,
jQuery's manipulation and modification functions have been left off (no point in
modifying data in the parsed tree of the HTML, it has no effect).
To provide a chainable interface, error management is strict, and goquery panics Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
if an invalid Cascadia selector is used (this is consistent with the behavior of the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
jQuery/Sizzle/document.querySelectorAll, where an error is thrown). This is
necessary since multiple return values cannot be used to allow a chainable Supported functions are query-oriented features (`hasClass()`, `attr()` and the likes),
interface. as well as traversing functions that make sense given what we have to work with.
This makes GoQuery a great library for scraping web pages.
Syntax-wise, it is as close as possible to jQuery, with the same function names when
possible, and that warm and fuzzy chainable interface. jQuery being the
ultra-popular library that it is, I felt that writing a similar HTML-manipulating
library was better to follow its API than to start anew (in the same spirit as
Go's fmt package), even though some of its methods are less than intuitive (looking
at you, index()...).
It is hosted on GitHub, along with additional documentation in the README.md It is hosted on GitHub, along with additional documentation in the README.md
file: https://github.com/puerkitobio/goquery file: https://github.com/puerkitobio/goquery

View File

@@ -4,37 +4,37 @@ import (
"code.google.com/p/go.net/html" "code.google.com/p/go.net/html"
) )
// Add() adds the selector string's matching nodes to those in the current // Add adds the selector string's matching nodes to those in the current
// selection and returns a new Selection object. // selection and returns a new Selection object.
// The selector string is run in the context of the document of the current // The selector string is run in the context of the document of the current
// Selection object. // Selection object.
func (this *Selection) Add(selector string) *Selection { func (s *Selection) Add(selector string) *Selection {
return this.AddNodes(findWithSelector([]*html.Node{this.document.rootNode}, selector)...) return s.AddNodes(findWithSelector([]*html.Node{s.document.rootNode}, selector)...)
} }
// AddSelection() adds the specified Selection object's nodes to those in the // AddSelection adds the specified Selection object's nodes to those in the
// current selection and returns a new Selection object. // current selection and returns a new Selection object.
func (this *Selection) AddSelection(sel *Selection) *Selection { func (s *Selection) AddSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.AddNodes() return s.AddNodes()
} }
return this.AddNodes(sel.Nodes...) return s.AddNodes(sel.Nodes...)
} }
// Union() is an alias for AddSelection(). // Union is an alias for AddSelection.
func (this *Selection) Union(sel *Selection) *Selection { func (s *Selection) Union(sel *Selection) *Selection {
return this.AddSelection(sel) return s.AddSelection(sel)
} }
// AddNodes() adds the specified nodes to those in the // AddNodes adds the specified nodes to those in the
// current selection and returns a new Selection object. // current selection and returns a new Selection object.
func (this *Selection) AddNodes(nodes ...*html.Node) *Selection { func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
return pushStack(this, appendWithoutDuplicates(this.Nodes, nodes)) return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes))
} }
// AndSelf() adds the previous set of elements on the stack to the current set. // AndSelf adds the previous set of elements on the stack to the current set.
// It returns a new Selection object containing the current Selection combined // It returns a new Selection object containing the current Selection combined
// with the previous one. // with the previous one.
func (this *Selection) AndSelf() *Selection { func (s *Selection) AndSelf() *Selection {
return this.AddSelection(this.prevSel) return s.AddSelection(s.prevSel)
} }

View File

@@ -5,82 +5,82 @@ import (
"code.google.com/p/go.net/html" "code.google.com/p/go.net/html"
) )
// Filter() reduces the set of matched elements to those that match the selector string. // Filter reduces the set of matched elements to those that match the selector string.
// It returns a new Selection object for this subset of matching elements. // It returns a new Selection object for this subset of matching elements.
func (this *Selection) Filter(selector string) *Selection { func (s *Selection) Filter(selector string) *Selection {
return pushStack(this, winnow(this, selector, true)) return pushStack(s, winnow(s, selector, true))
} }
// Not() removes elements from the Selection that match the selector string. // Not removes elements from the Selection that match the selector string.
// It returns a new Selection object with the matching elements removed. // It returns a new Selection object with the matching elements removed.
func (this *Selection) Not(selector string) *Selection { func (s *Selection) Not(selector string) *Selection {
return pushStack(this, winnow(this, selector, false)) return pushStack(s, winnow(s, selector, false))
} }
// FilterFunction() reduces the set of matched elements to those that pass the function's test. // FilterFunction reduces the set of matched elements to those that pass the function's test.
// It returns a new Selection object for this subset of elements. // It returns a new Selection object for this subset of elements.
func (this *Selection) FilterFunction(f func(int, *Selection) bool) *Selection { func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
return pushStack(this, winnowFunction(this, f, true)) return pushStack(s, winnowFunction(s, f, true))
} }
// Not() removes elements from the Selection that pass the function's test. // Not removes elements from the Selection that pass the function's test.
// It returns a new Selection object with the matching elements removed. // It returns a new Selection object with the matching elements removed.
func (this *Selection) NotFunction(f func(int, *Selection) bool) *Selection { func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
return pushStack(this, winnowFunction(this, f, false)) return pushStack(s, winnowFunction(s, f, false))
} }
// FilterNodes() reduces the set of matched elements to those that match the specified nodes. // FilterNodes reduces the set of matched elements to those that match the specified nodes.
// It returns a new Selection object for this subset of elements. // It returns a new Selection object for this subset of elements.
func (this *Selection) FilterNodes(nodes ...*html.Node) *Selection { func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
return pushStack(this, winnowNodes(this, nodes, true)) return pushStack(s, winnowNodes(s, nodes, true))
} }
// Not() removes elements from the Selection that match the specified nodes. // Not removes elements from the Selection that match the specified nodes.
// It returns a new Selection object with the matching elements removed. // It returns a new Selection object with the matching elements removed.
func (this *Selection) NotNodes(nodes ...*html.Node) *Selection { func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
return pushStack(this, winnowNodes(this, nodes, false)) return pushStack(s, winnowNodes(s, nodes, false))
} }
// FilterSelection() reduces the set of matched elements to those that match a // FilterSelection reduces the set of matched elements to those that match a
// node in the specified Selection object. // node in the specified Selection object.
// It returns a new Selection object for this subset of elements. // It returns a new Selection object for this subset of elements.
func (this *Selection) FilterSelection(s *Selection) *Selection { func (s *Selection) FilterSelection(sel *Selection) *Selection {
if s == nil { if sel == nil {
return pushStack(this, winnowNodes(this, nil, true)) return pushStack(s, winnowNodes(s, nil, true))
} }
return pushStack(this, winnowNodes(this, s.Nodes, true)) return pushStack(s, winnowNodes(s, sel.Nodes, true))
} }
// Not() removes elements from the Selection that match a node in the specified // Not removes elements from the Selection that match a node in the specified
// Selection object. // Selection object.
// It returns a new Selection object with the matching elements removed. // It returns a new Selection object with the matching elements removed.
func (this *Selection) NotSelection(s *Selection) *Selection { func (s *Selection) NotSelection(sel *Selection) *Selection {
if s == nil { if sel == nil {
return pushStack(this, winnowNodes(this, nil, false)) return pushStack(s, winnowNodes(s, nil, false))
} }
return pushStack(this, winnowNodes(this, s.Nodes, false)) return pushStack(s, winnowNodes(s, sel.Nodes, false))
} }
// Intersection() is an alias for FilterSelection(). // Intersection is an alias for FilterSelection.
func (this *Selection) Intersection(s *Selection) *Selection { func (s *Selection) Intersection(sel *Selection) *Selection {
return this.FilterSelection(s) return s.FilterSelection(sel)
} }
// Has() reduces the set of matched elements to those that have a descendant // Has reduces the set of matched elements to those that have a descendant
// that matches the selector. // that matches the selector.
// It returns a new Selection object with the matching elements. // It returns a new Selection object with the matching elements.
func (this *Selection) Has(selector string) *Selection { func (s *Selection) Has(selector string) *Selection {
return this.HasSelection(this.document.Find(selector)) return s.HasSelection(s.document.Find(selector))
} }
// HasNodes() reduces the set of matched elements to those that have a // HasNodes reduces the set of matched elements to those that have a
// descendant that matches one of the nodes. // descendant that matches one of the nodes.
// It returns a new Selection object with the matching elements. // It returns a new Selection object with the matching elements.
func (this *Selection) HasNodes(nodes ...*html.Node) *Selection { func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
return this.FilterFunction(func(_ int, s *Selection) bool { return s.FilterFunction(func(_ int, sel *Selection) bool {
// Add all nodes that contain one of the specified nodes // Add all nodes that contain one of the specified nodes
for _, n := range nodes { for _, n := range nodes {
if s.Contains(n) { if sel.Contains(n) {
return true return true
} }
} }
@@ -88,23 +88,23 @@ func (this *Selection) HasNodes(nodes ...*html.Node) *Selection {
}) })
} }
// HasSelection() reduces the set of matched elements to those that have a // HasSelection reduces the set of matched elements to those that have a
// descendant that matches one of the nodes of the specified Selection object. // descendant that matches one of the nodes of the specified Selection object.
// It returns a new Selection object with the matching elements. // It returns a new Selection object with the matching elements.
func (this *Selection) HasSelection(sel *Selection) *Selection { func (s *Selection) HasSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.HasNodes() return s.HasNodes()
} }
return this.HasNodes(sel.Nodes...) return s.HasNodes(sel.Nodes...)
} }
// End() ends the most recent filtering operation in the current chain and // End ends the most recent filtering operation in the current chain and
// returns the set of matched elements to its previous state. // returns the set of matched elements to its previous state.
func (this *Selection) End() *Selection { func (s *Selection) End() *Selection {
if this.prevSel != nil { if s.prevSel != nil {
return this.prevSel return s.prevSel
} }
return newEmptySelection(this.document) return newEmptySelection(s.document)
} }
// Filter based on a selector string, and the indicator to keep (Filter) or // Filter based on a selector string, and the indicator to keep (Filter) or

View File

@@ -1,32 +1,32 @@
package goquery package goquery
// Each() iterates over a Selection object, executing a function for each // Each iterates over a Selection object, executing a function for each
// matched element. It returns the current Selection object. // matched element. It returns the current Selection object.
func (this *Selection) Each(f func(int, *Selection)) *Selection { func (s *Selection) Each(f func(int, *Selection)) *Selection {
for i, n := range this.Nodes { for i, n := range s.Nodes {
f(i, newSingleSelection(n, this.document)) f(i, newSingleSelection(n, s.document))
} }
return this return s
} }
// EachWithBreak() iterates over a Selection object, executing a function for each // EachWithBreak iterates over a Selection object, executing a function for each
// matched element. It is identical to `Each()` except that it is possible to break // matched element. It is identical to Each except that it is possible to break
// out of the loop by returning `false` in the callback function. It returns the // out of the loop by returning false in the callback function. It returns the
// current Selection object. // current Selection object.
func (this *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection { func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
for i, n := range this.Nodes { for i, n := range s.Nodes {
if !f(i, newSingleSelection(n, this.document)) { if !f(i, newSingleSelection(n, s.document)) {
return this return s
} }
} }
return this return s
} }
// Map() passes each element in the current matched set through a function, // Map passes each element in the current matched set through a function,
// producing a slice of string holding the returned values. // producing a slice of string holding the returned values.
func (this *Selection) Map(f func(int, *Selection) string) (result []string) { func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
for i, n := range this.Nodes { for i, n := range s.Nodes {
result = append(result, f(i, newSingleSelection(n, this.document))) result = append(result, f(i, newSingleSelection(n, s.document)))
} }
return result return result

View File

@@ -2,50 +2,51 @@ package goquery
import ( import (
"bytes" "bytes"
"code.google.com/p/go.net/html" "code.google.com/p/go.net/html"
) )
// Attr() gets the specified attribute's value for the first element in the // Attr gets the specified attribute's value for the first element in the
// Selection. To get the value for each element individually, use a looping // Selection. To get the value for each element individually, use a looping
// construct such as Each() or Map() method. // construct such as Each or Map method.
func (this *Selection) Attr(attrName string) (val string, exists bool) { func (s *Selection) Attr(attrName string) (val string, exists bool) {
if len(this.Nodes) == 0 { if len(s.Nodes) == 0 {
return return
} }
return getAttributeValue(attrName, this.Nodes[0]) return getAttributeValue(attrName, s.Nodes[0])
} }
// Text() gets the combined text contents of each element in the set of matched // Text gets the combined text contents of each element in the set of matched
// elements, including their descendants. // elements, including their descendants.
func (this *Selection) Text() string { func (s *Selection) Text() string {
var buf bytes.Buffer var buf bytes.Buffer
// Slightly optimized vs calling Each(): no single selection object created // Slightly optimized vs calling Each: no single selection object created
for _, n := range this.Nodes { for _, n := range s.Nodes {
buf.WriteString(getNodeText(n)) buf.WriteString(getNodeText(n))
} }
return buf.String() return buf.String()
} }
// Size() is an alias for Length(). // Size is an alias for Length.
func (this *Selection) Size() int { func (s *Selection) Size() int {
return this.Length() return s.Length()
} }
// Length() returns the number of elements in the Selection object. // Length returns the number of elements in the Selection object.
func (this *Selection) Length() int { func (s *Selection) Length() int {
return len(this.Nodes) return len(s.Nodes)
} }
// Html() gets the HTML contents of the first element in the set of matched // Html gets the HTML contents of the first element in the set of matched
// elements. It includes text and comment nodes. // elements. It includes text and comment nodes.
func (this *Selection) Html() (ret string, e error) { func (s *Selection) Html() (ret string, e error) {
// Since there is no .innerHtml, the HTML content must be re-created from // Since there is no .innerHtml, the HTML content must be re-created from
// the nodes usint html.Render(). // the nodes using html.Render.
var buf bytes.Buffer var buf bytes.Buffer
if len(this.Nodes) > 0 { if len(s.Nodes) > 0 {
for c := this.Nodes[0].FirstChild; c != nil; c = c.NextSibling { for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
e = html.Render(&buf, c) e = html.Render(&buf, c)
if e != nil { if e != nil {
return return

View File

@@ -1,53 +1,54 @@
package goquery package goquery
import ( import (
"code.google.com/p/cascadia"
"code.google.com/p/go.net/html"
"regexp" "regexp"
"strings" "strings"
"code.google.com/p/cascadia"
"code.google.com/p/go.net/html"
) )
var rxClassTrim = regexp.MustCompile("[\t\r\n]") var rxClassTrim = regexp.MustCompile("[\t\r\n]")
// Is() checks the current matched set of elements against a selector and // Is checks the current matched set of elements against a selector and
// returns true if at least one of these elements matches. // returns true if at least one of these elements matches.
func (this *Selection) Is(selector string) bool { func (s *Selection) Is(selector string) bool {
if len(this.Nodes) > 0 { if len(s.Nodes) > 0 {
// Attempt a match with the selector // Attempt a match with the selector
cs := cascadia.MustCompile(selector) cs := cascadia.MustCompile(selector)
if len(this.Nodes) == 1 { if len(s.Nodes) == 1 {
return cs.Match(this.Nodes[0]) return cs.Match(s.Nodes[0])
} else { } else {
return len(cs.Filter(this.Nodes)) > 0 return len(cs.Filter(s.Nodes)) > 0
} }
} }
return false return false
} }
// IsFunction() checks the current matched set of elements against a predicate and // IsFunction checks the current matched set of elements against a predicate and
// returns true if at least one of these elements matches. // returns true if at least one of these elements matches.
func (this *Selection) IsFunction(f func(int, *Selection) bool) bool { func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
return this.FilterFunction(f).Length() > 0 return s.FilterFunction(f).Length() > 0
} }
// IsSelection() checks the current matched set of elements against a Selection object // IsSelection checks the current matched set of elements against a Selection object
// and returns true if at least one of these elements matches. // and returns true if at least one of these elements matches.
func (this *Selection) IsSelection(s *Selection) bool { func (s *Selection) IsSelection(sel *Selection) bool {
return this.FilterSelection(s).Length() > 0 return s.FilterSelection(sel).Length() > 0
} }
// IsNodes() checks the current matched set of elements against the specified nodes // IsNodes checks the current matched set of elements against the specified nodes
// and returns true if at least one of these elements matches. // and returns true if at least one of these elements matches.
func (this *Selection) IsNodes(nodes ...*html.Node) bool { func (s *Selection) IsNodes(nodes ...*html.Node) bool {
return this.FilterNodes(nodes...).Length() > 0 return s.FilterNodes(nodes...).Length() > 0
} }
// HasClass() determines whether any of the matched elements are assigned the // HasClass determines whether any of the matched elements are assigned the
// given class. // given class.
func (this *Selection) HasClass(class string) bool { func (s *Selection) HasClass(class string) bool {
class = " " + class + " " class = " " + class + " "
for _, n := range this.Nodes { for _, n := range s.Nodes {
// Applies only to element nodes // Applies only to element nodes
if n.Type == html.ElementNode { if n.Type == html.ElementNode {
if elClass, ok := getAttributeValue("class", n); ok { if elClass, ok := getAttributeValue("class", n); ok {
@@ -61,11 +62,11 @@ func (this *Selection) HasClass(class string) bool {
return false return false
} }
// Contains() returns true if the specified Node is within, // Contains returns true if the specified Node is within,
// at any depth, one of the nodes in the Selection object. // at any depth, one of the nodes in the Selection object.
// It is NOT inclusive, to behave like jQuery's implementation, and // It is NOT inclusive, to behave like jQuery's implementation, and
// unlike Javascript's .contains(), so if the contained // unlike Javascript's .contains, so if the contained
// node is itself in the selection, it returns false. // node is itself in the selection, it returns false.
func (this *Selection) Contains(n *html.Node) bool { func (s *Selection) Contains(n *html.Node) bool {
return sliceContains(this.Nodes, n) return sliceContains(s.Nodes, n)
} }

View File

@@ -20,85 +20,85 @@ const (
siblingAllIncludingNonElements siblingAllIncludingNonElements
) )
// Find() gets the descendants of each element in the current set of matched // Find gets the descendants of each element in the current set of matched
// elements, filtered by a selector. It returns a new Selection object // elements, filtered by a selector. It returns a new Selection object
// containing these matched elements. // containing these matched elements.
func (this *Selection) Find(selector string) *Selection { func (s *Selection) Find(selector string) *Selection {
return pushStack(this, findWithSelector(this.Nodes, selector)) return pushStack(s, findWithSelector(s.Nodes, selector))
} }
// FindSelection() gets the descendants of each element in the current // FindSelection gets the descendants of each element in the current
// Selection, filtered by a Selection. It returns a new Selection object // Selection, filtered by a Selection. It returns a new Selection object
// containing these matched elements. // containing these matched elements.
func (this *Selection) FindSelection(sel *Selection) *Selection { func (s *Selection) FindSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return pushStack(this, nil) return pushStack(s, nil)
} }
return this.FindNodes(sel.Nodes...) return s.FindNodes(sel.Nodes...)
} }
// FindNodes() gets the descendants of each element in the current // FindNodes gets the descendants of each element in the current
// Selection, filtered by some nodes. It returns a new Selection object // Selection, filtered by some nodes. It returns a new Selection object
// containing these matched elements. // containing these matched elements.
func (this *Selection) FindNodes(nodes ...*html.Node) *Selection { func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
return pushStack(this, mapNodes(nodes, func(i int, n *html.Node) []*html.Node { return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
if sliceContains(this.Nodes, n) { if sliceContains(s.Nodes, n) {
return []*html.Node{n} return []*html.Node{n}
} }
return nil return nil
})) }))
} }
// Contents() gets the children of each element in the Selection, // Contents gets the children of each element in the Selection,
// including text and comment nodes. It returns a new Selection object // including text and comment nodes. It returns a new Selection object
// containing these elements. // containing these elements.
func (this *Selection) Contents() *Selection { func (s *Selection) Contents() *Selection {
return pushStack(this, getChildrenNodes(this.Nodes, siblingAllIncludingNonElements)) return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
} }
// ContentsFiltered() gets the children of each element in the Selection, // ContentsFiltered gets the children of each element in the Selection,
// filtered by the specified selector. It returns a new Selection // filtered by the specified selector. It returns a new Selection
// object containing these elements. Since selectors only act on Element nodes, // object containing these elements. Since selectors only act on Element nodes,
// this function is an alias to ChildrenFiltered() unless the selector is empty, // this function is an alias to ChildrenFiltered unless the selector is empty,
// in which case it is an alias to Contents(). // in which case it is an alias to Contents.
func (this *Selection) ContentsFiltered(selector string) *Selection { func (s *Selection) ContentsFiltered(selector string) *Selection {
if selector != "" { if selector != "" {
return this.ChildrenFiltered(selector) return s.ChildrenFiltered(selector)
} }
return this.Contents() return s.Contents()
} }
// Children() gets the child elements of each element in the Selection. // Children gets the child elements of each element in the Selection.
// It returns a new Selection object containing these elements. // It returns a new Selection object containing these elements.
func (this *Selection) Children() *Selection { func (s *Selection) Children() *Selection {
return pushStack(this, getChildrenNodes(this.Nodes, siblingAll)) return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
} }
// ChildrenFiltered() gets the child elements of each element in the Selection, // ChildrenFiltered gets the child elements of each element in the Selection,
// filtered by the specified selector. It returns a new // filtered by the specified selector. It returns a new
// Selection object containing these elements. // Selection object containing these elements.
func (this *Selection) ChildrenFiltered(selector string) *Selection { func (s *Selection) ChildrenFiltered(selector string) *Selection {
return filterAndPush(this, getChildrenNodes(this.Nodes, siblingAll), selector) return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), selector)
} }
// Parent() gets the parent of each element in the Selection. It returns a // Parent gets the parent of each element in the Selection. It returns a
// new Selection object containing the matched elements. // new Selection object containing the matched elements.
func (this *Selection) Parent() *Selection { func (s *Selection) Parent() *Selection {
return pushStack(this, getParentNodes(this.Nodes)) return pushStack(s, getParentNodes(s.Nodes))
} }
// ParentFiltered() 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. // selector. It returns a new Selection object containing the matched elements.
func (this *Selection) ParentFiltered(selector string) *Selection { func (s *Selection) ParentFiltered(selector string) *Selection {
return filterAndPush(this, getParentNodes(this.Nodes), selector) return filterAndPush(s, getParentNodes(s.Nodes), selector)
} }
// Closest() gets the first element that matches the selector by testing the // Closest gets the first element that matches the selector by testing the
// element itself and traversing up through its ancestors in the DOM tree. // element itself and traversing up through its ancestors in the DOM tree.
func (this *Selection) Closest(selector string) *Selection { func (s *Selection) Closest(selector string) *Selection {
cs := cascadia.MustCompile(selector) cs := cascadia.MustCompile(selector)
return pushStack(this, mapNodes(this.Nodes, func(i int, n *html.Node) []*html.Node { return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
// For each node in the selection, test the node itself, then each parent // For each node in the selection, test the node itself, then each parent
// until a match is found. // until a match is found.
for ; n != nil; n = n.Parent { for ; n != nil; n = n.Parent {
@@ -110,10 +110,10 @@ func (this *Selection) Closest(selector string) *Selection {
})) }))
} }
// ClosestNodes() gets the first element that matches one of the nodes by testing the // ClosestNodes gets the first element that matches one of the nodes by testing the
// element itself and traversing up through its ancestors in the DOM tree. // element itself and traversing up through its ancestors in the DOM tree.
func (this *Selection) ClosestNodes(nodes ...*html.Node) *Selection { func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
return pushStack(this, mapNodes(this.Nodes, func(i int, n *html.Node) []*html.Node { return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
// For each node in the selection, test the node itself, then each parent // For each node in the selection, test the node itself, then each parent
// until a match is found. // until a match is found.
for ; n != nil; n = n.Parent { for ; n != nil; n = n.Parent {
@@ -125,242 +125,242 @@ func (this *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
})) }))
} }
// ClosestSelection() gets the first element that matches one of the nodes in the // ClosestSelection gets the first element that matches one of the nodes in the
// Selection by testing the element itself and traversing up through its ancestors // Selection by testing the element itself and traversing up through its ancestors
// in the DOM tree. // in the DOM tree.
func (this *Selection) ClosestSelection(s *Selection) *Selection { func (s *Selection) ClosestSelection(sel *Selection) *Selection {
if s == nil { if sel == nil {
return pushStack(this, nil) return pushStack(s, nil)
} }
return this.ClosestNodes(s.Nodes...) return s.ClosestNodes(sel.Nodes...)
} }
// Parents() gets the ancestors of each element in the current Selection. It // Parents gets the ancestors of each element in the current Selection. It
// returns a new Selection object with the matched elements. // returns a new Selection object with the matched elements.
func (this *Selection) Parents() *Selection { func (s *Selection) Parents() *Selection {
return pushStack(this, getParentsNodes(this.Nodes, "", nil)) return pushStack(s, getParentsNodes(s.Nodes, "", nil))
} }
// ParentsFiltered() gets the ancestors of each element in the current // ParentsFiltered gets the ancestors of each element in the current
// Selection. It returns a new Selection object with the matched elements. // Selection. It returns a new Selection object with the matched elements.
func (this *Selection) ParentsFiltered(selector string) *Selection { func (s *Selection) ParentsFiltered(selector string) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, "", nil), selector) return filterAndPush(s, getParentsNodes(s.Nodes, "", nil), selector)
} }
// ParentsUntil() gets the ancestors of each element in the Selection, up to but // ParentsUntil gets the ancestors of each element in the Selection, up to but
// not including the element matched by the selector. It returns a new Selection // not including the element matched by the selector. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) ParentsUntil(selector string) *Selection { func (s *Selection) ParentsUntil(selector string) *Selection {
return pushStack(this, getParentsNodes(this.Nodes, selector, nil)) return pushStack(s, getParentsNodes(s.Nodes, selector, nil))
} }
// ParentsUntilSelection() gets the ancestors of each element in the Selection, // ParentsUntilSelection gets the ancestors of each element in the Selection,
// up to but not including the elements in the specified Selection. It returns a // up to but not including the elements in the specified Selection. It returns a
// new Selection object containing the matched elements. // new Selection object containing the matched elements.
func (this *Selection) ParentsUntilSelection(sel *Selection) *Selection { func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.Parents() return s.Parents()
} }
return this.ParentsUntilNodes(sel.Nodes...) return s.ParentsUntilNodes(sel.Nodes...)
} }
// ParentsUntilNodes() gets the ancestors of each element in the Selection, // ParentsUntilNodes gets the ancestors of each element in the Selection,
// up to but not including the specified nodes. It returns a // up to but not including the specified nodes. It returns a
// new Selection object containing the matched elements. // new Selection object containing the matched elements.
func (this *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection { func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getParentsNodes(this.Nodes, "", nodes)) return pushStack(s, getParentsNodes(s.Nodes, "", nodes))
} }
// ParentsFilteredUntil() is like ParentsUntil(), with the option to filter the // ParentsFilteredUntil is like ParentsUntil, with the option to filter the
// results based on a selector string. It returns a new Selection // results based on a selector string. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection { func (s *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, untilSelector, nil), filterSelector) return filterAndPush(s, getParentsNodes(s.Nodes, untilSelector, nil), filterSelector)
} }
// ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the // ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.ParentsFiltered(filterSelector) return s.ParentsFiltered(filterSelector)
} }
return this.ParentsFilteredUntilNodes(filterSelector, sel.Nodes...) return s.ParentsFilteredUntilNodes(filterSelector, sel.Nodes...)
} }
// ParentsFilteredUntilNodes() is like ParentsUntilNodes(), with the // ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, "", nodes), filterSelector) return filterAndPush(s, getParentsNodes(s.Nodes, "", nodes), filterSelector)
} }
// Siblings() gets the siblings of each element in the Selection. It returns // Siblings gets the siblings of each element in the Selection. It returns
// a new Selection object containing the matched elements. // a new Selection object containing the matched elements.
func (this *Selection) Siblings() *Selection { func (s *Selection) Siblings() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingAll, "", nil)) return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, "", nil))
} }
// SiblingsFiltered() gets the siblings of each element in the Selection // SiblingsFiltered gets the siblings of each element in the Selection
// filtered by a selector. It returns a new Selection object containing the // filtered by a selector. It returns a new Selection object containing the
// matched elements. // matched elements.
func (this *Selection) SiblingsFiltered(selector string) *Selection { func (s *Selection) SiblingsFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll, "", nil), selector) return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, "", nil), selector)
} }
// Next() gets the immediately following sibling of each element in the // Next gets the immediately following sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Next() *Selection { func (s *Selection) Next() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNext, "", nil)) return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, "", nil))
} }
// NextFiltered() gets the immediately following sibling of each element in the // NextFiltered gets the immediately following sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) NextFiltered(selector string) *Selection { func (s *Selection) NextFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext, "", nil), selector) return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, "", nil), selector)
} }
// NextAll() gets all the following siblings of each element in the // NextAll gets all the following siblings of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) NextAll() *Selection { func (s *Selection) NextAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil)) return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, "", nil))
} }
// NextAllFiltered() gets all the following siblings of each element in the // NextAllFiltered gets all the following siblings of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) NextAllFiltered(selector string) *Selection { func (s *Selection) NextAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil), selector) return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, "", nil), selector)
} }
// Prev() gets the immediately preceding sibling of each element in the // Prev gets the immediately preceding sibling of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) Prev() *Selection { func (s *Selection) Prev() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil)) return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, "", nil))
} }
// PrevFiltered() gets the immediately preceding sibling of each element in the // PrevFiltered gets the immediately preceding sibling of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) PrevFiltered(selector string) *Selection { func (s *Selection) PrevFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil), selector) return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, "", nil), selector)
} }
// PrevAll() gets all the preceding siblings of each element in the // PrevAll gets all the preceding siblings of each element in the
// Selection. It returns a new Selection object containing the matched elements. // Selection. It returns a new Selection object containing the matched elements.
func (this *Selection) PrevAll() *Selection { func (s *Selection) PrevAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil)) return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, "", nil))
} }
// PrevAllFiltered() gets all the preceding siblings of each element in the // PrevAllFiltered gets all the preceding siblings of each element in the
// Selection filtered by a selector. It returns a new Selection object // Selection filtered by a selector. It returns a new Selection object
// containing the matched elements. // containing the matched elements.
func (this *Selection) PrevAllFiltered(selector string) *Selection { func (s *Selection) PrevAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil), selector) return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, "", nil), selector)
} }
// NextUntil() gets all following siblings of each element up to but not // NextUntil gets all following siblings of each element up to but not
// including the element matched by the selector. It returns a new Selection // including the element matched by the selector. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) NextUntil(selector string) *Selection { func (s *Selection) NextUntil(selector string) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil, return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
selector, nil)) selector, nil))
} }
// NextUntilSelection() gets all following siblings of each element up to but not // NextUntilSelection gets all following siblings of each element up to but not
// including the element matched by the Selection. It returns a new Selection // including the element matched by the Selection. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) NextUntilSelection(sel *Selection) *Selection { func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.NextAll() return s.NextAll()
} }
return this.NextUntilNodes(sel.Nodes...) return s.NextUntilNodes(sel.Nodes...)
} }
// NextUntilNodes() gets all following siblings of each element up to but not // NextUntilNodes gets all following siblings of each element up to but not
// including the element matched by the nodes. It returns a new Selection // including the element matched by the nodes. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) NextUntilNodes(nodes ...*html.Node) *Selection { func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil, return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
"", nodes)) "", nodes))
} }
// PrevUntil() gets all preceding siblings of each element up to but not // PrevUntil gets all preceding siblings of each element up to but not
// including the element matched by the selector. It returns a new Selection // including the element matched by the selector. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) PrevUntil(selector string) *Selection { func (s *Selection) PrevUntil(selector string) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil, return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
selector, nil)) selector, nil))
} }
// PrevUntilSelection() gets all preceding siblings of each element up to but not // PrevUntilSelection gets all preceding siblings of each element up to but not
// including the element matched by the Selection. It returns a new Selection // including the element matched by the Selection. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) PrevUntilSelection(sel *Selection) *Selection { func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.PrevAll() return s.PrevAll()
} }
return this.PrevUntilNodes(sel.Nodes...) return s.PrevUntilNodes(sel.Nodes...)
} }
// PrevUntilNodes() gets all preceding siblings of each element up to but not // PrevUntilNodes gets all preceding siblings of each element up to but not
// including the element matched by the nodes. It returns a new Selection // including the element matched by the nodes. It returns a new Selection
// object containing the matched elements. // object containing the matched elements.
func (this *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection { func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil, return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
"", nodes)) "", nodes))
} }
// NextFilteredUntil() is like NextUntil(), with the option to filter // NextFilteredUntil is like NextUntil, with the option to filter
// the results based on a selector string. // the results based on a selector string.
// It returns a new Selection object containing the matched elements. // It returns a new Selection object containing the matched elements.
func (this *Selection) NextFilteredUntil(filterSelector string, untilSelector string) *Selection { func (s *Selection) NextFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil, return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
untilSelector, nil), filterSelector) untilSelector, nil), filterSelector)
} }
// NextFilteredUntilSelection() is like NextUntilSelection(), with the // NextFilteredUntilSelection is like NextUntilSelection, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.NextFiltered(filterSelector) return s.NextFiltered(filterSelector)
} }
return this.NextFilteredUntilNodes(filterSelector, sel.Nodes...) return s.NextFilteredUntilNodes(filterSelector, sel.Nodes...)
} }
// NextFilteredUntilNodes() is like NextUntilNodes(), with the // NextFilteredUntilNodes is like NextUntilNodes, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil, return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
"", nodes), filterSelector) "", nodes), filterSelector)
} }
// PrevFilteredUntil() is like PrevUntil(), with the option to filter // PrevFilteredUntil is like PrevUntil, with the option to filter
// the results based on a selector string. // the results based on a selector string.
// It returns a new Selection object containing the matched elements. // It returns a new Selection object containing the matched elements.
func (this *Selection) PrevFilteredUntil(filterSelector string, untilSelector string) *Selection { func (s *Selection) PrevFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil, return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
untilSelector, nil), filterSelector) untilSelector, nil), filterSelector)
} }
// PrevFilteredUntilSelection() is like PrevUntilSelection(), with the // PrevFilteredUntilSelection is like PrevUntilSelection, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
if sel == nil { if sel == nil {
return this.PrevFiltered(filterSelector) return s.PrevFiltered(filterSelector)
} }
return this.PrevFilteredUntilNodes(filterSelector, sel.Nodes...) return s.PrevFilteredUntilNodes(filterSelector, sel.Nodes...)
} }
// PrevFilteredUntilNodes() is like PrevUntilNodes(), with the // PrevFilteredUntilNodes is like PrevUntilNodes, with the
// option to filter the results based on a selector string. It returns a new // option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements. // Selection object containing the matched elements.
func (this *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil, return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
"", nodes), filterSelector) "", nodes), filterSelector)
} }
@@ -379,7 +379,7 @@ func findWithSelector(nodes []*html.Node, selector string) []*html.Node {
sel := cascadia.MustCompile(selector) sel := cascadia.MustCompile(selector)
// Map nodes to find the matches within the children of each node // Map nodes to find the matches within the children of each node
return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) { return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
// Go down one level, becausejQuery's Find() selects only within descendants // Go down one level, becausejQuery's Find selects only within descendants
for c := n.FirstChild; c != nil; c = c.NextSibling { for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode { if c.Type == html.ElementNode {
result = append(result, sel.MatchAll(c)...) result = append(result, sel.MatchAll(c)...)
@@ -416,7 +416,7 @@ func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.
func getSiblingNodes(nodes []*html.Node, st siblingType, untilSelector string, untilNodes []*html.Node) []*html.Node { func getSiblingNodes(nodes []*html.Node, st siblingType, untilSelector string, untilNodes []*html.Node) []*html.Node {
var f func(*html.Node) bool var f func(*html.Node) bool
// If the requested siblings are ...Until(), create the test function to // If the requested siblings are ...Until, create the test function to
// determine if the until condition is reached (returns true if it is) // determine if the until condition is reached (returns true if it is)
if st == siblingNextUntil || st == siblingPrevUntil { if st == siblingNextUntil || st == siblingPrevUntil {
f = func(n *html.Node) bool { f = func(n *html.Node) bool {
@@ -497,7 +497,7 @@ func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *htm
} }
for c := iter(nil); c != nil; c = iter(c) { for c := iter(nil); c != nil; c = iter(c) {
// If this is an ...Until() case, test before append (returns true // If this is an ...Until case, test before append (returns true
// if the until condition is reached) // if the until condition is reached)
if st == siblingNextUntil || st == siblingPrevUntil { if st == siblingNextUntil || st == siblingPrevUntil {
if untilFunc(c) { if untilFunc(c) {

27
type.go
View File

@@ -1,10 +1,11 @@
package goquery package goquery
import ( import (
"code.google.com/p/go.net/html"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"code.google.com/p/go.net/html"
) )
// Document represents an HTML document to be manipulated. Unlike jQuery, which // Document represents an HTML document to be manipulated. Unlike jQuery, which
@@ -18,30 +19,30 @@ type Document struct {
rootNode *html.Node rootNode *html.Node
} }
// NewDocumentFromNode() is a Document constructor that takes a root html Node // NewDocumentFromNode is a Document constructor that takes a root html Node
// as argument. // as argument.
func NewDocumentFromNode(root *html.Node) (d *Document) { func NewDocumentFromNode(root *html.Node) *Document {
return newDocument(root, nil) return newDocument(root, nil)
} }
// NewDocument() is a Document constructor that takes a string URL as argument. // NewDocument is a Document constructor that takes a string URL as argument.
// It loads the specified document, parses it, and stores the root Document // It loads the specified document, parses it, and stores the root Document
// node, ready to be manipulated. // node, ready to be manipulated.
func NewDocument(url string) (d *Document, e error) { func NewDocument(url string) (*Document, error) {
// Load the URL // Load the URL
res, e := http.Get(url) res, e := http.Get(url)
if e != nil { if e != nil {
return return nil, e
} }
return NewDocumentFromResponse(res) return NewDocumentFromResponse(res)
} }
// NewDocumentFromReader() returns a Document from a generic reader. // NewDocumentFromReader returns a Document from a generic reader.
// It returns an error as second value if the reader's data cannot be parsed // It returns an error as second value if the reader's data cannot be parsed
// as html. It does *not* check if the reader is also an io.Closer, so the // as html. It does *not* check if the reader is also an io.Closer, so the
// provided reader is never closed by this call, it is the responsibility // provided reader is never closed by this call, it is the responsibility
// of the caller to close it if required. // of the caller to close it if required.
func NewDocumentFromReader(r io.Reader) (d *Document, e error) { func NewDocumentFromReader(r io.Reader) (*Document, error) {
root, e := html.Parse(r) root, e := html.Parse(r)
if e != nil { if e != nil {
return nil, e return nil, e
@@ -49,16 +50,16 @@ func NewDocumentFromReader(r io.Reader) (d *Document, e error) {
return newDocument(root, nil), nil return newDocument(root, nil), nil
} }
// NewDocumentFromResponse() is another Document constructor that takes an http resonse as argument. // NewDocumentFromResponse is another Document constructor that takes an http resonse as argument.
// It loads the specified response's document, parses it, and stores the root Document // It loads the specified response's document, parses it, and stores the root Document
// node, ready to be manipulated. // node, ready to be manipulated. The response's body is closed on return.
func NewDocumentFromResponse(res *http.Response) (d *Document, e error) { func NewDocumentFromResponse(res *http.Response) (*Document, error) {
defer res.Body.Close() defer res.Body.Close()
// Parse the HTML into nodes // Parse the HTML into nodes
root, e := html.Parse(res.Body) root, e := html.Parse(res.Body)
if e != nil { if e != nil {
return return nil, e
} }
// Create and fill the document // Create and fill the document
@@ -74,7 +75,7 @@ func newDocument(root *html.Node, url *url.URL) *Document {
} }
// Selection represents a collection of nodes matching some criteria. The // Selection represents a collection of nodes matching some criteria. The
// initial Selection can be created by using Document.Find(), and then // initial Selection can be created by using Document.Find, and then
// manipulated using the jQuery-like chainable syntax and methods. // manipulated using the jQuery-like chainable syntax and methods.
type Selection struct { type Selection struct {
Nodes []*html.Node Nodes []*html.Node

View File

@@ -8,7 +8,7 @@ func getChildren(n *html.Node) (result []*html.Node) {
for c := n.FirstChild; c != nil; c = c.NextSibling { for c := n.FirstChild; c != nil; c = c.NextSibling {
result = append(result, c) result = append(result, c)
} }
return return result
} }
// Loop through all container nodes to search for the target node. // Loop through all container nodes to search for the target node.
@@ -55,9 +55,7 @@ func indexInSlice(slice []*html.Node, node *html.Node) int {
// There is no check to the original state of the target slice, so it may still // There is no check to the original state of the target slice, so it may still
// contain duplicates. The target slice is returned because append() may create // contain duplicates. The target slice is returned because append() may create
// a new underlying array. // a new underlying array.
func appendWithoutDuplicates(target []*html.Node, func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
nodes []*html.Node) []*html.Node {
for _, n := range nodes { for _, n := range nodes {
if !isInSlice(target, n) { if !isInSlice(target, n) {
target = append(target, n) target = append(target, n)
@@ -70,18 +68,17 @@ func appendWithoutDuplicates(target []*html.Node,
// Loop through a selection, returning only those nodes that pass the predicate // Loop through a selection, returning only those nodes that pass the predicate
// function. // function.
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) { func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
for i, n := range sel.Nodes { for i, n := range sel.Nodes {
if predicate(i, newSingleSelection(n, sel.document)) { if predicate(i, newSingleSelection(n, sel.document)) {
result = append(result, n) result = append(result, n)
} }
} }
return return result
} }
// Creates a new Selection object based on the specified nodes, and keeps the // Creates a new Selection object based on the specified nodes, and keeps the
// source Selection object on the stack (linked list). // source Selection object on the stack (linked list).
func pushStack(fromSel *Selection, nodes []*html.Node) (result *Selection) { func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
result = &Selection{nodes, fromSel.document, fromSel} result := &Selection{nodes, fromSel.document, fromSel}
return return result
} }