Support for sass 3.1 alphas

This commit is contained in:
Chris Eppstein 2010-11-21 15:12:52 -08:00
parent 9e033f7df5
commit f06d928c4b
3 changed files with 516 additions and 289 deletions

View File

@ -1,17 +1,58 @@
module Compass::SassExtensions::Functions::Constants module Compass::SassExtensions::Functions::Constants
# returns the opposite position of a side or corner. if defined?(Sass::Script::List)
def opposite_position(position) # returns the opposite position of a side or corner.
opposite = position.value.split(/ +/).map do |pos| def opposite_position(position)
case pos position = unless position.is_a?(Sass::Script::List)
when "top" then "bottom" Sass::Script::List.new([position], :space)
when "bottom" then "top"
when "left" then "right"
when "right" then "left"
when "center" then "center"
else else
pos Sass::Script::List.new(position.value.dup, position.separator)
end
position.value.map! do |pos|
if pos.is_a? Sass::Script::String
opposite = case pos.value
when "top" then "bottom"
when "bottom" then "top"
when "left" then "right"
when "right" then "left"
when "center" then "center"
else
Compass::Util.compass_warn("Cannot determine the opposite position of: #{pos}")
pos.value
end
Sass::Script::String.new(opposite)
elsif pos.is_a? Sass::Script::Number
if pos.numerator_units == ["%"] && pos.denominator_units == []
Sass::Script::Number.new(100-pos.value, ["%"])
else
Compass::Util.compass_warn("Cannot determine the opposite position of: #{pos}")
pos
end
else
Compass::Util.compass_warn("Cannot determine the opposite position of: #{pos}")
pos
end
end
if position.value.size == 1
position.value.first
else
position
end end
end end
Sass::Script::String.new(opposite.join(" "), position.type) else
# returns the opposite position of a side or corner.
def opposite_position(position)
opposite = position.value.split(/ +/).map do |pos|
case pos
when "top" then "bottom"
when "bottom" then "top"
when "left" then "right"
when "right" then "left"
when "center" then "center"
else
pos
end
end
Sass::Script::String.new(opposite.join(" "), position.type)
end
end end
end end

View File

@ -1,246 +1,41 @@
module Compass::SassExtensions::Functions::GradientSupport module Compass::SassExtensions::Functions::GradientSupport
class List < Sass::Script::Literal module ListFreeSassSupport
attr_accessor :values class List < Sass::Script::Literal
def initialize(*values) attr_accessor :values
self.values = values def children
end values
def join_with
", "
end
def inspect
to_s
end
def to_s
values.map {|v| v.to_s }.join(join_with)
end
def size
values.size
end
end
class SpaceList < List
def join_with
" "
end
end
class ColorStop < Sass::Script::Literal
attr_accessor :color, :stop
def initialize(color, stop = nil)
unless Sass::Script::Color === color || Sass::Script::Funcall === color
raise Sass::SyntaxError, "Expected a color. Got: #{color}"
end end
self.color, self.stop = color, stop def value
end # duck type to a Sass List
def inspect values
to_s
end
def to_s
s = color.inspect.dup
if stop
s << " "
if stop.unitless?
s << stop.times(Sass::Script::Number.new(100, ["%"])).inspect
else
s << stop.inspect
end
end end
s def initialize(*values)
end self.values = values
end
class RadialGradient < Sass::Script::Literal
attr_accessor :position_and_angle, :shape_and_size, :color_stops
def initialize(position_and_angle, shape_and_size, color_stops)
unless color_stops.values.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
end end
self.position_and_angle = position_and_angle def join_with
self.shape_and_size = shape_and_size ", "
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s
s = "radial-gradient("
s << position_and_angle.to_s << ", " if position_and_angle
s << shape_and_size.to_s << ", " if shape_and_size
s << color_stops.to_s
s << ")"
end
def to_webkit
args = [
grad_point(position_and_angle || Sass::Script::String.new("center center")),
"0",
grad_point(position_and_angle || Sass::Script::String.new("center center")),
grad_end_position(color_stops, Sass::Script::Bool.new(true)),
grad_color_stops(color_stops)
]
Sass::Script::String.new("-webkit-gradient(radial, #{args.join(', ')})")
end
def to_moz
Sass::Script::String.new("-moz-#{to_s}")
end
def to_svg
# XXX Add shape support if possible
radial_svg_gradient(color_stops, position_and_angle || Sass::Script::String.new("center center"))
end
def to_pie
Compass::Logger.new.record(:warning, "PIE does not support radial-gradient.")
Sass::Script::String.new("-pie-radial-gradient(unsupported)")
end
end
class LinearGradient < Sass::Script::Literal
attr_accessor :color_stops, :position_and_angle
def initialize(position_and_angle, color_stops)
unless color_stops.values.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
end end
self.position_and_angle = position_and_angle def inspect
self.color_stops = color_stops to_s
end
def inspect
to_s
end
def to_s
s = "linear-gradient("
s << position_and_angle.to_s << ", " if position_and_angle
s << color_stops.to_s
s << ")"
end
def to_webkit
args = []
args << grad_point(position_and_angle || Sass::Script::String.new("top"))
args << grad_point(opposite_position(position_and_angle || Sass::Script::String.new("top")))
args << grad_color_stops(color_stops)
Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})")
end
def to_moz
Sass::Script::String.new("-moz-#{to_s}")
end
def to_svg
linear_svg_gradient(color_stops, position_and_angle || Sass::Script::String.new("top"))
end
def to_pie
# PIE just uses the standard rep, but the property is prefixed so
# the presence of this attribute helps flag when to render a special rule.
to_s
end
end
module Functions
def radial_gradient(position_and_angle, shape_and_size, *color_stops)
# Have to deal with variable length/meaning arguments.
if color_stop?(shape_and_size)
color_stops.unshift(shape_and_size)
shape_and_size = nil
elsif list_of_color_stops?(shape_and_size)
# Support legacy use of the color-stops() function
color_stops = shape_and_size.values + color_stops
shape_and_size = nil
end end
shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses def to_s(options = self.options)
# ditto for position_and_angle values.map {|v| v.to_s }.join(join_with)
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.values + color_stops
position_and_angle = nil
end end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool def size
values.size
# Support legacy use of the color-stops() function
if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
color_stops = color_stops.first.values
end
RadialGradient.new(position_and_angle, shape_and_size, send(:color_stops, *color_stops))
end
def linear_gradient(position_and_angle, *color_stops)
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.values + color_stops
position_and_angle = nil
end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool
# Support legacy use of the color-stops() function
if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
color_stops = color_stops.first.values
end
LinearGradient.new(position_and_angle, send(:color_stops, *color_stops))
end
# returns color-stop() calls for use in webkit.
def grad_color_stops(color_list)
stops = color_stops_in_percentages(color_list).map do |stop, color|
"color-stop(#{stop.inspect}, #{color.inspect})"
end
Sass::Script::String.new(stops.join(", "))
end
def color_stops_in_percentages(color_list)
assert_list(color_list)
color_list = normalize_stops(color_list)
max = color_list.values.last.stop
last_value = nil
color_stops = color_list.values.map do |pos|
# have to convert absolute units to percentages for use in color stop functions.
stop = pos.stop
stop = stop.div(max).times(Sass::Script::Number.new(100,["%"])) if stop.numerator_units == max.numerator_units && max.numerator_units != ["%"]
# Make sure the color stops are specified in the right order.
if last_value && last_value.value > stop.value
raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
end
last_value = stop
[stop, pos.color]
end end
end end
# returns the end position of the gradient from the color stop class SpaceList < List
def grad_end_position(color_list, radial = Sass::Script::Bool.new(false)) def join_with
assert_list(color_list) " "
default = Sass::Script::Number.new(100)
grad_position(color_list, Sass::Script::Number.new(color_list.values.size), default, radial)
end
def grad_position(color_list, index, default, radial = Sass::Script::Bool.new(false))
assert_list(color_list)
stop = color_list.values[index.value - 1].stop
if stop && radial.to_bool
orig_stop = stop
if stop.unitless?
if stop.value <= 1
# A unitless number is assumed to be a percentage when it's between 0 and 1
stop = stop.times(Sass::Script::Number.new(100, ["%"]))
else
# Otherwise, a unitless number is assumed to be in pixels
stop = stop.times(Sass::Script::Number.new(1, ["px"]))
end
end
if stop.numerator_units == ["%"] && color_list.values.last.stop && color_list.values.last.stop.numerator_units == ["px"]
stop = stop.times(color_list.values.last.stop).div(Sass::Script::Number.new(100, ["%"]))
end
Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
stop.div(Sass::Script::Number.new(1, stop.numerator_units, stop.denominator_units))
elsif stop
stop
else
default
end end
end end
# the given a position, return a point in percents # given a position list, return a corresponding position in percents
def grad_point(position) def grad_point(position)
position = position.value position = position.is_a?(Sass::Script::String) ? position.value : position.to_s
position = if position[" "] position = if position[" "]
if position =~ /(top|bottom|center) (left|right|center)/ if position =~ /(top|bottom|center) (left|right|center)/
"#{$2} #{$1}" "#{$2} #{$1}"
@ -256,15 +51,16 @@ module Compass::SassExtensions::Functions::GradientSupport
when /center/ when /center/
"center center" "center center"
else else
position "#{position} center"
end end
end end
Sass::Script::String.new(position. position = position.
gsub(/top/, "0%"). gsub(/top/, "0%").
gsub(/bottom/, "100%"). gsub(/bottom/, "100%").
gsub(/left/,"0%"). gsub(/left/,"0%").
gsub(/right/,"100%"). gsub(/right/,"100%").
gsub(/center/, "50%")) gsub(/center/, "50%")
SpaceList.new(*position.split(/ /).map{|s| Sass::Script::String.new(s)})
end end
def color_stops(*args) def color_stops(*args)
@ -284,24 +80,6 @@ module Compass::SassExtensions::Functions::GradientSupport
end) end)
end end
def linear_svg_gradient(color_stops, start)
x1, y1 = grad_point(start).to_s.split
x2, y2 = grad_point(opposite_position(start)).to_s.split
stops = color_stops_in_percentages(color_stops)
svg = linear_svg(stops, x1, y1, x2, y2)
inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end
def radial_svg_gradient(color_stops, center)
cx, cy = grad_point(center).to_s.split
r = grad_end_position(color_stops, Sass::Script::Bool.new(true))
stops = color_stops_in_percentages(color_stops)
svg = radial_svg(stops, cx, cy, r)
inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end
# Returns a comma-delimited list after removing any non-true values # Returns a comma-delimited list after removing any non-true values
def compact(*args) def compact(*args)
List.new(*args.reject{|a| !a.to_bool}) List.new(*args.reject{|a| !a.to_bool})
@ -329,21 +107,6 @@ module Compass::SassExtensions::Functions::GradientSupport
SpaceList.new(*values) SpaceList.new(*values)
end end
def _compass_list_size(list)
Sass::Script::Number.new(list.size)
end
# Get the nth value from a list
def _compass_nth(list, place)
if place.value == "last"
list.values.last
elsif place.value == "first"
list.values.first
else
list.values[place.value - 1]
end
end
def _compass_space_list(list) def _compass_space_list(list)
if list.is_a?(List) && !list.is_a?(SpaceList) if list.is_a?(List) && !list.is_a?(SpaceList)
SpaceList.new(*list.values) SpaceList.new(*list.values)
@ -354,6 +117,10 @@ module Compass::SassExtensions::Functions::GradientSupport
end end
end end
def _compass_list_size(list)
Sass::Script::Number.new(list.size)
end
# slice a sublist from a list # slice a sublist from a list
def _compass_slice(list, start_index, end_index = nil) def _compass_slice(list, start_index, end_index = nil)
end_index ||= Sass::Script::Number.new(-1) end_index ||= Sass::Script::Number.new(-1)
@ -364,6 +131,13 @@ module Compass::SassExtensions::Functions::GradientSupport
list.class.new *list.values[start_index..end_index] list.class.new *list.values[start_index..end_index]
end end
# Check if any of the arguments passed have a tendency towards vendor prefixing.
def prefixed(prefix, *args)
method = prefix.value.sub(/^-/,"to_").to_sym
args.map!{|a| a.is_a?(List) ? a.values : a}.flatten!
Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)})
end
%w(webkit moz o ms svg pie).each do |prefix| %w(webkit moz o ms svg pie).each do |prefix|
class_eval <<-RUBY, __FILE__, __LINE__ + 1 class_eval <<-RUBY, __FILE__, __LINE__ + 1
def _#{prefix}(*args) def _#{prefix}(*args)
@ -378,14 +152,419 @@ module Compass::SassExtensions::Functions::GradientSupport
RUBY RUBY
end end
protected
def color_stop?(arg)
parse_color_stop(arg)
rescue
nil
end
def assert_list(value)
unless value.is_a?(List)
raise ArgumentError.new("#{value.inspect} is not a list")
end
end
end
module ListBasedSassSupport
# given a position list, return a corresponding position in percents
def grad_point(position)
position = unless position.is_a?(Sass::Script::List)
Sass::Script::List.new([position], :space)
else
Sass::Script::List.new(position.value.dup, position.separator)
end
position.value.reject!{|p| p.is_a?(Sass::Script::Number) && p.numerator_units.include?("deg")}
if (position.value.first.value =~ /top|bottom/) or (position.value.last.value =~ /left|right/)
# browsers are pretty forgiving of reversed positions so we are too.
position.value.reverse!
end
if position.value.size == 1
if position.value.first.value =~ /top|bottom/
position.value.unshift Sass::Script::String.new("center")
elsif position.value.first.value =~ /left|right/
position.value.push Sass::Script::String.new("center")
end
end
position.value.map! do |p|
case p.value
when /top|left/
Sass::Script::Number.new(0, ["%"])
when /bottom|right/
Sass::Script::Number.new(100, ["%"])
when /center/
Sass::Script::Number.new(50, ["%"])
else
p
end
end
position
end
def color_stops(*args)
Sass::Script::List.new(args.map do |arg|
case arg
when ColorStop
arg
when Sass::Script::Color
ColorStop.new(arg)
when Sass::Script::List
ColorStop.new(*arg.value)
else
raise Sass::SyntaxError, "Not a valid color stop: #{arg.class.name}: #{arg}"
end
end, :comma)
end
# Returns a comma-delimited list after removing any non-true values
def compact(*args)
Sass::Script::List.new(args.reject{|a| !a.to_bool}, :comma)
end
# Returns a list object from a value that was passed.
# This can be used to unpack a space separated list that got turned
# into a string by sass before it was passed to a mixin.
def _compass_list(arg)
if arg.is_a?(Sass::Script::List)
Sass::Script::List.new(arg.value.dup, arg.separator)
else
Sass::Script::List.new([arg], :space)
end
end
def _compass_space_list(list)
if list.is_a?(Sass::Script::List)
Sass::Script::List.new(list.value.dup, :space)
else
Sass::Script::List.new([list], :space)
end
end
def _compass_list_size(list)
assert_list list
Sass::Script::Number.new(list.value.size)
end
# slice a sublist from a list
def _compass_slice(list, start_index, end_index = nil)
end_index ||= Sass::Script::Number.new(-1)
start_index = start_index.value
end_index = end_index.value
start_index -= 1 unless start_index < 0
end_index -= 1 unless end_index < 0
Sass::Script::List.new list.values[start_index..end_index], list.separator
end
# Check if any of the arguments passed have a tendency towards vendor prefixing. # Check if any of the arguments passed have a tendency towards vendor prefixing.
def prefixed(prefix, *args) def prefixed(prefix, *args)
method = prefix.value.sub(/^-/,"to_").to_sym method = prefix.value.sub(/^-/,"to_").to_sym
args.map!{|a| a.is_a?(List) ? a.values : a}.flatten! args.map!{|a| a.is_a?(Sass::Script::List) ? a.value : a}.flatten!
Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)}) Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)})
end end
%w(webkit moz o ms svg pie).each do |prefix|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def _#{prefix}(*args)
Sass::Script::List.new(args.map! do |a|
a.options = options
if a.is_a?(Sass::Script::List)
Sass::Script::List.new(a.value.map do |v|
v.options = options
v.respond_to?(:to_#{prefix}) ? v.to_#{prefix} : v
end, a.separator)
else
a.respond_to?(:to_#{prefix}) ? a.to_#{prefix} : a
end
end, :comma)
end
RUBY
end
protected
def color_stop?(arg)
arg.is_a?(ColorStop) ||
(arg.is_a?(Sass::Script::List) && ColorStop.new(*arg.value)) ||
ColorStop.new(arg)
rescue
nil
end
def assert_list(value)
unless value.is_a?(Sass::Script::List)
raise ArgumentError.new("#{value.inspect} is not a list")
end
end
end
class ColorStop < Sass::Script::Literal
attr_accessor :color, :stop
def children
[color, stop].compact
end
def initialize(color, stop = nil)
unless Sass::Script::Color === color || Sass::Script::Funcall === color
raise Sass::SyntaxError, "Expected a color. Got: #{color}"
end
if stop && !stop.is_a?(Sass::Script::Number)
raise Sass::SyntaxError, "Expected a number. Got: #{stop}"
end
self.color, self.stop = color, stop
end
def inspect
to_s
end
def to_s(options = self.options)
s = color.inspect.dup
if stop
s << " "
if stop.unitless?
s << stop.times(Sass::Script::Number.new(100, ["%"])).inspect
else
s << stop.inspect
end
end
s
end
end
class RadialGradient < Sass::Script::Literal
attr_accessor :position_and_angle, :shape_and_size, :color_stops
def children
[color_stops, position_and_angle, shape_and_size].compact
end
def initialize(position_and_angle, shape_and_size, color_stops)
unless color_stops.value.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
end
self.position_and_angle = position_and_angle
self.shape_and_size = shape_and_size
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s(options = self.options)
s = "radial-gradient("
s << position_and_angle.to_s(options) << ", " if position_and_angle
s << shape_and_size.to_s(options) << ", " if shape_and_size
s << color_stops.to_s(options)
s << ")"
end
def to_webkit(options = self.options)
args = [
grad_point(position_and_angle || _center_position),
Sass::Script::String.new("0"),
grad_point(position_and_angle || _center_position),
grad_end_position(color_stops, Sass::Script::Bool.new(true)),
grad_color_stops(color_stops)
]
args.each {|a| a.options = options}
Sass::Script::String.new("-webkit-gradient(radial, #{args.join(', ')})")
end
def to_moz(options = self.options)
Sass::Script::String.new("-moz-#{to_s(options)}")
end
def to_svg(options = self.options)
# XXX Add shape support if possible
radial_svg_gradient(color_stops, position_and_angle || _center_position)
end
def to_pie(options = self.options)
Compass::Logger.new.record(:warning, "PIE does not support radial-gradient.")
Sass::Script::String.new("-pie-radial-gradient(unsupported)")
end
end
class LinearGradient < Sass::Script::Literal
attr_accessor :color_stops, :position_and_angle
def children
[color_stops, position_and_angle].compact
end
def initialize(position_and_angle, color_stops)
unless color_stops.value.size >= 2
raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
end
self.position_and_angle = position_and_angle
self.color_stops = color_stops
end
def inspect
to_s
end
def to_s(options = self.options)
s = "linear-gradient("
s << position_and_angle.to_s(options) << ", " if position_and_angle
s << color_stops.to_s(options)
s << ")"
end
def to_webkit(options = self.options)
args = []
args << grad_point(position_and_angle || Sass::Script::String.new("top"))
args << grad_point(opposite_position(position_and_angle || Sass::Script::String.new("top")))
args << grad_color_stops(color_stops)
args.each{|a| a.options = options}
Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})")
end
def to_moz(options = self.options)
Sass::Script::String.new("-moz-#{to_s(options)}")
end
def to_svg(options = self.options)
linear_svg_gradient(color_stops, position_and_angle || Sass::Script::String.new("top"))
end
def to_pie(options = self.options)
# PIE just uses the standard rep, but the property is prefixed so
# the presence of this attribute helps flag when to render a special rule.
Sass::Script::String.new to_s(options)
end
end
module Functions
# While supporting sass 3.1 and older versions, we need two different implementations.
if defined?(Sass::Script::List)
include ListBasedSassSupport
else
include ListFreeSassSupport
end
def radial_gradient(position_and_angle, shape_and_size, *color_stops)
# Have to deal with variable length/meaning arguments.
if color_stop?(shape_and_size)
color_stops.unshift(shape_and_size)
shape_and_size = nil
elsif list_of_color_stops?(shape_and_size)
# Support legacy use of the color-stops() function
color_stops = shape_and_size.value + color_stops
shape_and_size = nil
end
shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses
# ditto for position_and_angle
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.value + color_stops
position_and_angle = nil
end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool
# Support legacy use of the color-stops() function
if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
color_stops = color_stops.first.value
end
RadialGradient.new(position_and_angle, shape_and_size, send(:color_stops, *color_stops))
end
def linear_gradient(position_and_angle, *color_stops)
if color_stop?(position_and_angle)
color_stops.unshift(position_and_angle)
position_and_angle = nil
elsif list_of_color_stops?(position_and_angle)
color_stops = position_and_angle.value + color_stops
position_and_angle = nil
end
position_and_angle = nil if position_and_angle && !position_and_angle.to_bool
# Support legacy use of the color-stops() function
if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
color_stops = color_stops.first.value
end
LinearGradient.new(position_and_angle, send(:color_stops, *color_stops))
end
# returns color-stop() calls for use in webkit.
def grad_color_stops(color_list)
stops = color_stops_in_percentages(color_list).map do |stop, color|
"color-stop(#{stop.inspect}, #{color.inspect})"
end
Sass::Script::String.new(stops.join(", "))
end
def color_stops_in_percentages(color_list)
assert_list(color_list)
color_list = normalize_stops(color_list)
max = color_list.value.last.stop
last_value = nil
color_stops = color_list.value.map do |pos|
# have to convert absolute units to percentages for use in color stop functions.
stop = pos.stop
stop = stop.div(max).times(Sass::Script::Number.new(100,["%"])) if stop.numerator_units == max.numerator_units && max.numerator_units != ["%"]
# Make sure the color stops are specified in the right order.
if last_value && last_value.value > stop.value
raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
end
last_value = stop
[stop, pos.color]
end
end
# returns the end position of the gradient from the color stop
def grad_end_position(color_list, radial = Sass::Script::Bool.new(false))
assert_list(color_list)
default = Sass::Script::Number.new(100)
grad_position(color_list, Sass::Script::Number.new(color_list.value.size), default, radial)
end
def grad_position(color_list, index, default, radial = Sass::Script::Bool.new(false))
assert_list(color_list)
stop = color_list.value[index.value - 1].stop
if stop && radial.to_bool
orig_stop = stop
if stop.unitless?
if stop.value <= 1
# A unitless number is assumed to be a percentage when it's between 0 and 1
stop = stop.times(Sass::Script::Number.new(100, ["%"]))
else
# Otherwise, a unitless number is assumed to be in pixels
stop = stop.times(Sass::Script::Number.new(1, ["px"]))
end
end
if stop.numerator_units == ["%"] && color_list.value.last.stop && color_list.value.last.stop.numerator_units == ["px"]
stop = stop.times(color_list.value.last.stop).div(Sass::Script::Number.new(100, ["%"]))
end
Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
stop.div(Sass::Script::Number.new(1, stop.numerator_units, stop.denominator_units))
elsif stop
stop
else
default
end
end
def linear_svg_gradient(color_stops, start)
x1, y1 = *grad_point(start).value
x2, y2 = *grad_point(opposite_position(start)).value
stops = color_stops_in_percentages(color_stops)
svg = linear_svg(stops, x1, y1, x2, y2)
inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end
def radial_svg_gradient(color_stops, center)
cx, cy = *grad_point(center).value
r = grad_end_position(color_stops, Sass::Script::Bool.new(true))
stops = color_stops_in_percentages(color_stops)
svg = radial_svg(stops, cx, cy, r)
inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end
# Get the nth value from a list
def _compass_nth(list, place)
assert_list list
if place.value == "first"
list.value.first
elsif place.value == "last"
list.value.last
else
list.value[place.value - 1]
end
end
private private
# After using the sass script parser to parse a string, this reconstructs # After using the sass script parser to parse a string, this reconstructs
# a list from operands to the space/concat operation # a list from operands to the space/concat operation
def extract_list_values(operation) def extract_list_values(operation)
@ -439,12 +618,11 @@ module Compass::SassExtensions::Functions::GradientSupport
positions.last.stop.eq(Sass::Script::Number.new(0, ["%"])).to_bool) positions.last.stop.eq(Sass::Script::Number.new(0, ["%"])).to_bool)
raise Sass::SyntaxError.new("Color stops must be specified in increasing order") raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
end end
nil if defined?(Sass::Script::List)
end Sass::Script::List.new(positions, color_list.separator)
else
def assert_list(value) color_list.class.new(*positions)
return if value.is_a?(List) end
raise ArgumentError.new("#{value.inspect} is not a list of color stops. Expected: color_stops(<color> <number>?, ...)")
end end
def parse_color_stop(arg) def parse_color_stop(arg)
@ -470,14 +648,8 @@ module Compass::SassExtensions::Functions::GradientSupport
ColorStop.new(color, stop) ColorStop.new(color, stop)
end end
def color_stop?(arg)
parse_color_stop(arg)
rescue
nil
end
def list_of_color_stops?(arg) def list_of_color_stops?(arg)
arg.is_a?(List) && arg.values.first.is_a?(ColorStop) arg.value.is_a?(Array) && arg.value.all?{|a| a.is_a?(ColorStop)}
end end
def linear_svg(color_stops, x1, y1, x2, y2) def linear_svg(color_stops, x1, y1, x2, y2)
@ -503,6 +675,18 @@ module Compass::SassExtensions::Functions::GradientSupport
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"><defs>#{gradient}</defs><rect x="0" y="0" width="100%" height="100%" fill="url(#grad)" /></svg> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"><defs>#{gradient}</defs><rect x="0" y="0" width="100%" height="100%" fill="url(#grad)" /></svg>
EOS EOS
end end
def _center_position
if defined?(Sass::Script::List)
Sass::Script::List.new([
Sass::Script::String.new("center"),
Sass::Script::String.new("center")
],:space)
else
Sass::Script::String.new("center center")
end
end
end end
class LinearGradient < Sass::Script::Literal class LinearGradient < Sass::Script::Literal
include Functions include Functions

View File

@ -2,6 +2,8 @@ module Compass::SassExtensions::Functions::Lists
def first_value_of(list) def first_value_of(list)
if list.is_a?(Sass::Script::String) if list.is_a?(Sass::Script::String)
Sass::Script::String.new(list.value.split(/\s+/).first) Sass::Script::String.new(list.value.split(/\s+/).first)
elsif defined?(Sass::Script::List) && list.is_a?(Sass::Script::List)
list.value.first
else else
list list
end end