module Compass::Core::SassExtensions::Functions::GradientSupport::Functions

Public Instance Methods

_build_linear_gradient(position_or_angle, *color_stops) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 595
def _build_linear_gradient(position_or_angle, *color_stops)
  if color_stop?(position_or_angle)
    color_stops.unshift(position_or_angle)
    position_or_angle = nil
  elsif list_of_color_stops?(position_or_angle)
    color_stops = position_or_angle.value + color_stops
    position_or_angle = nil
  end
  position_or_angle = nil if position_or_angle && !position_or_angle.to_bool

  # Support legacy use of the color-stops() function
  if color_stops.size == 1 && (stops = list_of_color_stops?(color_stops.first))
    color_stops = stops
  end
  return [position_or_angle, color_stops]
end
_linear_gradient(position_or_angle, *color_stops) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 612
def _linear_gradient(position_or_angle, *color_stops)
  position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
  LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
end
_linear_gradient_legacy(position_or_angle, *color_stops) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 617
def _linear_gradient_legacy(position_or_angle, *color_stops)
  position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
  LinearGradient.new(position_or_angle, send(:color_stops, *color_stops), true)
end
color_stops(*args) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 542
def color_stops(*args)
  opts(list(args.map do |arg|
    if ColorStop === arg
      arg
    elsif Sass::Script::Value::Color === arg
      ColorStop.new(arg)
    elsif Sass::Script::Value::List === arg
      ColorStop.new(*arg.value)
    elsif Sass::Script::Value::String === arg && arg.value == "transparent"
      ColorStop.new(arg)
    elsif Sass::Script::Value::String === arg && arg.value == "currentColor"
      ColorStop.new(arg)
    else
      raise Sass::SyntaxError, "Not a valid color stop: #{arg.class.name}: #{arg}"
    end
  end, :comma))
end
color_stops_in_percentages(color_list) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 630
def color_stops_in_percentages(color_list)
  assert_type color_list, :List
  color_list = normalize_stops(color_list)
  max = color_list.value.last.stop
  last_value = nil
  color_list.value.map do |pos|
    next [pos.stop, pos.color] if pos.stop.is_a?(Sass::Script::Value::String)
    # have to convert absolute units to percentages for use in color stop functions.
    stop = pos.stop
    stop = stop.div(max).times(number(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 && stop.numerator_units == last_value.numerator_units && stop.denominator_units == last_value.denominator_units && (stop.value * 1000).round < (last_value.value * 1000).round
      raise Sass::SyntaxError.new("Color stops must be specified in increasing order. #{stop.value} came after #{last_value.value}.")
    end
    last_value = stop
    [stop, pos.color]
  end
end
convert_angle_from_offical(deg) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 481
def convert_angle_from_offical(deg)
  if deg.is_a?(Sass::Script::Value::Number)
    return number((deg.value.to_f - 450).abs % 360, 'deg')
  else
    args = deg.value
    direction = []
    if args[0] == identifier('to')
      if args.size < 2
        direction = args
      else
        direction << opposite_position(args[1])
      end
    else
      direction << identifier('to')
      args.each do |pos|
        direction << opposite_position(pos)
      end
    end
    return opts(list(direction, :space))
  end
end
grad_color_stops(color_list) click to toggle source

returns color-stop() calls for use in webkit.

# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 623
def grad_color_stops(color_list)
  stops = color_stops_in_percentages(color_list).map do |stop, color|
    Sass::Script::String.new("color-stop(#{stop.to_s}, #{ColorStop.color_to_s(color)})")
  end
  opts(list(stops, :comma))
end
grad_end_position(color_list, radial = bool(false)) click to toggle source

returns the end position of the gradient from the color stop

# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 673
def grad_end_position(color_list, radial = bool(false))
  assert_type color_list, :List
  default = number(100)
  grad_position(color_list, number(color_list.value.size), default, radial)
end
grad_point(position) click to toggle source

given a position list, return a corresponding position in percents otherwise, returns the original argument

# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 505
def grad_point(position)
  original_value = position
  position = unless position.is_a?(Sass::Script::Value::List)
    opts(list([position], :space))
  else
    opts(list(position.value.dup, position.separator))
  end
  # Handle unknown arguments by passing them along untouched.
  unless position.value.all?{|p| is_position(p) }
    return original_value
  end
  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 = opts(list(position.value.reverse, position.separator))
  end
  if position.value.size == 1
    if position.value.first.value =~ /top|bottom/
      position = opts(list(identifier("center"), position.value.first, position.separator))
    elsif position.value.first.value =~ /left|right/
      position = opts(list(position.value.first, identifier("center"), position.separator))
    end
  end
  position = opts(list(position.value.map do |p|
    case p.value
    when /top|left/
      number(0, "%")
    when /bottom|right/
      number(100, "%")
    when /center/
      number(50, "%")
    else
      p
    end
  end, position.separator))
  position
end
grad_position(color_list, index, default, radial = bool(false)) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 679
def grad_position(color_list, index, default, radial = bool(false))
  assert_type color_list, :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(number(100, "%"))
      else
        # Otherwise, a unitless number is assumed to be in pixels
        stop = stop.times(number(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(number(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::Value::Number.new(1, stop.numerator_units, stop.denominator_units))
  elsif stop
    stop
  else
    default
  end
end
linear_end_position(position_or_angle, start_point, end_target) click to toggle source

only used for webkit

# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 650
def linear_end_position(position_or_angle, start_point, end_target)
  end_point = grad_point(opposite_position(position_or_angle))

  if end_target && end_target.numerator_units == ["px"]
    if start_point.value.first == end_point.value.first && start_point.value.last.value == 0
      # this means top-to-bottom
      new_end_point = end_point.value.dup
      new_end_point[1] = number(end_target.value)

      end_point = opts(list(new_end_point, end_point.separator))
    elsif start_point.value.last == end_point.value.last && start_point.value.first.value == 0
      # this implies left-to-right

      new_end_point = end_point.value.dup
      new_end_point[0] = number(end_target.value)

      end_point = opts(list(new_end_point, end_point.separator))
    end
  end
  end_point
end
linear_svg_gradient(color_stops, start) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 705
def linear_svg_gradient(color_stops, start)
  converter = CSS3AngleToSVGConverter.new(start)
  stops = color_stops_in_percentages(color_stops)

  svg = linear_svg(stops, converter.x1, converter.y1, converter.x2, converter.y2)
  inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end
radial_gradient(position_or_angle, shape_and_size, *color_stops) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 560
def radial_gradient(position_or_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_or_angle
  if color_stop?(position_or_angle)
    color_stops.unshift(position_or_angle)
    position_or_angle = nil
  elsif list_of_color_stops?(position_or_angle)
    color_stops = position_or_angle.value + color_stops
    position_or_angle = nil
  end
  position_or_angle = nil if position_or_angle && !position_or_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
  if position_or_angle.is_a?(Sass::Script::Value::List) &&
     (i = position_or_angle.value.index {|word| word.is_a?(Sass::Script::Value::String) && word.value == "at"})
    shape_and_size = list(position_or_angle.value[0..(i-1)], :space)
    shape_and_size.options = options
    position_or_angle = list(position_or_angle.value[(i+1)..-1], :space)
    position_or_angle.options = options
  end
  RadialGradient.new(position_or_angle, shape_and_size, send(:color_stops, *color_stops))
end
radial_svg_gradient(color_stops, center) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 713
def radial_svg_gradient(color_stops, center)
  cx, cy = *grad_point(center).value
  r = grad_end_position(color_stops,  bool(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
reverse_side_or_corner(position) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 463
def reverse_side_or_corner(position)
  position_array = position.nil? ? [identifier('top')] : position.value.dup
  if position_array.first == identifier('to')
    # Remove the 'to' element from the array
    position_array.shift

    # Reverse all the positions
    reversed_position = position_array.map do |pos|
      opposite_position(pos)
    end
  else
    # When the position does not have the 'to' element we don't need to
    # reverse the direction of the gradient
    reversed_position = position_array
  end
  opts(list(reversed_position, :space))
end

Private Instance Methods

_center_position() click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 826
def _center_position
  opts(list(identifier("center"), identifier("center"), :space))
end
color_stop?(arg) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 724
def color_stop?(arg)
  arg.is_a?(ColorStop) ||
  (arg.is_a?(Sass::Script::Value::List) && ColorStop.new(*arg.value)) ||
  ColorStop.new(arg)
rescue
  nil
end
color_stops_svg(color_stops) click to toggle source

#color_stops = array of: [stop, color]

# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 809
def color_stops_svg(color_stops)
  color_stops.each.map{ |stop, color|
    s = %Q{<stop offset="#{stop.to_s}"}
    s << %Q{ stop-color="#{ColorStop.color_to_svg_s(color)}"}
    alpha = ColorStop.color_to_svg_alpha(color)
    s << %Q{ stop-opacity="#{alpha}"} if alpha != 1
    s << "/>"
  }.join
end
linear_svg(color_stops, x1, y1, x2, y2) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 798
def linear_svg(color_stops, x1, y1, x2, y2)
  gradient = %Q{<linearGradient id="grad" gradientUnits="objectBoundingBox" x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}">#{color_stops_svg(color_stops)}</linearGradient>}
  svg(gradient)
end
list_of_color_stops?(arg) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 790
def list_of_color_stops?(arg)
  if arg.respond_to?(:value)
    arg.value.is_a?(Array) && arg.value.all?{|a| color_stop?(a)} ? arg.value : nil
  elsif arg.is_a?(Array)
    arg.all?{|a| color_stop?(a)} ? arg : nil
  end
end
normalize_stops(color_list) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 732
def normalize_stops(color_list)
  positions = color_list.value.map{|obj| obj.dup}
  # fill in the start and end positions, if unspecified
  positions.first.stop = number(0) unless positions.first.stop
  positions.last.stop = number(100, "%") unless positions.last.stop
  # fill in empty values
  for i in 0...positions.size
    if positions[i].stop.nil?
      num = 2.0
      for j in (i+1)...positions.size
        if positions[j].stop
          positions[i].stop = positions[i-1].stop.plus((positions[j].stop.minus(positions[i-1].stop)).div(number(num)))
          break
        else
          num += 1
        end
      end
    end
  end
  # normalize unitless numbers
  positions.each do |pos|
    next pos if pos.stop.is_a?(Sass::Script::Value::String)
    if pos.stop.unitless? && pos.stop.value <= 1
      pos.stop = pos.stop.times(number(100, "%"))
    elsif pos.stop.unitless?
      pos.stop = pos.stop.times(number(1, "px"))
    end
  end
  if (positions.last.stop.eq(number(0, "px")).to_bool ||
     positions.last.stop.eq(number(0, "%")).to_bool)
     raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
   end
   opts(list(positions, color_list.separator))
end
opts(v) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 830
def opts(v)
  v.options = options
  v
end
parse_color_stop(arg) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 767
def parse_color_stop(arg)
  return ColorStop.new(arg) if arg.is_a?(Sass::Script::Value::Color)
  return nil unless arg.is_a?(Sass::Script::Value::String)
  color = stop = nil
  expr = Sass::Script::Parser.parse(arg.value, 0, 0)
  case expr
  when Sass::Script::Value::Color
    color = expr
  when Sass::Script::Tree::Funcall
    color = expr
  when Sass::Script::Tree::Operation
    unless [:concat, :space].include?(expr.instance_variable_get("@operator"))
      # This should never happen.
      raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
    end
    color = expr.instance_variable_get("@operand1")
    stop = expr.instance_variable_get("@operand2")
  else
    raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
  end
  ColorStop.new(color, stop)
end
radial_svg(color_stops, cx, cy, r) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 803
def radial_svg(color_stops, cx, cy, r)
  gradient = %Q{<radialGradient id="grad" gradientUnits="userSpaceOnUse" cx="#{cx}" cy="#{cy}" r="#{r}%">#{color_stops_svg(color_stops)}</radialGradient>}
  svg(gradient)
end
svg(gradient) click to toggle source
# File lib/compass/core/sass_extensions/functions/gradient_support.rb, line 819
    def svg(gradient)
      svg = <<-EOS
<?xml version="1.0" encoding="utf-8"?>
<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
    end