diff --git a/doc-src/SASS_CHANGELOG.md b/doc-src/SASS_CHANGELOG.md index f97a03c7..a7d0bdcc 100644 --- a/doc-src/SASS_CHANGELOG.md +++ b/doc-src/SASS_CHANGELOG.md @@ -53,6 +53,11 @@ Several bug fixes and minor improvements have been made, including: ## 2.2.14 (Unreleased) +* {Sass::Script::Color#value} attribute is deprecated. + Use {Sass::Script::Color#rgb} instead. + This only affects people defining their own Sass functions + in Ruby. + ### Rack Support Sass 2.2.14 includes Rack middleware for running Sass, diff --git a/lib/sass/script/color.rb b/lib/sass/script/color.rb index e00babb1..f87a4288 100644 --- a/lib/sass/script/color.rb +++ b/lib/sass/script/color.rb @@ -38,10 +38,9 @@ module Sass::Script # @raise [Sass::SyntaxError] if any color value isn't between 0 and 255, # or the alpha value is negative def initialize(rgba) - rgba = rgba.dup - rgba[0...3] = rgba[0...3].map {|c| c.to_i} - rgba[3] = rgba[3].nil? ? 1 : rgba[3].to_f - super(rgba) + @red, @green, @blue = rgba[0...3].map {|c| c.to_i} + @alpha = rgba[3] ? rgba[3].to_f : 1 + super(nil) unless rgb.all? {|c| (0..255).include?(c)} raise Sass::SyntaxError.new("Color values must be between 0 and 255") @@ -52,6 +51,66 @@ module Sass::Script end end + # The red component of the color. + # + # @return [Fixnum] + attr_reader :red + + # The green component of the color. + # + # @return [Fixnum] + attr_reader :green + + # The blue component of the color. + # + # @return [Fixnum] + attr_reader :blue + + # The alpha channel (opacity) of the color. + # This is 1 unless otherwise defined. + # + # @return [Fixnum] + attr_reader :alpha + + # Returns whether this color object is translucent; + # that is, whether the alpha channel is non-1. + # + # @return [Boolean] + def alpha? + alpha < 1 + end + + # @deprecated This will be removed in version 2.6. + # @see #rgb + def value + warn <] A three-element array of the red, green, and blue + # values (respectively) of the color + def rgb + [red, green, blue] + end + + # The SassScript `==` operation. + # **Note that this returns a {Sass::Script::Bool} object, + # not a Ruby boolean**. + # + # @param other [Literal] The right-hand side of the operator + # @return [Bool] True if this literal is the same as the other, + # false otherwise + def eq(other) + Sass::Script::Bool.new( + other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha) + end + # The SassScript `+` operation. # Its functionality depends on the type of its argument: # @@ -180,31 +239,6 @@ module Sass::Script end alias_method :inspect, :to_s - # Returns whether or not the alpha channel is defined (and not 1) - # for this color object. - # - # @return [Boolean] - def alpha? - alpha < 1 - end - - # Returns the red, green, and blue components of the color. - # - # @return [Array] A three-element array of the red, green, and blue - # values (respectively) of the color - def rgb - @value[0...3] - end - - # Returns the alpha channel of the color. - # This is 1 unless otherwise defined. - # This is never negative, but it may be greater than 1. - # - # @return [Numeric] The alpha channel - def alpha - @value[3] || 1 - end - private def piecewise(other, operation) diff --git a/test/sass/data/hsl-rgb.txt b/test/sass/data/hsl-rgb.txt new file mode 100644 index 00000000..2e9e470d --- /dev/null +++ b/test/sass/data/hsl-rgb.txt @@ -0,0 +1,319 @@ +hsl(0, 100%, 50%) +hsl(60, 100%, 50%) +hsl(120, 100%, 50%) +hsl(180, 100%, 50%) +hsl(240, 100%, 50%) +hsl(300, 100%, 50%) +==== +rgb(255, 0, 0) +rgb(255, 255, 0) +rgb(0, 255, 0) +rgb(0, 255, 255) +rgb(0, 0, 255) +rgb(255, 0, 255) + +hsl(-360, 100%, 50%) +hsl(-300, 100%, 50%) +hsl(-240, 100%, 50%) +hsl(-180, 100%, 50%) +hsl(-120, 100%, 50%) +hsl(-60, 100%, 50%) +==== +rgb(255, 0, 0) +rgb(255, 255, 0) +rgb(0, 255, 0) +rgb(0, 255, 255) +rgb(0, 0, 255) +rgb(255, 0, 255) + +hsl(360, 100%, 50%) +hsl(420, 100%, 50%) +hsl(480, 100%, 50%) +hsl(540, 100%, 50%) +hsl(600, 100%, 50%) +hsl(660, 100%, 50%) +==== +rgb(255, 0, 0) +rgb(255, 255, 0) +rgb(0, 255, 0) +rgb(0, 255, 255) +rgb(0, 0, 255) +rgb(255, 0, 255) + +hsl(6120, 100%, 50%) +hsl(-9660, 100%, 50%) +hsl(99840, 100%, 50%) +hsl(-900, 100%, 50%) +hsl(-104880, 100%, 50%) +hsl(2820, 100%, 50%) +==== +rgb(255, 0, 0) +rgb(255, 255, 0) +rgb(0, 255, 0) +rgb(0, 255, 255) +rgb(0, 0, 255) +rgb(255, 0, 255) + +hsl(0, 100%, 50%) +hsl(12, 100%, 50%) +hsl(24, 100%, 50%) +hsl(36, 100%, 50%) +hsl(48, 100%, 50%) +hsl(60, 100%, 50%) +hsl(72, 100%, 50%) +hsl(84, 100%, 50%) +hsl(96, 100%, 50%) +hsl(108, 100%, 50%) +hsl(120, 100%, 50%) +==== +rgb(255, 0, 0) +rgb(255, 51, 0) +rgb(255, 102, 0) +rgb(255, 153, 0) +rgb(255, 204, 0) +rgb(255, 255, 0) +rgb(204, 255, 0) +rgb(153, 255, 0) +rgb(102, 255, 0) +rgb(51, 255, 0) +rgb(0, 255, 0) + +hsl(120, 100%, 50%) +hsl(132, 100%, 50%) +hsl(144, 100%, 50%) +hsl(156, 100%, 50%) +hsl(168, 100%, 50%) +hsl(180, 100%, 50%) +hsl(192, 100%, 50%) +hsl(204, 100%, 50%) +hsl(216, 100%, 50%) +hsl(228, 100%, 50%) +hsl(240, 100%, 50%) +==== +rgb(0, 255, 0) +rgb(0, 255, 51) +rgb(0, 255, 102) +rgb(0, 255, 153) +rgb(0, 255, 204) +rgb(0, 255, 255) +rgb(0, 204, 255) +rgb(0, 153, 255) +rgb(0, 102, 255) +rgb(0, 51, 255) +rgb(0, 0, 255) + +hsl(240, 100%, 50%) +hsl(252, 100%, 50%) +hsl(264, 100%, 50%) +hsl(276, 100%, 50%) +hsl(288, 100%, 50%) +hsl(300, 100%, 50%) +hsl(312, 100%, 50%) +hsl(324, 100%, 50%) +hsl(336, 100%, 50%) +hsl(348, 100%, 50%) +hsl(360, 100%, 50%) +==== +rgb(0, 0, 255) +rgb(51, 0, 255) +rgb(102, 0, 255) +rgb(153, 0, 255) +rgb(204, 0, 255) +rgb(255, 0, 255) +rgb(255, 0, 204) +rgb(255, 0, 153) +rgb(255, 0, 102) +rgb(255, 0, 51) +rgb(255, 0, 0) + +hsl(0, 20%, 50%) +hsl(0, 60%, 50%) +hsl(0, 100%, 50%) +==== +rgb(153, 102, 102) +rgb(204, 51, 51) +rgb(255, 0, 0) + +hsl(60, 20%, 50%) +hsl(60, 60%, 50%) +hsl(60, 100%, 50%) +==== +rgb(153, 153, 102) +rgb(204, 204, 51) +rgb(255, 255, 0) + +hsl(120, 20%, 50%) +hsl(120, 60%, 50%) +hsl(120, 100%, 50%) +==== +rgb(102, 153, 102) +rgb(51, 204, 51) +rgb(0, 255, 0) + +hsl(180, 20%, 50%) +hsl(180, 60%, 50%) +hsl(180, 100%, 50%) +==== +rgb(102, 153, 153) +rgb(51, 204, 204) +rgb(0, 255, 255) + +hsl(240, 20%, 50%) +hsl(240, 60%, 50%) +hsl(240, 100%, 50%) +==== +rgb(102, 102, 153) +rgb(51, 51, 204) +rgb(0, 0, 255) + +hsl(300, 20%, 50%) +hsl(300, 60%, 50%) +hsl(300, 100%, 50%) +==== +rgb(153, 102, 153) +rgb(204, 51, 204) +rgb(255, 0, 255) + +hsl(0, 100%, 0%) +hsl(0, 100%, 10%) +hsl(0, 100%, 20%) +hsl(0, 100%, 30%) +hsl(0, 100%, 40%) +hsl(0, 100%, 50%) +hsl(0, 100%, 60%) +hsl(0, 100%, 70%) +hsl(0, 100%, 80%) +hsl(0, 100%, 90%) +hsl(0, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(51, 0, 0) +rgb(102, 0, 0) +rgb(153, 0, 0) +rgb(204, 0, 0) +rgb(255, 0, 0) +rgb(255, 51, 51) +rgb(255, 102, 102) +rgb(255, 153, 153) +rgb(255, 204, 204) +rgb(255, 255, 255) + +hsl(60, 100%, 0%) +hsl(60, 100%, 10%) +hsl(60, 100%, 20%) +hsl(60, 100%, 30%) +hsl(60, 100%, 40%) +hsl(60, 100%, 50%) +hsl(60, 100%, 60%) +hsl(60, 100%, 70%) +hsl(60, 100%, 80%) +hsl(60, 100%, 90%) +hsl(60, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(51, 51, 0) +rgb(102, 102, 0) +rgb(153, 153, 0) +rgb(204, 204, 0) +rgb(255, 255, 0) +rgb(255, 255, 51) +rgb(255, 255, 102) +rgb(255, 255, 153) +rgb(255, 255, 204) +rgb(255, 255, 255) + +hsl(120, 100%, 0%) +hsl(120, 100%, 10%) +hsl(120, 100%, 20%) +hsl(120, 100%, 30%) +hsl(120, 100%, 40%) +hsl(120, 100%, 50%) +hsl(120, 100%, 60%) +hsl(120, 100%, 70%) +hsl(120, 100%, 80%) +hsl(120, 100%, 90%) +hsl(120, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(0, 51, 0) +rgb(0, 102, 0) +rgb(0, 153, 0) +rgb(0, 204, 0) +rgb(0, 255, 0) +rgb(51, 255, 51) +rgb(102, 255, 102) +rgb(153, 255, 153) +rgb(204, 255, 204) +rgb(255, 255, 255) + +hsl(180, 100%, 0%) +hsl(180, 100%, 10%) +hsl(180, 100%, 20%) +hsl(180, 100%, 30%) +hsl(180, 100%, 40%) +hsl(180, 100%, 50%) +hsl(180, 100%, 60%) +hsl(180, 100%, 70%) +hsl(180, 100%, 80%) +hsl(180, 100%, 90%) +hsl(180, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(0, 51, 51) +rgb(0, 102, 102) +rgb(0, 153, 153) +rgb(0, 204, 204) +rgb(0, 255, 255) +rgb(51, 255, 255) +rgb(102, 255, 255) +rgb(153, 255, 255) +rgb(204, 255, 255) +rgb(255, 255, 255) + +hsl(240, 100%, 0%) +hsl(240, 100%, 10%) +hsl(240, 100%, 20%) +hsl(240, 100%, 30%) +hsl(240, 100%, 40%) +hsl(240, 100%, 50%) +hsl(240, 100%, 60%) +hsl(240, 100%, 70%) +hsl(240, 100%, 80%) +hsl(240, 100%, 90%) +hsl(240, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(0, 0, 51) +rgb(0, 0, 102) +rgb(0, 0, 153) +rgb(0, 0, 204) +rgb(0, 0, 255) +rgb(51, 51, 255) +rgb(102, 102, 255) +rgb(153, 153, 255) +rgb(204, 204, 255) +rgb(255, 255, 255) + +hsl(300, 100%, 0%) +hsl(300, 100%, 10%) +hsl(300, 100%, 20%) +hsl(300, 100%, 30%) +hsl(300, 100%, 40%) +hsl(300, 100%, 50%) +hsl(300, 100%, 60%) +hsl(300, 100%, 70%) +hsl(300, 100%, 80%) +hsl(300, 100%, 90%) +hsl(300, 100%, 100%) +==== +rgb(0, 0, 0) +rgb(51, 0, 51) +rgb(102, 0, 102) +rgb(153, 0, 153) +rgb(204, 0, 204) +rgb(255, 0, 255) +rgb(255, 51, 255) +rgb(255, 102, 255) +rgb(255, 153, 255) +rgb(255, 204, 255) +rgb(255, 255, 255) diff --git a/test/sass/functions_test.rb b/test/sass/functions_test.rb index 8ae0faee..66b827d4 100644 --- a/test/sass/functions_test.rb +++ b/test/sass/functions_test.rb @@ -3,44 +3,17 @@ require File.dirname(__FILE__) + '/../../lib/sass' require 'sass/script' class SassFunctionTest < Test::Unit::TestCase - def test_hsl - # These tests adapted from the w3c browser tests - # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-h-rotating-b.htm - red = [255, 0, 0] - assert_rgb_hsl(red, ['0', '100%', '50%']) - assert_rgb_hsl(red, ['-360', '100%', '50%']) - assert_rgb_hsl(red, ['360', '100%', '50%']) - assert_rgb_hsl(red, ['6120', '100%', '50%']) - - yellow = [255, 255, 0] - assert_rgb_hsl(yellow, ['60', '100%', '50%']) - assert_rgb_hsl(yellow, ['-300', '100%', '50%']) - assert_rgb_hsl(yellow, ['420', '100%', '50%']) - assert_rgb_hsl(yellow, ['-9660', '100%', '50%']) - - green = [0, 255, 0] - assert_rgb_hsl(green, ['120', '100%', '50%']) - assert_rgb_hsl(green, ['-240', '100%', '50%']) - assert_rgb_hsl(green, ['480', '100%', '50%']) - assert_rgb_hsl(green, ['99840', '100%', '50%']) - - cyan = [0, 255, 255] - assert_rgb_hsl(cyan, ['180', '100%', '50%']) - assert_rgb_hsl(cyan, ['-180', '100%', '50%']) - assert_rgb_hsl(cyan, ['540', '100%', '50%']) - assert_rgb_hsl(cyan, ['-900', '100%', '50%']) - - blue = [0, 0, 255] - assert_rgb_hsl(blue, ['240', '100%', '50%']) - assert_rgb_hsl(blue, ['-120', '100%', '50%']) - assert_rgb_hsl(blue, ['600', '100%', '50%']) - assert_rgb_hsl(blue, ['-104880', '100%', '50%']) - - purple = [255, 0, 255] - assert_rgb_hsl(purple, ['300', '100%', '50%']) - assert_rgb_hsl(purple, ['-60', '100%', '50%']) - assert_rgb_hsl(purple, ['660', '100%', '50%']) - assert_rgb_hsl(purple, ['2820', '100%', '50%']) + # Tests taken from: + # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-h-rotating-b.htm + # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-values-b.htm + File.read(File.dirname(__FILE__) + "/data/hsl-rgb.txt").split("\n\n").each do |chunk| + hsls, rgbs = chunk.strip.split("====") + hsls.strip.split("\n").zip(rgbs.strip.split("\n")) do |hsl, rgb| + method = "test_hsl: #{hsl} = #{rgb}" + define_method(method) do + assert_equal(evaluate(rgb), evaluate(hsl)) + end + end end def test_hsl_checks_bounds @@ -141,11 +114,6 @@ class SassFunctionTest < Test::Unit::TestCase private - def assert_rgb_hsl(rgb, hsl) - hsl = hsl.map {|v| Sass::Script::Parser.parse v, 0, 0} - assert_equal(rgb, Sass::Script::Functions::EvaluationContext.new({}).hsl(*hsl).rgb) - end - def evaluate(value) Sass::Script::Parser.parse(value, 0, 0).perform(Sass::Environment.new).to_s end