class Sass::Selector::Pseudo

contain selectors (e.g. ‘:nth-child(2n+1 of .foo)`).
selector. It can have arguments (e.g. `:nth-child(2n+1)`) which can
A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`)

def initialize(syntactic_type, name, arg, selector)

Parameters:
  • selector (nil, CommaSequence) -- See \{#selector}
  • arg (nil, String) -- See \{#arg}
  • name (String) -- See \{#name}
  • syntactic_type (Symbol) -- See \{#syntactic_type}
def initialize(syntactic_type, name, arg, selector)
  @syntactic_type = syntactic_type
  @name = name
  @arg = arg
  @selector = selector
end

def invisible?

placeholder.
Whether or not this selector should be hidden due to containing a
def invisible?
  # :not() is a special case—if you eliminate all the placeholders from
  # it, it should match anything.
  name != 'not' && @selector && @selector.members.all? {|s| s.invisible?}
end

def normalized_name

Returns:
  • (String) -
def normalized_name
  @normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
end

def specificity

Other tags:
    See: AbstractSequence#specificity -
def specificity
  return 1 if type == :element
  return SPECIFICITY_BASE unless selector
  @specificity ||=
    if normalized_name == 'not'
      min = 0
      max = 0
      selector.members.each do |seq|
        spec = seq.specificity
        if spec.is_a?(Range)
          min = Sass::Util.max(spec.begin, min)
          max = Sass::Util.max(spec.end, max)
        else
          min = Sass::Util.max(spec, min)
          max = Sass::Util.max(spec, max)
        end
      end
      min == max ? max : (min..max)
    else
      min = 0
      max = 0
      selector.members.each do |seq|
        spec = seq.specificity
        if spec.is_a?(Range)
          min = Sass::Util.min(spec.begin, min)
          max = Sass::Util.max(spec.end, max)
        else
          min = Sass::Util.min(spec, min)
          max = Sass::Util.max(spec, max)
        end
      end
      min == max ? max : (min..max)
    end
end

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

def to_s(opts = {})

Other tags:
    See: Selector#to_s -
def to_s(opts = {})
  # :not() is a special case, because :not(<nothing>) should match
  # everything.
  return '' if name == 'not' && @selector && @selector.members.all? {|m| m.invisible?}
  res = (syntactic_type == :class ? ":" : "::") + @name
  if @arg || @selector
    res << "("
    res << @arg.strip if @arg
    res << " " if @arg && @selector
    res << @selector.to_s(opts) if @selector
    res << ")"
  end
  res
end

def type

Returns:
  • (Symbol) -
def type
  ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
end

def unify(sels)

Other tags:
    See: SimpleSequence#unify -
def unify(sels)
  return if type == :element && sels.any? do |sel|
    sel.is_a?(Pseudo) && sel.type == :element &&
      (sel.name != name || sel.arg != arg || sel.selector != selector)
  end
  super
end

def unique?

def unique?
  type == :class && normalized_name == 'root'
end

def with_selector(new_selector)

Returns:
  • (Array) -

Parameters:
  • new_selector (CommaSequence) --
def with_selector(new_selector)
  result = Pseudo.new(syntactic_type, name, arg,
    CommaSequence.new(new_selector.members.map do |seq|
      next seq unless seq.members.length == 1
      sseq = seq.members.first
      next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
      sel = sseq.members.first
      next seq unless sel.is_a?(Pseudo) && sel.selector
      case normalized_name
      when 'not'
        # In theory, if there's a nested :not its contents should be
        # unified with the return value. For example, if :not(.foo)
        # extends .bar, :not(.bar) should become .foo:not(.bar). However,
        # this is a narrow edge case and supporting it properly would make
        # this code and the code calling it a lot more complicated, so
        # it's not supported for now.
        next [] unless sel.normalized_name == 'matches'
        sel.selector.members
      when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
        # As above, we could theoretically support :not within :matches, but
        # doing so would require this method and its callers to handle much
        # more complex cases that likely aren't worth the pain.
        next [] unless sel.name == name && sel.arg == arg
        sel.selector.members
      when 'has', 'host', 'host-context', 'slotted'
        # We can't expand nested selectors here, because each layer adds an
        # additional layer of semantics. For example, `:has(:has(img))`
        # doesn't match `<div><img></div>` but `:has(img)` does.
        sel
      else
        []
      end
    end.flatten))
  # Older browsers support :not but only with a single complex selector.
  # In order to support those browsers, we break up the contents of a :not
  # unless it originally contained a selector list.
  return [result] unless normalized_name == 'not'
  return [result] if selector.members.length > 1
  result.selector.members.map do |seq|
    Pseudo.new(syntactic_type, name, arg, CommaSequence.new([seq]))
  end
end