From 0411f1584c049a2fe2f8a337009c3377e843bbfd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 14:42:40 -0800 Subject: [PATCH 01/25] Add color schema type --- package.json | 1 + spec/config-spec.coffee | 19 +++++++++++++++++++ src/config.coffee | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/package.json b/package.json index 990b1e626..10e69ff38 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "clear-cut": "0.4.0", "coffee-script": "1.8.0", "coffeestack": "0.8.0", + "color": "^0.7.3", "delegato": "^1", "emissary": "^1.3.1", "event-kit": "^1.0.1", diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 31e0756fc..ed3f3d3de 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1287,6 +1287,25 @@ describe "Config", -> atom.config.set 'foo.bar', ['2', '3', '4'] expect(atom.config.get('foo.bar')).toEqual [2, 3, 4] + describe 'when the value has a "color" type', -> + beforeEach -> + schema = + type: 'color' + default: true + atom.config.setSchema('foo.bar.aColor', schema) + + it 'coerces various types to a color object', -> + atom.config.set('foo.bar.aColor', 'red') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} + atom.config.set('foo.bar.aColor', '#020') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 34, blue: 0, alpha: 1} + atom.config.set('foo.bar.aColor', '#abcdef') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 171, green: 205, blue: 239, alpha: 1} + atom.config.set('foo.bar.aColor', 'rgb(1,2,3)') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 1, green: 2, blue: 3, alpha: 1} + atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 4, green: 5, blue: 6, alpha: .7} + describe 'when the `enum` key is used', -> beforeEach -> schema = diff --git a/src/config.coffee b/src/config.coffee index 0f6498e14..15c2b0aa7 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -5,6 +5,7 @@ EmitterMixin = require('emissary').Emitter CSON = require 'season' path = require 'path' async = require 'async' +Color = require 'color' pathWatcher = require 'pathwatcher' Grim = require 'grim' @@ -1103,6 +1104,18 @@ Config.addSchemaEnforcers else value + 'color': + coerce: (keyPath, value, schema) -> + try + color = new Color(value) + catch error + throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") + + red: color.red() + green: color.green() + blue: color.blue() + alpha: color.alpha() + '*': coerceMinimumAndMaximum: (keyPath, value, schema) -> return value unless typeof value is 'number' From 1267a1e14eb4a066dc6a3b618ec7ceaba2992bac Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 14:44:15 -0800 Subject: [PATCH 02/25] Add spec for default color value --- spec/config-spec.coffee | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index ed3f3d3de..8189f4eec 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1291,7 +1291,7 @@ describe "Config", -> beforeEach -> schema = type: 'color' - default: true + default: 'white' atom.config.setSchema('foo.bar.aColor', schema) it 'coerces various types to a color object', -> @@ -1306,6 +1306,13 @@ describe "Config", -> atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 4, green: 5, blue: 6, alpha: .7} + it 'reverts back to the default value when undefined is passed to set', -> + atom.config.set('foo.bar.aColor', 'rgb(255,255,255)') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + + atom.config.set('foo.bar.aColor', undefined) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + describe 'when the `enum` key is used', -> beforeEach -> schema = From 319ec244e16a652706f6ea83c1b30035d0ade3c0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 14:45:52 -0800 Subject: [PATCH 03/25] Add spec for invalid values --- spec/config-spec.coffee | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 8189f4eec..88e0535aa 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1313,6 +1313,13 @@ describe "Config", -> atom.config.set('foo.bar.aColor', undefined) expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + it 'will not set non-colors', -> + atom.config.set('foo.bar.aColor', null) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + + atom.config.set('foo.bar.aColor', 'nope') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + describe 'when the `enum` key is used', -> beforeEach -> schema = From ec96c5f1fc4745f252c6b85fd44cab298ddab0b2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 14:50:48 -0800 Subject: [PATCH 04/25] :memo: Add color docs --- src/config.coffee | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/config.coffee b/src/config.coffee index 15c2b0aa7..01b05ef53 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -217,6 +217,21 @@ ScopeDescriptor = require './scope-descriptor' # maximum: 11.5 # ``` # +# #### color +# +# Values will be coerced into an object with `red`, `green`, `blue`, and `alpha` +# properties that all have numeric values. `red`, `green`, `blue` will be in +# the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any +# valid CSS color format such as `#abc`, `#abcdef`, `white`, +# `rgb(50, 100, 150)`, and `rgba(25, 75, 125, .75)`. +# +# ```coffee +# config: +# someSetting: +# type: 'color' +# default: 'white' +# ``` +# # ### Other Supported Keys # # #### enum From 66e30dd38af99225e8176fb243f12667fa78e62b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 14:57:43 -0800 Subject: [PATCH 05/25] :arrow_up: legal-eagle@0.7 --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index 45abeacb4..e4b1488e7 100644 --- a/build/package.json +++ b/build/package.json @@ -24,7 +24,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "harmony-collections": "~0.3.8", - "legal-eagle": "~0.6.0", + "legal-eagle": "~0.7.0", "minidump": "~0.8", "npm": "~1.4.5", "rcedit": "~0.3.0", From 1609b6c51b78a9fb761a55e31dbc5eb0e776005e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 15:01:58 -0800 Subject: [PATCH 06/25] :arrow_up: legal-eagle@0.8 --- build/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package.json b/build/package.json index e4b1488e7..4cc5ac639 100644 --- a/build/package.json +++ b/build/package.json @@ -24,7 +24,7 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "harmony-collections": "~0.3.8", - "legal-eagle": "~0.7.0", + "legal-eagle": "~0.8.0", "minidump": "~0.8", "npm": "~1.4.5", "rcedit": "~0.3.0", From b923b57bcca52b5b8a15f30b7f97e3958e8f1177 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 15:34:35 -0800 Subject: [PATCH 07/25] Add specs for hsl and hsla colors --- spec/config-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 88e0535aa..acb9e1d62 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1305,6 +1305,10 @@ describe "Config", -> expect(atom.config.get('foo.bar.aColor')).toEqual {red: 1, green: 2, blue: 3, alpha: 1} atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 4, green: 5, blue: 6, alpha: .7} + atom.config.set('foo.bar.aColor', 'hsl(120,100%,50%)') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: 1} + atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)') + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: .3} it 'reverts back to the default value when undefined is passed to set', -> atom.config.set('foo.bar.aColor', 'rgb(255,255,255)') From c480080dd291ca6bb424f21d704739dba1b572b1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 15:50:03 -0800 Subject: [PATCH 08/25] Add fallback values when NaN is parsed --- src/config.coffee | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 01b05ef53..9d51b907d 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -1126,10 +1126,16 @@ Config.addSchemaEnforcers catch error throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") - red: color.red() - green: color.green() - blue: color.blue() - alpha: color.alpha() + value = + red: color.red() + green: color.green() + blue: color.blue() + alpha: color.alpha() + value.red = 0 if isNaN(value.red) + value.green = 0 if isNaN(value.green) + value.blue = 0 if isNaN(value.blue) + value.alpha = 1 if isNaN(value.alpha) + value '*': coerceMinimumAndMaximum: (keyPath, value, schema) -> From fc899f54cf8f14ad8026f60a53d3c84dee0124ff Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 15:50:10 -0800 Subject: [PATCH 09/25] Add specs for setting with an object --- spec/config-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index acb9e1d62..313605e57 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1309,6 +1309,10 @@ describe "Config", -> expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: 1} atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: .3} + atom.config.set('foo.bar.aColor', {red: 100, green: 255, blue: 2, alpha: .5}) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 100, green: 255, blue: 2, alpha: .5} + atom.config.set('foo.bar.aColor', {red: 255}) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} it 'reverts back to the default value when undefined is passed to set', -> atom.config.set('foo.bar.aColor', 'rgb(255,255,255)') From 5a2c2088045f49d9ced10d0e3751a452d3e6ab03 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 15:51:24 -0800 Subject: [PATCH 10/25] Add specs with invalid objects --- spec/config-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 313605e57..19026abd6 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1313,6 +1313,10 @@ describe "Config", -> expect(atom.config.get('foo.bar.aColor')).toEqual {red: 100, green: 255, blue: 2, alpha: .5} atom.config.set('foo.bar.aColor', {red: 255}) expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} + atom.config.set('foo.bar.aColor', {red: 1000}) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} + atom.config.set('foo.bar.aColor', {red: 'dark'}) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 0, blue: 0, alpha: 1} it 'reverts back to the default value when undefined is passed to set', -> atom.config.set('foo.bar.aColor', 'rgb(255,255,255)') From 8c3981867071ec453b2a219ae68d7b341bb62472 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 16:05:38 -0800 Subject: [PATCH 11/25] Don't blow up when asking for schema at non-existent path --- spec/config-spec.coffee | 3 +++ src/config.coffee | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 19026abd6..c60b88e24 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1060,6 +1060,9 @@ describe "Config", -> type: 'integer' default: 12 + expect(atom.config.getSchema('foo.baz')).toBeUndefined() + expect(atom.config.getSchema('foo.bar.anInt.baz')).toBeUndefined() + it "respects the schema for scoped settings", -> schema = type: 'string' diff --git a/src/config.coffee b/src/config.coffee index 9d51b907d..17c697930 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -703,7 +703,7 @@ class Config schema = @schema for key in keys break unless schema? - schema = schema.properties[key] + schema = schema.properties?[key] schema # Deprecated: Returns a new {Object} containing all of the global settings and From b8822b32db7f7a7700cce6f20d905e7e8cc13c45 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 17:11:18 -0800 Subject: [PATCH 12/25] Only coerce strings and plain objects --- spec/config-spec.coffee | 9 ++++++--- src/config.coffee | 8 +++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index c60b88e24..d6d082042 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1322,9 +1322,6 @@ describe "Config", -> expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 0, blue: 0, alpha: 1} it 'reverts back to the default value when undefined is passed to set', -> - atom.config.set('foo.bar.aColor', 'rgb(255,255,255)') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - atom.config.set('foo.bar.aColor', undefined) expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} @@ -1335,6 +1332,12 @@ describe "Config", -> atom.config.set('foo.bar.aColor', 'nope') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + atom.config.set('foo.bar.aColor', 30) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + + atom.config.set('foo.bar.aColor', false) + expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} + describe 'when the `enum` key is used', -> beforeEach -> schema = diff --git a/src/config.coffee b/src/config.coffee index 17c697930..e44fc4491 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -1121,9 +1121,11 @@ Config.addSchemaEnforcers 'color': coerce: (keyPath, value, schema) -> - try - color = new Color(value) - catch error + if isPlainObject(value) or typeof value is 'string' + try + color = new Color(value) + + unless color? throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") value = From 574c56713cc61f47f5f42de8cf3fd291f9a87a16 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 18:05:51 -0800 Subject: [PATCH 13/25] Add Color class with string helpers --- src/color.coffee | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/config.coffee | 19 +++---------------- 2 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 src/color.coffee diff --git a/src/color.coffee b/src/color.coffee new file mode 100644 index 000000000..7dfce7cc8 --- /dev/null +++ b/src/color.coffee @@ -0,0 +1,46 @@ +_ = require 'underscore-plus' +ParsedColor = require 'color' + +# A simple Color class returned from `atom.config.get` when the value at the +# key path is of type 'color'. +module.exports = +class Color + @parse: (value) -> + return null if _.isArray(value) + return null if _.isFunction(value) + return null unless _.isObject(value) or _.isString(value) + + try + parsedColor = new ParsedColor(value) + catch error + return null + + new Color(parsedColor) + + constructor: (color) -> + @red = color.red() + @green = color.green() + @blue = color.blue() + @alpha = color.alpha() + + @red = 0 if isNaN(@red) + @green = 0 if isNaN(@green) + @blue = 0 if isNaN(@blue) + @alpha = 1 if isNaN(@alpha) + + # Public: Returns a {String} in the form `'#abcdef'` + toHexString: -> + hexRed = if @red < 10 then "0#{@red.toString(16)}" else @red.toString(16) + hexGreen = if @green < 10 then "0#{@green.toString(16)}" else @green.toString(16) + hexBlue = if @blue < 10 then "0#{@blue.toString(16)}" else @blue.toString(16) + "##{hexRed}#{hexGreen}#{hexBlue}" + + # Public: Returns a {String} in the form `'rgba(25, 50, 75, .9)'` + toRGBAString: -> + "rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})" + + isEqual: (color) -> + return true if this is color + color = Color.parse(color) unless color instanceof Color + return false unless color? + color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha diff --git a/src/config.coffee b/src/config.coffee index e44fc4491..e09b5fc5b 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -5,7 +5,7 @@ EmitterMixin = require('emissary').Emitter CSON = require 'season' path = require 'path' async = require 'async' -Color = require 'color' +Color = require './color' pathWatcher = require 'pathwatcher' Grim = require 'grim' @@ -1121,23 +1121,10 @@ Config.addSchemaEnforcers 'color': coerce: (keyPath, value, schema) -> - if isPlainObject(value) or typeof value is 'string' - try - color = new Color(value) - + color = Color.parse(value) unless color? throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") - - value = - red: color.red() - green: color.green() - blue: color.blue() - alpha: color.alpha() - value.red = 0 if isNaN(value.red) - value.green = 0 if isNaN(value.green) - value.blue = 0 if isNaN(value.blue) - value.alpha = 1 if isNaN(value.alpha) - value + color '*': coerceMinimumAndMaximum: (keyPath, value, schema) -> From b256d30bc7f32a109031b4e1cdebf4b10511b6aa Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 8 Jan 2015 18:06:57 -0800 Subject: [PATCH 14/25] Mark Color as public --- src/color.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index 7dfce7cc8..86fff11dc 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -1,8 +1,8 @@ _ = require 'underscore-plus' ParsedColor = require 'color' -# A simple Color class returned from `atom.config.get` when the value at the -# key path is of type 'color'. +# Public: A simple Color class returned from `atom.config.get` when the value at +# the key path is of type 'color'. module.exports = class Color @parse: (value) -> From 44939a12a0e335e8b158bd0ff0420a4199cd8ad1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 09:59:54 -0800 Subject: [PATCH 15/25] Validate color properties when set --- spec/config-spec.coffee | 13 +++++++++ src/color.coffee | 63 ++++++++++++++++++++++++++++++++--------- src/config.coffee | 12 ++++++-- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index d6d082042..86e57b344 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1297,6 +1297,19 @@ describe "Config", -> default: 'white' atom.config.setSchema('foo.bar.aColor', schema) + it 'returns a Color object', -> + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe '#ffffff' + expect(color.toRGBAString()).toBe 'rgba(255, 255, 255, 1)' + + color.red = 0 + color.green = 0 + color.blue = 0 + color.alpha = 0 + atom.config.set('foo.bar.aColor', color) + expect(color.toHexString()).toBe '#000000' + expect(color.toRGBAString()).toBe 'rgba(0, 0, 0, 0)' + it 'coerces various types to a color object', -> atom.config.set('foo.bar.aColor', 'red') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} diff --git a/src/color.coffee b/src/color.coffee index 86fff11dc..c71462e78 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -15,25 +15,39 @@ class Color catch error return null - new Color(parsedColor) + new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha()) - constructor: (color) -> - @red = color.red() - @green = color.green() - @blue = color.blue() - @alpha = color.alpha() + constructor: (red, green, blue, alpha) -> + red = parseColor(red) + green = parseColor(green) + blue = parseColor(blue) + alpha = parseColor(alpha) - @red = 0 if isNaN(@red) - @green = 0 if isNaN(@green) - @blue = 0 if isNaN(@blue) - @alpha = 1 if isNaN(@alpha) + Object.defineProperties this, + 'red': + set: (newRed) -> red = parseColor(newRed) + get: -> red + enumerable: true + configurable: false + 'green': + set: (newGreen) -> green = parseColor(newGreen) + get: -> green + enumerable: true + configurable: false + 'blue': + set: (newBlue) -> blue = parseColor(newBlue) + get: -> blue + enumerable: true + configurable: false + 'alpha': + set: (newAlpha) -> alpha = parseAlpha(newAlpha) + get: -> alpha + enumerable: true + configurable: false # Public: Returns a {String} in the form `'#abcdef'` toHexString: -> - hexRed = if @red < 10 then "0#{@red.toString(16)}" else @red.toString(16) - hexGreen = if @green < 10 then "0#{@green.toString(16)}" else @green.toString(16) - hexBlue = if @blue < 10 then "0#{@blue.toString(16)}" else @blue.toString(16) - "##{hexRed}#{hexGreen}#{hexBlue}" + "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" # Public: Returns a {String} in the form `'rgba(25, 50, 75, .9)'` toRGBAString: -> @@ -44,3 +58,24 @@ class Color color = Color.parse(color) unless color instanceof Color return false unless color? color.red is @red and color.blue is @blue and color.green is @green and color.alpha is @alpha + + clone: -> new Color(@red, @green, @blue, @alpha) + +parseColor = (color) -> + color = parseInt(color) + color = 0 if isNaN(color) + color = Math.max(color, 0) + color = Math.min(color, 255) + color + +parseAlpha = (alpha) -> + alpha = parseFloat(alpha) + alpha = 1 if isNaN(alpha) + alpha = Math.max(alpha, 1) + alpha = Math.min(alpha, 0) + alpha + +numberToHexString = (number) -> + hex = number.toString(16) + hex = "0#{hex}" if number < 10 + hex diff --git a/src/config.coffee b/src/config.coffee index e09b5fc5b..60f51e4fd 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -888,10 +888,16 @@ class Config defaultValue = _.valueForKeyPath(@defaultSettings, keyPath) if value? - value = _.deepClone(value) - _.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue) + if value instanceof Color + value = value.clone() + else + value = _.deepClone(value) + _.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue) else - value = _.deepClone(defaultValue) + if defaultValue instanceof Color + value = defaultValue.clone() + else + value = _.deepClone(defaultValue) value From f48a7ac210c22e06f2f00d11cde224ea27085254 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 10:03:10 -0800 Subject: [PATCH 16/25] Mark methods as essential --- src/color.coffee | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index c71462e78..53f81f9dd 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -1,10 +1,17 @@ _ = require 'underscore-plus' ParsedColor = require 'color' -# Public: A simple Color class returned from `atom.config.get` when the value at -# the key path is of type 'color'. +# Essential: A simple Color class returned from `atom.config.get` when the value +# at the key path is of type 'color'. module.exports = class Color + # Essential: Parse a {String} or {Object} into a {Color} + # + # * `value` - A {String} such as `'white'`, `#ff00ff`, or + # `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, + # `blue`, and `alpha` properties. + # + # Returns a {Color} or `null` if it cannot be parsed. @parse: (value) -> return null if _.isArray(value) return null if _.isFunction(value) @@ -45,11 +52,11 @@ class Color enumerable: true configurable: false - # Public: Returns a {String} in the form `'#abcdef'` + # Esssential: Returns a {String} in the form `'#abcdef'` toHexString: -> "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" - # Public: Returns a {String} in the form `'rgba(25, 50, 75, .9)'` + # Esssential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'` toRGBAString: -> "rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})" From 88f489715f8c4abe739aa78bd35fcfea67af38fe Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 10:03:32 -0800 Subject: [PATCH 17/25] :memo: Add period --- src/color.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/color.coffee b/src/color.coffee index 53f81f9dd..52b08c0a4 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -5,7 +5,7 @@ ParsedColor = require 'color' # at the key path is of type 'color'. module.exports = class Color - # Essential: Parse a {String} or {Object} into a {Color} + # Essential: Parse a {String} or {Object} into a {Color}. # # * `value` - A {String} such as `'white'`, `#ff00ff`, or # `'rgba(255, 15, 60, .75)'` or an {Object} with `red`, `green`, From c015a5c03b5f8977c6cf7deef84138cdab82b64b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 10:03:40 -0800 Subject: [PATCH 18/25] :memo: Add period --- src/color.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index 52b08c0a4..6400f6b96 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -52,11 +52,11 @@ class Color enumerable: true configurable: false - # Esssential: Returns a {String} in the form `'#abcdef'` + # Esssential: Returns a {String} in the form `'#abcdef'`. toHexString: -> "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" - # Esssential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'` + # Esssential: Returns a {String} in the form `'rgba(25, 50, 75, .9)'`. toRGBAString: -> "rgba(#{@red}, #{@green}, #{@blue}, #{@alpha})" From 640f8920fbc869ca9c1fecb9769a8e5e9233e8fe Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:22:39 -0800 Subject: [PATCH 19/25] Fix bounds checking of alpha --- spec/config-spec.coffee | 12 ++++++++++++ src/color.coffee | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 86e57b344..c2a6f304f 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1307,9 +1307,21 @@ describe "Config", -> color.blue = 0 color.alpha = 0 atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') expect(color.toHexString()).toBe '#000000' expect(color.toRGBAString()).toBe 'rgba(0, 0, 0, 0)' + color.red = 300 + color.green = -200 + color.blue = -1 + color.alpha = 'not see through' + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe '#ff0000' + expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)' + it 'coerces various types to a color object', -> atom.config.set('foo.bar.aColor', 'red') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} diff --git a/src/color.coffee b/src/color.coffee index 6400f6b96..9bdb6fc06 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -78,8 +78,8 @@ parseColor = (color) -> parseAlpha = (alpha) -> alpha = parseFloat(alpha) alpha = 1 if isNaN(alpha) - alpha = Math.max(alpha, 1) - alpha = Math.min(alpha, 0) + alpha = Math.max(alpha, 0) + alpha = Math.min(alpha, 1) alpha numberToHexString = (number) -> From 3f224716a1db734b7606c91b9e85eb48f7717164 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:24:08 -0800 Subject: [PATCH 20/25] :lipstick: --- src/color.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index 9bdb6fc06..5c3c5d0ec 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -31,22 +31,22 @@ class Color alpha = parseColor(alpha) Object.defineProperties this, - 'red': + red: set: (newRed) -> red = parseColor(newRed) get: -> red enumerable: true configurable: false - 'green': + green: set: (newGreen) -> green = parseColor(newGreen) get: -> green enumerable: true configurable: false - 'blue': + blue: set: (newBlue) -> blue = parseColor(newBlue) get: -> blue enumerable: true configurable: false - 'alpha': + alpha: set: (newAlpha) -> alpha = parseAlpha(newAlpha) get: -> alpha enumerable: true From e257395f7d871609693eff80c9d396676d0393ce Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:25:59 -0800 Subject: [PATCH 21/25] Always use settings for parsing --- src/color.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index 5c3c5d0ec..fdf339920 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -25,11 +25,6 @@ class Color new Color(parsedColor.red(), parsedColor.green(), parsedColor.blue(), parsedColor.alpha()) constructor: (red, green, blue, alpha) -> - red = parseColor(red) - green = parseColor(green) - blue = parseColor(blue) - alpha = parseColor(alpha) - Object.defineProperties this, red: set: (newRed) -> red = parseColor(newRed) @@ -52,6 +47,11 @@ class Color enumerable: true configurable: false + @red = red + @green = green + @blue = blue + @alpha = alpha + # Esssential: Returns a {String} in the form `'#abcdef'`. toHexString: -> "##{numberToHexString(@red)}#{numberToHexString(@green)}#{numberToHexString(@blue)}" From 432c31c4dceae6bb679761e93bd51cd6378397ec Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:30:21 -0800 Subject: [PATCH 22/25] :lipstick: --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index 60f51e4fd..f4b96b434 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -5,10 +5,10 @@ EmitterMixin = require('emissary').Emitter CSON = require 'season' path = require 'path' async = require 'async' -Color = require './color' pathWatcher = require 'pathwatcher' Grim = require 'grim' +Color = require './color' ScopedPropertyStore = require 'scoped-property-store' ScopeDescriptor = require './scope-descriptor' From 9d3e9d19e1df0cd1de7e1364d20e117835f76023 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:55:30 -0800 Subject: [PATCH 23/25] :memo: Link to Color --- src/config.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index f4b96b434..721c522ad 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -219,7 +219,7 @@ ScopeDescriptor = require './scope-descriptor' # # #### color # -# Values will be coerced into an object with `red`, `green`, `blue`, and `alpha` +# Values will be coerced into a {Color} with `red`, `green`, `blue`, and `alpha` # properties that all have numeric values. `red`, `green`, `blue` will be in # the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any # valid CSS color format such as `#abc`, `#abcdef`, `white`, From 55c8b3739588eb1d8a4361c706ecf0aecc4da774 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:57:47 -0800 Subject: [PATCH 24/25] :memo: Link to config method --- src/color.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/color.coffee b/src/color.coffee index fdf339920..8af61603c 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -1,7 +1,7 @@ _ = require 'underscore-plus' ParsedColor = require 'color' -# Essential: A simple Color class returned from `atom.config.get` when the value +# Essential: A simple color class returned from {Config::get} when the value # at the key path is of type 'color'. module.exports = class Color From 1f3708115ee5135942be5979e22b712df61fb387 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 9 Jan 2015 11:59:10 -0800 Subject: [PATCH 25/25] :lipstick: --- src/color.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/color.coffee b/src/color.coffee index 8af61603c..3dffabb48 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -13,8 +13,7 @@ class Color # # Returns a {Color} or `null` if it cannot be parsed. @parse: (value) -> - return null if _.isArray(value) - return null if _.isFunction(value) + return null if _.isArray(value) or _.isFunction(value) return null unless _.isObject(value) or _.isString(value) try