You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
5.8 KiB

  1. package goquery
  2. import "golang.org/x/net/html"
  3. // Filter reduces the set of matched elements to those that match the selector string.
  4. // It returns a new Selection object for this subset of matching elements.
  5. func (s *Selection) Filter(selector string) *Selection {
  6. return s.FilterMatcher(compileMatcher(selector))
  7. }
  8. // FilterMatcher reduces the set of matched elements to those that match
  9. // the given matcher. It returns a new Selection object for this subset
  10. // of matching elements.
  11. func (s *Selection) FilterMatcher(m Matcher) *Selection {
  12. return pushStack(s, winnow(s, m, true))
  13. }
  14. // Not removes elements from the Selection that match the selector string.
  15. // It returns a new Selection object with the matching elements removed.
  16. func (s *Selection) Not(selector string) *Selection {
  17. return s.NotMatcher(compileMatcher(selector))
  18. }
  19. // NotMatcher removes elements from the Selection that match the given matcher.
  20. // It returns a new Selection object with the matching elements removed.
  21. func (s *Selection) NotMatcher(m Matcher) *Selection {
  22. return pushStack(s, winnow(s, m, false))
  23. }
  24. // FilterFunction reduces the set of matched elements to those that pass the function's test.
  25. // It returns a new Selection object for this subset of elements.
  26. func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
  27. return pushStack(s, winnowFunction(s, f, true))
  28. }
  29. // NotFunction removes elements from the Selection that pass the function's test.
  30. // It returns a new Selection object with the matching elements removed.
  31. func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
  32. return pushStack(s, winnowFunction(s, f, false))
  33. }
  34. // FilterNodes reduces the set of matched elements to those that match the specified nodes.
  35. // It returns a new Selection object for this subset of elements.
  36. func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
  37. return pushStack(s, winnowNodes(s, nodes, true))
  38. }
  39. // NotNodes removes elements from the Selection that match the specified nodes.
  40. // It returns a new Selection object with the matching elements removed.
  41. func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
  42. return pushStack(s, winnowNodes(s, nodes, false))
  43. }
  44. // FilterSelection reduces the set of matched elements to those that match a
  45. // node in the specified Selection object.
  46. // It returns a new Selection object for this subset of elements.
  47. func (s *Selection) FilterSelection(sel *Selection) *Selection {
  48. if sel == nil {
  49. return pushStack(s, winnowNodes(s, nil, true))
  50. }
  51. return pushStack(s, winnowNodes(s, sel.Nodes, true))
  52. }
  53. // NotSelection removes elements from the Selection that match a node in the specified
  54. // Selection object. It returns a new Selection object with the matching elements removed.
  55. func (s *Selection) NotSelection(sel *Selection) *Selection {
  56. if sel == nil {
  57. return pushStack(s, winnowNodes(s, nil, false))
  58. }
  59. return pushStack(s, winnowNodes(s, sel.Nodes, false))
  60. }
  61. // Intersection is an alias for FilterSelection.
  62. func (s *Selection) Intersection(sel *Selection) *Selection {
  63. return s.FilterSelection(sel)
  64. }
  65. // Has reduces the set of matched elements to those that have a descendant
  66. // that matches the selector.
  67. // It returns a new Selection object with the matching elements.
  68. func (s *Selection) Has(selector string) *Selection {
  69. return s.HasSelection(s.document.Find(selector))
  70. }
  71. // HasMatcher reduces the set of matched elements to those that have a descendant
  72. // that matches the matcher.
  73. // It returns a new Selection object with the matching elements.
  74. func (s *Selection) HasMatcher(m Matcher) *Selection {
  75. return s.HasSelection(s.document.FindMatcher(m))
  76. }
  77. // HasNodes reduces the set of matched elements to those that have a
  78. // descendant that matches one of the nodes.
  79. // It returns a new Selection object with the matching elements.
  80. func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
  81. return s.FilterFunction(func(_ int, sel *Selection) bool {
  82. // Add all nodes that contain one of the specified nodes
  83. for _, n := range nodes {
  84. if sel.Contains(n) {
  85. return true
  86. }
  87. }
  88. return false
  89. })
  90. }
  91. // HasSelection reduces the set of matched elements to those that have a
  92. // descendant that matches one of the nodes of the specified Selection object.
  93. // It returns a new Selection object with the matching elements.
  94. func (s *Selection) HasSelection(sel *Selection) *Selection {
  95. if sel == nil {
  96. return s.HasNodes()
  97. }
  98. return s.HasNodes(sel.Nodes...)
  99. }
  100. // End ends the most recent filtering operation in the current chain and
  101. // returns the set of matched elements to its previous state.
  102. func (s *Selection) End() *Selection {
  103. if s.prevSel != nil {
  104. return s.prevSel
  105. }
  106. return newEmptySelection(s.document)
  107. }
  108. // Filter based on the matcher, and the indicator to keep (Filter) or
  109. // to get rid of (Not) the matching elements.
  110. func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
  111. // Optimize if keep is requested
  112. if keep {
  113. return m.Filter(sel.Nodes)
  114. }
  115. // Use grep
  116. return grep(sel, func(i int, s *Selection) bool {
  117. return !m.Match(s.Get(0))
  118. })
  119. }
  120. // Filter based on an array of nodes, and the indicator to keep (Filter) or
  121. // to get rid of (Not) the matching elements.
  122. func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
  123. if len(nodes)+len(sel.Nodes) < minNodesForSet {
  124. return grep(sel, func(i int, s *Selection) bool {
  125. return isInSlice(nodes, s.Get(0)) == keep
  126. })
  127. }
  128. set := make(map[*html.Node]bool)
  129. for _, n := range nodes {
  130. set[n] = true
  131. }
  132. return grep(sel, func(i int, s *Selection) bool {
  133. return set[s.Get(0)] == keep
  134. })
  135. }
  136. // Filter based on a function test, and the indicator to keep (Filter) or
  137. // to get rid of (Not) the matching elements.
  138. func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
  139. return grep(sel, func(i int, s *Selection) bool {
  140. return f(i, s) == keep
  141. })
  142. }