sass-embedded 1.58.0-x86-linux-musl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/ext/sass/embedded.rb +12 -0
  5. data/ext/sass/embedded_sass_pb.rb +349 -0
  6. data/ext/sass/sass_embedded/dart-sass-embedded +20 -0
  7. data/ext/sass/sass_embedded/src/LICENSE +1434 -0
  8. data/ext/sass/sass_embedded/src/dart +0 -0
  9. data/ext/sass/sass_embedded/src/dart-sass-embedded.snapshot +0 -0
  10. data/lib/sass/compile_error.rb +28 -0
  11. data/lib/sass/compile_result.rb +23 -0
  12. data/lib/sass/embedded/async.rb +58 -0
  13. data/lib/sass/embedded/channel.rb +61 -0
  14. data/lib/sass/embedded/compiler.rb +60 -0
  15. data/lib/sass/embedded/dispatcher.rb +98 -0
  16. data/lib/sass/embedded/host/function_registry.rb +89 -0
  17. data/lib/sass/embedded/host/importer_registry.rb +104 -0
  18. data/lib/sass/embedded/host/logger_registry.rb +50 -0
  19. data/lib/sass/embedded/host/value_protofier.rb +241 -0
  20. data/lib/sass/embedded/host.rb +141 -0
  21. data/lib/sass/embedded/protofier.rb +78 -0
  22. data/lib/sass/embedded/structifier.rb +39 -0
  23. data/lib/sass/embedded/varint.rb +35 -0
  24. data/lib/sass/embedded/version.rb +7 -0
  25. data/lib/sass/embedded.rb +251 -0
  26. data/lib/sass/logger/silent.rb +26 -0
  27. data/lib/sass/logger/source_location.rb +21 -0
  28. data/lib/sass/logger/source_span.rb +27 -0
  29. data/lib/sass/script_error.rb +10 -0
  30. data/lib/sass/value/argument_list.rb +37 -0
  31. data/lib/sass/value/boolean.rb +52 -0
  32. data/lib/sass/value/color.rb +253 -0
  33. data/lib/sass/value/function.rb +54 -0
  34. data/lib/sass/value/fuzzy_math.rb +80 -0
  35. data/lib/sass/value/list.rb +79 -0
  36. data/lib/sass/value/map.rb +71 -0
  37. data/lib/sass/value/null.rb +48 -0
  38. data/lib/sass/value/number/unit.rb +186 -0
  39. data/lib/sass/value/number.rb +365 -0
  40. data/lib/sass/value/string.rb +55 -0
  41. data/lib/sass/value.rb +128 -0
  42. data/lib/sass-embedded.rb +4 -0
  43. metadata +186 -0
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ext/sass/embedded'
4
+ require_relative '../../ext/sass/embedded_sass_pb'
5
+ require_relative 'compile_error'
6
+ require_relative 'compile_result'
7
+ require_relative 'embedded/async'
8
+ require_relative 'embedded/channel'
9
+ require_relative 'embedded/compiler'
10
+ require_relative 'embedded/dispatcher'
11
+ require_relative 'embedded/host'
12
+ require_relative 'embedded/protofier'
13
+ require_relative 'embedded/structifier'
14
+ require_relative 'embedded/varint'
15
+ require_relative 'embedded/version'
16
+ require_relative 'logger/silent'
17
+ require_relative 'logger/source_location'
18
+ require_relative 'logger/source_span'
19
+ require_relative 'value'
20
+
21
+ # The Sass module.
22
+ #
23
+ # This communicates with Embedded Dart Sass using the Embedded Sass protocol.
24
+ #
25
+ # @example
26
+ # Sass.compile('style.scss')
27
+ #
28
+ # @example
29
+ # Sass.compile_string('h1 { font-size: 40px; }')
30
+ module Sass
31
+ @instance = nil
32
+ @mutex = Mutex.new
33
+
34
+ # rubocop:disable Layout/LineLength
35
+ class << self
36
+ # Compiles the Sass file at +path+ to CSS.
37
+ # @overload compile(path, load_paths: [], charset: true, source_map: false, source_map_include_sources: false, style: :expanded, functions: {}, importers: [], alert_ascii: false, alert_color: nil, logger: nil, quiet_deps: false, verbose: false)
38
+ # @param (see Embedded#compile)
39
+ # @return (see Embedded#compile)
40
+ # @raise (see Embedded#compile)
41
+ # @see Embedded#compile
42
+ def compile(path, **kwargs)
43
+ instance.compile(path, **kwargs)
44
+ end
45
+
46
+ # Compiles a stylesheet whose contents is +source+ to CSS.
47
+ # @overload compile_string(source, importer: nil, load_paths: [], syntax: :scss, url: nil, charset: true, source_map: false, source_map_include_sources: false, style: :expanded, functions: {}, importers: [], alert_ascii: false, alert_color: nil, logger: nil, quiet_deps: false, verbose: false)
48
+ # @param (see Embedded#compile_string)
49
+ # @return (see Embedded#compile_string)
50
+ # @raise (see Embedded#compile_string)
51
+ # @see Embedded#compile_string
52
+ def compile_string(source, **kwargs)
53
+ instance.compile_string(source, **kwargs)
54
+ end
55
+
56
+ # @param (see Embedded#info)
57
+ # @return (see Embedded#info)
58
+ # @raise (see Embedded#info)
59
+ # @see Embedded#info
60
+ def info
61
+ instance.info
62
+ end
63
+
64
+ private
65
+
66
+ def instance
67
+ return @instance if @instance
68
+
69
+ @mutex.synchronize do
70
+ return @instance if @instance
71
+
72
+ @instance = Embedded.new
73
+ at_exit do
74
+ @instance.close
75
+ end
76
+ end
77
+
78
+ @instance
79
+ end
80
+ end
81
+ # rubocop:enable Layout/LineLength
82
+
83
+ # The {Embedded} host for using dart-sass-embedded. Each instance creates
84
+ # its own communication {Channel} with a dedicated compiler process.
85
+ #
86
+ # @example
87
+ # embedded = Sass::Embedded.new
88
+ # result = embedded.compile_string('h1 { font-size: 40px; }')
89
+ # result = embedded.compile('style.scss')
90
+ # embedded.close
91
+ class Embedded
92
+ def initialize
93
+ @channel = Channel.new
94
+ end
95
+
96
+ # Compiles the Sass file at +path+ to CSS.
97
+ # @param path [String]
98
+ # @param load_paths [Array<String>] Paths in which to look for stylesheets loaded by rules like
99
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
100
+ # @param charset [Boolean] By default, if the CSS document contains non-ASCII characters, Sass adds a +@charset+
101
+ # declaration (in expanded output mode) or a byte-order mark (in compressed mode) to indicate its encoding to
102
+ # browsers or other consumers. If +charset+ is +false+, these annotations are omitted.
103
+ # @param source_map [Boolean] Whether or not Sass should generate a source map.
104
+ # @param source_map_include_sources [Boolean] Whether Sass should include the sources in the generated source map.
105
+ # @param style [String, Symbol] The OutputStyle of the compiled CSS.
106
+ # @param functions [Hash<String, Proc>] Additional built-in Sass functions that are available in all stylesheets.
107
+ # @param importers [Array<Object>] Custom importers that control how Sass resolves loads from rules like
108
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
109
+ # @param alert_ascii [Boolean] If this is +true+, the compiler will exclusively use ASCII characters in its error
110
+ # and warning messages. Otherwise, it may use non-ASCII Unicode characters as well.
111
+ # @param alert_color [Boolean] If this is +true+, the compiler will use ANSI color escape codes in its error and
112
+ # warning messages. If it's +false+, it won't use these. If it's +nil+, the compiler will determine whether or
113
+ # not to use colors depending on whether the user is using an interactive terminal.
114
+ # @param logger [Object] An object to use to handle warnings and/or debug messages from Sass.
115
+ # @param quiet_deps [Boolean] If this option is set to +true+, Sass won’t print warnings that are caused by
116
+ # dependencies. A “dependency” is defined as any file that’s loaded through +load_paths+ or +importer+.
117
+ # Stylesheets that are imported relative to the entrypoint are not considered dependencies.
118
+ # @param verbose [Boolean] By default, Dart Sass will print only five instances of the same deprecation warning per
119
+ # compilation to avoid deluging users in console noise. If you set verbose to +true+, it will instead print every
120
+ # deprecation warning it encounters.
121
+ # @return [CompileResult]
122
+ # @raise [CompileError]
123
+ # @see https://sass-lang.com/documentation/js-api/modules#compile
124
+ def compile(path,
125
+ load_paths: [],
126
+
127
+ charset: true,
128
+ source_map: false,
129
+ source_map_include_sources: false,
130
+ style: :expanded,
131
+
132
+ functions: {},
133
+ importers: [],
134
+
135
+ alert_ascii: false,
136
+ alert_color: nil,
137
+ logger: nil,
138
+ quiet_deps: false,
139
+ verbose: false)
140
+ raise ArgumentError, 'path must be set' if path.nil?
141
+
142
+ Host.new(@channel).compile_request(
143
+ path: path,
144
+ source: nil,
145
+ importer: nil,
146
+ load_paths: load_paths,
147
+ syntax: nil,
148
+ url: nil,
149
+ charset: charset,
150
+ source_map: source_map,
151
+ source_map_include_sources: source_map_include_sources,
152
+ style: style,
153
+ functions: functions,
154
+ importers: importers,
155
+ alert_color: alert_color,
156
+ alert_ascii: alert_ascii,
157
+ logger: logger,
158
+ quiet_deps: quiet_deps,
159
+ verbose: verbose
160
+ )
161
+ end
162
+
163
+ # Compiles a stylesheet whose contents is +source+ to CSS.
164
+ # @param source [String]
165
+ # @param importer [Object] The importer to use to handle loads that are relative to the entrypoint stylesheet.
166
+ # @param load_paths [Array<String>] Paths in which to look for stylesheets loaded by rules like
167
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
168
+ # @param syntax [String, Symbol] The Syntax to use to parse the entrypoint stylesheet.
169
+ # @param url [String] The canonical URL of the entrypoint stylesheet. If this is passed along with +importer+, it's
170
+ # used to resolve relative loads in the entrypoint stylesheet.
171
+ # @param charset [Boolean] By default, if the CSS document contains non-ASCII characters, Sass adds a +@charset+
172
+ # declaration (in expanded output mode) or a byte-order mark (in compressed mode) to indicate its encoding to
173
+ # browsers or other consumers. If +charset+ is +false+, these annotations are omitted.
174
+ # @param source_map [Boolean] Whether or not Sass should generate a source map.
175
+ # @param source_map_include_sources [Boolean] Whether Sass should include the sources in the generated source map.
176
+ # @param style [String, Symbol] The OutputStyle of the compiled CSS.
177
+ # @param functions [Hash<String, Proc>] Additional built-in Sass functions that are available in all stylesheets.
178
+ # @param importers [Array<Object>] Custom importers that control how Sass resolves loads from rules like
179
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
180
+ # @param alert_ascii [Boolean] If this is +true+, the compiler will exclusively use ASCII characters in its error
181
+ # and warning messages. Otherwise, it may use non-ASCII Unicode characters as well.
182
+ # @param alert_color [Boolean] If this is +true+, the compiler will use ANSI color escape codes in its error and
183
+ # warning messages. If it's +false+, it won't use these. If it's +nil+, the compiler will determine whether or
184
+ # not to use colors depending on whether the user is using an interactive terminal.
185
+ # @param logger [Object] An object to use to handle warnings and/or debug messages from Sass.
186
+ # @param quiet_deps [Boolean] If this option is set to +true+, Sass won’t print warnings that are caused by
187
+ # dependencies. A “dependency” is defined as any file that’s loaded through +load_paths+ or +importer+.
188
+ # Stylesheets that are imported relative to the entrypoint are not considered dependencies.
189
+ # @param verbose [Boolean] By default, Dart Sass will print only five instances of the same deprecation warning per
190
+ # compilation to avoid deluging users in console noise. If you set verbose to +true+, it will instead print every
191
+ # deprecation warning it encounters.
192
+ # @return [CompileResult]
193
+ # @raise [CompileError]
194
+ # @see https://sass-lang.com/documentation/js-api/modules#compileString
195
+ def compile_string(source,
196
+ importer: nil,
197
+ load_paths: [],
198
+ syntax: :scss,
199
+ url: nil,
200
+
201
+ charset: true,
202
+ source_map: false,
203
+ source_map_include_sources: false,
204
+ style: :expanded,
205
+
206
+ functions: {},
207
+ importers: [],
208
+
209
+ alert_ascii: false,
210
+ alert_color: nil,
211
+ logger: nil,
212
+ quiet_deps: false,
213
+ verbose: false)
214
+ raise ArgumentError, 'source must be set' if source.nil?
215
+
216
+ Host.new(@channel).compile_request(
217
+ path: nil,
218
+ source: source,
219
+ importer: importer,
220
+ load_paths: load_paths,
221
+ syntax: syntax,
222
+ url: url,
223
+ charset: charset,
224
+ source_map: source_map,
225
+ source_map_include_sources: source_map_include_sources,
226
+ style: style,
227
+ functions: functions,
228
+ importers: importers,
229
+ alert_color: alert_color,
230
+ alert_ascii: alert_ascii,
231
+ logger: logger,
232
+ quiet_deps: quiet_deps,
233
+ verbose: verbose
234
+ )
235
+ end
236
+
237
+ # @return [String] Information about the Sass implementation.
238
+ # @see https://sass-lang.com/documentation/js-api/modules#info
239
+ def info
240
+ @info ||= Host.new(@channel).version_request
241
+ end
242
+
243
+ def close
244
+ @channel.close
245
+ end
246
+
247
+ def closed?
248
+ @channel.closed?
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # A namespace for built-in Loggers.
5
+ #
6
+ # @see https://sass-lang.com/documentation/js-api/modules/Logger
7
+ module Logger
8
+ module_function
9
+
10
+ # A Logger that silently ignores all warnings and debug messages.
11
+ def silent
12
+ Silent
13
+ end
14
+
15
+ # A Logger that silently ignores all warnings and debug messages.
16
+ module Silent
17
+ module_function
18
+
19
+ def warn(message, deprecation: false, span: nil, stack: nil); end
20
+
21
+ def debug(message, span: nil); end
22
+ end
23
+
24
+ private_constant :Silent
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Logger
5
+ # A specific location within a source file.
6
+ #
7
+ # This is always associated with a {SourceSpan} which indicates which file it refers to.
8
+ #
9
+ # @see https://sass-lang.com/documentation/js-api/interfaces/SourceLocation
10
+ class SourceLocation
11
+ # @return [Integer]
12
+ attr_reader :offset, :line, :column
13
+
14
+ def initialize(offset, line, column)
15
+ @offset = offset
16
+ @line = line
17
+ @column = column
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Logger
5
+ # A span of text within a source file.
6
+ #
7
+ # @see https://sass-lang.com/documentation/js-api/interfaces/SourceSpan
8
+ class SourceSpan
9
+ # @return [SourceLocation]
10
+ attr_reader :start, :end
11
+
12
+ # @return [String]
13
+ attr_reader :text
14
+
15
+ # @return [String, nil]
16
+ attr_reader :url, :context
17
+
18
+ def initialize(start, end_, text, url, context)
19
+ @start = start
20
+ @end = end_
21
+ @text = text
22
+ @url = url == '' ? nil : url
23
+ @context = context == '' ? nil : context
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # An exception thrown by Sass Script.
5
+ class ScriptError < StandardError
6
+ def initialize(message, name = nil)
7
+ super(name.nil? ? message : "$#{name}: #{message}")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Value
5
+ # Sass's argument list type.
6
+ #
7
+ # An argument list comes from a rest argument. It's distinct from a normal {List} in that it may contain a keyword
8
+ # map as well as the positional arguments.
9
+ #
10
+ # @see https://sass-lang.com/documentation/js-api/classes/SassArgumentList
11
+ class ArgumentList < Value::List
12
+ # @param contents [Array<Value>]
13
+ # @param keywords [Hash<::String, Value>]
14
+ # @param separator [::String]
15
+ def initialize(contents = [], keywords = {}, separator = ',')
16
+ super(contents, separator: separator)
17
+
18
+ @id = 0
19
+ @keywords_accessed = false
20
+ @keywords = keywords.transform_keys(&:to_s).freeze
21
+ end
22
+
23
+ # @return [Hash<::String, Value>]
24
+ def keywords
25
+ @keywords_accessed = true
26
+ @keywords
27
+ end
28
+
29
+ private
30
+
31
+ def initialize_copy(orig)
32
+ super
33
+ @id = 0
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Value
5
+ # Sass's boolean type.
6
+ #
7
+ # @see https://sass-lang.com/documentation/js-api/classes/SassBoolean
8
+ class Boolean
9
+ include Value
10
+
11
+ # @param value [::Boolean]
12
+ def initialize(value)
13
+ @value = value
14
+ end
15
+
16
+ # @return [::Boolean]
17
+ attr_reader :value
18
+
19
+ # @return [Boolean]
20
+ def !
21
+ value ? Boolean::FALSE : Boolean::TRUE
22
+ end
23
+
24
+ # @return [::Boolean]
25
+ def ==(other)
26
+ other.is_a?(Sass::Value::Boolean) && other.value == value
27
+ end
28
+
29
+ # @return [Integer]
30
+ def hash
31
+ @hash ||= value.hash
32
+ end
33
+
34
+ alias to_bool value
35
+
36
+ # @return [Boolean]
37
+ def assert_boolean(_name = nil)
38
+ self
39
+ end
40
+
41
+ # Sass's true value.
42
+ TRUE = Boolean.new(true)
43
+
44
+ # Sass's false value.
45
+ FALSE = Boolean.new(false)
46
+
47
+ def self.new(value)
48
+ value ? Boolean::TRUE : Boolean::FALSE
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Value
5
+ # Sass's color type.
6
+ #
7
+ # No matter what representation was originally used to create this color, all of its channels are accessible.
8
+ #
9
+ # @see https://sass-lang.com/documentation/js-api/classes/SassColor
10
+ class Color
11
+ include Value
12
+
13
+ # @param red [Numeric]
14
+ # @param green [Numeric]
15
+ # @param blue [Numeric]
16
+ # @param hue [Numeric]
17
+ # @param saturation [Numeric]
18
+ # @param lightness [Numeric]
19
+ # @param whiteness [Numeric]
20
+ # @param blackness [Numeric]
21
+ # @param alpha [Numeric]
22
+ def initialize(red: nil,
23
+ green: nil,
24
+ blue: nil,
25
+ hue: nil,
26
+ saturation: nil,
27
+ lightness: nil,
28
+ whiteness: nil,
29
+ blackness: nil,
30
+ alpha: nil)
31
+ @alpha = alpha.nil? ? 1 : FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
32
+ if red && green && blue
33
+ @red = FuzzyMath.assert_between(FuzzyMath.round(red), 0, 255, 'red')
34
+ @green = FuzzyMath.assert_between(FuzzyMath.round(green), 0, 255, 'green')
35
+ @blue = FuzzyMath.assert_between(FuzzyMath.round(blue), 0, 255, 'blue')
36
+ elsif hue && saturation && lightness
37
+ @hue = hue % 360
38
+ @saturation = FuzzyMath.assert_between(saturation, 0, 100, 'saturation')
39
+ @lightness = FuzzyMath.assert_between(lightness, 0, 100, 'lightness')
40
+ elsif hue && whiteness && blackness
41
+ @hue = hue % 360
42
+ @whiteness = FuzzyMath.assert_between(whiteness, 0, 100, 'whiteness')
43
+ @blackness = FuzzyMath.assert_between(blackness, 0, 100, 'blackness')
44
+ hwb_to_rgb
45
+ @whiteness = @blackness = nil
46
+ else
47
+ raise Sass::ScriptError, 'Invalid Color'
48
+ end
49
+ end
50
+
51
+ # @return [Integer]
52
+ def red
53
+ hsl_to_rgb unless defined? @red
54
+
55
+ @red
56
+ end
57
+
58
+ # @return [Integer]
59
+ def green
60
+ hsl_to_rgb unless defined? @green
61
+
62
+ @green
63
+ end
64
+
65
+ # @return [Integer]
66
+ def blue
67
+ hsl_to_rgb unless defined? @blue
68
+
69
+ @blue
70
+ end
71
+
72
+ # @return [Numeric]
73
+ def hue
74
+ rgb_to_hsl unless defined? @hue
75
+
76
+ @hue
77
+ end
78
+
79
+ # @return [Numeric]
80
+ def saturation
81
+ rgb_to_hsl unless defined? @saturation
82
+
83
+ @saturation
84
+ end
85
+
86
+ # @return [Numeric]
87
+ def lightness
88
+ rgb_to_hsl unless defined? @lightness
89
+
90
+ @lightness
91
+ end
92
+
93
+ # @return [Numeric]
94
+ def whiteness
95
+ @whiteness ||= Rational([red, green, blue].min, 255) * 100
96
+ end
97
+
98
+ # @return [Numeric]
99
+ def blackness
100
+ @blackness ||= 100 - (Rational([red, green, blue].max, 255) * 100)
101
+ end
102
+
103
+ # @return [Numeric]
104
+ attr_reader :alpha
105
+
106
+ # @param red [Numeric]
107
+ # @param green [Numeric]
108
+ # @param blue [Numeric]
109
+ # @param hue [Numeric]
110
+ # @param saturation [Numeric]
111
+ # @param lightness [Numeric]
112
+ # @param whiteness [Numeric]
113
+ # @param blackness [Numeric]
114
+ # @param alpha [Numeric]
115
+ # @return [Color]
116
+ def change(red: nil,
117
+ green: nil,
118
+ blue: nil,
119
+ hue: nil,
120
+ saturation: nil,
121
+ lightness: nil,
122
+ whiteness: nil,
123
+ blackness: nil,
124
+ alpha: nil)
125
+ if whiteness || blackness
126
+ Sass::Value::Color.new(hue: hue || self.hue,
127
+ whiteness: whiteness || self.whiteness,
128
+ blackness: blackness || self.blackness,
129
+ alpha: alpha || self.alpha)
130
+ elsif hue || saturation || lightness
131
+ Sass::Value::Color.new(hue: hue || self.hue,
132
+ saturation: saturation || self.saturation,
133
+ lightness: lightness || self.lightness,
134
+ alpha: alpha || self.alpha)
135
+ elsif red || green || blue
136
+ Sass::Value::Color.new(red: red ? FuzzyMath.round(red) : self.red,
137
+ green: green ? FuzzyMath.round(green) : self.green,
138
+ blue: blue ? FuzzyMath.round(blue) : self.blue,
139
+ alpha: alpha || self.alpha)
140
+ else
141
+ dup.instance_eval do
142
+ @alpha = FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
143
+ self
144
+ end
145
+ end
146
+ end
147
+
148
+ # @return [::Boolean]
149
+ def ==(other)
150
+ other.is_a?(Sass::Value::Color) &&
151
+ other.red == red &&
152
+ other.green == green &&
153
+ other.blue == blue &&
154
+ other.alpha == alpha
155
+ end
156
+
157
+ # @return [Integer]
158
+ def hash
159
+ @hash ||= [red, green, blue, alpha].hash
160
+ end
161
+
162
+ # @return [Color]
163
+ def assert_color(_name = nil)
164
+ self
165
+ end
166
+
167
+ private
168
+
169
+ def rgb_to_hsl
170
+ scaled_red = Rational(red, 255)
171
+ scaled_green = Rational(green, 255)
172
+ scaled_blue = Rational(blue, 255)
173
+
174
+ max = [scaled_red, scaled_green, scaled_blue].max
175
+ min = [scaled_red, scaled_green, scaled_blue].min
176
+ delta = max - min
177
+
178
+ if max == min
179
+ @hue = 0
180
+ elsif max == scaled_red
181
+ @hue = ((scaled_green - scaled_blue) * 60 / delta) % 360
182
+ elsif max == scaled_green
183
+ @hue = (((scaled_blue - scaled_red) * 60 / delta) + 120) % 360
184
+ elsif max == scaled_blue
185
+ @hue = (((scaled_red - scaled_green) * 60 / delta) + 240) % 360
186
+ end
187
+
188
+ lightness = @lightness = (max + min) * 50
189
+
190
+ @saturation = if max == min
191
+ 0
192
+ elsif lightness < 50
193
+ delta * 100 / (max + min)
194
+ else
195
+ delta * 100 / (2 - max - min)
196
+ end
197
+ end
198
+
199
+ def hsl_to_rgb
200
+ scaled_hue = Rational(hue, 360)
201
+ scaled_saturation = Rational(saturation, 100)
202
+ scaled_lightness = Rational(lightness, 100)
203
+
204
+ tmp2 = if scaled_lightness <= 0.5
205
+ scaled_lightness * (scaled_saturation + 1)
206
+ else
207
+ scaled_lightness + scaled_saturation - (scaled_lightness * scaled_saturation)
208
+ end
209
+ tmp1 = (scaled_lightness * 2) - tmp2
210
+ @red = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue + Rational(1, 3)) * 255)
211
+ @green = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue) * 255)
212
+ @blue = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue - Rational(1, 3)) * 255)
213
+ end
214
+
215
+ def hsl_hue_to_rgb(tmp1, tmp2, hue)
216
+ hue += 1 if hue.negative?
217
+ hue -= 1 if hue > 1
218
+
219
+ if hue < Rational(1, 6)
220
+ tmp1 + ((tmp2 - tmp1) * hue * 6)
221
+ elsif hue < Rational(1, 2)
222
+ tmp2
223
+ elsif hue < Rational(2, 3)
224
+ tmp1 + ((tmp2 - tmp1) * (Rational(2, 3) - hue) * 6)
225
+ else
226
+ tmp1
227
+ end
228
+ end
229
+
230
+ def hwb_to_rgb
231
+ scaled_hue = Rational(hue, 360)
232
+ scaled_whiteness = Rational(whiteness, 100)
233
+ scaled_blackness = Rational(blackness, 100)
234
+
235
+ sum = scaled_whiteness + scaled_blackness
236
+ if sum > 1
237
+ scaled_whiteness /= sum
238
+ scaled_blackness /= sum
239
+ end
240
+
241
+ factor = 1 - scaled_whiteness - scaled_blackness
242
+ @red = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue + Rational(1, 3))
243
+ @green = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
244
+ @blue = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue - Rational(1, 3))
245
+ end
246
+
247
+ def hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
248
+ channel = (hsl_hue_to_rgb(0, 1, scaled_hue) * factor) + scaled_whiteness
249
+ FuzzyMath.round(channel * 255)
250
+ end
251
+ end
252
+ end
253
+ end