2010-04-19 17:22:32 -07:00
|
|
|
require 'sass/selector/simple'
|
2010-04-19 17:12:39 -07:00
|
|
|
require 'sass/selector/abstract_sequence'
|
|
|
|
require 'sass/selector/comma_sequence'
|
|
|
|
require 'sass/selector/sequence'
|
|
|
|
require 'sass/selector/simple_sequence'
|
|
|
|
|
2010-02-22 21:56:57 -08:00
|
|
|
module Sass
|
|
|
|
# A namespace for nodes in the parse tree for selectors.
|
2010-04-21 18:12:32 -07:00
|
|
|
#
|
|
|
|
# {CommaSequence} is the toplevel seelctor,
|
|
|
|
# representing a comma-separated sequence of {Sequence}s,
|
|
|
|
# such as `foo bar, baz bang`.
|
|
|
|
# {Sequence} is the next level,
|
|
|
|
# representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
|
|
|
|
# such as `foo bar` or `foo > bar baz`.
|
|
|
|
# {SimpleSequence} is a sequence of selectors that all apply to a single element,
|
|
|
|
# such as `foo.bar[attr=val]`.
|
|
|
|
# Finally, {Simple} is the superclass of the simplest selectors,
|
|
|
|
# such as `.foo` or `#bar`.
|
2010-02-22 21:56:57 -08:00
|
|
|
module Selector
|
2010-02-23 22:37:50 -08:00
|
|
|
# A parent-referencing selector (`&` in Sass).
|
|
|
|
# The function of this is to be replaced by the parent selector
|
|
|
|
# in the nested hierarchy.
|
2010-04-19 17:22:32 -07:00
|
|
|
class Parent < Simple
|
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
|
|
|
["&"]
|
|
|
|
end
|
2010-03-08 14:11:18 -08:00
|
|
|
|
|
|
|
# Always raises an exception.
|
|
|
|
#
|
|
|
|
# @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#unify
|
2010-03-08 14:11:18 -08:00
|
|
|
def unify(sels)
|
|
|
|
raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
|
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# A class selector (e.g. `.foo`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Class < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The class name.
|
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :name
|
|
|
|
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param name [Array<String, Sass::Script::Node>] The class name
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(name)
|
|
|
|
@name = name
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
2010-04-10 17:15:43 -07:00
|
|
|
[".", *@name]
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# An id selector (e.g. `#foo`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Id < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The id name.
|
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :name
|
|
|
|
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param name [Array<String, Sass::Script::Node>] The id name
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(name)
|
|
|
|
@name = name
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
2010-04-10 17:15:43 -07:00
|
|
|
["#", *@name]
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
2010-03-08 14:11:18 -08:00
|
|
|
|
|
|
|
# Returns `nil` if `sels` contains an {Id} selector
|
|
|
|
# with a different name than this one.
|
|
|
|
#
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#unify
|
2010-03-08 14:11:18 -08:00
|
|
|
def unify(sels)
|
|
|
|
return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
|
|
|
|
super
|
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# A universal selector (`*` in CSS).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Universal < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The selector namespace.
|
|
|
|
# `nil` means the default namespace,
|
2010-04-10 17:15:43 -07:00
|
|
|
# `[""]` means no namespace,
|
|
|
|
# `["*"]` means any namespace.
|
2010-03-08 14:11:18 -08:00
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>, nil]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :namespace
|
|
|
|
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(namespace)
|
|
|
|
@namespace = namespace
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
2010-04-10 17:15:43 -07:00
|
|
|
@namespace ? @namespace + ["|*"] : ["*"]
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
2010-03-08 14:11:18 -08:00
|
|
|
|
|
|
|
# Unification of a universal selector is somewhat complicated,
|
|
|
|
# especially when a namespace is specified.
|
|
|
|
# If there is no namespace specified
|
|
|
|
# or any namespace is specified (namespace `"*"`),
|
|
|
|
# then `sel` is returned without change
|
|
|
|
# (unless it's empty, in which case `"*"` is required).
|
|
|
|
#
|
|
|
|
# If a namespace is specified
|
|
|
|
# but `sel` does not specify a namespace,
|
|
|
|
# then the given namespace is applied to `sel`,
|
|
|
|
# either by adding this {Universal} selector
|
|
|
|
# or applying this namespace to an existing {Element} selector.
|
|
|
|
#
|
|
|
|
# If both this selector *and* `sel` specify namespaces,
|
2010-04-21 18:12:32 -07:00
|
|
|
# those namespaces are unified via {Simple#unify_namespaces}
|
2010-03-08 14:11:18 -08:00
|
|
|
# and the unified namespace is used, if possible.
|
|
|
|
#
|
|
|
|
# @todo There are lots of cases that this documentation specifies;
|
|
|
|
# make sure we thoroughly test **all of them**.
|
|
|
|
# @todo Keep track of whether a default namespace has been declared
|
|
|
|
# and handle namespace-unspecified selectors accordingly.
|
2010-03-09 02:55:41 -08:00
|
|
|
# @todo If any branch of a CommaSequence ends up being just `"*"`,
|
|
|
|
# then all other branches should be eliminated
|
2010-03-08 14:11:18 -08:00
|
|
|
#
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#unify
|
2010-03-08 14:11:18 -08:00
|
|
|
def unify(sels)
|
|
|
|
name =
|
|
|
|
case sels.first
|
|
|
|
when Universal; :universal
|
|
|
|
when Element; sels.first.name
|
|
|
|
else
|
2010-04-10 17:15:43 -07:00
|
|
|
return [self] + sels unless namespace.nil? || namespace == ['*']
|
2010-03-09 02:55:41 -08:00
|
|
|
return sels unless sels.empty?
|
|
|
|
return [self]
|
2010-03-08 14:11:18 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
ns, accept = unify_namespaces(namespace, sels.first.namespace)
|
|
|
|
return unless accept
|
2010-03-08 23:40:31 -08:00
|
|
|
[name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
|
2010-03-08 14:11:18 -08:00
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# An element selector (e.g. `h1`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Element < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The element name.
|
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :name
|
|
|
|
|
|
|
|
# The selector namespace.
|
|
|
|
# `nil` means the default namespace,
|
2010-04-10 17:15:43 -07:00
|
|
|
# `[""]` means no namespace,
|
|
|
|
# `["*"]` means any namespace.
|
2010-03-08 14:11:18 -08:00
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>, nil]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :namespace
|
|
|
|
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param name [Array<String, Sass::Script::Node>] The element name
|
|
|
|
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(name, namespace)
|
|
|
|
@name = name
|
|
|
|
@namespace = namespace
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
2010-04-10 17:15:43 -07:00
|
|
|
@namespace ? @namespace + ["|"] + @name : @name
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
2010-03-08 14:11:18 -08:00
|
|
|
|
|
|
|
# Unification of an element selector is somewhat complicated,
|
|
|
|
# especially when a namespace is specified.
|
|
|
|
# First, if `sel` contains another {Element} with a different \{#name},
|
|
|
|
# then the selectors can't be unified and `nil` is returned.
|
|
|
|
#
|
|
|
|
# Otherwise, if `sel` doesn't specify a namespace,
|
|
|
|
# or it specifies any namespace (via `"*"`),
|
|
|
|
# then it's returned with this element selector
|
|
|
|
# (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
|
|
|
|
# Similarly, if this selector doesn't specify a namespace,
|
|
|
|
# the namespace from `sel` is used.
|
|
|
|
#
|
|
|
|
# If both this selector *and* `sel` specify namespaces,
|
2010-04-21 18:12:32 -07:00
|
|
|
# those namespaces are unified via {Simple#unify_namespaces}
|
2010-03-08 14:11:18 -08:00
|
|
|
# and the unified namespace is used, if possible.
|
|
|
|
#
|
|
|
|
# @todo There are lots of cases that this documentation specifies;
|
|
|
|
# make sure we thoroughly test **all of them**.
|
|
|
|
# @todo Keep track of whether a default namespace has been declared
|
|
|
|
# and handle namespace-unspecified selectors accordingly.
|
|
|
|
#
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#unify
|
2010-03-08 14:11:18 -08:00
|
|
|
def unify(sels)
|
|
|
|
case sels.first
|
|
|
|
when Universal;
|
|
|
|
when Element; return unless name == sels.first.name
|
|
|
|
else return [self] + sels
|
|
|
|
end
|
|
|
|
|
|
|
|
ns, accept = unify_namespaces(namespace, sels.first.namespace)
|
|
|
|
return unless accept
|
2010-03-08 23:40:31 -08:00
|
|
|
[Element.new(name, ns)] + sels[1..-1]
|
2010-03-08 14:11:18 -08:00
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# Selector interpolation (`#{}` in Sass).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Interpolation < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The script to run.
|
|
|
|
#
|
|
|
|
# @return [Sass::Script::Node]
|
|
|
|
attr_reader :script
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# @param script [Sass::Script::Node] The script to run
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(script)
|
|
|
|
@script = script
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
|
|
|
[@script]
|
|
|
|
end
|
2010-03-08 14:11:18 -08:00
|
|
|
|
|
|
|
# Always raises an exception.
|
|
|
|
#
|
|
|
|
# @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#unify
|
2010-03-08 14:11:18 -08:00
|
|
|
def unify(sels)
|
|
|
|
raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
|
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# An attribute selector (e.g. `[href^="http://"]`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Attribute < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The attribute name.
|
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :name
|
|
|
|
|
|
|
|
# The attribute namespace.
|
|
|
|
# `nil` means the default namespace,
|
2010-04-10 17:15:43 -07:00
|
|
|
# `[""]` means no namespace,
|
|
|
|
# `["*"]` means any namespace.
|
2010-03-08 14:11:18 -08:00
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>, nil]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :namespace
|
|
|
|
|
|
|
|
# The matching operator, e.g. `"="` or `"^="`.
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
attr_reader :operator
|
|
|
|
|
|
|
|
# The right-hand side of the operator.
|
|
|
|
#
|
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
|
|
|
attr_reader :value
|
|
|
|
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param name [Array<String, Sass::Script::Node>] The attribute name
|
|
|
|
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
|
2010-02-23 22:37:50 -08:00
|
|
|
# @param operator [String] The matching operator, e.g. `"="` or `"^="`
|
2010-03-08 14:11:18 -08:00
|
|
|
# @param value [Array<String, Sass::Script::Node>] See \{#value}
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(name, namespace, operator, value)
|
|
|
|
@name = name
|
|
|
|
@namespace = namespace
|
|
|
|
@operator = operator
|
|
|
|
@value = value
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
|
|
|
res = ["["]
|
2010-04-10 17:15:43 -07:00
|
|
|
res.concat(@namespace) << "|" if @namespace
|
|
|
|
res.concat @name
|
2010-02-23 22:37:50 -08:00
|
|
|
(res << @operator).concat @value if @value
|
2010-02-22 21:56:57 -08:00
|
|
|
res << "]"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
|
|
|
|
# It can have arguments (e.g. `:nth-child(2n+1)`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Pseudo < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The type of the selector.
|
|
|
|
# `:class` if this is a pseudoclass selector,
|
|
|
|
# `:element` if it's a pseudoelement.
|
|
|
|
#
|
|
|
|
# @return [Symbol]
|
|
|
|
attr_reader :type
|
|
|
|
|
|
|
|
# The name of the selector.
|
|
|
|
#
|
2010-04-10 17:15:43 -07:00
|
|
|
# @return [Array<String, Sass::Script::Node>]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :name
|
|
|
|
|
|
|
|
# The argument to the selector,
|
|
|
|
# or `nil` if no argument was given.
|
|
|
|
#
|
|
|
|
# This may include SassScript nodes that will be run during resolution.
|
|
|
|
# Note that this should not include SassScript nodes
|
|
|
|
# after resolution has taken place.
|
|
|
|
#
|
|
|
|
# @return [Array<String, Sass::Script::Node>, nil]
|
|
|
|
attr_reader :arg
|
|
|
|
|
|
|
|
# @param type [Symbol] See \{#type}
|
2010-04-10 17:15:43 -07:00
|
|
|
# @param name [Array<String, Sass::Script::Node>] The name of the selector
|
2010-02-23 22:37:50 -08:00
|
|
|
# @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
|
|
|
|
# or nil if no argument was given
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(type, name, arg)
|
|
|
|
@type = type
|
|
|
|
@name = name
|
|
|
|
@arg = arg
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
2010-04-10 17:15:43 -07:00
|
|
|
res = [@type == :class ? ":" : "::"] + @name
|
2010-04-10 17:51:40 -07:00
|
|
|
(res << "(").concat(Haml::Util.strip_string_array(@arg)) << ")" if @arg
|
2010-02-22 21:56:57 -08:00
|
|
|
res
|
|
|
|
end
|
2010-03-09 13:31:08 -08:00
|
|
|
|
2010-04-21 02:25:59 -07:00
|
|
|
# Returns `nil` if this is a pseudoclass selector
|
|
|
|
# and `sels` contains a pseudoclass selector different than this one.
|
|
|
|
#
|
|
|
|
# @see Selector#unify
|
2010-03-09 13:31:08 -08:00
|
|
|
def unify(sels)
|
|
|
|
return if type == :element && sels.any? do |sel|
|
|
|
|
sel.is_a?(Pseudo) && sel.type == :element &&
|
|
|
|
(sel.name != self.name || sel.arg != self.arg)
|
|
|
|
end
|
|
|
|
super
|
|
|
|
end
|
2010-02-22 21:56:57 -08:00
|
|
|
end
|
|
|
|
|
2010-02-23 22:37:50 -08:00
|
|
|
# A negation pseudoclass selector (e.g. `:not(.foo)`).
|
2010-04-19 17:22:32 -07:00
|
|
|
class Negation < Simple
|
2010-03-08 14:11:18 -08:00
|
|
|
# The selector to negate.
|
|
|
|
#
|
2010-04-19 17:22:32 -07:00
|
|
|
# @return [Selector]
|
2010-03-08 14:11:18 -08:00
|
|
|
attr_reader :selector
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @param [Selector] The selector to negate
|
2010-02-22 21:56:57 -08:00
|
|
|
def initialize(selector)
|
|
|
|
@selector = selector
|
|
|
|
end
|
|
|
|
|
2010-04-19 17:22:32 -07:00
|
|
|
# @see Selector#to_a
|
2010-02-22 21:56:57 -08:00
|
|
|
def to_a
|
|
|
|
[":not("] + @selector.to_a + [")"]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|