github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/goquery/traversal.go (about)

     1  package goquery
     2  
     3  import (
     4  	"code.google.com/p/cascadia"
     5  	"code.google.com/p/go.net/html"
     6  )
     7  
     8  type siblingType int
     9  
    10  // Sibling type, used internally when iterating over children at the same
    11  // level (siblings) to specify which nodes are requested.
    12  const (
    13  	siblingPrevUntil siblingType = iota - 3
    14  	siblingPrevAll
    15  	siblingPrev
    16  	siblingAll
    17  	siblingNext
    18  	siblingNextAll
    19  	siblingNextUntil
    20  	siblingAllIncludingNonElements
    21  )
    22  
    23  // Find() gets the descendants of each element in the current set of matched
    24  // elements, filtered by a selector. It returns a new Selection object
    25  // containing these matched elements.
    26  func (this *Selection) Find(selector string) *Selection {
    27  	return pushStack(this, findWithSelector(this.Nodes, selector))
    28  }
    29  
    30  // FindSelection() gets the descendants of each element in the current
    31  // Selection, filtered by a Selection. It returns a new Selection object
    32  // containing these matched elements.
    33  func (this *Selection) FindSelection(sel *Selection) *Selection {
    34  	if sel == nil {
    35  		return pushStack(this, nil)
    36  	}
    37  	return this.FindNodes(sel.Nodes...)
    38  }
    39  
    40  // FindNodes() gets the descendants of each element in the current
    41  // Selection, filtered by some nodes. It returns a new Selection object
    42  // containing these matched elements.
    43  func (this *Selection) FindNodes(nodes ...*html.Node) *Selection {
    44  	return pushStack(this, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
    45  		if sliceContains(this.Nodes, n) {
    46  			return []*html.Node{n}
    47  		}
    48  		return nil
    49  	}))
    50  }
    51  
    52  // Contents() gets the children of each element in the Selection,
    53  // including text and comment nodes. It returns a new Selection object
    54  // containing these elements.
    55  func (this *Selection) Contents() *Selection {
    56  	return pushStack(this, getChildrenNodes(this.Nodes, siblingAllIncludingNonElements))
    57  }
    58  
    59  // ContentsFiltered() gets the children of each element in the Selection,
    60  // filtered by the specified selector. It returns a new Selection
    61  // object containing these elements. Since selectors only act on Element nodes,
    62  // this function is an alias to ChildrenFiltered() unless the selector is empty,
    63  // in which case it is an alias to Contents().
    64  func (this *Selection) ContentsFiltered(selector string) *Selection {
    65  	if selector != "" {
    66  		return this.ChildrenFiltered(selector)
    67  	}
    68  	return this.Contents()
    69  }
    70  
    71  // Children() gets the child elements of each element in the Selection.
    72  // It returns a new Selection object containing these elements.
    73  func (this *Selection) Children() *Selection {
    74  	return pushStack(this, getChildrenNodes(this.Nodes, siblingAll))
    75  }
    76  
    77  // ChildrenFiltered() gets the child elements of each element in the Selection,
    78  // filtered by the specified selector. It returns a new
    79  // Selection object containing these elements.
    80  func (this *Selection) ChildrenFiltered(selector string) *Selection {
    81  	return filterAndPush(this, getChildrenNodes(this.Nodes, siblingAll), selector)
    82  }
    83  
    84  // Parent() gets the parent of each element in the Selection. It returns a 
    85  // new Selection object containing the matched elements.
    86  func (this *Selection) Parent() *Selection {
    87  	return pushStack(this, getParentNodes(this.Nodes))
    88  }
    89  
    90  // ParentFiltered() gets the parent of each element in the Selection filtered by a
    91  // selector. It returns a new Selection object containing the matched elements.
    92  func (this *Selection) ParentFiltered(selector string) *Selection {
    93  	return filterAndPush(this, getParentNodes(this.Nodes), selector)
    94  }
    95  
    96  // Closest() gets the first element that matches the selector by testing the
    97  // element itself and traversing up through its ancestors in the DOM tree.
    98  func (this *Selection) Closest(selector string) *Selection {
    99  	cs := cascadia.MustCompile(selector)
   100  
   101  	return pushStack(this, mapNodes(this.Nodes, func(i int, n *html.Node) []*html.Node {
   102  		// For each node in the selection, test the node itself, then each parent
   103  		// until a match is found.
   104  		for ; n != nil; n = n.Parent {
   105  			if cs.Match(n) {
   106  				return []*html.Node{n}
   107  			}
   108  		}
   109  		return nil
   110  	}))
   111  }
   112  
   113  // ClosestNodes() gets the first element that matches one of the nodes by testing the
   114  // element itself and traversing up through its ancestors in the DOM tree.
   115  func (this *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
   116  	return pushStack(this, mapNodes(this.Nodes, func(i int, n *html.Node) []*html.Node {
   117  		// For each node in the selection, test the node itself, then each parent
   118  		// until a match is found.
   119  		for ; n != nil; n = n.Parent {
   120  			if isInSlice(nodes, n) {
   121  				return []*html.Node{n}
   122  			}
   123  		}
   124  		return nil
   125  	}))
   126  }
   127  
   128  // ClosestSelection() gets the first element that matches one of the nodes in the 
   129  // Selection by testing the element itself and traversing up through its ancestors
   130  // in the DOM tree.
   131  func (this *Selection) ClosestSelection(s *Selection) *Selection {
   132  	if s == nil {
   133  		return pushStack(this, nil)
   134  	}
   135  	return this.ClosestNodes(s.Nodes...)
   136  }
   137  
   138  // Parents() gets the ancestors of each element in the current Selection. It
   139  // returns a new Selection object with the matched elements.
   140  func (this *Selection) Parents() *Selection {
   141  	return pushStack(this, getParentsNodes(this.Nodes, "", nil))
   142  }
   143  
   144  // ParentsFiltered() gets the ancestors of each element in the current
   145  // Selection. It returns a new Selection object with the matched elements.
   146  func (this *Selection) ParentsFiltered(selector string) *Selection {
   147  	return filterAndPush(this, getParentsNodes(this.Nodes, "", nil), selector)
   148  }
   149  
   150  // ParentsUntil() gets the ancestors of each element in the Selection, up to but
   151  // not including the element matched by the selector. It returns a new Selection
   152  // object containing the matched elements.
   153  func (this *Selection) ParentsUntil(selector string) *Selection {
   154  	return pushStack(this, getParentsNodes(this.Nodes, selector, nil))
   155  }
   156  
   157  // ParentsUntilSelection() gets the ancestors of each element in the Selection,
   158  // up to but not including the elements in the specified Selection. It returns a
   159  // new Selection object containing the matched elements.
   160  func (this *Selection) ParentsUntilSelection(sel *Selection) *Selection {
   161  	if sel == nil {
   162  		return this.Parents()
   163  	}
   164  	return this.ParentsUntilNodes(sel.Nodes...)
   165  }
   166  
   167  // ParentsUntilNodes() gets the ancestors of each element in the Selection,
   168  // up to but not including the specified nodes. It returns a
   169  // new Selection object containing the matched elements.
   170  func (this *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
   171  	return pushStack(this, getParentsNodes(this.Nodes, "", nodes))
   172  }
   173  
   174  // ParentsFilteredUntil() is like ParentsUntil(), with the option to filter the
   175  // results based on a selector string. It returns a new Selection
   176  // object containing the matched elements.
   177  func (this *Selection) ParentsFilteredUntil(filterSelector string, untilSelector string) *Selection {
   178  	return filterAndPush(this, getParentsNodes(this.Nodes, untilSelector, nil), filterSelector)
   179  }
   180  
   181  // ParentsFilteredUntilSelection() is like ParentsUntilSelection(), with the
   182  // option to filter the results based on a selector string. It returns a new
   183  // Selection object containing the matched elements.
   184  func (this *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
   185  	if sel == nil {
   186  		return this.ParentsFiltered(filterSelector)
   187  	}
   188  	return this.ParentsFilteredUntilNodes(filterSelector, sel.Nodes...)
   189  }
   190  
   191  // ParentsFilteredUntilNodes() is like ParentsUntilNodes(), with the
   192  // option to filter the results based on a selector string. It returns a new
   193  // Selection object containing the matched elements.
   194  func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
   195  	return filterAndPush(this, getParentsNodes(this.Nodes, "", nodes), filterSelector)
   196  }
   197  
   198  // Siblings() gets the siblings of each element in the Selection. It returns
   199  // a new Selection object containing the matched elements.
   200  func (this *Selection) Siblings() *Selection {
   201  	return pushStack(this, getSiblingNodes(this.Nodes, siblingAll, "", nil))
   202  }
   203  
   204  // SiblingsFiltered() gets the siblings of each element in the Selection
   205  // filtered by a selector. It returns a new Selection object containing the
   206  // matched elements.
   207  func (this *Selection) SiblingsFiltered(selector string) *Selection {
   208  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll, "", nil), selector)
   209  }
   210  
   211  // Next() gets the immediately following sibling of each element in the
   212  // Selection. It returns a new Selection object containing the matched elements.
   213  func (this *Selection) Next() *Selection {
   214  	return pushStack(this, getSiblingNodes(this.Nodes, siblingNext, "", nil))
   215  }
   216  
   217  // NextFiltered() gets the immediately following sibling of each element in the
   218  // Selection filtered by a selector. It returns a new Selection object
   219  // containing the matched elements.
   220  func (this *Selection) NextFiltered(selector string) *Selection {
   221  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext, "", nil), selector)
   222  }
   223  
   224  // NextAll() gets all the following siblings of each element in the
   225  // Selection. It returns a new Selection object containing the matched elements.
   226  func (this *Selection) NextAll() *Selection {
   227  	return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil))
   228  }
   229  
   230  // NextAllFiltered() gets all the following siblings of each element in the
   231  // Selection filtered by a selector. It returns a new Selection object
   232  // containing the matched elements.
   233  func (this *Selection) NextAllFiltered(selector string) *Selection {
   234  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil), selector)
   235  }
   236  
   237  // Prev() gets the immediately preceding sibling of each element in the
   238  // Selection. It returns a new Selection object containing the matched elements.
   239  func (this *Selection) Prev() *Selection {
   240  	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil))
   241  }
   242  
   243  // PrevFiltered() gets the immediately preceding sibling of each element in the
   244  // Selection filtered by a selector. It returns a new Selection object
   245  // containing the matched elements.
   246  func (this *Selection) PrevFiltered(selector string) *Selection {
   247  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil), selector)
   248  }
   249  
   250  // PrevAll() gets all the preceding siblings of each element in the
   251  // Selection. It returns a new Selection object containing the matched elements.
   252  func (this *Selection) PrevAll() *Selection {
   253  	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil))
   254  }
   255  
   256  // PrevAllFiltered() gets all the preceding siblings of each element in the
   257  // Selection filtered by a selector. It returns a new Selection object
   258  // containing the matched elements.
   259  func (this *Selection) PrevAllFiltered(selector string) *Selection {
   260  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil), selector)
   261  }
   262  
   263  // NextUntil() gets all following siblings of each element up to but not
   264  // including the element matched by the selector. It returns a new Selection
   265  // object containing the matched elements.
   266  func (this *Selection) NextUntil(selector string) *Selection {
   267  	return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil,
   268  		selector, nil))
   269  }
   270  
   271  // NextUntilSelection() gets all following siblings of each element up to but not
   272  // including the element matched by the Selection. It returns a new Selection
   273  // object containing the matched elements.
   274  func (this *Selection) NextUntilSelection(sel *Selection) *Selection {
   275  	if sel == nil {
   276  		return this.NextAll()
   277  	}
   278  	return this.NextUntilNodes(sel.Nodes...)
   279  }
   280  
   281  // NextUntilNodes() gets all following siblings of each element up to but not
   282  // including the element matched by the nodes. It returns a new Selection
   283  // object containing the matched elements.
   284  func (this *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
   285  	return pushStack(this, getSiblingNodes(this.Nodes, siblingNextUntil,
   286  		"", nodes))
   287  }
   288  
   289  // PrevUntil() gets all preceding siblings of each element up to but not
   290  // including the element matched by the selector. It returns a new Selection
   291  // object containing the matched elements.
   292  func (this *Selection) PrevUntil(selector string) *Selection {
   293  	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
   294  		selector, nil))
   295  }
   296  
   297  // PrevUntilSelection() gets all preceding siblings of each element up to but not
   298  // including the element matched by the Selection. It returns a new Selection
   299  // object containing the matched elements.
   300  func (this *Selection) PrevUntilSelection(sel *Selection) *Selection {
   301  	if sel == nil {
   302  		return this.PrevAll()
   303  	}
   304  	return this.PrevUntilNodes(sel.Nodes...)
   305  }
   306  
   307  // PrevUntilNodes() gets all preceding siblings of each element up to but not
   308  // including the element matched by the nodes. It returns a new Selection
   309  // object containing the matched elements.
   310  func (this *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
   311  	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
   312  		"", nodes))
   313  }
   314  
   315  // NextFilteredUntil() is like NextUntil(), with the option to filter
   316  // the results based on a selector string.
   317  // It returns a new Selection object containing the matched elements.
   318  func (this *Selection) NextFilteredUntil(filterSelector string, untilSelector string) *Selection {
   319  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil,
   320  		untilSelector, nil), filterSelector)
   321  }
   322  
   323  // NextFilteredUntilSelection() is like NextUntilSelection(), with the
   324  // option to filter the results based on a selector string. It returns a new
   325  // Selection object containing the matched elements.
   326  func (this *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
   327  	if sel == nil {
   328  		return this.NextFiltered(filterSelector)
   329  	}
   330  	return this.NextFilteredUntilNodes(filterSelector, sel.Nodes...)
   331  }
   332  
   333  // NextFilteredUntilNodes() is like NextUntilNodes(), with the
   334  // option to filter the results based on a selector string. It returns a new
   335  // Selection object containing the matched elements.
   336  func (this *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
   337  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextUntil,
   338  		"", nodes), filterSelector)
   339  }
   340  
   341  // PrevFilteredUntil() is like PrevUntil(), with the option to filter
   342  // the results based on a selector string.
   343  // It returns a new Selection object containing the matched elements.
   344  func (this *Selection) PrevFilteredUntil(filterSelector string, untilSelector string) *Selection {
   345  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
   346  		untilSelector, nil), filterSelector)
   347  }
   348  
   349  // PrevFilteredUntilSelection() is like PrevUntilSelection(), with the
   350  // option to filter the results based on a selector string. It returns a new
   351  // Selection object containing the matched elements.
   352  func (this *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
   353  	if sel == nil {
   354  		return this.PrevFiltered(filterSelector)
   355  	}
   356  	return this.PrevFilteredUntilNodes(filterSelector, sel.Nodes...)
   357  }
   358  
   359  // PrevFilteredUntilNodes() is like PrevUntilNodes(), with the
   360  // option to filter the results based on a selector string. It returns a new
   361  // Selection object containing the matched elements.
   362  func (this *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
   363  	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevUntil,
   364  		"", nodes), filterSelector)
   365  }
   366  
   367  // Filter and push filters the nodes based on a selector, and pushes the results
   368  // on the stack, with the srcSel as previous selection.
   369  func filterAndPush(srcSel *Selection, nodes []*html.Node, selector string) *Selection {
   370  	// Create a temporary Selection with the specified nodes to filter using winnow
   371  	sel := &Selection{nodes, srcSel.document, nil}
   372  	// Filter based on selector and push on stack
   373  	return pushStack(srcSel, winnow(sel, selector, true))
   374  }
   375  
   376  // Internal implementation of Find that return raw nodes.
   377  func findWithSelector(nodes []*html.Node, selector string) []*html.Node {
   378  	// Compile the selector once
   379  	sel := cascadia.MustCompile(selector)
   380  	// Map nodes to find the matches within the children of each node
   381  	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
   382  		// Go down one level, becausejQuery's Find() selects only within descendants
   383  		for c := n.FirstChild; c != nil; c = c.NextSibling {
   384  			if c.Type == html.ElementNode {
   385  				result = append(result, sel.MatchAll(c)...)
   386  			}
   387  		}
   388  		return
   389  	})
   390  }
   391  
   392  // Internal implementation to get all parent nodes, stopping at the specified 
   393  // node (or nil if no stop).
   394  func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html.Node) []*html.Node {
   395  	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
   396  		for p := n.Parent; p != nil; p = p.Parent {
   397  			sel := newSingleSelection(p, nil)
   398  			if stopSelector != "" {
   399  				if sel.Is(stopSelector) {
   400  					break
   401  				}
   402  			} else if len(stopNodes) > 0 {
   403  				if sel.IsNodes(stopNodes...) {
   404  					break
   405  				}
   406  			}
   407  			if p.Type == html.ElementNode {
   408  				result = append(result, p)
   409  			}
   410  		}
   411  		return
   412  	})
   413  }
   414  
   415  // Internal implementation of sibling nodes that return a raw slice of matches.
   416  func getSiblingNodes(nodes []*html.Node, st siblingType, untilSelector string, untilNodes []*html.Node) []*html.Node {
   417  	var f func(*html.Node) bool
   418  
   419  	// If the requested siblings are ...Until(), create the test function to 
   420  	// determine if the until condition is reached (returns true if it is)
   421  	if st == siblingNextUntil || st == siblingPrevUntil {
   422  		f = func(n *html.Node) bool {
   423  			if untilSelector != "" {
   424  				// Selector-based condition
   425  				sel := newSingleSelection(n, nil)
   426  				return sel.Is(untilSelector)
   427  			} else if len(untilNodes) > 0 {
   428  				// Nodes-based condition
   429  				sel := newSingleSelection(n, nil)
   430  				return sel.IsNodes(untilNodes...)
   431  			}
   432  			return false
   433  		}
   434  	}
   435  
   436  	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
   437  		return getChildrenWithSiblingType(n.Parent, st, n, f)
   438  	})
   439  }
   440  
   441  // Gets the children nodes of each node in the specified slice of nodes,
   442  // based on the sibling type request.
   443  func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
   444  	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
   445  		return getChildrenWithSiblingType(n, st, nil, nil)
   446  	})
   447  }
   448  
   449  // Gets the children of the specified parent, based on the requested sibling
   450  // type, skipping a specified node if required.
   451  func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
   452  	untilFunc func(*html.Node) bool) (result []*html.Node) {
   453  
   454  	// Create the iterator function
   455  	var iter = func(cur *html.Node) (ret *html.Node) {
   456  		// Based on the sibling type requested, iterate the right way
   457  		for {
   458  			switch st {
   459  			case siblingAll, siblingAllIncludingNonElements:
   460  				if cur == nil {
   461  					// First iteration, start with first child of parent
   462  					// Skip node if required
   463  					if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
   464  						ret = skipNode.NextSibling
   465  					}
   466  				} else {
   467  					// Skip node if required
   468  					if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
   469  						ret = skipNode.NextSibling
   470  					}
   471  				}
   472  			case siblingPrev, siblingPrevAll, siblingPrevUntil:
   473  				if cur == nil {
   474  					// Start with previous sibling of the skip node
   475  					ret = skipNode.PrevSibling
   476  				} else {
   477  					ret = cur.PrevSibling
   478  				}
   479  			case siblingNext, siblingNextAll, siblingNextUntil:
   480  				if cur == nil {
   481  					// Start with next sibling of the skip node
   482  					ret = skipNode.NextSibling
   483  				} else {
   484  					ret = cur.NextSibling
   485  				}
   486  			default:
   487  				panic("Invalid sibling type.")
   488  			}
   489  			if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
   490  				return
   491  			} else {
   492  				// Not a valid node, try again from this one
   493  				cur = ret
   494  			}
   495  		}
   496  		panic("Unreachable code reached.")
   497  	}
   498  
   499  	for c := iter(nil); c != nil; c = iter(c) {
   500  		// If this is an ...Until() case, test before append (returns true
   501  		// if the until condition is reached)
   502  		if st == siblingNextUntil || st == siblingPrevUntil {
   503  			if untilFunc(c) {
   504  				return
   505  			}
   506  		}
   507  		result = append(result, c)
   508  		if st == siblingNext || st == siblingPrev {
   509  			// Only one node was requested (immediate next or previous), so exit
   510  			return
   511  		}
   512  	}
   513  	return
   514  }
   515  
   516  // Internal implementation of parent nodes that return a raw slice of Nodes.
   517  func getParentNodes(nodes []*html.Node) []*html.Node {
   518  	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
   519  		if n.Parent != nil && n.Parent.Type == html.ElementNode {
   520  			return []*html.Node{n.Parent}
   521  		}
   522  		return nil
   523  	})
   524  }
   525  
   526  // Internal map function used by many traversing methods. Takes the source nodes
   527  // to iterate on and the mapping function that returns an array of nodes.
   528  // Returns an array of nodes mapped by calling the callback function once for
   529  // each node in the source nodes.
   530  func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
   531  	for i, n := range nodes {
   532  		if vals := f(i, n); len(vals) > 0 {
   533  			result = appendWithoutDuplicates(result, vals)
   534  		}
   535  	}
   536  	return
   537  }