Skip to content

Instantly share code, notes, and snippets.

@jarek-foksa
Created August 25, 2013 21:44
Show Gist options
  • Select an option

  • Save jarek-foksa/6336503 to your computer and use it in GitHub Desktop.

Select an option

Save jarek-foksa/6336503 to your computer and use it in GitHub Desktop.
# @copyright
# © 2012-2013 Jarosław Foksa
#
# @info
# Prototype object representing CSS color, based on http://www.w3.org/TR/css3-color/ and
# https://developer.mozilla.org/en/CSS/color_value
#
# rgb - 'rgb(255, 255, 255)'
# rgba - 'rgba(255, 255, 255, 1)'
# hsl - 'hsl(360, 100%, 100%)'
# hsla - 'hsla(360, 100%, 100%, 1)'
# hex - '#ffffff'
# name - 'white'
#
# @usage
# fillColor = color.clone().init r: 0, g: 255, b: 0, a: 1
# fillColor.toString 'hsla'
# > "hsl(120, 100%, 50%)"
#
# strokeColor = color.clone().init name: 'yellow'
# fill.toString 'hex'
# > "#ffff00"
#
# transparentStrokeColor = strokeColor.copy().init a: 0.3
# transparentStrokeColor.toString()
# > "rgba(255, 255, 0, 0.3)"
{hslToRgb, rgbToHsl} = imports 'utils/color'
{normalize, round} = imports 'utils/math'
{isNumeric} = imports 'utils/type'
{namedColors} = imports 'spec'
exports color =
r: 0 # red, 0 ~ 255
g: 0 # green, 0 ~ 255
b: 0 # blue, 0 ~ 255
h: 0 # hue, 0 ~ 360
s: 0 # saturation, 0.0 - 100.0
l: 0 # lightness, 0.0 - 100.0
a: 1 # alpha, 0.00 - 1.00
init: (desc = {}) ->
for key of desc
@[key] = desc[key]
if desc.r || desc.g || desc.b
@_syncHslValues()
else if desc.h || desc.s || desc.l
@_syncRgbValues()
return @
# Accepts any color format from CSS3
initWithString: (string) ->
if string.startsWith 'rgba('
if string.contains '%'
@_initWithRgbaPercentsString string
else
@_initWithRgbaString string
else if string.startsWith 'rgb('
if string.contains '%'
@_initWithRgbPercentsString string
else
@_initWithRgbString string
else if string.startsWith '#'
@_initWithHexString string
else if string.startsWith 'hsla('
@_initWithHslaString string
else if string.startsWith 'hsl('
@_initWithHslString string
else
@_initWithNameString string
return @
toString: (format = 'rgba') ->
if format == 'rgba'
return @_toRgbaString()
else if format == 'rgb'
return @_toRgbString()
else if format == 'rgbaPercents'
return @_toRgbaPercentsString()
else if format == 'rgbPercents'
return @_toRgbPercentsString()
else if format == 'hsla'
return @_toHslaString()
else if format == 'hsl'
return @_toHslString()
else if format == 'hex'
return @_toHexString()
else if format == 'name'
return @_toNameString()
else
console.error 'Invalid string format name:', format
return null
copy: ->
copy = color.clone()
for key in ['r', 'g', 'b', 'h', 's', 'l', 'a']
copy[key] = @[key]
return copy
@a = normalize a, 0, 1, 2
return @
setA: (a) ->
@a = normalize a, 0, 1, 2
return @
setR: (r) ->
@r = normalize r, 0, 255, 0
@_syncHslValues()
return @
setG: (g) ->
@g = normalize g, 0, 255, 0
@_syncHslValues()
return @
setB: (b) ->
@b = normalize b, 0, 255, 0
@_syncHslValues()
return @
setH: (h) ->
@h = normalize h, 0, 360, 0
@_syncRgbValues()
return @
setS: (s) ->
@s = normalize s, 0, 100, 1
@_syncRgbValues()
return @
setL: (l) ->
@l = normalize l, 0, 100, 1
@_syncRgbValues()
return @
setRPercent: (rPercentage) ->
@r = normalize ((rPercentage/100) * 255), 0, 100, 0
@_syncHslValues()
return @
setGPercent: (gPercentage) ->
@g = normalize ((gPercentage/100) * 255), 0, 100, 0
@_syncHslValues()
return @
setBPercent: (bPercentage) ->
@b = normalize ((bPercentage/100) * 255), 0, 100, 0
@_syncHslValues()
return @
getRPercent: ->
return round ((@r/255) * 100), 1
getGPercent: ->
return round ((@g/255) * 100), 1
getBPercent: ->
@_syncRgbValues() if @b == null
return round ((@b/255) * 100), 1
_toRgbaString: ->
string = "rgba(#{@r}, #{@g}, #{@b}, #{@a})"
return string
_toRgbString: ->
string = "rgb(#{@r}, #{@g}, #{@b})"
return string
_toRgbaPercentsString: ->
string = "rgba(#{@getRPercent()}%, #{@getGPercent()}%, #{@getBPercent()}%, #{@a})"
return string
_toRgbPercentsString: ->
string = "rgb(#{@getRPercent()}%, #{@getGPercent()}%, #{@getBPercent()}%)"
return string
_toHslaString: ->
string = "hsla(#{@h}, #{@s}%, #{@l}%, #{@a})"
return string
_toHslString: ->
string = "hsl(#{@h}, #{@s}%, #{@l}%)"
return string
_toHexString: ->
hexRed = @r.toString 16
hexGreen = @g.toString 16
hexBlue = @b.toString 16
if hexRed.length == 1
hexRed = '0' + hexRed
if hexGreen.length == 1
hexGreen = '0' + hexGreen
if hexBlue.length == 1
hexBlue = '0' + hexBlue
hex = '#' + hexRed + hexGreen + hexBlue
return hex
_toNameString: ->
name = null
for key, value of namedColors
if value[0] == @r
if value[1] == @g
if value[2] == @b
name = key
break
return name
_initWithRgbString: (rgbString) ->
values = @_extractColorFunctionArgs rgbString, 'rgb'
error = "Invalid rgb() value assigned: '#{rgbString}'"
if values == null
console.error error
else if values.length == 3 && isNumeric(values[0]) && isNumeric(values[1]) && isNumeric(values[2])
@setR parseFloat(values[0])
@setG parseFloat(values[1])
@setB parseFloat(values[2])
@setA 1
@_syncHslValues()
else
console.error error
return @
_initWithRgbaString: (rgbaString) ->
values = @_extractColorFunctionArgs rgbaString, 'rgba'
error = "Invalid rgba() value assigned: '#{rgbaString}'"
if values == null
console.error error
else if (values.length == 4 && isNumeric(values[0]) && isNumeric(values[1]) &&
isNumeric(values[2]) && isNumeric(values[3]))
@setR parseFloat values[0]
@setG parseFloat values[1]
@setB parseFloat values[2]
@setA parseFloat values[3]
@_syncHslValues()
else
console.error error
return @
_initWithRgbPercentsString: (rgbString) ->
values = @_extractColorFunctionArgs rgbString, 'rgb'
error = "Invalid rgb() value assigned: '#{rgbString}'"
if values == null
console.error error
else if values.length == 3 && values[0].contains('%') && values[1].contains('%') && values[2].contains('%')
values[0] = values[0].replace '%', ''
values[1] = values[1].replace '%', ''
values[2] = values[2].replace '%', ''
if isNumeric(values[0]) && isNumeric(values[1]) && isNumeric(values[2])
@setRPercent parseFloat values[0]
@setGPercent parseFloat values[1]
@setBPercent parseFloat values[2]
@setA 1
@_syncHslValues()
else
console.error error
else
console.error error
return @
_initWithRgbaPercentsString: (rgbaString) ->
values = @_extractColorFunctionArgs rgbaString, 'rgba'
error = "Invalid rgba() value assigned: '#{rgbaString}'"
if values == null
console.error error
else if (values.length == 4 && values[0].contains('%') && values[1].contains('%') &&
values[2].contains('%') && isNumeric(values[3]))
values[0] = values[0].replace '%', ''
values[1] = values[1].replace '%', ''
values[2] = values[2].replace '%', ''
if isNumeric(values[0]) && isNumeric(values[1]) && isNumeric(values[2])
@setRPercent parseFloat values[0]
@setGPercent parseFloat values[1]
@setBPercent parseFloat values[2]
@setA parseFloat values[3]
@_syncHslValues()
else
console.error error
else
console.error error
return @
_initWithHslString: (hslString) ->
values = @_extractColorFunctionArgs hslString, 'hsl'
error = "Invalid hsl() value assigned: '#{hslString}'"
if values == null
console.error error
else if values.length == 3 && isNumeric(values[0]) && values[1].contains('%') && values[2].contains('%')
values[1] = values[1].replace '%', ''
values[2] = values[2].replace '%', ''
if isNumeric(values[1]) && isNumeric(values[2])
@setH parseFloat values[0]
@setS parseFloat values[1]
@setL parseFloat values[2]
@setA 1
@_syncRgbValues()
else
console.error error
else
console.error error
return null
_initWithHslaString: (hslaString) ->
values = @_extractColorFunctionArgs hslaString, 'hsla'
error = "Invalid hsl() value assigned: '#{hslaString}'"
if values == null
console.error error
else if (values.length == 4 && isNumeric(values[0]) && values[1].contains('%') &&
values[2].contains('%') && isNumeric(values[3]))
values[1] = values[1].replace '%', ''
values[2] = values[2].replace '%', ''
if isNumeric(values[1]) && isNumeric(values[2])
@setH parseFloat values[0]
@setS parseFloat values[1]
@setL parseFloat values[2]
@setA parseFloat values[3]
@_syncRgbValues()
else
console.error error
else
console.error error
return @
_initWithHexString: (hexString) ->
hexString = hexString.toLowerCase()
hexString = hexString.substring(1) # get rid of '#'
hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
for digit in hexString
if digit not in hexDigits
console.error 'color._initWithHexString(): Invalid hex digit ' + digit
return null
if hexString.length == 3
hexRed = hexString[0] + hexString[0]
hexGreen = hexString[1] + hexString[1]
hexBlue = hexString[2] + hexString[2]
else
hexRed = hexString[0] + hexString[1]
hexGreen = hexString[2] + hexString[3]
hexBlue = hexString[4] + hexString[5]
@setR parseInt hexRed, 16
@setG parseInt hexGreen, 16
@setB parseInt hexBlue, 16
@_syncHslValues()
return @
_initWithNameString: (name) ->
if namedColors[name]
@r = namedColors[name][0]
@g = namedColors[name][1]
@b = namedColors[name][2]
@a = 1
@_syncHslValues()
else
console.error "Invalid color string: '#{name}'"
return @
_extractColorFunctionArgs: (colorFunctionString, colorFunctionName = 'rgb') ->
normalizedString = colorFunctionString.replace /\s+/g, ''
normalizedString = normalizedString.toLowerCase()
if normalizedString.startsWith(colorFunctionName + '(') == false || normalizedString.endsWith(')') == false
return null
argsString = normalizedString.between colorFunctionName + '(', ')'
args = argsString.split ','
return args
_syncRgbValues: ->
rgb = hslToRgb @h, @s, @l
@r = rgb[0]
@g = rgb[1]
@b = rgb[2]
_syncHslValues: ->
hsl = rgbToHsl @r, @g, @b
@h = hsl[0]
@s = hsl[1]
@l = hsl[2]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment