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 }