class Sass::Selector::Pseudo

def superselector?(their_sseq, parents = [])

Returns:
  • (Boolean) -

Parameters:
  • parents (Array) -- The parent selectors of `their_sseq`, if any.
  • their_sseq (SimpleSequence) --
def superselector?(their_sseq, parents = [])
  case normalized_name
  when 'matches', 'any'
    # :matches can be a superselector of another selector in one of two
    # ways. Either its constituent selectors can be a superset of those of
    # another :matches in the other selector, or any of its constituent
    # selectors can individually be a superselector of the other selector.
    (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
      next false unless their_sel.is_a?(Pseudo)
      next false unless their_sel.name == name
      selector.superselector?(their_sel.selector)
    end || selector.members.any? do |our_seq|
      their_seq = Sequence.new(parents + [their_sseq])
      our_seq.superselector?(their_seq)
    end
  when 'has', 'host', 'host-context', 'slotted'
    # Like :matches, :has (et al) can be a superselector of another
    # selector if its constituent selectors are a superset of those of
    # another :has in the other selector. However, the :matches other case
    # doesn't work, because :has refers to nested elements.
    (their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
      next false unless their_sel.is_a?(Pseudo)
      next false unless their_sel.name == name
      selector.superselector?(their_sel.selector)
    end
  when 'not'
    selector.members.all? do |our_seq|
      their_sseq.members.any? do |their_sel|
        if their_sel.is_a?(Element) || their_sel.is_a?(Id)
          # `:not(a)` is a superselector of `h1` and `:not(#foo)` is a
          # superselector of `#bar`.
          our_sseq = our_seq.members.last
          next false unless our_sseq.is_a?(SimpleSequence)
          our_sseq.members.any? do |our_sel|
            our_sel.class == their_sel.class && our_sel != their_sel
          end
        else
          next false unless their_sel.is_a?(Pseudo)
          next false unless their_sel.name == name
          # :not(X) is a superselector of :not(Y) exactly when Y is a
          # superselector of X.
          their_sel.selector.superselector?(CommaSequence.new([our_seq]))
        end
      end
    end
  when 'current'
    (their_sseq.selector_pseudo_classes['current'] || []).any? do |their_current|
      next false if their_current.name != name
      # Explicitly don't check for nested superselector relationships
      # here. :current(.foo) isn't always a superselector of
      # :current(.foo.bar), since it matches the *innermost* ancestor of
      # the current element that matches the selector. For example:
      #
      #     <div class="foo bar">
      #       <p class="foo">
      #         <span>current element</span>
      #       </p>
      #     </div>
      #
      # Here :current(.foo) would match the p element and *not* the div
      # element, whereas :current(.foo.bar) would match the div and not
      # the p.
      selector == their_current.selector
    end
  when 'nth-child', 'nth-last-child'
    their_sseq.members.any? do |their_sel|
      # This misses a few edge cases. For example, `:nth-child(n of X)`
      # is a superselector of `X`, and `:nth-child(2n of X)` is a
      # superselector of `:nth-child(4n of X)`. These seem rare enough
      # not to be worth worrying about, though.
      next false unless their_sel.is_a?(Pseudo)
      next false unless their_sel.name == name
      next false unless their_sel.arg == arg
      selector.superselector?(their_sel.selector)
    end
  else
    throw "[BUG] Unknown selector pseudo class #{name}"
  end
end