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"
)
// 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
// the selection is empty.
func (this *Selection) First() *Selection {
return this.Eq(0)
func (s *Selection) First() *Selection {
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
// the selection is empty.
func (this *Selection) Last() *Selection {
return this.Eq(-1)
func (s *Selection) Last() *Selection {
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
// set. It returns a new Selection object, and an empty Selection object if the
// index is invalid.
func (this *Selection) Eq(index int) *Selection {
func (s *Selection) Eq(index int) *Selection {
if index < 0 {
index += len(this.Nodes)
index += len(s.Nodes)
}
if index >= len(this.Nodes) || index < 0 {
return newEmptySelection(this.document)
if index >= len(s.Nodes) || index < 0 {
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.
func (this *Selection) Slice(start int, end int) *Selection {
func (s *Selection) Slice(start int, end int) *Selection {
if start < 0 {
start += len(this.Nodes)
start += len(s.Nodes)
}
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() without parameter is not implemented, since the node array is available
// Get retrieves the underlying node at the specified index.
// Get without parameter is not implemented, since the node array is available
// on the Selection object.
func (this *Selection) Get(index int) *html.Node {
func (s *Selection) Get(index int) *html.Node {
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.
func (this *Selection) Index() int {
if len(this.Nodes) > 0 {
return newSingleSelection(this.Nodes[0], this.document).PrevAll().Length()
func (s *Selection) Index() int {
if len(s.Nodes) > 0 {
return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
}
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
// not found.
func (this *Selection) IndexSelector(selector string) int {
if len(this.Nodes) > 0 {
sel := this.document.Find(selector)
return indexInSlice(sel.Nodes, this.Nodes[0])
func (s *Selection) IndexSelector(selector string) int {
if len(s.Nodes) > 0 {
sel := s.document.Find(selector)
return indexInSlice(sel.Nodes, s.Nodes[0])
}
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.
func (this *Selection) IndexOfNode(node *html.Node) int {
return indexInSlice(this.Nodes, node)
func (s *Selection) IndexOfNode(node *html.Node) int {
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.
func (this *Selection) IndexOfSelection(s *Selection) int {
if s != nil && len(s.Nodes) > 0 {
return indexInSlice(this.Nodes, s.Nodes[0])
func (s *Selection) IndexOfSelection(sel *Selection) int {
if sel != nil && len(sel.Nodes) > 0 {
return indexInSlice(s.Nodes, sel.Nodes[0])
}
return -1
}

26
doc.go
View File

@@ -23,15 +23,27 @@
/*
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
if an invalid Cascadia selector is used (this is consistent with the behavior of
jQuery/Sizzle/document.querySelectorAll, where an error is thrown). This is
necessary since multiple return values cannot be used to allow a chainable
interface.
Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
Supported functions are query-oriented features (`hasClass()`, `attr()` and the likes),
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
file: https://github.com/puerkitobio/goquery

View File

@@ -4,37 +4,37 @@ import (
"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.
// 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(findWithSelector([]*html.Node{this.document.rootNode}, selector)...)
func (s *Selection) Add(selector string) *Selection {
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.
func (this *Selection) AddSelection(sel *Selection) *Selection {
func (s *Selection) AddSelection(sel *Selection) *Selection {
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().
func (this *Selection) Union(sel *Selection) *Selection {
return this.AddSelection(sel)
// Union is an alias for AddSelection.
func (s *Selection) Union(sel *Selection) *Selection {
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.
func (this *Selection) AddNodes(nodes ...*html.Node) *Selection {
return pushStack(this, appendWithoutDuplicates(this.Nodes, nodes))
func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
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
// with the previous one.
func (this *Selection) AndSelf() *Selection {
return this.AddSelection(this.prevSel)
func (s *Selection) AndSelf() *Selection {
return s.AddSelection(s.prevSel)
}

View File

@@ -5,82 +5,82 @@ import (
"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.
func (this *Selection) Filter(selector string) *Selection {
return pushStack(this, winnow(this, selector, true))
func (s *Selection) Filter(selector string) *Selection {
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.
func (this *Selection) Not(selector string) *Selection {
return pushStack(this, winnow(this, selector, false))
func (s *Selection) Not(selector string) *Selection {
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.
func (this *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
return pushStack(this, winnowFunction(this, f, true))
func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
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.
func (this *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
return pushStack(this, winnowFunction(this, f, false))
func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
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.
func (this *Selection) FilterNodes(nodes ...*html.Node) *Selection {
return pushStack(this, winnowNodes(this, nodes, true))
func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
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.
func (this *Selection) NotNodes(nodes ...*html.Node) *Selection {
return pushStack(this, winnowNodes(this, nodes, false))
func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
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.
// It returns a new Selection object for this subset of elements.
func (this *Selection) FilterSelection(s *Selection) *Selection {
if s == nil {
return pushStack(this, winnowNodes(this, nil, true))
func (s *Selection) FilterSelection(sel *Selection) *Selection {
if sel == nil {
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.
// It returns a new Selection object with the matching elements removed.
func (this *Selection) NotSelection(s *Selection) *Selection {
if s == nil {
return pushStack(this, winnowNodes(this, nil, false))
func (s *Selection) NotSelection(sel *Selection) *Selection {
if sel == nil {
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().
func (this *Selection) Intersection(s *Selection) *Selection {
return this.FilterSelection(s)
// Intersection is an alias for FilterSelection.
func (s *Selection) Intersection(sel *Selection) *Selection {
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.
// It returns a new Selection object with the matching elements.
func (this *Selection) Has(selector string) *Selection {
return this.HasSelection(this.document.Find(selector))
func (s *Selection) Has(selector string) *Selection {
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.
// It returns a new Selection object with the matching elements.
func (this *Selection) HasNodes(nodes ...*html.Node) *Selection {
return this.FilterFunction(func(_ int, s *Selection) bool {
func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
return s.FilterFunction(func(_ int, sel *Selection) bool {
// Add all nodes that contain one of the specified nodes
for _, n := range nodes {
if s.Contains(n) {
if sel.Contains(n) {
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.
// 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 {
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.
func (this *Selection) End() *Selection {
if this.prevSel != nil {
return this.prevSel
func (s *Selection) End() *Selection {
if s.prevSel != nil {
return s.prevSel
}
return newEmptySelection(this.document)
return newEmptySelection(s.document)
}
// Filter based on a selector string, and the indicator to keep (Filter) or

View File

@@ -1,32 +1,32 @@
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.
func (this *Selection) Each(f func(int, *Selection)) *Selection {
for i, n := range this.Nodes {
f(i, newSingleSelection(n, this.document))
func (s *Selection) Each(f func(int, *Selection)) *Selection {
for i, n := range s.Nodes {
f(i, newSingleSelection(n, s.document))
}
return this
return s
}
// 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
// out of the loop by returning `false` in the callback function. It returns the
// 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
// out of the loop by returning false in the callback function. It returns the
// current Selection object.
func (this *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
for i, n := range this.Nodes {
if !f(i, newSingleSelection(n, this.document)) {
return this
func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
for i, n := range s.Nodes {
if !f(i, newSingleSelection(n, s.document)) {
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.
func (this *Selection) Map(f func(int, *Selection) string) (result []string) {
for i, n := range this.Nodes {
result = append(result, f(i, newSingleSelection(n, this.document)))
func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
for i, n := range s.Nodes {
result = append(result, f(i, newSingleSelection(n, s.document)))
}
return result

View File

@@ -2,50 +2,51 @@ package goquery
import (
"bytes"
"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
// construct such as Each() or Map() method.
func (this *Selection) Attr(attrName string) (val string, exists bool) {
if len(this.Nodes) == 0 {
// construct such as Each or Map method.
func (s *Selection) Attr(attrName string) (val string, exists bool) {
if len(s.Nodes) == 0 {
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.
func (this *Selection) Text() string {
func (s *Selection) Text() string {
var buf bytes.Buffer
// Slightly optimized vs calling Each(): no single selection object created
for _, n := range this.Nodes {
// Slightly optimized vs calling Each: no single selection object created
for _, n := range s.Nodes {
buf.WriteString(getNodeText(n))
}
return buf.String()
}
// Size() is an alias for Length().
func (this *Selection) Size() int {
return this.Length()
// Size is an alias for Length.
func (s *Selection) Size() int {
return s.Length()
}
// Length() returns the number of elements in the Selection object.
func (this *Selection) Length() int {
return len(this.Nodes)
// Length returns the number of elements in the Selection object.
func (s *Selection) Length() int {
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.
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
// the nodes usint html.Render().
// the nodes using html.Render.
var buf bytes.Buffer
if len(this.Nodes) > 0 {
for c := this.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
if len(s.Nodes) > 0 {
for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
e = html.Render(&buf, c)
if e != nil {
return

View File

@@ -1,53 +1,54 @@
package goquery
import (
"code.google.com/p/cascadia"
"code.google.com/p/go.net/html"
"regexp"
"strings"
"code.google.com/p/cascadia"
"code.google.com/p/go.net/html"
)
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.
func (this *Selection) Is(selector string) bool {
if len(this.Nodes) > 0 {
func (s *Selection) Is(selector string) bool {
if len(s.Nodes) > 0 {
// Attempt a match with the selector
cs := cascadia.MustCompile(selector)
if len(this.Nodes) == 1 {
return cs.Match(this.Nodes[0])
if len(s.Nodes) == 1 {
return cs.Match(s.Nodes[0])
} else {
return len(cs.Filter(this.Nodes)) > 0
return len(cs.Filter(s.Nodes)) > 0
}
}
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.
func (this *Selection) IsFunction(f func(int, *Selection) bool) bool {
return this.FilterFunction(f).Length() > 0
func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
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.
func (this *Selection) IsSelection(s *Selection) bool {
return this.FilterSelection(s).Length() > 0
func (s *Selection) IsSelection(sel *Selection) bool {
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.
func (this *Selection) IsNodes(nodes ...*html.Node) bool {
return this.FilterNodes(nodes...).Length() > 0
func (s *Selection) IsNodes(nodes ...*html.Node) bool {
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.
func (this *Selection) HasClass(class string) bool {
func (s *Selection) HasClass(class string) bool {
class = " " + class + " "
for _, n := range this.Nodes {
for _, n := range s.Nodes {
// Applies only to element nodes
if n.Type == html.ElementNode {
if elClass, ok := getAttributeValue("class", n); ok {
@@ -61,11 +62,11 @@ func (this *Selection) HasClass(class string) bool {
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.
// 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.
func (this *Selection) Contains(n *html.Node) bool {
return sliceContains(this.Nodes, n)
func (s *Selection) Contains(n *html.Node) bool {
return sliceContains(s.Nodes, n)
}

View File

@@ -20,85 +20,85 @@ const (
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
// containing these matched elements.
func (this *Selection) Find(selector string) *Selection {
return pushStack(this, findWithSelector(this.Nodes, selector))
func (s *Selection) Find(selector string) *Selection {
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
// containing these matched elements.
func (this *Selection) FindSelection(sel *Selection) *Selection {
func (s *Selection) FindSelection(sel *Selection) *Selection {
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
// containing these matched elements.
func (this *Selection) FindNodes(nodes ...*html.Node) *Selection {
return pushStack(this, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
if sliceContains(this.Nodes, n) {
func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
if sliceContains(s.Nodes, n) {
return []*html.Node{n}
}
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
// containing these elements.
func (this *Selection) Contents() *Selection {
return pushStack(this, getChildrenNodes(this.Nodes, siblingAllIncludingNonElements))
func (s *Selection) Contents() *Selection {
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
// object containing these elements. Since selectors only act on Element nodes,
// this function is an alias to ChildrenFiltered() unless the selector is empty,
// in which case it is an alias to Contents().
func (this *Selection) ContentsFiltered(selector string) *Selection {
// this function is an alias to ChildrenFiltered unless the selector is empty,
// in which case it is an alias to Contents.
func (s *Selection) ContentsFiltered(selector string) *Selection {
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.
func (this *Selection) Children() *Selection {
return pushStack(this, getChildrenNodes(this.Nodes, siblingAll))
func (s *Selection) Children() *Selection {
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
// Selection object containing these elements.
func (this *Selection) ChildrenFiltered(selector string) *Selection {
return filterAndPush(this, getChildrenNodes(this.Nodes, siblingAll), selector)
func (s *Selection) ChildrenFiltered(selector string) *Selection {
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.
func (this *Selection) Parent() *Selection {
return pushStack(this, getParentNodes(this.Nodes))
func (s *Selection) Parent() *Selection {
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.
func (this *Selection) ParentFiltered(selector string) *Selection {
return filterAndPush(this, getParentNodes(this.Nodes), selector)
func (s *Selection) ParentFiltered(selector string) *Selection {
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.
func (this *Selection) Closest(selector string) *Selection {
func (s *Selection) Closest(selector string) *Selection {
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
// until a match is found.
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.
func (this *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
return pushStack(this, mapNodes(this.Nodes, func(i int, n *html.Node) []*html.Node {
func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
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
// until a match is found.
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
// in the DOM tree.
func (this *Selection) ClosestSelection(s *Selection) *Selection {
if s == nil {
return pushStack(this, nil)
func (s *Selection) ClosestSelection(sel *Selection) *Selection {
if sel == 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.
func (this *Selection) Parents() *Selection {
return pushStack(this, getParentsNodes(this.Nodes, "", nil))
func (s *Selection) Parents() *Selection {
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.
func (this *Selection) ParentsFiltered(selector string) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, "", nil), selector)
func (s *Selection) ParentsFiltered(selector string) *Selection {
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
// object containing the matched elements.
func (this *Selection) ParentsUntil(selector string) *Selection {
return pushStack(this, getParentsNodes(this.Nodes, selector, nil))
func (s *Selection) ParentsUntil(selector string) *Selection {
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
// new Selection object containing the matched elements.
func (this *Selection) ParentsUntilSelection(sel *Selection) *Selection {
func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
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
// new Selection object containing the matched elements.
func (this *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getParentsNodes(this.Nodes, "", nodes))
func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
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
// object containing the matched elements.
func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, untilSelector, nil), filterSelector)
func (s *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
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
// 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 {
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
// Selection object containing the matched elements.
func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getParentsNodes(this.Nodes, "", nodes), filterSelector)
func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
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.
func (this *Selection) Siblings() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingAll, "", nil))
func (s *Selection) Siblings() *Selection {
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
// matched elements.
func (this *Selection) SiblingsFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll, "", nil), selector)
func (s *Selection) SiblingsFiltered(selector string) *Selection {
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.
func (this *Selection) Next() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNext, "", nil))
func (s *Selection) Next() *Selection {
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
// containing the matched elements.
func (this *Selection) NextFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext, "", nil), selector)
func (s *Selection) NextFiltered(selector string) *Selection {
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.
func (this *Selection) NextAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil))
func (s *Selection) NextAll() *Selection {
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
// containing the matched elements.
func (this *Selection) NextAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil), selector)
func (s *Selection) NextAllFiltered(selector string) *Selection {
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.
func (this *Selection) Prev() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil))
func (s *Selection) Prev() *Selection {
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
// containing the matched elements.
func (this *Selection) PrevFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil), selector)
func (s *Selection) PrevFiltered(selector string) *Selection {
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.
func (this *Selection) PrevAll() *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil))
func (s *Selection) PrevAll() *Selection {
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
// containing the matched elements.
func (this *Selection) PrevAllFiltered(selector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil), selector)
func (s *Selection) PrevAllFiltered(selector string) *Selection {
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
// object containing the matched elements.
func (this *Selection) NextUntil(selector string) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil,
func (s *Selection) NextUntil(selector string) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
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
// object containing the matched elements.
func (this *Selection) NextUntilSelection(sel *Selection) *Selection {
func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
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
// object containing the matched elements.
func (this *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil,
func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
"", 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
// object containing the matched elements.
func (this *Selection) PrevUntil(selector string) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
func (s *Selection) PrevUntil(selector string) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
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
// object containing the matched elements.
func (this *Selection) PrevUntilSelection(sel *Selection) *Selection {
func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
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
// object containing the matched elements.
func (this *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
"", 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.
// It returns a new Selection object containing the matched elements.
func (this *Selection) NextFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil,
func (s *Selection) NextFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
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
// 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 {
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
// Selection object containing the matched elements.
func (this *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil,
func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
"", 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.
// It returns a new Selection object containing the matched elements.
func (this *Selection) PrevFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
func (s *Selection) PrevFilteredUntil(filterSelector string, untilSelector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
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
// 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 {
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
// Selection object containing the matched elements.
func (this *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
"", nodes), filterSelector)
}
@@ -379,7 +379,7 @@ func findWithSelector(nodes []*html.Node, selector string) []*html.Node {
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
// Go down one level, becausejQuery's Find selects only within descendants
for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == html.ElementNode {
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 {
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)
if st == siblingNextUntil || st == siblingPrevUntil {
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) {
// 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 st == siblingNextUntil || st == siblingPrevUntil {
if untilFunc(c) {

27
type.go
View File

@@ -1,10 +1,11 @@
package goquery
import (
"code.google.com/p/go.net/html"
"io"
"net/http"
"net/url"
"code.google.com/p/go.net/html"
)
// Document represents an HTML document to be manipulated. Unlike jQuery, which
@@ -18,30 +19,30 @@ type Document struct {
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.
func NewDocumentFromNode(root *html.Node) (d *Document) {
func NewDocumentFromNode(root *html.Node) *Document {
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
// node, ready to be manipulated.
func NewDocument(url string) (d *Document, e error) {
func NewDocument(url string) (*Document, error) {
// Load the URL
res, e := http.Get(url)
if e != nil {
return
return nil, e
}
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
// 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
// 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)
if e != nil {
return nil, e
@@ -49,16 +50,16 @@ func NewDocumentFromReader(r io.Reader) (d *Document, e error) {
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
// node, ready to be manipulated.
func NewDocumentFromResponse(res *http.Response) (d *Document, e error) {
// node, ready to be manipulated. The response's body is closed on return.
func NewDocumentFromResponse(res *http.Response) (*Document, error) {
defer res.Body.Close()
// Parse the HTML into nodes
root, e := html.Parse(res.Body)
if e != nil {
return
return nil, e
}
// 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
// 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.
type Selection struct {
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 {
result = append(result, c)
}
return
return result
}
// 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
// contain duplicates. The target slice is returned because append() may create
// a new underlying array.
func appendWithoutDuplicates(target []*html.Node,
nodes []*html.Node) []*html.Node {
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
for _, n := range nodes {
if !isInSlice(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
// function.
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
for i, n := range sel.Nodes {
if predicate(i, newSingleSelection(n, sel.document)) {
result = append(result, n)
}
}
return
return result
}
// Creates a new Selection object based on the specified nodes, and keeps the
// source Selection object on the stack (linked list).
func pushStack(fromSel *Selection, nodes []*html.Node) (result *Selection) {
result = &Selection{nodes, fromSel.document, fromSel}
return
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
result := &Selection{nodes, fromSel.document, fromSel}
return result
}