color_contrast_calc 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.travis.yml +10 -3
- data/README.ja.md +18 -9
- data/README.md +18 -9
- data/color_contrast_calc.gemspec +5 -5
- data/examples/sort_colors.rb +17 -9
- data/lib/color_contrast_calc/color.rb +36 -20
- data/lib/color_contrast_calc/color_function_parser.rb +86 -26
- data/lib/color_contrast_calc/invalid_color_representation_error.rb +5 -1
- data/lib/color_contrast_calc/sorter.rb +151 -107
- data/lib/color_contrast_calc/transparency_calc.rb +1 -1
- data/lib/color_contrast_calc/utils.rb +58 -8
- data/lib/color_contrast_calc/version.rb +1 -1
- metadata +12 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07b766d958883d015466220f97ed047512e0fc6dd2a51288b5274c6b0287f0fe
|
4
|
+
data.tar.gz: 1e740d3598c423f07efb3632b31bf155dbf337f8f9265ede39f3b12bd3e50456
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d3becf7316ad107082efb72a1e777fb23272a4fe73239170296e55e978d7d0935ad23e7b0a9203c0dc94164ac2dca5efc5763139c49e19cf116c50c28539e1c
|
7
|
+
data.tar.gz: 6e3e1efa303a5ea42e56fd4011ccee3cd19677f0ae770a279c9a4b6ffa9340bb63d5d192224b4f7b18a1364e2b299ecab878dee3a76aa657843ee333a619341b
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.3
|
5
|
-
- 2.4
|
6
4
|
- 2.5
|
7
5
|
- 2.6
|
8
6
|
- 2.7
|
7
|
+
- 3.0
|
9
8
|
- ruby-head
|
10
|
-
- jruby-head
|
11
9
|
before_install: gem install bundler -v '~>2.0'
|
10
|
+
jobs:
|
11
|
+
include:
|
12
|
+
- rvm: 2.4
|
13
|
+
before_install:
|
14
|
+
- yes | gem update --system --force
|
15
|
+
- gem install bundler
|
16
|
+
- rvm: jruby-head
|
17
|
+
before_install:
|
18
|
+
- bundle add rake --version "13.0.0"
|
data/README.ja.md
CHANGED
@@ -289,32 +289,40 @@ The grayscale of #ffa500 is #acacac.
|
|
289
289
|
```ruby
|
290
290
|
require 'color_contrast_calc'
|
291
291
|
|
292
|
-
color_names = ['red', '
|
293
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
292
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
294
293
|
|
295
294
|
# Sort by hSL order. An uppercase for a component of color means
|
296
295
|
# that component should be sorted in descending order.
|
297
296
|
|
298
|
-
hsl_ordered = ColorContrastCalc.sort(
|
299
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
297
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
298
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
300
299
|
|
301
300
|
# Sort by RGB order.
|
302
301
|
|
303
|
-
rgb_ordered = ColorContrastCalc.sort(
|
304
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
302
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
303
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
305
304
|
|
306
305
|
# You can also change the precedence of components.
|
307
306
|
|
308
|
-
grb_ordered = ColorContrastCalc.sort(
|
309
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
307
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
308
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
310
309
|
|
311
310
|
# And you can directly sort hex color codes.
|
312
311
|
|
313
312
|
## Hex color codes that correspond to the color_names given above.
|
314
|
-
hex_codes = ['#ff0000', '#
|
313
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
315
314
|
|
316
315
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
317
316
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
317
|
+
|
318
|
+
# If you want to sort colors in different notations,
|
319
|
+
# you should specify a key_mapper.
|
320
|
+
|
321
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
322
|
+
|
323
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
324
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
325
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
318
326
|
```
|
319
327
|
|
320
328
|
以下のように実行します:
|
@@ -325,6 +333,7 @@ Colors sorted in the order of hSL: ["red", "yellow", "lime", "cyan", "blue", "fu
|
|
325
333
|
Colors sorted in the order of RGB: ["yellow", "fuchsia", "red", "cyan", "lime", "blue"]
|
326
334
|
Colors sorted in the order of GRB: ["yellow", "cyan", "lime", "fuchsia", "red", "blue"]
|
327
335
|
Colors sorted in the order of hSL: ["#ff0000", "#ff0", "#00ff00", "#0ff", "#0000FF", "#f0f"]
|
336
|
+
Colors sorted in the order of hSL: ["rgb(255 0 0)", "hwb(60 0% 0%)", "hsl(120 100% 50%)", "#0ff", "#0000ff", [255, 0, 255]]
|
328
337
|
```
|
329
338
|
|
330
339
|
### 例5: 定義済みの色のリスト
|
data/README.md
CHANGED
@@ -292,32 +292,40 @@ For example, save the following code as `sort_colors.rb`:
|
|
292
292
|
```ruby
|
293
293
|
require 'color_contrast_calc'
|
294
294
|
|
295
|
-
color_names = ['red', '
|
296
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
295
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
297
296
|
|
298
297
|
# Sort by hSL order. An uppercase for a component of color means
|
299
298
|
# that component should be sorted in descending order.
|
300
299
|
|
301
|
-
hsl_ordered = ColorContrastCalc.sort(
|
302
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
300
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
301
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
303
302
|
|
304
303
|
# Sort by RGB order.
|
305
304
|
|
306
|
-
rgb_ordered = ColorContrastCalc.sort(
|
307
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
305
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
306
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
308
307
|
|
309
308
|
# You can also change the precedence of components.
|
310
309
|
|
311
|
-
grb_ordered = ColorContrastCalc.sort(
|
312
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
310
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
311
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
313
312
|
|
314
313
|
# And you can directly sort hex color codes.
|
315
314
|
|
316
315
|
## Hex color codes that correspond to the color_names given above.
|
317
|
-
hex_codes = ['#ff0000', '#
|
316
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
318
317
|
|
319
318
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
320
319
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
320
|
+
|
321
|
+
# If you want to sort colors in different notations,
|
322
|
+
# you should specify a key_mapper.
|
323
|
+
|
324
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
325
|
+
|
326
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
327
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
328
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
321
329
|
```
|
322
330
|
|
323
331
|
Then execute the script:
|
@@ -328,6 +336,7 @@ Colors sorted in the order of hSL: ["red", "yellow", "lime", "cyan", "blue", "fu
|
|
328
336
|
Colors sorted in the order of RGB: ["yellow", "fuchsia", "red", "cyan", "lime", "blue"]
|
329
337
|
Colors sorted in the order of GRB: ["yellow", "cyan", "lime", "fuchsia", "red", "blue"]
|
330
338
|
Colors sorted in the order of hSL: ["#ff0000", "#ff0", "#00ff00", "#0ff", "#0000FF", "#f0f"]
|
339
|
+
Colors sorted in the order of hSL: ["rgb(255 0 0)", "hwb(60 0% 0%)", "hsl(120 100% 50%)", "#0ff", "#0000ff", [255, 0, 255]]
|
331
340
|
```
|
332
341
|
|
333
342
|
### Example 5: Lists of predefined colors
|
data/color_contrast_calc.gemspec
CHANGED
@@ -7,7 +7,7 @@ require 'color_contrast_calc/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'color_contrast_calc'
|
9
9
|
spec.version = ColorContrastCalc::VERSION
|
10
|
-
spec.required_ruby_version = ">= 2.
|
10
|
+
spec.required_ruby_version = ">= 2.4"
|
11
11
|
spec.authors = ['HASHIMOTO, Naoki']
|
12
12
|
spec.email = ['hashimoto.naoki@gmail.com']
|
13
13
|
|
@@ -23,9 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
26
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
27
27
|
spec.add_development_dependency 'rake', '~> 13.0'
|
28
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
29
|
-
spec.add_development_dependency 'rubocop', '~>
|
30
|
-
spec.add_development_dependency 'yard', '~> 0.9
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.7'
|
30
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
31
31
|
end
|
data/examples/sort_colors.rb
CHANGED
@@ -2,29 +2,37 @@
|
|
2
2
|
|
3
3
|
require 'color_contrast_calc'
|
4
4
|
|
5
|
-
color_names = ['red', '
|
6
|
-
colors = color_names.map {|c| ColorContrastCalc.color_from(c) }
|
5
|
+
color_names = ['red', 'lime', 'cyan', 'yellow', 'fuchsia', 'blue']
|
7
6
|
|
8
7
|
# Sort by hSL order. An uppercase for a component of color means
|
9
8
|
# that component should be sorted in descending order.
|
10
9
|
|
11
|
-
hsl_ordered = ColorContrastCalc.sort(
|
12
|
-
puts("Colors sorted in the order of hSL: #{hsl_ordered
|
10
|
+
hsl_ordered = ColorContrastCalc.sort(color_names, 'hSL')
|
11
|
+
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
13
12
|
|
14
13
|
# Sort by RGB order.
|
15
14
|
|
16
|
-
rgb_ordered = ColorContrastCalc.sort(
|
17
|
-
puts("Colors sorted in the order of RGB: #{rgb_ordered
|
15
|
+
rgb_ordered = ColorContrastCalc.sort(color_names, 'RGB')
|
16
|
+
puts("Colors sorted in the order of RGB: #{rgb_ordered}")
|
18
17
|
|
19
18
|
# You can also change the precedence of components.
|
20
19
|
|
21
|
-
grb_ordered = ColorContrastCalc.sort(
|
22
|
-
puts("Colors sorted in the order of GRB: #{grb_ordered
|
20
|
+
grb_ordered = ColorContrastCalc.sort(color_names, 'GRB')
|
21
|
+
puts("Colors sorted in the order of GRB: #{grb_ordered}")
|
23
22
|
|
24
23
|
# And you can directly sort hex color codes.
|
25
24
|
|
26
25
|
## Hex color codes that correspond to the color_names given above.
|
27
|
-
hex_codes = ['#ff0000', '#
|
26
|
+
hex_codes = ['#ff0000', '#00ff00', '#0ff', '#ff0', '#f0f', '#0000FF']
|
28
27
|
|
29
28
|
hsl_ordered = ColorContrastCalc.sort(hex_codes, 'hSL')
|
30
29
|
puts("Colors sorted in the order of hSL: #{hsl_ordered}")
|
30
|
+
|
31
|
+
# If you want to sort colors in different notations,
|
32
|
+
# you should specify a key_mapper.
|
33
|
+
|
34
|
+
colors = ['rgb(255 0 0)', 'hsl(120 100% 50%)', '#0ff', 'hwb(60 0% 0%)', [255, 0, 255], '#0000ff']
|
35
|
+
|
36
|
+
key_mapper = proc {|c| ColorContrastCalc.color_from(c) }
|
37
|
+
colors_in_hsl_order = ColorContrastCalc.sort(colors, 'hSL', key_mapper)
|
38
|
+
puts("Colors sorted in the order of hSL: #{colors_in_hsl_order}")
|
@@ -45,8 +45,7 @@ module ColorContrastCalc
|
|
45
45
|
# @return [Color] Instance of Color
|
46
46
|
|
47
47
|
def from_rgb(rgb, name = nil)
|
48
|
-
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] ||
|
49
|
-
Color.new(rgb, name)
|
48
|
+
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] || new(rgb, name)
|
50
49
|
end
|
51
50
|
|
52
51
|
##
|
@@ -58,8 +57,7 @@ module ColorContrastCalc
|
|
58
57
|
|
59
58
|
def from_hex(hex, name = nil)
|
60
59
|
normalized_hex = Utils.normalize_hex(hex)
|
61
|
-
!name && List::HEX_TO_COLOR[normalized_hex] ||
|
62
|
-
Color.new(normalized_hex, name)
|
60
|
+
!name && List::HEX_TO_COLOR[normalized_hex] || new(normalized_hex, name)
|
63
61
|
end
|
64
62
|
|
65
63
|
##
|
@@ -72,12 +70,11 @@ module ColorContrastCalc
|
|
72
70
|
def from_hsl(hsl, name = nil)
|
73
71
|
if hsl.length == 4
|
74
72
|
rgb = Utils.hsl_to_rgb(hsl[0, 3])
|
75
|
-
return
|
73
|
+
return new(rgb.push(hsl.last), name) unless opaque?(hsl)
|
76
74
|
end
|
77
75
|
|
78
76
|
rgb ||= Utils.hsl_to_rgb(hsl)
|
79
|
-
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] ||
|
80
|
-
Color.new(rgb, name)
|
77
|
+
!name && List::HEX_TO_COLOR[Utils.rgb_to_hex(rgb)] || new(rgb, name)
|
81
78
|
end
|
82
79
|
|
83
80
|
##
|
@@ -141,19 +138,19 @@ module ColorContrastCalc
|
|
141
138
|
end
|
142
139
|
|
143
140
|
def opaque?(color_value)
|
144
|
-
color_value[-1] ==
|
141
|
+
color_value[-1] == Utils::MAX_OPACITY
|
145
142
|
end
|
146
143
|
|
147
144
|
private :opaque?
|
148
145
|
|
149
146
|
def color_from_rgba(rgba_value, name = nil)
|
150
147
|
unless Utils.valid_rgb?(rgba_value[0, 3])
|
151
|
-
raise
|
148
|
+
raise InvalidColorRepresentationError.from_value(rgba_value)
|
152
149
|
end
|
153
150
|
|
154
151
|
return color_from_rgb(rgba_value[0, 3], name) if opaque?(rgba_value)
|
155
152
|
|
156
|
-
|
153
|
+
new(rgba_value, name)
|
157
154
|
end
|
158
155
|
|
159
156
|
private :color_from_rgba
|
@@ -164,7 +161,7 @@ module ColorContrastCalc
|
|
164
161
|
end
|
165
162
|
|
166
163
|
hex_code = Utils.rgb_to_hex(rgb_value)
|
167
|
-
!name && List::HEX_TO_COLOR[hex_code] ||
|
164
|
+
!name && List::HEX_TO_COLOR[hex_code] || new(rgb_value, name)
|
168
165
|
end
|
169
166
|
|
170
167
|
private :color_from_rgb
|
@@ -193,7 +190,7 @@ module ColorContrastCalc
|
|
193
190
|
end
|
194
191
|
|
195
192
|
hex_code = Utils.normalize_hex(color_value)
|
196
|
-
!name && List::HEX_TO_COLOR[hex_code] ||
|
193
|
+
!name && List::HEX_TO_COLOR[hex_code] || new(hex_code, name)
|
197
194
|
end
|
198
195
|
|
199
196
|
private :color_from_str
|
@@ -234,6 +231,12 @@ module ColorContrastCalc
|
|
234
231
|
@relative_luminance = Checker.relative_luminance(@rgb)
|
235
232
|
end
|
236
233
|
|
234
|
+
def create(rgb, name = nil)
|
235
|
+
self.class.new(rgb, name)
|
236
|
+
end
|
237
|
+
|
238
|
+
private :create
|
239
|
+
|
237
240
|
##
|
238
241
|
# Return HSL value of the color.
|
239
242
|
#
|
@@ -248,6 +251,20 @@ module ColorContrastCalc
|
|
248
251
|
@hsl ||= Utils.rgb_to_hsl(@rgb)
|
249
252
|
end
|
250
253
|
|
254
|
+
##
|
255
|
+
# Return HWB value of the color.
|
256
|
+
#
|
257
|
+
# The value is calculated from the RGB value, so if you create
|
258
|
+
# the instance by Color.color_from method, the value used to
|
259
|
+
# create the color does not necessarily correspond to the value
|
260
|
+
# of this property.
|
261
|
+
#
|
262
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
263
|
+
|
264
|
+
def hwb
|
265
|
+
@hwb ||= Utils.rgb_to_hwb(@rgb)
|
266
|
+
end
|
267
|
+
|
251
268
|
##
|
252
269
|
# Return a {https://www.w3.org/TR/SVG/types.html#ColorKeywords
|
253
270
|
# color keyword name} when the name corresponds to the hex code
|
@@ -256,8 +273,7 @@ module ColorContrastCalc
|
|
256
273
|
# @return [String] Color keyword name or hex color code
|
257
274
|
|
258
275
|
def common_name
|
259
|
-
|
260
|
-
named_color && named_color.name || @hex
|
276
|
+
List::HEX_TO_COLOR[@hex]&.name || @hex
|
261
277
|
end
|
262
278
|
|
263
279
|
##
|
@@ -347,7 +363,7 @@ module ColorContrastCalc
|
|
347
363
|
# @return [Color] New complementary color
|
348
364
|
def complementary(name = nil)
|
349
365
|
minmax = rgb.minmax.reduce {|min, max| min + max }
|
350
|
-
|
366
|
+
create(rgb.map {|c| minmax - c }, name)
|
351
367
|
end
|
352
368
|
|
353
369
|
##
|
@@ -363,8 +379,8 @@ module ColorContrastCalc
|
|
363
379
|
# of +other_color+
|
364
380
|
|
365
381
|
def find_brightness_threshold(other_color, level = Checker::Level::AA)
|
366
|
-
other_color =
|
367
|
-
|
382
|
+
other_color = create(other_color) unless other_color.is_a? Color
|
383
|
+
create(ThresholdFinder::Brightness.find(rgb, other_color.rgb, level))
|
368
384
|
end
|
369
385
|
|
370
386
|
##
|
@@ -380,8 +396,8 @@ module ColorContrastCalc
|
|
380
396
|
# of +other_color+
|
381
397
|
|
382
398
|
def find_lightness_threshold(other_color, level = Checker::Level::AA)
|
383
|
-
other_color =
|
384
|
-
|
399
|
+
other_color = create(other_color) unless other_color.is_a? Color
|
400
|
+
create(ThresholdFinder::Lightness.find(rgb, other_color.rgb, level))
|
385
401
|
end
|
386
402
|
|
387
403
|
##
|
@@ -522,7 +538,7 @@ module ColorContrastCalc
|
|
522
538
|
|
523
539
|
def generate_new_color(calc, ratio, name = nil)
|
524
540
|
new_rgb = calc.calc_rgb(rgb, ratio)
|
525
|
-
|
541
|
+
create(new_rgb, name)
|
526
542
|
end
|
527
543
|
|
528
544
|
private :generate_new_color
|
@@ -267,7 +267,7 @@ module ColorContrastCalc
|
|
267
267
|
# @return [true, false] return true when the opacity equals 1.0
|
268
268
|
|
269
269
|
def opaque?
|
270
|
-
opacity ==
|
270
|
+
opacity == Utils::MAX_OPACITY
|
271
271
|
end
|
272
272
|
|
273
273
|
# @private
|
@@ -335,6 +335,16 @@ module ColorContrastCalc
|
|
335
335
|
|
336
336
|
private_constant :MAX_SOURCE_LENGTH
|
337
337
|
|
338
|
+
def self.compose_error_message(scanner, message)
|
339
|
+
color_value = sanitized_source(scanner)
|
340
|
+
out = StringIO.new
|
341
|
+
out.print message
|
342
|
+
out.print ' '
|
343
|
+
ErrorReporter.print_error_pos!(out, color_value, scanner.charpos)
|
344
|
+
out.puts
|
345
|
+
out.string
|
346
|
+
end
|
347
|
+
|
338
348
|
def self.format_error_message(scanner, re)
|
339
349
|
out = StringIO.new
|
340
350
|
color_value = sanitized_source(scanner)
|
@@ -368,7 +378,7 @@ module ColorContrastCalc
|
|
368
378
|
|
369
379
|
class Parser
|
370
380
|
class << self
|
371
|
-
attr_accessor :parsers
|
381
|
+
attr_accessor :parsers, :main, :value, :function
|
372
382
|
end
|
373
383
|
|
374
384
|
def skip_spaces!(scanner)
|
@@ -449,7 +459,7 @@ module ColorContrastCalc
|
|
449
459
|
read_unit!(scanner, parsed_value)
|
450
460
|
end
|
451
461
|
|
452
|
-
|
462
|
+
protected :read_number!
|
453
463
|
|
454
464
|
def read_unit!(scanner, parsed_value)
|
455
465
|
unit = scanner.scan(TokenRe::UNIT)
|
@@ -463,15 +473,10 @@ module ColorContrastCalc
|
|
463
473
|
|
464
474
|
def read_separator!(scanner, parsed_value)
|
465
475
|
if next_spaces_as_separator?(scanner)
|
466
|
-
return read_number!(scanner, parsed_value)
|
476
|
+
return Parser.function.read_number!(scanner, parsed_value)
|
467
477
|
end
|
468
478
|
|
469
|
-
|
470
|
-
check_next_token(scanner, TokenRe::SLASH)
|
471
|
-
return read_opacity!(scanner, parsed_value)
|
472
|
-
end
|
473
|
-
|
474
|
-
read_comma!(scanner, parsed_value)
|
479
|
+
Parser.value.read_comma!(scanner, parsed_value)
|
475
480
|
end
|
476
481
|
|
477
482
|
private :read_separator!
|
@@ -496,11 +501,6 @@ module ColorContrastCalc
|
|
496
501
|
|
497
502
|
private :next_spaces_as_separator?
|
498
503
|
|
499
|
-
def read_opacity!(scanner, parsed_value)
|
500
|
-
read_token!(scanner, TokenRe::SLASH)
|
501
|
-
read_number!(scanner, parsed_value)
|
502
|
-
end
|
503
|
-
|
504
504
|
def read_comma!(scanner, parsed_value)
|
505
505
|
skip_spaces!(scanner)
|
506
506
|
|
@@ -510,10 +510,74 @@ module ColorContrastCalc
|
|
510
510
|
read_number!(scanner, parsed_value)
|
511
511
|
end
|
512
512
|
|
513
|
-
|
513
|
+
protected :read_comma!
|
514
|
+
end
|
515
|
+
|
516
|
+
class ValueParser < Parser
|
517
|
+
def read_separator!(scanner, parsed_value)
|
518
|
+
if next_spaces_as_separator?(scanner)
|
519
|
+
error_message = report_wrong_separator!(scanner, parsed_value)
|
520
|
+
raise InvalidColorRepresentationError, error_message
|
521
|
+
end
|
522
|
+
|
523
|
+
read_comma!(scanner, parsed_value)
|
524
|
+
end
|
525
|
+
|
526
|
+
def report_wrong_separator!(scanner, parsed_value)
|
527
|
+
scheme = parsed_value[:scheme].upcase
|
528
|
+
template = '" " and "," as a separator should not be used mixedly in %s functions.'
|
529
|
+
message = format(template, scheme)
|
530
|
+
ErrorReporter.compose_error_message(scanner, message)
|
531
|
+
end
|
532
|
+
|
533
|
+
private :report_wrong_separator!
|
514
534
|
end
|
515
535
|
|
516
536
|
class FunctionParser < Parser
|
537
|
+
def read_separator!(scanner, parsed_value)
|
538
|
+
if next_spaces_as_separator?(scanner)
|
539
|
+
error_if_opacity_separator_expected(scanner, parsed_value)
|
540
|
+
read_number!(scanner, parsed_value)
|
541
|
+
elsif opacity_separator_is_next?(scanner, parsed_value)
|
542
|
+
read_opacity!(scanner, parsed_value)
|
543
|
+
else
|
544
|
+
read_comma!(scanner, parsed_value)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
private :read_separator!
|
549
|
+
|
550
|
+
def error_if_opacity_separator_expected(scanner, parsed_value)
|
551
|
+
return unless parsed_value[:parameters].length == 3
|
552
|
+
|
553
|
+
error_message = report_wrong_opacity_separator!(scanner, parsed_value)
|
554
|
+
raise InvalidColorRepresentationError, error_message
|
555
|
+
end
|
556
|
+
|
557
|
+
private :error_if_opacity_separator_expected
|
558
|
+
|
559
|
+
def report_wrong_opacity_separator!(scanner, parsed_value)
|
560
|
+
scheme = parsed_value[:scheme].upcase
|
561
|
+
message = "\"/\" is expected as a separator for opacity in #{scheme} functions."
|
562
|
+
ErrorReporter.compose_error_message(scanner, message)
|
563
|
+
end
|
564
|
+
|
565
|
+
private :report_wrong_opacity_separator!
|
566
|
+
|
567
|
+
def opacity_separator_is_next?(scanner, parsed_value)
|
568
|
+
parsed_value[:parameters].length == 3 &&
|
569
|
+
check_next_token(scanner, TokenRe::SLASH)
|
570
|
+
end
|
571
|
+
|
572
|
+
private :opacity_separator_is_next?
|
573
|
+
|
574
|
+
def read_opacity!(scanner, parsed_value)
|
575
|
+
read_token!(scanner, TokenRe::SLASH)
|
576
|
+
read_number!(scanner, parsed_value)
|
577
|
+
end
|
578
|
+
|
579
|
+
private :read_opacity!
|
580
|
+
|
517
581
|
def read_comma!(scanner, parsed_value)
|
518
582
|
skip_spaces!(scanner)
|
519
583
|
|
@@ -527,15 +591,9 @@ module ColorContrastCalc
|
|
527
591
|
end
|
528
592
|
|
529
593
|
def report_wrong_separator!(scanner, parsed_value)
|
530
|
-
out = StringIO.new
|
531
|
-
color_value = scanner.string
|
532
594
|
scheme = parsed_value[:scheme].upcase
|
533
|
-
|
534
|
-
|
535
|
-
out.print "\",\" is not a valid separator for #{scheme} functions. "
|
536
|
-
ErrorReporter.print_error_pos!(out, color_value, scanner.charpos)
|
537
|
-
out.puts
|
538
|
-
out.string
|
595
|
+
message = "\",\" is not a valid separator for #{scheme} functions."
|
596
|
+
ErrorReporter.compose_error_message(scanner, message)
|
539
597
|
end
|
540
598
|
|
541
599
|
private :report_wrong_separator!
|
@@ -552,7 +610,9 @@ module ColorContrastCalc
|
|
552
610
|
Scheme::HWB => FunctionParser.new
|
553
611
|
}
|
554
612
|
|
555
|
-
|
613
|
+
Parser.main = Parser.new
|
614
|
+
Parser.value = ValueParser.new
|
615
|
+
Parser.function = FunctionParser.new
|
556
616
|
|
557
617
|
##
|
558
618
|
# Parse an RGB/HSL/HWB function and store the result as an instance of
|
@@ -563,7 +623,7 @@ module ColorContrastCalc
|
|
563
623
|
# @return [ColorFunction] An instance of ColorFunctionParser::ColorFunction
|
564
624
|
|
565
625
|
def self.parse(color_value)
|
566
|
-
parsed_value =
|
626
|
+
parsed_value = Parser.main.read_scheme!(StringScanner.new(color_value))
|
567
627
|
ColorFunction.create(parsed_value, color_value)
|
568
628
|
end
|
569
629
|
|
@@ -7,6 +7,10 @@ module ColorContrastCalc
|
|
7
7
|
class InvalidColorRepresentationError < StandardError
|
8
8
|
module Template
|
9
9
|
RGB = 'An RGB value should be in form of [r, g, b], but %s.'
|
10
|
+
RGBA = <<~RGBA_MESSAGE
|
11
|
+
An RGB value should be in form of [r, g, b, opacity]
|
12
|
+
(r, g, b should be in the range between 0 and 255), but %s.
|
13
|
+
RGBA_MESSAGE
|
10
14
|
COLOR_NAME = '%s seems to be an undefined color name.'
|
11
15
|
HEX = 'A hex code #xxxxxx where 0 <= x <= f is expected, but %s.'
|
12
16
|
UNEXPECTED = 'A color should be given as an array or string, but %s.'
|
@@ -22,7 +26,7 @@ module ColorContrastCalc
|
|
22
26
|
def self.select_message_template(value)
|
23
27
|
case value
|
24
28
|
when Array
|
25
|
-
Template::RGB
|
29
|
+
value.length == 3 ? Template::RGB : Template::RGBA
|
26
30
|
when String
|
27
31
|
may_be_name?(value) ? Template::COLOR_NAME : Template::HEX
|
28
32
|
else
|
@@ -18,6 +18,7 @@ module ColorContrastCalc
|
|
18
18
|
module ColorComponent
|
19
19
|
RGB = 'rgb'.chars
|
20
20
|
HSL = 'hsl'.chars
|
21
|
+
HWB = 'hwb'.chars
|
21
22
|
end
|
22
23
|
|
23
24
|
module CompFunc
|
@@ -45,6 +46,9 @@ module ColorContrastCalc
|
|
45
46
|
# The function returned by Sorter.compile_compare_function() expects
|
46
47
|
# hex color codes as key values when this constants is specified.
|
47
48
|
HEX = :hex
|
49
|
+
# The function returned by Sorter.compile_compare_function() expects
|
50
|
+
# color functions as key values when this constants is specified.
|
51
|
+
FUNCTION = :function
|
48
52
|
# @private
|
49
53
|
CLASS_TO_TYPE = {
|
50
54
|
Color => COLOR,
|
@@ -62,14 +66,157 @@ module ColorContrastCalc
|
|
62
66
|
|
63
67
|
def self.guess(color, key_mapper = nil)
|
64
68
|
key = key_mapper ? key_mapper[color] : color
|
69
|
+
return FUNCTION if non_hex_code_string?(key)
|
65
70
|
CLASS_TO_TYPE[key.class]
|
66
71
|
end
|
72
|
+
|
73
|
+
def self.non_hex_code_string?(color)
|
74
|
+
color.is_a?(String) && !Utils.valid_hex?(color)
|
75
|
+
end
|
76
|
+
|
77
|
+
private_class_method :non_hex_code_string?
|
78
|
+
end
|
79
|
+
|
80
|
+
class CompareFunctionCompiler
|
81
|
+
def initialize(converters = nil)
|
82
|
+
@converters = converters
|
83
|
+
end
|
84
|
+
|
85
|
+
def compile(color_order)
|
86
|
+
order = parse_color_order(color_order)
|
87
|
+
create_proc(order, color_order)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @private
|
91
|
+
|
92
|
+
def parse_color_order(color_order)
|
93
|
+
ordered_components = select_ordered_components(color_order)
|
94
|
+
pos = color_component_pos(color_order, ordered_components)
|
95
|
+
funcs = []
|
96
|
+
pos.each_with_index do |ci, i|
|
97
|
+
c = color_order[i]
|
98
|
+
funcs[ci] = Utils.uppercase?(c) ? CompFunc::DESCEND : CompFunc::ASCEND
|
99
|
+
end
|
100
|
+
{ pos: pos, funcs: funcs }
|
101
|
+
end
|
102
|
+
|
103
|
+
def select_ordered_components(color_order)
|
104
|
+
case color_order
|
105
|
+
when /[hsl]{3}/i
|
106
|
+
ColorComponent::HSL
|
107
|
+
when /[hwb]{3}/i
|
108
|
+
ColorComponent::HWB
|
109
|
+
else
|
110
|
+
ColorComponent::RGB
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private :select_ordered_components
|
115
|
+
|
116
|
+
# @private
|
117
|
+
|
118
|
+
def color_component_pos(color_order, ordered_components)
|
119
|
+
color_order.downcase.chars.map do |component|
|
120
|
+
ordered_components.index(component)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_proc(order, color_order)
|
125
|
+
if @converters
|
126
|
+
conv = select_converter(color_order)
|
127
|
+
proc {|color1, color2| compare(conv[color1], conv[color2], order) }
|
128
|
+
else
|
129
|
+
proc {|color1, color2| compare(color1, color2, order) }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private :create_proc
|
134
|
+
|
135
|
+
# @private
|
136
|
+
|
137
|
+
def compare_components(color1, color2, order)
|
138
|
+
funcs = order[:funcs]
|
139
|
+
order[:pos].each do |i|
|
140
|
+
result = funcs[i][color1[i], color2[i]]
|
141
|
+
return result unless result.zero?
|
142
|
+
end
|
143
|
+
|
144
|
+
0
|
145
|
+
end
|
146
|
+
|
147
|
+
alias compare compare_components
|
148
|
+
|
149
|
+
def select_converter(color_order)
|
150
|
+
scheme = select_scheme(color_order)
|
151
|
+
@converters[scheme]
|
152
|
+
end
|
153
|
+
|
154
|
+
private :select_converter
|
155
|
+
|
156
|
+
def select_scheme(color_order)
|
157
|
+
case color_order
|
158
|
+
when /[hsl]{3}/i
|
159
|
+
:hsl
|
160
|
+
when /[hwb]{3}/i
|
161
|
+
:hwb
|
162
|
+
else
|
163
|
+
:rgb
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
private :select_scheme
|
168
|
+
end
|
169
|
+
|
170
|
+
class CachingCompiler < CompareFunctionCompiler
|
171
|
+
def create_proc(order, color_order)
|
172
|
+
converter = select_converter(color_order)
|
173
|
+
cache = {}
|
174
|
+
|
175
|
+
proc do |color1, color2|
|
176
|
+
c1 = to_components(color1, converter, cache)
|
177
|
+
c2 = to_components(color2, converter, cache)
|
178
|
+
|
179
|
+
compare(c1, c2, order)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_components(color, converter, cache)
|
184
|
+
cached_components = cache[color]
|
185
|
+
return cached_components if cached_components
|
186
|
+
|
187
|
+
components = converter[color]
|
188
|
+
cache[color] = components
|
189
|
+
|
190
|
+
components
|
191
|
+
end
|
192
|
+
|
193
|
+
private :to_components
|
67
194
|
end
|
68
195
|
|
69
|
-
|
70
|
-
|
196
|
+
hex_to_components = {
|
197
|
+
# shorthands for Utils.hex_to_rgb() and .hex_to_hsl()
|
71
198
|
rgb: Utils.method(:hex_to_rgb),
|
72
|
-
hsl: Utils.method(:hex_to_hsl)
|
199
|
+
hsl: Utils.method(:hex_to_hsl),
|
200
|
+
hwb: Utils.method(:hex_to_hwb)
|
201
|
+
}
|
202
|
+
|
203
|
+
function_to_components = {
|
204
|
+
rgb: proc {|func| ColorContrastCalc.color_from(func).rgb },
|
205
|
+
hsl: proc {|func| ColorContrastCalc.color_from(func).hsl },
|
206
|
+
hwb: proc {|func| ColorContrastCalc.color_from(func).hwb }
|
207
|
+
}
|
208
|
+
|
209
|
+
color_to_components = {
|
210
|
+
rgb: proc {|color| color.rgb },
|
211
|
+
hsl: proc {|color| color.hsl },
|
212
|
+
hwb: proc {|color| color.hwb }
|
213
|
+
}
|
214
|
+
|
215
|
+
COMPARE_FUNCTION_COMPILERS = {
|
216
|
+
KeyTypes::COLOR => CompareFunctionCompiler.new(color_to_components),
|
217
|
+
KeyTypes::COMPONENTS => CompareFunctionCompiler.new,
|
218
|
+
KeyTypes::HEX => CachingCompiler.new(hex_to_components),
|
219
|
+
KeyTypes::FUNCTION => CachingCompiler.new(function_to_components)
|
73
220
|
}.freeze
|
74
221
|
|
75
222
|
##
|
@@ -114,14 +261,7 @@ module ColorContrastCalc
|
|
114
261
|
key_mapper = nil, &key_mapper_block)
|
115
262
|
key_mapper = key_mapper_block if !key_mapper && key_mapper_block
|
116
263
|
|
117
|
-
|
118
|
-
when KeyTypes::COLOR
|
119
|
-
compare = compile_color_compare_function(color_order)
|
120
|
-
when KeyTypes::COMPONENTS
|
121
|
-
compare = compile_components_compare_function(color_order)
|
122
|
-
when KeyTypes::HEX
|
123
|
-
compare = compile_hex_compare_function(color_order)
|
124
|
-
end
|
264
|
+
compare = COMPARE_FUNCTION_COMPILERS[key_type].compile(color_order)
|
125
265
|
|
126
266
|
compose_function(compare, key_mapper)
|
127
267
|
end
|
@@ -135,101 +275,5 @@ module ColorContrastCalc
|
|
135
275
|
compare_function[key_mapper[color1], key_mapper[color2]]
|
136
276
|
end
|
137
277
|
end
|
138
|
-
|
139
|
-
# @private
|
140
|
-
|
141
|
-
def self.color_component_pos(color_order, ordered_components)
|
142
|
-
color_order.downcase.chars.map do |component|
|
143
|
-
ordered_components.index(component)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# @private
|
148
|
-
|
149
|
-
def self.parse_color_order(color_order)
|
150
|
-
ordered_components = ColorComponent::RGB
|
151
|
-
ordered_components = ColorComponent::HSL if hsl_order?(color_order)
|
152
|
-
pos = color_component_pos(color_order, ordered_components)
|
153
|
-
funcs = []
|
154
|
-
pos.each_with_index do |ci, i|
|
155
|
-
c = color_order[i]
|
156
|
-
funcs[ci] = Utils.uppercase?(c) ? CompFunc::DESCEND : CompFunc::ASCEND
|
157
|
-
end
|
158
|
-
{ pos: pos, funcs: funcs }
|
159
|
-
end
|
160
|
-
|
161
|
-
# @private
|
162
|
-
|
163
|
-
def self.hsl_order?(color_order)
|
164
|
-
/[hsl]{3}/i.match?(color_order)
|
165
|
-
end
|
166
|
-
|
167
|
-
# @private
|
168
|
-
|
169
|
-
def self.compare_color_components(color1, color2, order)
|
170
|
-
funcs = order[:funcs]
|
171
|
-
order[:pos].each do |i|
|
172
|
-
result = funcs[i][color1[i], color2[i]]
|
173
|
-
return result unless result.zero?
|
174
|
-
end
|
175
|
-
|
176
|
-
0
|
177
|
-
end
|
178
|
-
|
179
|
-
# @private
|
180
|
-
|
181
|
-
def self.compile_components_compare_function(color_order)
|
182
|
-
order = parse_color_order(color_order)
|
183
|
-
|
184
|
-
proc do |color1, color2|
|
185
|
-
compare_color_components(color1, color2, order)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# @private
|
190
|
-
|
191
|
-
def self.compile_hex_compare_function(color_order)
|
192
|
-
order = parse_color_order(color_order)
|
193
|
-
converter = HEX_TO_COMPONENTS[:rgb]
|
194
|
-
converter = HEX_TO_COMPONENTS[:hsl] if hsl_order?(color_order)
|
195
|
-
cache = {}
|
196
|
-
|
197
|
-
proc do |hex1, hex2|
|
198
|
-
color1 = hex_to_components(hex1, converter, cache)
|
199
|
-
color2 = hex_to_components(hex2, converter, cache)
|
200
|
-
|
201
|
-
compare_color_components(color1, color2, order)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# @private
|
206
|
-
|
207
|
-
def self.hex_to_components(hex, converter, cache)
|
208
|
-
cached_components = cache[hex]
|
209
|
-
return cached_components if cached_components
|
210
|
-
|
211
|
-
components = converter[hex]
|
212
|
-
cache[hex] = components
|
213
|
-
|
214
|
-
components
|
215
|
-
end
|
216
|
-
|
217
|
-
private_class_method :hex_to_components
|
218
|
-
|
219
|
-
# @private
|
220
|
-
|
221
|
-
def self.compile_color_compare_function(color_order)
|
222
|
-
order = parse_color_order(color_order)
|
223
|
-
|
224
|
-
if hsl_order?(color_order)
|
225
|
-
proc do |color1, color2|
|
226
|
-
compare_color_components(color1.hsl, color2.hsl, order)
|
227
|
-
end
|
228
|
-
else
|
229
|
-
proc do |color1, color2|
|
230
|
-
compare_color_components(color1.rgb, color2.rgb, order)
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
278
|
end
|
235
279
|
end
|
@@ -12,6 +12,9 @@ module ColorContrastCalc
|
|
12
12
|
module Utils
|
13
13
|
using Shim unless //.respond_to? :match?
|
14
14
|
|
15
|
+
MIN_OPACITY = 0
|
16
|
+
MAX_OPACITY = 1.0
|
17
|
+
|
15
18
|
HSL_UPPER_LIMIT = [360, 100, 100].freeze
|
16
19
|
|
17
20
|
private_constant :HSL_UPPER_LIMIT
|
@@ -196,11 +199,9 @@ module ColorContrastCalc
|
|
196
199
|
# @return [true, false] true if a valid HSL value is passed
|
197
200
|
|
198
201
|
def self.valid_hsl?(hsl)
|
199
|
-
|
200
|
-
|
201
|
-
return false if !c.is_a?(Numeric) || c < 0 || c > HSL_UPPER_LIMIT[i]
|
202
|
+
hsl.length == 3 && hsl.each_with_index.all? do |c, i|
|
203
|
+
c.is_a?(Numeric) && c >= 0 && c <= HSL_UPPER_LIMIT[i]
|
202
204
|
end
|
203
|
-
true
|
204
205
|
end
|
205
206
|
|
206
207
|
##
|
@@ -238,10 +239,10 @@ module ColorContrastCalc
|
|
238
239
|
end
|
239
240
|
|
240
241
|
module Hwb
|
241
|
-
|
242
|
-
# ref: https://www.w3.org/TR/2019/WD-css-color-4-20191105/
|
242
|
+
HWB_UPPER_LIMIT = [360, 100, 100].freeze
|
243
243
|
|
244
244
|
def normalize_hwb(hwb)
|
245
|
+
# https://www.w3.org/TR/2019/WD-css-color-4-20191105/
|
245
246
|
h, w, b = hwb
|
246
247
|
|
247
248
|
achromatic_percent = w + b
|
@@ -255,6 +256,12 @@ module ColorContrastCalc
|
|
255
256
|
|
256
257
|
private :normalize_hwb
|
257
258
|
|
259
|
+
##
|
260
|
+
# Convert an HWB value to an RGB value.
|
261
|
+
#
|
262
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
263
|
+
# @return [Array<Integer>] RGB value represented as an array of integers
|
264
|
+
|
258
265
|
def hwb_to_rgb(hwb)
|
259
266
|
hue, white, black = normalize_hwb(hwb)
|
260
267
|
rgb = Utils.hsl_to_rgb([hue, 100, 50])
|
@@ -264,8 +271,51 @@ module ColorContrastCalc
|
|
264
271
|
end
|
265
272
|
end
|
266
273
|
|
267
|
-
|
268
|
-
|
274
|
+
##
|
275
|
+
# Convert an HWB value to hex color code.
|
276
|
+
#
|
277
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
278
|
+
# @return [String] Hex color code such as "#ffff00"
|
279
|
+
|
280
|
+
def hwb_to_hex(hwb)
|
281
|
+
rgb_to_hex(hwb_to_rgb(hwb))
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# Convert an RGB value to an HWB value.
|
286
|
+
#
|
287
|
+
# @param rgb [Array<Integer>] RGB value represented as an array of
|
288
|
+
# integers
|
289
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
290
|
+
|
291
|
+
def rgb_to_hwb(rgb)
|
292
|
+
# https://www.w3.org/TR/2020/WD-css-color-4-20201112/
|
293
|
+
hsl = Utils.rgb_to_hsl(rgb)
|
294
|
+
white = rgb.min
|
295
|
+
black = 255 - rgb.max
|
296
|
+
[hsl[0], white * 100 / 255.0, black * 100 / 255.0]
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# Convert hex color code to an HWB value.
|
301
|
+
#
|
302
|
+
# @param hex_code [String] Hex color code such as "#ffff00"
|
303
|
+
# @return [Array<Float>] HWB value represented as an array of numbers
|
304
|
+
|
305
|
+
def hex_to_hwb(hex_code)
|
306
|
+
rgb_to_hwb(Utils.hex_to_rgb(hex_code))
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# Check if a given array is a valid representation of HWB color.
|
311
|
+
#
|
312
|
+
# @param hwb [Array<Float>] HWB value represented as an array of numbers
|
313
|
+
# @return [true, false] true if a valid HWB value is passed
|
314
|
+
|
315
|
+
def valid_hwb?(hwb)
|
316
|
+
hwb.length == 3 && hwb.each_with_index.all? do |c, i|
|
317
|
+
c.is_a?(Numeric) && c >= 0 && c <= HWB_UPPER_LIMIT[i]
|
318
|
+
end
|
269
319
|
end
|
270
320
|
end
|
271
321
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: color_contrast_calc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HASHIMOTO, Naoki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.1'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,42 +44,42 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.10'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.10'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '1.7'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '1.7'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.9
|
75
|
+
version: '0.9'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.9
|
82
|
+
version: '0.9'
|
83
83
|
description:
|
84
84
|
email:
|
85
85
|
- hashimoto.naoki@gmail.com
|
@@ -135,15 +135,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '2.
|
138
|
+
version: '2.4'
|
139
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
143
|
version: '0'
|
144
144
|
requirements: []
|
145
|
-
|
146
|
-
rubygems_version: 2.7.6
|
145
|
+
rubygems_version: 3.1.4
|
147
146
|
signing_key:
|
148
147
|
specification_version: 4
|
149
148
|
summary: Utility that helps you choose colors with sufficient contrast, WCAG 2.0 in
|