sass-embedded 1.54.6-x86_64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +9 -0
  5. data/ext/sass/embedded_sass_pb.rb +349 -0
  6. data/ext/sass/sass_embedded/dart-sass-embedded +17 -0
  7. data/ext/sass/sass_embedded/src/LICENSE +1476 -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 +65 -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 +90 -0
  16. data/lib/sass/embedded/host/function_registry.rb +90 -0
  17. data/lib/sass/embedded/host/importer_registry.rb +108 -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 +36 -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 +245 -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 +6 -0
  30. data/lib/sass/value/argument_list.rb +30 -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 +81 -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 +358 -0
  40. data/lib/sass/value/string.rb +55 -0
  41. data/lib/sass/value.rb +132 -0
  42. data/lib/sass-embedded.rb +4 -0
  43. metadata +186 -0
@@ -0,0 +1,245 @@
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
+ class << self
32
+ # Compiles the Sass file at +path+ to CSS.
33
+ # @param (see Embedded#compile)
34
+ # @return (see Embedded#compile)
35
+ # @raise (see Embedded#compile)
36
+ # @see Embedded#compile
37
+ def compile(path, **kwargs)
38
+ instance.compile(path, **kwargs)
39
+ end
40
+
41
+ # Compiles a stylesheet whose contents is +source+ to CSS.
42
+ # @param (see Embedded#compile_string)
43
+ # @return (see Embedded#compile_string)
44
+ # @raise (see Embedded#compile_string)
45
+ # @see Embedded#compile_string
46
+ def compile_string(source, **kwargs)
47
+ instance.compile_string(source, **kwargs)
48
+ end
49
+
50
+ # @param (see Embedded#info)
51
+ # @return (see Embedded#info)
52
+ # @raise (see Embedded#info)
53
+ # @see Embedded#info
54
+ def info
55
+ instance.info
56
+ end
57
+
58
+ private
59
+
60
+ def instance
61
+ if defined? @instance
62
+ @instance = Embedded.new if @instance.closed?
63
+ else
64
+ @instance = Embedded.new
65
+ at_exit do
66
+ @instance.close
67
+ end
68
+ end
69
+ @instance
70
+ end
71
+ end
72
+
73
+ # The {Embedded} host for using dart-sass-embedded. Each instance creates
74
+ # its own communication {Channel} with a dedicated compiler process.
75
+ #
76
+ # @example
77
+ # embedded = Sass::Embedded.new
78
+ # result = embedded.compile_string('h1 { font-size: 40px; }')
79
+ # result = embedded.compile('style.scss')
80
+ # embedded.close
81
+ class Embedded
82
+ def initialize
83
+ @channel = Channel.new
84
+ end
85
+
86
+ # Compiles the Sass file at +path+ to CSS.
87
+ # @param path [String]
88
+ # @param load_paths [Array<String>] Paths in which to look for stylesheets loaded by rules like
89
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
90
+ # @param charset [Boolean] By default, if the CSS document contains non-ASCII characters, Sass adds a +@charset+
91
+ # declaration (in expanded output mode) or a byte-order mark (in compressed mode) to indicate its encoding to
92
+ # browsers or other consumers. If +charset+ is +false+, these annotations are omitted.
93
+ # @param source_map [Boolean] Whether or not Sass should generate a source map.
94
+ # @param source_map_include_sources [Boolean] Whether Sass should include the sources in the generated source map.
95
+ # @param style [String, Symbol] The OutputStyle of the compiled CSS.
96
+ # @param functions [Hash<String, Proc>] Additional built-in Sass functions that are available in all stylesheets.
97
+ # @param importers [Array<Object>] Custom importers that control how Sass resolves loads from rules like
98
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
99
+ # @param alert_ascii [Boolean] If this is +true+, the compiler will exclusively use ASCII characters in its error
100
+ # and warning messages. Otherwise, it may use non-ASCII Unicode characters as well.
101
+ # @param alert_color [Boolean] If this is +true+, the compiler will use ANSI color escape codes in its error and
102
+ # warning messages. If it's +false+, it won't use these. If it's +nil+, the compiler will determine whether or
103
+ # not to use colors depending on whether the user is using an interactive terminal.
104
+ # @param logger [Object] An object to use to handle warnings and/or debug messages from Sass.
105
+ # @param quiet_deps [Boolean] If this option is set to +true+, Sass won’t print warnings that are caused by
106
+ # dependencies. A “dependency” is defined as any file that’s loaded through +load_paths+ or +importer+.
107
+ # Stylesheets that are imported relative to the entrypoint are not considered dependencies.
108
+ # @param verbose [Boolean] By default, Dart Sass will print only five instances of the same deprecation warning per
109
+ # compilation to avoid deluging users in console noise. If you set verbose to +true+, it will instead print every
110
+ # deprecation warning it encounters.
111
+ # @return [CompileResult]
112
+ # @raise [CompileError]
113
+ # @see https://sass-lang.com/documentation/js-api/modules#compile
114
+ def compile(path,
115
+ load_paths: [],
116
+
117
+ charset: true,
118
+ source_map: false,
119
+ source_map_include_sources: false,
120
+ style: :expanded,
121
+
122
+ functions: {},
123
+ importers: [],
124
+
125
+ alert_ascii: false,
126
+ alert_color: nil,
127
+ logger: nil,
128
+ quiet_deps: false,
129
+ verbose: false)
130
+ raise ArgumentError, 'path must be set' if path.nil?
131
+
132
+ Protofier.from_proto_compile_response(
133
+ Host.new(@channel).compile_request(
134
+ path: path,
135
+ source: nil,
136
+ importer: nil,
137
+ load_paths: load_paths,
138
+ syntax: nil,
139
+ url: nil,
140
+ charset: charset,
141
+ source_map: source_map,
142
+ source_map_include_sources: source_map_include_sources,
143
+ style: style,
144
+ functions: functions,
145
+ importers: importers,
146
+ alert_color: alert_color,
147
+ alert_ascii: alert_ascii,
148
+ logger: logger,
149
+ quiet_deps: quiet_deps,
150
+ verbose: verbose
151
+ )
152
+ )
153
+ end
154
+
155
+ # Compiles a stylesheet whose contents is +source+ to CSS.
156
+ # @param source [String]
157
+ # @param importer [Object] The importer to use to handle loads that are relative to the entrypoint stylesheet.
158
+ # @param load_paths [Array<String>] Paths in which to look for stylesheets loaded by rules like
159
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
160
+ # @param syntax [String, Symbol] The Syntax to use to parse the entrypoint stylesheet.
161
+ # @param url [String] The canonical URL of the entrypoint stylesheet. If this is passed along with +importer+, it's
162
+ # used to resolve relative loads in the entrypoint stylesheet.
163
+ # @param charset [Boolean] By default, if the CSS document contains non-ASCII characters, Sass adds a +@charset+
164
+ # declaration (in expanded output mode) or a byte-order mark (in compressed mode) to indicate its encoding to
165
+ # browsers or other consumers. If +charset+ is +false+, these annotations are omitted.
166
+ # @param source_map [Boolean] Whether or not Sass should generate a source map.
167
+ # @param source_map_include_sources [Boolean] Whether Sass should include the sources in the generated source map.
168
+ # @param style [String, Symbol] The OutputStyle of the compiled CSS.
169
+ # @param functions [Hash<String, Proc>] Additional built-in Sass functions that are available in all stylesheets.
170
+ # @param importers [Array<Object>] Custom importers that control how Sass resolves loads from rules like
171
+ # {@use}[https://sass-lang.com/documentation/at-rules/use] and {@import}[https://sass-lang.com/documentation/at-rules/import].
172
+ # @param alert_ascii [Boolean] If this is +true+, the compiler will exclusively use ASCII characters in its error
173
+ # and warning messages. Otherwise, it may use non-ASCII Unicode characters as well.
174
+ # @param alert_color [Boolean] If this is +true+, the compiler will use ANSI color escape codes in its error and
175
+ # warning messages. If it's +false+, it won't use these. If it's +nil+, the compiler will determine whether or
176
+ # not to use colors depending on whether the user is using an interactive terminal.
177
+ # @param logger [Object] An object to use to handle warnings and/or debug messages from Sass.
178
+ # @param quiet_deps [Boolean] If this option is set to +true+, Sass won’t print warnings that are caused by
179
+ # dependencies. A “dependency” is defined as any file that’s loaded through +load_paths+ or +importer+.
180
+ # Stylesheets that are imported relative to the entrypoint are not considered dependencies.
181
+ # @param verbose [Boolean] By default, Dart Sass will print only five instances of the same deprecation warning per
182
+ # compilation to avoid deluging users in console noise. If you set verbose to +true+, it will instead print every
183
+ # deprecation warning it encounters.
184
+ # @return [CompileResult]
185
+ # @raise [CompileError]
186
+ # @see https://sass-lang.com/documentation/js-api/modules#compileString
187
+ def compile_string(source,
188
+ importer: nil,
189
+ load_paths: [],
190
+ syntax: :scss,
191
+ url: nil,
192
+
193
+ charset: true,
194
+ source_map: false,
195
+ source_map_include_sources: false,
196
+ style: :expanded,
197
+
198
+ functions: {},
199
+ importers: [],
200
+
201
+ alert_ascii: false,
202
+ alert_color: nil,
203
+ logger: nil,
204
+ quiet_deps: false,
205
+ verbose: false)
206
+ raise ArgumentError, 'source must be set' if source.nil?
207
+
208
+ Protofier.from_proto_compile_response(
209
+ Host.new(@channel).compile_request(
210
+ path: nil,
211
+ source: source,
212
+ importer: importer,
213
+ load_paths: load_paths,
214
+ syntax: syntax,
215
+ url: url,
216
+ charset: charset,
217
+ source_map: source_map,
218
+ source_map_include_sources: source_map_include_sources,
219
+ style: style,
220
+ functions: functions,
221
+ importers: importers,
222
+ alert_color: alert_color,
223
+ alert_ascii: alert_ascii,
224
+ logger: logger,
225
+ quiet_deps: quiet_deps,
226
+ verbose: verbose
227
+ )
228
+ )
229
+ end
230
+
231
+ # @return [String] Information about the Sass implementation.
232
+ # @see https://sass-lang.com/documentation/js-api/modules#info
233
+ def info
234
+ @info ||= "sass-embedded\t#{Host.new(@channel).version_request.implementation_version}"
235
+ end
236
+
237
+ def close
238
+ @channel.close
239
+ end
240
+
241
+ def closed?
242
+ @channel.closed?
243
+ end
244
+ end
245
+ 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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # An exception thrown by Sass Script.
5
+ class ScriptError < StandardError; end
6
+ end
@@ -0,0 +1,30 @@
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
+ end
29
+ end
30
+ 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 error '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 = (60 * (scaled_green - scaled_blue) / delta) % 360
182
+ elsif max == scaled_green
183
+ @hue = (120 + (60 * (scaled_blue - scaled_red) / delta)) % 360
184
+ elsif max == scaled_blue
185
+ @hue = (240 + (60 * (scaled_red - scaled_green) / delta)) % 360
186
+ end
187
+
188
+ lightness = @lightness = 50 * (max + min)
189
+
190
+ @saturation = if max == min
191
+ 0
192
+ elsif lightness < 50
193
+ 100 * delta / (max + min)
194
+ else
195
+ 100 * delta / (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