sass4 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +13 -0
- data/AGENTS.md +534 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +20 -0
- data/README.md +242 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/sass-spec-ref.sh +40 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +34 -0
- data/lib/sass/cache_stores/filesystem.rb +60 -0
- data/lib/sass/cache_stores/memory.rb +46 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +67 -0
- data/lib/sass/css.rb +407 -0
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +1236 -0
- data/lib/sass/environment.rb +236 -0
- data/lib/sass/error.rb +198 -0
- data/lib/sass/exec/base.rb +188 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +436 -0
- data/lib/sass/exec.rb +9 -0
- data/lib/sass/features.rb +48 -0
- data/lib/sass/importers/base.rb +182 -0
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +221 -0
- data/lib/sass/importers.rb +23 -0
- data/lib/sass/logger/base.rb +47 -0
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +17 -0
- data/lib/sass/media.rb +210 -0
- data/lib/sass/plugin/compiler.rb +552 -0
- data/lib/sass/plugin/configuration.rb +134 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +134 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/css_lexer.rb +33 -0
- data/lib/sass/script/css_parser.rb +36 -0
- data/lib/sass/script/functions.rb +3103 -0
- data/lib/sass/script/lexer.rb +518 -0
- data/lib/sass/script/parser.rb +1164 -0
- data/lib/sass/script/tree/funcall.rb +314 -0
- data/lib/sass/script/tree/interpolation.rb +220 -0
- data/lib/sass/script/tree/list_literal.rb +119 -0
- data/lib/sass/script/tree/literal.rb +49 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/tree/node.rb +119 -0
- data/lib/sass/script/tree/operation.rb +149 -0
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +125 -0
- data/lib/sass/script/tree/unary_operation.rb +69 -0
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +258 -0
- data/lib/sass/script/value/bool.rb +35 -0
- data/lib/sass/script/value/callable.rb +25 -0
- data/lib/sass/script/value/color.rb +704 -0
- data/lib/sass/script/value/function.rb +19 -0
- data/lib/sass/script/value/helpers.rb +298 -0
- data/lib/sass/script/value/list.rb +135 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +44 -0
- data/lib/sass/script/value/number.rb +564 -0
- data/lib/sass/script/value/string.rb +138 -0
- data/lib/sass/script/value.rb +13 -0
- data/lib/sass/script.rb +66 -0
- data/lib/sass/scss/css_parser.rb +61 -0
- data/lib/sass/scss/parser.rb +1343 -0
- data/lib/sass/scss/rx.rb +134 -0
- data/lib/sass/scss/static_parser.rb +351 -0
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/selector/abstract_sequence.rb +112 -0
- data/lib/sass/selector/comma_sequence.rb +195 -0
- data/lib/sass/selector/pseudo.rb +291 -0
- data/lib/sass/selector/sequence.rb +661 -0
- data/lib/sass/selector/simple.rb +124 -0
- data/lib/sass/selector/simple_sequence.rb +348 -0
- data/lib/sass/selector.rb +327 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/source/map.rb +209 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +140 -0
- data/lib/sass/supports.rb +225 -0
- data/lib/sass/tree/at_root_node.rb +83 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +68 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +59 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +43 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +44 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/media_node.rb +48 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +52 -0
- data/lib/sass/tree/node.rb +240 -0
- data/lib/sass/tree/prop_node.rb +162 -0
- data/lib/sass/tree/return_node.rb +19 -0
- data/lib/sass/tree/root_node.rb +44 -0
- data/lib/sass/tree/rule_node.rb +153 -0
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +36 -0
- data/lib/sass/tree/visitors/base.rb +72 -0
- data/lib/sass/tree/visitors/check_nesting.rb +173 -0
- data/lib/sass/tree/visitors/convert.rb +350 -0
- data/lib/sass/tree/visitors/cssize.rb +362 -0
- data/lib/sass/tree/visitors/deep_copy.rb +107 -0
- data/lib/sass/tree/visitors/extend.rb +64 -0
- data/lib/sass/tree/visitors/perform.rb +572 -0
- data/lib/sass/tree/visitors/set_options.rb +139 -0
- data/lib/sass/tree/visitors/to_css.rb +440 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +151 -0
- data/lib/sass/util/normalized_map.rb +122 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +1137 -0
- data/lib/sass/version.rb +120 -0
- data/lib/sass.rb +102 -0
- data/rails/init.rb +1 -0
- metadata +283 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
module Sass
|
2
|
+
module Selector
|
3
|
+
# A comma-separated sequence of selectors.
|
4
|
+
class CommaSequence < AbstractSequence
|
5
|
+
@@compound_extend_deprecation = Sass::Deprecation.new
|
6
|
+
|
7
|
+
# The comma-separated selector sequences
|
8
|
+
# represented by this class.
|
9
|
+
#
|
10
|
+
# @return [Array<Sequence>]
|
11
|
+
attr_reader :members
|
12
|
+
|
13
|
+
# @param seqs [Array<Sequence>] See \{#members}
|
14
|
+
def initialize(seqs)
|
15
|
+
@members = seqs
|
16
|
+
end
|
17
|
+
|
18
|
+
# Resolves the {Parent} selectors within this selector
|
19
|
+
# by replacing them with the given parent selector,
|
20
|
+
# handling commas appropriately.
|
21
|
+
#
|
22
|
+
# @param super_cseq [CommaSequence] The parent selector
|
23
|
+
# @param implicit_parent [Boolean] Whether the the parent
|
24
|
+
# selector should automatically be prepended to the resolved
|
25
|
+
# selector if it contains no parent refs.
|
26
|
+
# @return [CommaSequence] This selector, with parent references resolved
|
27
|
+
# @raise [Sass::SyntaxError] If a parent selector is invalid
|
28
|
+
def resolve_parent_refs(super_cseq, implicit_parent = true)
|
29
|
+
if super_cseq.nil?
|
30
|
+
if contains_parent_ref?
|
31
|
+
raise Sass::SyntaxError.new(
|
32
|
+
"Base-level rules cannot contain the parent-selector-referencing character '&'.")
|
33
|
+
end
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
CommaSequence.new(Sass::Util.flatten_vertically(@members.map do |seq|
|
38
|
+
seq.resolve_parent_refs(super_cseq, implicit_parent).members
|
39
|
+
end))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns whether there's a {Parent} selector anywhere in this sequence.
|
43
|
+
#
|
44
|
+
# @return [Boolean]
|
45
|
+
def contains_parent_ref?
|
46
|
+
@members.any? {|sel| sel.contains_parent_ref?}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Non-destrucively extends this selector with the extensions specified in a hash
|
50
|
+
# (which should come from {Sass::Tree::Visitors::Cssize}).
|
51
|
+
#
|
52
|
+
# @todo Link this to the reference documentation on `@extend`
|
53
|
+
# when such a thing exists.
|
54
|
+
#
|
55
|
+
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
|
56
|
+
# Sass::Tree::Visitors::Cssize::Extend}]
|
57
|
+
# The extensions to perform on this selector
|
58
|
+
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
|
59
|
+
# The directives containing this selector.
|
60
|
+
# @param replace [Boolean]
|
61
|
+
# Whether to replace the original selector entirely or include
|
62
|
+
# it in the result.
|
63
|
+
# @param seen [Set<Array<Selector::Simple>>]
|
64
|
+
# The set of simple sequences that are currently being replaced.
|
65
|
+
# @param original [Boolean]
|
66
|
+
# Whether this is the original selector being extended, as opposed to
|
67
|
+
# the result of a previous extension that's being re-extended.
|
68
|
+
# @return [CommaSequence] A copy of this selector,
|
69
|
+
# with extensions made according to `extends`
|
70
|
+
def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
|
71
|
+
original = true)
|
72
|
+
CommaSequence.new(members.map do |seq|
|
73
|
+
seq.do_extend(extends, parent_directives, replace, seen, original)
|
74
|
+
end.flatten)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns whether or not this selector matches all elements
|
78
|
+
# that the given selector matches (as well as possibly more).
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# (.foo).superselector?(.foo.bar) #=> true
|
82
|
+
# (.foo).superselector?(.bar) #=> false
|
83
|
+
# @param cseq [CommaSequence]
|
84
|
+
# @return [Boolean]
|
85
|
+
def superselector?(cseq)
|
86
|
+
cseq.members.all? {|seq1| members.any? {|seq2| seq2.superselector?(seq1)}}
|
87
|
+
end
|
88
|
+
|
89
|
+
# Populates a subset map that can then be used to extend
|
90
|
+
# selectors. This registers an extension with this selector as
|
91
|
+
# the extender and `extendee` as the extendee.
|
92
|
+
#
|
93
|
+
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
|
94
|
+
# Sass::Tree::Visitors::Cssize::Extend}]
|
95
|
+
# The subset map representing the extensions to perform.
|
96
|
+
# @param extendee [CommaSequence] The selector being extended.
|
97
|
+
# @param extend_node [Sass::Tree::ExtendNode]
|
98
|
+
# The node that caused this extension.
|
99
|
+
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
|
100
|
+
# The parent directives containing `extend_node`.
|
101
|
+
# @param allow_compound_target [Boolean]
|
102
|
+
# Whether `extendee` is allowed to contain compound selectors.
|
103
|
+
# @raise [Sass::SyntaxError] if this extension is invalid.
|
104
|
+
def populate_extends(extends, extendee, extend_node = nil, parent_directives = [],
|
105
|
+
allow_compound_target = false)
|
106
|
+
extendee.members.each do |seq|
|
107
|
+
if seq.members.size > 1
|
108
|
+
raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend nested selectors")
|
109
|
+
end
|
110
|
+
|
111
|
+
sseq = seq.members.first
|
112
|
+
if !sseq.is_a?(Sass::Selector::SimpleSequence)
|
113
|
+
raise Sass::SyntaxError.new("Can't extend #{seq}: invalid selector")
|
114
|
+
elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
|
115
|
+
raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend parent selectors")
|
116
|
+
end
|
117
|
+
|
118
|
+
sel = sseq.members
|
119
|
+
if !allow_compound_target && sel.length > 1
|
120
|
+
@@compound_extend_deprecation.warn(sseq.filename, sseq.line, <<WARNING)
|
121
|
+
Extending a compound selector, #{sseq}, is deprecated and will not be supported in a future release.
|
122
|
+
Consider "@extend #{sseq.members.join(', ')}" instead.
|
123
|
+
See http://bit.ly/ExtendCompound for details.
|
124
|
+
WARNING
|
125
|
+
end
|
126
|
+
|
127
|
+
members.each do |member|
|
128
|
+
unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
129
|
+
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
130
|
+
end
|
131
|
+
|
132
|
+
extends[sel] = Sass::Tree::Visitors::Cssize::Extend.new(
|
133
|
+
member, sel, extend_node, parent_directives, false)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Unifies this with another comma selector to produce a selector
|
139
|
+
# that matches (a subset of) the intersection of the two inputs.
|
140
|
+
#
|
141
|
+
# @param other [CommaSequence]
|
142
|
+
# @return [CommaSequence, nil] The unified selector, or nil if unification failed.
|
143
|
+
# @raise [Sass::SyntaxError] If this selector cannot be unified.
|
144
|
+
# This will only ever occur when a dynamic selector,
|
145
|
+
# such as {Parent} or {Interpolation}, is used in unification.
|
146
|
+
# Since these selectors should be resolved
|
147
|
+
# by the time extension and unification happen,
|
148
|
+
# this exception will only ever be raised as a result of programmer error
|
149
|
+
def unify(other)
|
150
|
+
results = members.map {|seq1| other.members.map {|seq2| seq1.unify(seq2)}}.flatten.compact
|
151
|
+
results.empty? ? nil : CommaSequence.new(results.map {|cseq| cseq.members}.flatten)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns a SassScript representation of this selector.
|
155
|
+
#
|
156
|
+
# @return [Sass::Script::Value::List]
|
157
|
+
def to_sass_script
|
158
|
+
Sass::Script::Value::List.new(members.map do |seq|
|
159
|
+
Sass::Script::Value::List.new(seq.members.map do |component|
|
160
|
+
next if component == "\n"
|
161
|
+
Sass::Script::Value::String.new(component.to_s)
|
162
|
+
end.compact, separator: :space)
|
163
|
+
end, separator: :comma)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a string representation of the sequence.
|
167
|
+
# This is basically the selector string.
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
def inspect
|
171
|
+
members.map {|m| m.inspect}.join(", ")
|
172
|
+
end
|
173
|
+
|
174
|
+
# @see AbstractSequence#to_s
|
175
|
+
def to_s(opts = {})
|
176
|
+
@members.map do |m|
|
177
|
+
next if opts[:placeholder] == false && m.invisible?
|
178
|
+
m.to_s(opts)
|
179
|
+
end.compact.
|
180
|
+
join(opts[:style] == :compressed ? "," : ", ").
|
181
|
+
gsub(", \n", ",\n")
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def _hash
|
187
|
+
members.hash
|
188
|
+
end
|
189
|
+
|
190
|
+
def _eql?(other)
|
191
|
+
other.class == self.class && other.members.eql?(members)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Sass
|
3
|
+
module Selector
|
4
|
+
# A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`)
|
5
|
+
# selector. It can have arguments (e.g. `:nth-child(2n+1)`) which can
|
6
|
+
# contain selectors (e.g. `:nth-child(2n+1 of .foo)`).
|
7
|
+
class Pseudo < Simple
|
8
|
+
# Some pseudo-class-syntax selectors are actually considered
|
9
|
+
# pseudo-elements and must be treated differently. This is a list of such
|
10
|
+
# selectors.
|
11
|
+
#
|
12
|
+
# @return [Set<String>]
|
13
|
+
ACTUALLY_ELEMENTS = %w(after before first-line first-letter).to_set
|
14
|
+
|
15
|
+
# Like \{#type}, but returns the type of selector this looks like, rather
|
16
|
+
# than the type it is semantically. This only differs from type for
|
17
|
+
# selectors in \{ACTUALLY\_ELEMENTS}.
|
18
|
+
#
|
19
|
+
# @return [Symbol]
|
20
|
+
attr_reader :syntactic_type
|
21
|
+
|
22
|
+
# The name of the selector.
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :name
|
26
|
+
|
27
|
+
# The argument to the selector,
|
28
|
+
# or `nil` if no argument was given.
|
29
|
+
#
|
30
|
+
# @return [String, nil]
|
31
|
+
attr_reader :arg
|
32
|
+
|
33
|
+
# The selector argument, or `nil` if no selector exists.
|
34
|
+
#
|
35
|
+
# If this and \{#arg\} are both set, \{#arg\} is considered a non-selector
|
36
|
+
# prefix.
|
37
|
+
#
|
38
|
+
# @return [CommaSequence]
|
39
|
+
attr_reader :selector
|
40
|
+
|
41
|
+
# @param syntactic_type [Symbol] See \{#syntactic_type}
|
42
|
+
# @param name [String] See \{#name}
|
43
|
+
# @param arg [nil, String] See \{#arg}
|
44
|
+
# @param selector [nil, CommaSequence] See \{#selector}
|
45
|
+
def initialize(syntactic_type, name, arg, selector)
|
46
|
+
@syntactic_type = syntactic_type
|
47
|
+
@name = name
|
48
|
+
@arg = arg
|
49
|
+
@selector = selector
|
50
|
+
end
|
51
|
+
|
52
|
+
def unique?
|
53
|
+
type == :class && normalized_name == 'root'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether or not this selector should be hidden due to containing a
|
57
|
+
# placeholder.
|
58
|
+
def invisible?
|
59
|
+
# :not() is a special case—if you eliminate all the placeholders from
|
60
|
+
# it, it should match anything.
|
61
|
+
name != 'not' && @selector && @selector.members.all? {|s| s.invisible?}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns a copy of this with \{#selector} set to \{#new\_selector}.
|
65
|
+
#
|
66
|
+
# @param new_selector [CommaSequence]
|
67
|
+
# @return [Array<Simple>]
|
68
|
+
def with_selector(new_selector)
|
69
|
+
result = Pseudo.new(syntactic_type, name, arg,
|
70
|
+
CommaSequence.new(new_selector.members.map do |seq|
|
71
|
+
next seq unless seq.members.length == 1
|
72
|
+
sseq = seq.members.first
|
73
|
+
next seq unless sseq.is_a?(SimpleSequence) && sseq.members.length == 1
|
74
|
+
sel = sseq.members.first
|
75
|
+
next seq unless sel.is_a?(Pseudo) && sel.selector
|
76
|
+
|
77
|
+
case normalized_name
|
78
|
+
when 'not'
|
79
|
+
# In theory, if there's a nested :not its contents should be
|
80
|
+
# unified with the return value. For example, if :not(.foo)
|
81
|
+
# extends .bar, :not(.bar) should become .foo:not(.bar). However,
|
82
|
+
# this is a narrow edge case and supporting it properly would make
|
83
|
+
# this code and the code calling it a lot more complicated, so
|
84
|
+
# it's not supported for now.
|
85
|
+
# Support :has, :host, :host-context, :slotted within :not
|
86
|
+
if sel.normalized_name == 'matches'
|
87
|
+
sel.selector.members
|
88
|
+
elsif %w(has host host-context slotted).include?(sel.normalized_name)
|
89
|
+
# For :has, :host, :host-context, :slotted, we need to preserve the selector
|
90
|
+
# as-is to maintain the semantic meaning
|
91
|
+
sel
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
when 'matches', 'any', 'current', 'nth-child', 'nth-last-child'
|
96
|
+
# As above, we could theoretically support :not within :matches, but
|
97
|
+
# doing so would require this method and its callers to handle much
|
98
|
+
# more complex cases that likely aren't worth the pain.
|
99
|
+
next [] unless sel.name == name && sel.arg == arg
|
100
|
+
sel.selector.members
|
101
|
+
when 'has', 'host', 'host-context', 'slotted'
|
102
|
+
# We can't expand nested selectors here, because each layer adds an
|
103
|
+
# additional layer of semantics. For example, `:has(:has(img))`
|
104
|
+
# doesn't match `<div><img></div>` but `:has(img)` does.
|
105
|
+
sel
|
106
|
+
else
|
107
|
+
[]
|
108
|
+
end
|
109
|
+
end.flatten))
|
110
|
+
|
111
|
+
# Older browsers support :not but only with a single complex selector.
|
112
|
+
# In order to support those browsers, we break up the contents of a :not
|
113
|
+
# unless it originally contained a selector list.
|
114
|
+
return [result] unless normalized_name == 'not'
|
115
|
+
return [result] if selector.members.length > 1
|
116
|
+
result.selector.members.map do |seq|
|
117
|
+
Pseudo.new(syntactic_type, name, arg, CommaSequence.new([seq]))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# The type of the selector. `:class` if this is a pseudoclass selector,
|
122
|
+
# `:element` if it's a pseudoelement.
|
123
|
+
#
|
124
|
+
# @return [Symbol]
|
125
|
+
def type
|
126
|
+
ACTUALLY_ELEMENTS.include?(normalized_name) ? :element : syntactic_type
|
127
|
+
end
|
128
|
+
|
129
|
+
# Like \{#name\}, but without any vendor prefix.
|
130
|
+
#
|
131
|
+
# @return [String]
|
132
|
+
def normalized_name
|
133
|
+
@normalized_name ||= name.gsub(/^-[a-zA-Z0-9]+-/, '')
|
134
|
+
end
|
135
|
+
|
136
|
+
# @see Selector#to_s
|
137
|
+
def to_s(opts = {})
|
138
|
+
# :not() is a special case, because :not(<nothing>) should match
|
139
|
+
# everything.
|
140
|
+
return '' if name == 'not' && @selector && @selector.members.all? {|m| m.invisible?}
|
141
|
+
|
142
|
+
res = (syntactic_type == :class ? ":" : "::") + @name
|
143
|
+
if @arg || @selector
|
144
|
+
res << "("
|
145
|
+
res << Sass::Util.strip_except_escapes(@arg) if @arg
|
146
|
+
res << " " if @arg && @selector
|
147
|
+
res << @selector.to_s(opts) if @selector
|
148
|
+
res << ")"
|
149
|
+
end
|
150
|
+
res
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns `nil` if this is a pseudoelement selector
|
154
|
+
# and `sels` contains a pseudoelement selector different than this one.
|
155
|
+
#
|
156
|
+
# @see SimpleSequence#unify
|
157
|
+
def unify(sels)
|
158
|
+
return if type == :element && sels.any? do |sel|
|
159
|
+
sel.is_a?(Pseudo) && sel.type == :element &&
|
160
|
+
(sel.name != name || sel.arg != arg || sel.selector != selector)
|
161
|
+
end
|
162
|
+
super
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns whether or not this selector matches all elements
|
166
|
+
# that the given selector matches (as well as possibly more).
|
167
|
+
#
|
168
|
+
# @example
|
169
|
+
# (.foo).superselector?(.foo.bar) #=> true
|
170
|
+
# (.foo).superselector?(.bar) #=> false
|
171
|
+
# @param their_sseq [SimpleSequence]
|
172
|
+
# @param parents [Array<SimpleSequence, String>] The parent selectors of `their_sseq`, if any.
|
173
|
+
# @return [Boolean]
|
174
|
+
def superselector?(their_sseq, parents = [])
|
175
|
+
case normalized_name
|
176
|
+
when 'matches', 'any'
|
177
|
+
# :matches can be a superselector of another selector in one of two
|
178
|
+
# ways. Either its constituent selectors can be a superset of those of
|
179
|
+
# another :matches in the other selector, or any of its constituent
|
180
|
+
# selectors can individually be a superselector of the other selector.
|
181
|
+
(their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
|
182
|
+
next false unless their_sel.is_a?(Pseudo)
|
183
|
+
next false unless their_sel.name == name
|
184
|
+
selector.superselector?(their_sel.selector)
|
185
|
+
end || selector.members.any? do |our_seq|
|
186
|
+
their_seq = Sequence.new(parents + [their_sseq])
|
187
|
+
our_seq.superselector?(their_seq)
|
188
|
+
end
|
189
|
+
when 'has', 'host', 'host-context', 'slotted'
|
190
|
+
# Like :matches, :has (et al) can be a superselector of another
|
191
|
+
# selector if its constituent selectors are a superset of those of
|
192
|
+
# another :has in the other selector. However, the :matches other case
|
193
|
+
# doesn't work, because :has refers to nested elements.
|
194
|
+
(their_sseq.selector_pseudo_classes[normalized_name] || []).any? do |their_sel|
|
195
|
+
next false unless their_sel.is_a?(Pseudo)
|
196
|
+
next false unless their_sel.name == name
|
197
|
+
selector.superselector?(their_sel.selector)
|
198
|
+
end
|
199
|
+
when 'not'
|
200
|
+
selector.members.all? do |our_seq|
|
201
|
+
their_sseq.members.any? do |their_sel|
|
202
|
+
if their_sel.is_a?(Element) || their_sel.is_a?(Id)
|
203
|
+
# `:not(a)` is a superselector of `h1` and `:not(#foo)` is a
|
204
|
+
# superselector of `#bar`.
|
205
|
+
our_sseq = our_seq.members.last
|
206
|
+
next false unless our_sseq.is_a?(SimpleSequence)
|
207
|
+
our_sseq.members.any? do |our_sel|
|
208
|
+
our_sel.class == their_sel.class && our_sel != their_sel
|
209
|
+
end
|
210
|
+
else
|
211
|
+
next false unless their_sel.is_a?(Pseudo)
|
212
|
+
next false unless their_sel.name == name
|
213
|
+
# :not(X) is a superselector of :not(Y) exactly when Y is a
|
214
|
+
# superselector of X.
|
215
|
+
their_sel.selector.superselector?(CommaSequence.new([our_seq]))
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
when 'current'
|
220
|
+
(their_sseq.selector_pseudo_classes['current'] || []).any? do |their_current|
|
221
|
+
next false if their_current.name != name
|
222
|
+
# Explicitly don't check for nested superselector relationships
|
223
|
+
# here. :current(.foo) isn't always a superselector of
|
224
|
+
# :current(.foo.bar), since it matches the *innermost* ancestor of
|
225
|
+
# the current element that matches the selector. For example:
|
226
|
+
#
|
227
|
+
# <div class="foo bar">
|
228
|
+
# <p class="foo">
|
229
|
+
# <span>current element</span>
|
230
|
+
# </p>
|
231
|
+
# </div>
|
232
|
+
#
|
233
|
+
# Here :current(.foo) would match the p element and *not* the div
|
234
|
+
# element, whereas :current(.foo.bar) would match the div and not
|
235
|
+
# the p.
|
236
|
+
selector == their_current.selector
|
237
|
+
end
|
238
|
+
when 'nth-child', 'nth-last-child'
|
239
|
+
their_sseq.members.any? do |their_sel|
|
240
|
+
# This misses a few edge cases. For example, `:nth-child(n of X)`
|
241
|
+
# is a superselector of `X`, and `:nth-child(2n of X)` is a
|
242
|
+
# superselector of `:nth-child(4n of X)`. These seem rare enough
|
243
|
+
# not to be worth worrying about, though.
|
244
|
+
next false unless their_sel.is_a?(Pseudo)
|
245
|
+
next false unless their_sel.name == name
|
246
|
+
next false unless their_sel.arg == arg
|
247
|
+
selector.superselector?(their_sel.selector)
|
248
|
+
end
|
249
|
+
else
|
250
|
+
throw "[BUG] Unknown selector pseudo class #{name}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# @see AbstractSequence#specificity
|
255
|
+
def specificity
|
256
|
+
return 1 if type == :element
|
257
|
+
return SPECIFICITY_BASE unless selector
|
258
|
+
@specificity ||=
|
259
|
+
if normalized_name == 'not'
|
260
|
+
min = 0
|
261
|
+
max = 0
|
262
|
+
selector.members.each do |seq|
|
263
|
+
spec = seq.specificity
|
264
|
+
if spec.is_a?(Range)
|
265
|
+
min = Sass::Util.max(spec.begin, min)
|
266
|
+
max = Sass::Util.max(spec.end, max)
|
267
|
+
else
|
268
|
+
min = Sass::Util.max(spec, min)
|
269
|
+
max = Sass::Util.max(spec, max)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
min == max ? max : (min..max)
|
273
|
+
else
|
274
|
+
min = 0
|
275
|
+
max = 0
|
276
|
+
selector.members.each do |seq|
|
277
|
+
spec = seq.specificity
|
278
|
+
if spec.is_a?(Range)
|
279
|
+
min = Sass::Util.min(spec.begin, min)
|
280
|
+
max = Sass::Util.max(spec.end, max)
|
281
|
+
else
|
282
|
+
min = Sass::Util.min(spec, min)
|
283
|
+
max = Sass::Util.max(spec, max)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
min == max ? max : (min..max)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|