mirror of
				https://github.com/PuerkitoBio/goquery
				synced 2025-10-31 19:53:05 +08:00 
			
		
		
		
	start work on PrevUntil() and NextUntil()
This commit is contained in:
		| @@ -62,7 +62,7 @@ Taken from example_test.go: | ||||
|         title = s.Find("em").Text() | ||||
|         if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil { | ||||
|           // Not a valid float, ignore score | ||||
|           fmt.Printf("Review %d: %s - %s", i, band, title) | ||||
|           fmt.Printf("Review %d: %s - %s.\n", i, band, title) | ||||
|         } else { | ||||
|           // Print all, including score | ||||
|           fmt.Printf("Review %d: %s - %s (%2.1f).\n", i, band, title, score) | ||||
| @@ -79,7 +79,6 @@ Taken from example_test.go: | ||||
| ## TODOs | ||||
|  | ||||
| *    Implement NextUntil() and PrevUntil(). | ||||
| *    Fix Prev...() functions to return nodes in the same order as jQuery (starting with the immediately preceding node, up until first child of the parent). | ||||
| *    Benchmarks so that future changes have a baseline to compare to. | ||||
| *    Add jQuery's `Closest()`? Other missing functions? | ||||
| *    Support negative indices in `Slice()`, like jQuery. | ||||
|   | ||||
| @@ -28,7 +28,7 @@ func ExampleScrape_MetalReview() { | ||||
| 		title = s.Find("em").Text() | ||||
| 		if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil { | ||||
| 			// Not a valid float, ignore score | ||||
| 			fmt.Printf("Review %d: %s - %s", i, band, title) | ||||
| 			fmt.Printf("Review %d: %s - %s.\n", i, band, title) | ||||
| 		} else { | ||||
| 			// Print all, including score | ||||
| 			fmt.Printf("Review %d: %s - %s (%2.1f).\n", i, band, title, score) | ||||
|   | ||||
							
								
								
									
										78
									
								
								traversal.go
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								traversal.go
									
									
									
									
									
								
							| @@ -158,66 +158,70 @@ func (this *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes .. | ||||
| // 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)) | ||||
| 	return pushStack(this, getSiblingNodes(this.Nodes, siblingAll, "", nil)) | ||||
| } | ||||
|  | ||||
| // 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), selector) | ||||
| 	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingAll, "", nil), selector) | ||||
| } | ||||
|  | ||||
| // 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)) | ||||
| 	return pushStack(this, getSiblingNodes(this.Nodes, siblingNext, "", nil)) | ||||
| } | ||||
|  | ||||
| // 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), selector) | ||||
| 	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNext, "", nil), selector) | ||||
| } | ||||
|  | ||||
| // 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)) | ||||
| 	return pushStack(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil)) | ||||
| } | ||||
|  | ||||
| // 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), selector) | ||||
| 	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingNextAll, "", nil), selector) | ||||
| } | ||||
|  | ||||
| // 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)) | ||||
| 	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil)) | ||||
| } | ||||
|  | ||||
| // 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), selector) | ||||
| 	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrev, "", nil), selector) | ||||
| } | ||||
|  | ||||
| // 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)) | ||||
| 	return pushStack(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil)) | ||||
| } | ||||
|  | ||||
| // 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), selector) | ||||
| 	return filterAndPush(this, getSiblingNodes(this.Nodes, siblingPrevAll, "", nil), selector) | ||||
| } | ||||
|  | ||||
| func (this *Selection) PrevUntil(selector string) *Selection { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Filter and push filters the nodes based on a selector, and pushes the results | ||||
| @@ -269,22 +273,50 @@ func getParentsNodes(nodes []*html.Node, stopSelector string, stopNodes []*html. | ||||
| } | ||||
|  | ||||
| // Internal implementation of sibling nodes that return a raw slice of matches. | ||||
| func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node { | ||||
| 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  | ||||
| 	// determine if the until condition is reached (returns true if it is) | ||||
| 	if st == siblingNextUntil || st == siblingPrevUntil { | ||||
| 		f = func(n *html.Node) bool { | ||||
| 			if untilSelector != "" { | ||||
| 				// Selector-based condition | ||||
| 				sel := newSingleSelection(n, nil) | ||||
| 				return sel.Is(untilSelector) | ||||
| 			} else if len(untilNodes) > 0 { | ||||
| 				// Nodes-based condition | ||||
| 				sel := newSingleSelection(n, nil) | ||||
| 				return sel.IsNodes(untilNodes...) | ||||
| 			} | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node { | ||||
|  | ||||
| 		// Get the parent and loop through all children | ||||
| 		if p := n.Parent; p != nil { | ||||
| 			// For Prev() calls that may return more than one node (unlike Prev()), | ||||
| 			// start at the current node, and work backwards from there. Since | ||||
| 			// Prev() returns only one node, no advantage to find the current node | ||||
| 			// first and loop backwards to find the previous one. Better to just loop | ||||
| 			// from the start and stop once it is found. | ||||
| 			if st == siblingPrevAll || st == siblingPrevUntil { | ||||
| 				// Find the index of this node | ||||
| 				// Find the index of this node within its parent's children | ||||
| 				for i, c := range p.Child { | ||||
| 					if c == n { | ||||
| 						// Looking for previous nodes, so start at index - 1 upwards | ||||
| 						return getChildrenWithSiblingType(p, st, n, i-1, -1) | ||||
| 						// Looking for previous nodes, so start at index - 1 backwards | ||||
| 						return getChildrenWithSiblingType(p, st, n, i-1, -1, f) | ||||
| 					} | ||||
| 				} | ||||
| 				// Should never get here. | ||||
| 				panic(errors.New(fmt.Sprintf("Could not find node %+v in his parent's Child slice.", n))) | ||||
| 			} else { | ||||
| 				return getChildrenWithSiblingType(p, st, n, 0, 1) | ||||
| 				// Standard loop from start moving forwards. | ||||
| 				return getChildrenWithSiblingType(p, st, n, 0, 1, f) | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| @@ -295,14 +327,14 @@ func getSiblingNodes(nodes []*html.Node, st siblingType) []*html.Node { | ||||
| // based on the sibling type request. | ||||
| func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node { | ||||
| 	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node { | ||||
| 		return getChildrenWithSiblingType(n, st, nil, 0, 1) | ||||
| 		return getChildrenWithSiblingType(n, st, nil, 0, 1, nil) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Gets the children of the specified parent, based on the requested sibling | ||||
| // type, skipping a specified node if required. | ||||
| func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node, | ||||
| 	startIndex int, increment int) (result []*html.Node) { | ||||
| 	startIndex int, increment int, untilFunc func(*html.Node) bool) (result []*html.Node) { | ||||
|  | ||||
| 	var prev *html.Node | ||||
| 	var nFound bool | ||||
| @@ -337,8 +369,16 @@ func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *htm | ||||
| 			if c != skipNode && | ||||
| 				(st == siblingAll || | ||||
| 					st == siblingAllIncludingNonElements || | ||||
| 					(st == siblingPrevAll && !nFound) || | ||||
| 					(st == siblingNextAll && nFound)) { | ||||
| 					((st == siblingPrevAll || st == siblingPrevUntil) && !nFound) || | ||||
| 					((st == siblingNextAll || st == siblingNextUntil) && nFound)) { | ||||
|  | ||||
| 				// 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) { | ||||
| 						return | ||||
| 					} | ||||
| 				} | ||||
| 				result = append(result, c) | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Martin Angers
					Martin Angers