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.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/AGENTS.md +534 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +148 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +242 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/sass-spec-ref.sh +40 -0
  14. data/extra/update_watch.rb +13 -0
  15. data/init.rb +18 -0
  16. data/lib/sass/cache_stores/base.rb +88 -0
  17. data/lib/sass/cache_stores/chain.rb +34 -0
  18. data/lib/sass/cache_stores/filesystem.rb +60 -0
  19. data/lib/sass/cache_stores/memory.rb +46 -0
  20. data/lib/sass/cache_stores/null.rb +25 -0
  21. data/lib/sass/cache_stores.rb +15 -0
  22. data/lib/sass/callbacks.rb +67 -0
  23. data/lib/sass/css.rb +407 -0
  24. data/lib/sass/deprecation.rb +55 -0
  25. data/lib/sass/engine.rb +1236 -0
  26. data/lib/sass/environment.rb +236 -0
  27. data/lib/sass/error.rb +198 -0
  28. data/lib/sass/exec/base.rb +188 -0
  29. data/lib/sass/exec/sass_convert.rb +283 -0
  30. data/lib/sass/exec/sass_scss.rb +436 -0
  31. data/lib/sass/exec.rb +9 -0
  32. data/lib/sass/features.rb +48 -0
  33. data/lib/sass/importers/base.rb +182 -0
  34. data/lib/sass/importers/deprecated_path.rb +51 -0
  35. data/lib/sass/importers/filesystem.rb +221 -0
  36. data/lib/sass/importers.rb +23 -0
  37. data/lib/sass/logger/base.rb +47 -0
  38. data/lib/sass/logger/delayed.rb +50 -0
  39. data/lib/sass/logger/log_level.rb +45 -0
  40. data/lib/sass/logger.rb +17 -0
  41. data/lib/sass/media.rb +210 -0
  42. data/lib/sass/plugin/compiler.rb +552 -0
  43. data/lib/sass/plugin/configuration.rb +134 -0
  44. data/lib/sass/plugin/generic.rb +15 -0
  45. data/lib/sass/plugin/merb.rb +48 -0
  46. data/lib/sass/plugin/rack.rb +60 -0
  47. data/lib/sass/plugin/rails.rb +47 -0
  48. data/lib/sass/plugin/staleness_checker.rb +199 -0
  49. data/lib/sass/plugin.rb +134 -0
  50. data/lib/sass/railtie.rb +10 -0
  51. data/lib/sass/repl.rb +57 -0
  52. data/lib/sass/root.rb +7 -0
  53. data/lib/sass/script/css_lexer.rb +33 -0
  54. data/lib/sass/script/css_parser.rb +36 -0
  55. data/lib/sass/script/functions.rb +3103 -0
  56. data/lib/sass/script/lexer.rb +518 -0
  57. data/lib/sass/script/parser.rb +1164 -0
  58. data/lib/sass/script/tree/funcall.rb +314 -0
  59. data/lib/sass/script/tree/interpolation.rb +220 -0
  60. data/lib/sass/script/tree/list_literal.rb +119 -0
  61. data/lib/sass/script/tree/literal.rb +49 -0
  62. data/lib/sass/script/tree/map_literal.rb +64 -0
  63. data/lib/sass/script/tree/node.rb +119 -0
  64. data/lib/sass/script/tree/operation.rb +149 -0
  65. data/lib/sass/script/tree/selector.rb +26 -0
  66. data/lib/sass/script/tree/string_interpolation.rb +125 -0
  67. data/lib/sass/script/tree/unary_operation.rb +69 -0
  68. data/lib/sass/script/tree/variable.rb +57 -0
  69. data/lib/sass/script/tree.rb +16 -0
  70. data/lib/sass/script/value/arg_list.rb +36 -0
  71. data/lib/sass/script/value/base.rb +258 -0
  72. data/lib/sass/script/value/bool.rb +35 -0
  73. data/lib/sass/script/value/callable.rb +25 -0
  74. data/lib/sass/script/value/color.rb +704 -0
  75. data/lib/sass/script/value/function.rb +19 -0
  76. data/lib/sass/script/value/helpers.rb +298 -0
  77. data/lib/sass/script/value/list.rb +135 -0
  78. data/lib/sass/script/value/map.rb +70 -0
  79. data/lib/sass/script/value/null.rb +44 -0
  80. data/lib/sass/script/value/number.rb +564 -0
  81. data/lib/sass/script/value/string.rb +138 -0
  82. data/lib/sass/script/value.rb +13 -0
  83. data/lib/sass/script.rb +66 -0
  84. data/lib/sass/scss/css_parser.rb +61 -0
  85. data/lib/sass/scss/parser.rb +1343 -0
  86. data/lib/sass/scss/rx.rb +134 -0
  87. data/lib/sass/scss/static_parser.rb +351 -0
  88. data/lib/sass/scss.rb +14 -0
  89. data/lib/sass/selector/abstract_sequence.rb +112 -0
  90. data/lib/sass/selector/comma_sequence.rb +195 -0
  91. data/lib/sass/selector/pseudo.rb +291 -0
  92. data/lib/sass/selector/sequence.rb +661 -0
  93. data/lib/sass/selector/simple.rb +124 -0
  94. data/lib/sass/selector/simple_sequence.rb +348 -0
  95. data/lib/sass/selector.rb +327 -0
  96. data/lib/sass/shared.rb +76 -0
  97. data/lib/sass/source/map.rb +209 -0
  98. data/lib/sass/source/position.rb +39 -0
  99. data/lib/sass/source/range.rb +41 -0
  100. data/lib/sass/stack.rb +140 -0
  101. data/lib/sass/supports.rb +225 -0
  102. data/lib/sass/tree/at_root_node.rb +83 -0
  103. data/lib/sass/tree/charset_node.rb +22 -0
  104. data/lib/sass/tree/comment_node.rb +82 -0
  105. data/lib/sass/tree/content_node.rb +9 -0
  106. data/lib/sass/tree/css_import_node.rb +68 -0
  107. data/lib/sass/tree/debug_node.rb +18 -0
  108. data/lib/sass/tree/directive_node.rb +59 -0
  109. data/lib/sass/tree/each_node.rb +24 -0
  110. data/lib/sass/tree/error_node.rb +18 -0
  111. data/lib/sass/tree/extend_node.rb +43 -0
  112. data/lib/sass/tree/for_node.rb +36 -0
  113. data/lib/sass/tree/function_node.rb +44 -0
  114. data/lib/sass/tree/if_node.rb +52 -0
  115. data/lib/sass/tree/import_node.rb +75 -0
  116. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  117. data/lib/sass/tree/media_node.rb +48 -0
  118. data/lib/sass/tree/mixin_def_node.rb +38 -0
  119. data/lib/sass/tree/mixin_node.rb +52 -0
  120. data/lib/sass/tree/node.rb +240 -0
  121. data/lib/sass/tree/prop_node.rb +162 -0
  122. data/lib/sass/tree/return_node.rb +19 -0
  123. data/lib/sass/tree/root_node.rb +44 -0
  124. data/lib/sass/tree/rule_node.rb +153 -0
  125. data/lib/sass/tree/supports_node.rb +38 -0
  126. data/lib/sass/tree/trace_node.rb +33 -0
  127. data/lib/sass/tree/variable_node.rb +36 -0
  128. data/lib/sass/tree/visitors/base.rb +72 -0
  129. data/lib/sass/tree/visitors/check_nesting.rb +173 -0
  130. data/lib/sass/tree/visitors/convert.rb +350 -0
  131. data/lib/sass/tree/visitors/cssize.rb +362 -0
  132. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  133. data/lib/sass/tree/visitors/extend.rb +64 -0
  134. data/lib/sass/tree/visitors/perform.rb +572 -0
  135. data/lib/sass/tree/visitors/set_options.rb +139 -0
  136. data/lib/sass/tree/visitors/to_css.rb +440 -0
  137. data/lib/sass/tree/warn_node.rb +18 -0
  138. data/lib/sass/tree/while_node.rb +18 -0
  139. data/lib/sass/util/multibyte_string_scanner.rb +151 -0
  140. data/lib/sass/util/normalized_map.rb +122 -0
  141. data/lib/sass/util/subset_map.rb +109 -0
  142. data/lib/sass/util/test.rb +9 -0
  143. data/lib/sass/util.rb +1137 -0
  144. data/lib/sass/version.rb +120 -0
  145. data/lib/sass.rb +102 -0
  146. data/rails/init.rb +1 -0
  147. 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