Class: Sass::Selector::Pseudo
- Inherits:
- Simple
- Object
- Simple
- Sass::Selector::Pseudo
- Defined in:
- /Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb
Overview
A pseudoclass (e.g. :visited
) or pseudoelement (e.g. ::first-line
) selector. It can have arguments (e.g. :nth-child(2n+1)
) which can contain selectors (e.g. :nth-child(2n+1 of .foo)
).
Constant Summary
Instance Attribute Summary (collapse)
- - (String?) arg readonly
The argument to the selector, or
nil
if no argument was given. - - (String) name readonly
The name of the selector.
- - (CommaSequence) selector readonly
The selector argument, or
nil
if no selector exists. - - (Symbol) syntactic_type readonly
Like #type, but returns the type of selector this looks like, rather than the type it is semantically.
Attributes inherited from Simple
Instance Method Summary (collapse)
- - (Pseudo) initialize(syntactic_type, name, arg, selector) constructor
A new instance of Pseudo.
- - (String) normalized_name
Like #name, but without any vendor prefix.
- - specificity
- - (Boolean) superselector?(their_sseq, parents = [])
Returns whether or not this selector matches all elements that the given selector matches (as well as possibly more).
- - to_s(opts = {})
- - (Symbol) type
The type of the selector.
- - unify(sels)
Returns
nil
if this is a pseudoelement selector andsels
contains a pseudoelement selector different than this one. - - (Array<Simple>) with_selector(new_selector)
Returns a copy of this with #selector set to #new_selector.
Methods inherited from Simple
#eql?, #equality_key, #hash, #inspect, #unify_namespaces
Constructor Details
- (Pseudo) initialize(syntactic_type, name, arg, selector)
Returns a new instance of Pseudo
44 45 46 47 48 49 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 44
def initialize(syntactic_type, name, arg, selector)
@syntactic_type = syntactic_type
@name = name
@arg = arg
@selector = selector
end |
Instance Attribute Details
- (String?) arg (readonly)
The argument to the selector, or nil
if no argument was given.
30 31 32 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 30
def arg
@arg
end |
- (String) name (readonly)
The name of the selector.
24 25 26 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 24
def name
@name
end |
- (CommaSequence) selector (readonly)
38 39 40 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 38
def selector
@selector
end |
- (Symbol) syntactic_type (readonly)
Like #type, but returns the type of selector this looks like, rather than the type it is semantically. This only differs from type for selectors in ACTUALLY_ELEMENTS.
19 20 21 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 19
def syntactic_type
@syntactic_type
end |
Instance Method Details
- (String) normalized_name
Like #name, but without any vendor prefix.
111 112 113 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 111
def normalized_name
@normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
end |
- specificity
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 230
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 |
- (Boolean) superselector?(their_sseq, parents = [])
Returns whether or not this selector matches all elements that the given selector matches (as well as possibly more).
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 149
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'
# 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 |
- to_s(opts = {})
116 117 118 119 120 121 122 123 124 125 126 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 116
def to_s(opts = {})
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 |
- (Symbol) type
The type of the selector. :class
if this is a pseudoclass selector, :element
if it’s a pseudoelement.
104 105 106 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 104
def type
ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
end |
- unify(sels)
Returns nil
if this is a pseudoelement selector and sels
contains a pseudoelement selector different than this one.
132 133 134 135 136 137 138 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 132
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 |
- (Array<Simple>) with_selector(new_selector)
Returns a copy of this with #selector set to #new_selector.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | # File '/Users/ceppstei/Projects/sass-lang/.sass/lib/sass/selector/pseudo.rb', line 55
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'
# 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 |