1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 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
|
module Sass
module Selector
# A comma-separated sequence of selectors.
class CommaSequence < AbstractSequence
@@compound_extend_deprecation = Sass::Deprecation.new
# The comma-separated selector sequences
# represented by this class.
#
# @return [Array<Sequence>]
attr_reader :members
# @param seqs [Array<Sequence>] See \{#members}
def initialize(seqs)
@members = seqs
end
# Resolves the {Parent} selectors within this selector
# by replacing them with the given parent selector,
# handling commas appropriately.
#
# @param super_cseq [CommaSequence] The parent selector
# @param implicit_parent [Boolean] Whether the the parent
# selector should automatically be prepended to the resolved
# selector if it contains no parent refs.
# @return [CommaSequence] This selector, with parent references resolved
# @raise [Sass::SyntaxError] If a parent selector is invalid
def resolve_parent_refs(super_cseq, implicit_parent = true)
if super_cseq.nil?
if contains_parent_ref?
raise Sass::SyntaxError.new(
"Base-level rules cannot contain the parent-selector-referencing character '&'.")
end
return self
end
CommaSequence.new(Sass::Util.flatten_vertically(@members.map do |seq|
seq.resolve_parent_refs(super_cseq, implicit_parent).members
end))
end
# Returns whether there's a {Parent} selector anywhere in this sequence.
#
# @return [Boolean]
def contains_parent_ref?
@members.any? {|sel| sel.contains_parent_ref?}
end
# Non-destrucively extends this selector with the extensions specified in a hash
# (which should come from {Sass::Tree::Visitors::Cssize}).
#
# @todo Link this to the reference documentation on `@extend`
# when such a thing exists.
#
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
# Sass::Tree::Visitors::Cssize::Extend}]
# The extensions to perform on this selector
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
# The directives containing this selector.
# @param replace [Boolean]
# Whether to replace the original selector entirely or include
# it in the result.
# @param seen [Set<Array<Selector::Simple>>]
# The set of simple sequences that are currently being replaced.
# @param original [Boolean]
# Whether this is the original selector being extended, as opposed to
# the result of a previous extension that's being re-extended.
# @return [CommaSequence] A copy of this selector,
# with extensions made according to `extends`
def do_extend(extends, parent_directives = [], replace = false, seen = Set.new,
original = true)
CommaSequence.new(members.map do |seq|
seq.do_extend(extends, parent_directives, replace, seen, original)
end.flatten)
end
# Returns whether or not this selector matches all elements
# that the given selector matches (as well as possibly more).
#
# @example
# (.foo).superselector?(.foo.bar) #=> true
# (.foo).superselector?(.bar) #=> false
# @param cseq [CommaSequence]
# @return [Boolean]
def superselector?(cseq)
cseq.members.all? {|seq1| members.any? {|seq2| seq2.superselector?(seq1)}}
end
# Populates a subset map that can then be used to extend
# selectors. This registers an extension with this selector as
# the extender and `extendee` as the extendee.
#
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
# Sass::Tree::Visitors::Cssize::Extend}]
# The subset map representing the extensions to perform.
# @param extendee [CommaSequence] The selector being extended.
# @param extend_node [Sass::Tree::ExtendNode]
# The node that caused this extension.
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
# The parent directives containing `extend_node`.
# @param allow_compound_target [Boolean]
# Whether `extendee` is allowed to contain compound selectors.
# @raise [Sass::SyntaxError] if this extension is invalid.
def populate_extends(extends, extendee, extend_node = nil, parent_directives = [],
allow_compound_target = false)
extendee.members.each do |seq|
if seq.members.size > 1
raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend nested selectors")
end
sseq = seq.members.first
if !sseq.is_a?(Sass::Selector::SimpleSequence)
raise Sass::SyntaxError.new("Can't extend #{seq}: invalid selector")
elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
raise Sass::SyntaxError.new("Can't extend #{seq}: can't extend parent selectors")
end
sel = sseq.members
if !allow_compound_target && sel.length > 1
@@compound_extend_deprecation.warn(sseq.filename, sseq.line, <<WARNING)
Extending a compound selector, #{sseq}, is deprecated and will not be supported in a future release.
Consider "@extend #{sseq.members.join(', ')}" instead.
See http://bit.ly/ExtendCompound for details.
WARNING
end
members.each do |member|
unless member.members.last.is_a?(Sass::Selector::SimpleSequence)
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
end
extends[sel] = Sass::Tree::Visitors::Cssize::Extend.new(
member, sel, extend_node, parent_directives, false)
end
end
end
# Unifies this with another comma selector to produce a selector
# that matches (a subset of) the intersection of the two inputs.
#
# @param other [CommaSequence]
# @return [CommaSequence, nil] The unified selector, or nil if unification failed.
# @raise [Sass::SyntaxError] If this selector cannot be unified.
# This will only ever occur when a dynamic selector,
# such as {Parent} or {Interpolation}, is used in unification.
# Since these selectors should be resolved
# by the time extension and unification happen,
# this exception will only ever be raised as a result of programmer error
def unify(other)
results = members.map {|seq1| other.members.map {|seq2| seq1.unify(seq2)}}.flatten.compact
results.empty? ? nil : CommaSequence.new(results.map {|cseq| cseq.members}.flatten)
end
# Returns a SassScript representation of this selector.
#
# @return [Sass::Script::Value::List]
def to_sass_script
Sass::Script::Value::List.new(members.map do |seq|
Sass::Script::Value::List.new(seq.members.map do |component|
next if component == "\n"
Sass::Script::Value::String.new(component.to_s)
end.compact, separator: :space)
end, separator: :comma)
end
# Returns a string representation of the sequence.
# This is basically the selector string.
#
# @return [String]
def inspect
members.map {|m| m.inspect}.join(", ")
end
# @see AbstractSequence#to_s
def to_s(opts = {})
@members.map do |m|
next if opts[:placeholder] == false && m.invisible?
m.to_s(opts)
end.compact.
join(opts[:style] == :compressed ? "," : ", ").
gsub(", \n", ",\n")
end
private
def _hash
members.hash
end
def _eql?(other)
other.class == self.class && other.members.eql?(members)
end
end
end
end
|