PageRenderTime 34ms CodeModel.GetById 11ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/scalate-jruby/src/main/resources/haml-3.0.25/lib/sass/selector.rb

http://github.com/scalate/scalate
Ruby | 361 lines | 147 code | 40 blank | 174 comment | 20 complexity | 400645f96a2238dacf91757157c66c4f MD5 | raw file
  1require 'sass/selector/simple'
  2require 'sass/selector/abstract_sequence'
  3require 'sass/selector/comma_sequence'
  4require 'sass/selector/sequence'
  5require 'sass/selector/simple_sequence'
  6
  7module Sass
  8  # A namespace for nodes in the parse tree for selectors.
  9  #
 10  # {CommaSequence} is the toplevel seelctor,
 11  # representing a comma-separated sequence of {Sequence}s,
 12  # such as `foo bar, baz bang`.
 13  # {Sequence} is the next level,
 14  # representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
 15  # such as `foo bar` or `foo > bar baz`.
 16  # {SimpleSequence} is a sequence of selectors that all apply to a single element,
 17  # such as `foo.bar[attr=val]`.
 18  # Finally, {Simple} is the superclass of the simplest selectors,
 19  # such as `.foo` or `#bar`.
 20  module Selector
 21    # A parent-referencing selector (`&` in Sass).
 22    # The function of this is to be replaced by the parent selector
 23    # in the nested hierarchy.
 24    class Parent < Simple
 25      # @see Selector#to_a
 26      def to_a
 27        ["&"]
 28      end
 29
 30      # Always raises an exception.
 31      #
 32      # @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
 33      # @see Selector#unify
 34      def unify(sels)
 35        raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
 36      end
 37    end
 38
 39    # A class selector (e.g. `.foo`).
 40    class Class < Simple
 41      # The class name.
 42      #
 43      # @return [Array<String, Sass::Script::Node>]
 44      attr_reader :name
 45
 46      # @param name [Array<String, Sass::Script::Node>] The class name
 47      def initialize(name)
 48        @name = name
 49      end
 50
 51      # @see Selector#to_a
 52      def to_a
 53        [".", *@name]
 54      end
 55    end
 56
 57    # An id selector (e.g. `#foo`).
 58    class Id < Simple
 59      # The id name.
 60      #
 61      # @return [Array<String, Sass::Script::Node>]
 62      attr_reader :name
 63
 64      # @param name [Array<String, Sass::Script::Node>] The id name
 65      def initialize(name)
 66        @name = name
 67      end
 68
 69      # @see Selector#to_a
 70      def to_a
 71        ["#", *@name]
 72      end
 73
 74      # Returns `nil` if `sels` contains an {Id} selector
 75      # with a different name than this one.
 76      #
 77      # @see Selector#unify
 78      def unify(sels)
 79        return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
 80        super
 81      end
 82    end
 83
 84    # A universal selector (`*` in CSS).
 85    class Universal < Simple
 86      # The selector namespace.
 87      # `nil` means the default namespace,
 88      # `[""]` means no namespace,
 89      # `["*"]` means any namespace.
 90      #
 91      # @return [Array<String, Sass::Script::Node>, nil]
 92      attr_reader :namespace
 93
 94      # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
 95      def initialize(namespace)
 96        @namespace = namespace
 97      end
 98
 99      # @see Selector#to_a
100      def to_a
101        @namespace ? @namespace + ["|*"] : ["*"]
102      end
103
104      # Unification of a universal selector is somewhat complicated,
105      # especially when a namespace is specified.
106      # If there is no namespace specified
107      # or any namespace is specified (namespace `"*"`),
108      # then `sel` is returned without change
109      # (unless it's empty, in which case `"*"` is required).
110      #
111      # If a namespace is specified
112      # but `sel` does not specify a namespace,
113      # then the given namespace is applied to `sel`,
114      # either by adding this {Universal} selector
115      # or applying this namespace to an existing {Element} selector.
116      #
117      # If both this selector *and* `sel` specify namespaces,
118      # those namespaces are unified via {Simple#unify_namespaces}
119      # and the unified namespace is used, if possible.
120      #
121      # @todo There are lots of cases that this documentation specifies;
122      #   make sure we thoroughly test **all of them**.
123      # @todo Keep track of whether a default namespace has been declared
124      #   and handle namespace-unspecified selectors accordingly.
125      # @todo If any branch of a CommaSequence ends up being just `"*"`,
126      #   then all other branches should be eliminated
127      #
128      # @see Selector#unify
129      def unify(sels)
130        name =
131          case sels.first
132          when Universal; :universal
133          when Element; sels.first.name
134          else
135            return [self] + sels unless namespace.nil? || namespace == ['*']
136            return sels unless sels.empty?
137            return [self]
138          end
139
140        ns, accept = unify_namespaces(namespace, sels.first.namespace)
141        return unless accept
142        [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
143      end
144    end
145
146    # An element selector (e.g. `h1`).
147    class Element < Simple
148      # The element name.
149      #
150      # @return [Array<String, Sass::Script::Node>]
151      attr_reader :name
152
153      # The selector namespace.
154      # `nil` means the default namespace,
155      # `[""]` means no namespace,
156      # `["*"]` means any namespace.
157      #
158      # @return [Array<String, Sass::Script::Node>, nil]
159      attr_reader :namespace
160
161      # @param name [Array<String, Sass::Script::Node>] The element name
162      # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
163      def initialize(name, namespace)
164        @name = name
165        @namespace = namespace
166      end
167
168      # @see Selector#to_a
169      def to_a
170        @namespace ? @namespace + ["|"] + @name : @name
171      end
172
173      # Unification of an element selector is somewhat complicated,
174      # especially when a namespace is specified.
175      # First, if `sel` contains another {Element} with a different \{#name},
176      # then the selectors can't be unified and `nil` is returned.
177      #
178      # Otherwise, if `sel` doesn't specify a namespace,
179      # or it specifies any namespace (via `"*"`),
180      # then it's returned with this element selector
181      # (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
182      # Similarly, if this selector doesn't specify a namespace,
183      # the namespace from `sel` is used.
184      #
185      # If both this selector *and* `sel` specify namespaces,
186      # those namespaces are unified via {Simple#unify_namespaces}
187      # and the unified namespace is used, if possible.
188      #
189      # @todo There are lots of cases that this documentation specifies;
190      #   make sure we thoroughly test **all of them**.
191      # @todo Keep track of whether a default namespace has been declared
192      #   and handle namespace-unspecified selectors accordingly.
193      #
194      # @see Selector#unify
195      def unify(sels)
196        case sels.first
197        when Universal;
198        when Element; return unless name == sels.first.name
199        else return [self] + sels
200        end
201
202        ns, accept = unify_namespaces(namespace, sels.first.namespace)
203        return unless accept
204        [Element.new(name, ns)] + sels[1..-1]
205      end
206    end
207
208    # Selector interpolation (`#{}` in Sass).
209    class Interpolation < Simple
210      # The script to run.
211      #
212      # @return [Sass::Script::Node]
213      attr_reader :script
214
215      # @param script [Sass::Script::Node] The script to run
216      def initialize(script)
217        @script = script
218      end
219
220      # @see Selector#to_a
221      def to_a
222        [@script]
223      end
224
225      # Always raises an exception.
226      #
227      # @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
228      # @see Selector#unify
229      def unify(sels)
230        raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
231      end
232    end
233
234    # An attribute selector (e.g. `[href^="http://"]`).
235    class Attribute < Simple
236      # The attribute name.
237      #
238      # @return [Array<String, Sass::Script::Node>]
239      attr_reader :name
240
241      # The attribute namespace.
242      # `nil` means the default namespace,
243      # `[""]` means no namespace,
244      # `["*"]` means any namespace.
245      #
246      # @return [Array<String, Sass::Script::Node>, nil]
247      attr_reader :namespace
248
249      # The matching operator, e.g. `"="` or `"^="`.
250      #
251      # @return [String]
252      attr_reader :operator
253
254      # The right-hand side of the operator.
255      #
256      # @return [Array<String, Sass::Script::Node>]
257      attr_reader :value
258
259      # @param name [Array<String, Sass::Script::Node>] The attribute name
260      # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
261      # @param operator [String] The matching operator, e.g. `"="` or `"^="`
262      # @param value [Array<String, Sass::Script::Node>] See \{#value}
263      def initialize(name, namespace, operator, value)
264        @name = name
265        @namespace = namespace
266        @operator = operator
267        @value = value
268      end
269
270      # @see Selector#to_a
271      def to_a
272        res = ["["]
273        res.concat(@namespace) << "|" if @namespace
274        res.concat @name
275        (res << @operator).concat @value if @value
276        res << "]"
277      end
278    end
279
280    # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
281    # It can have arguments (e.g. `:nth-child(2n+1)`).
282    class Pseudo < Simple
283      # The type of the selector.
284      # `:class` if this is a pseudoclass selector,
285      # `:element` if it's a pseudoelement.
286      #
287      # @return [Symbol]
288      attr_reader :type
289
290      # The name of the selector.
291      #
292      # @return [Array<String, Sass::Script::Node>]
293      attr_reader :name
294
295      # The argument to the selector,
296      # or `nil` if no argument was given.
297      #
298      # This may include SassScript nodes that will be run during resolution.
299      # Note that this should not include SassScript nodes
300      # after resolution has taken place.
301      #
302      # @return [Array<String, Sass::Script::Node>, nil]
303      attr_reader :arg
304
305      # @param type [Symbol] See \{#type}
306      # @param name [Array<String, Sass::Script::Node>] The name of the selector
307      # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
308      #   or nil if no argument was given
309      def initialize(type, name, arg)
310        @type = type
311        @name = name
312        @arg = arg
313      end
314
315      # @see Selector#to_a
316      def to_a
317        res = [@type == :class ? ":" : "::"] + @name
318        (res << "(").concat(Haml::Util.strip_string_array(@arg)) << ")" if @arg
319        res
320      end
321
322      # Returns `nil` if this is a pseudoclass selector
323      # and `sels` contains a pseudoclass selector different than this one.
324      #
325      # @see Selector#unify
326      def unify(sels)
327        return if type == :element && sels.any? do |sel|
328          sel.is_a?(Pseudo) && sel.type == :element &&
329            (sel.name != self.name || sel.arg != self.arg)
330        end
331        super
332      end
333    end
334
335    # A pseudoclass selector whose argument is itself a selector
336    # (e.g. `:not(.foo)` or `:-moz-all(.foo, .bar)`).
337    class SelectorPseudoClass < Simple
338      # The name of the pseudoclass.
339      #
340      # @return [String]
341      attr_reader :name
342
343      # The selector argument.
344      #
345      # @return [Selector::Sequence]
346      attr_reader :selector
347
348      # @param [String] The name of the pseudoclass
349      # @param [Selector::Sequence] The selector argument
350      def initialize(name, selector)
351        @name = name
352        @selector = selector
353      end
354
355      # @see Selector#to_a
356      def to_a
357        [":", @name, "("] + @selector.to_a + [")"]
358      end
359    end
360  end
361end