Comparing version 0.3.0 to 0.4.0
628
color.js
@@ -5,6 +5,8 @@ /* MIT license */ | ||
module.exports = Color; | ||
module.exports = function(cssString) { | ||
return new Color(cssString); | ||
}; | ||
function Color(cssString) { | ||
var values = { | ||
var Color = function(cssString) { | ||
this.values = { | ||
rgb: [0, 0, 0], | ||
@@ -15,72 +17,4 @@ hsl: [0, 0, 0], | ||
alpha: 1 | ||
}; | ||
function getValues(space) { | ||
var vals = {}; | ||
for (var i = 0; i < space.length; i++) { | ||
vals[space[i]] = values[space][i]; | ||
} | ||
if (values.alpha != 1) { | ||
vals["a"] = values.alpha; | ||
} | ||
// {r: 255, g: 255, b: 255, a: 0.4} | ||
return vals; | ||
} | ||
function setValues(space, vals) { | ||
var spaces = { | ||
"rgb": ["red", "green", "blue"], | ||
"hsl": ["hue", "saturation", "lightness"], | ||
"hsv": ["hue", "saturation", "value"], | ||
"cmyk": ["cyan", "magenta", "yellow", "black"] | ||
} | ||
var maxes = { | ||
"rgb": [255, 255, 255], | ||
"hsl": [360, 100, 100], | ||
"hsv": [360, 100, 100], | ||
"cmyk": [100, 100, 100, 100], | ||
} | ||
var alpha = 1; | ||
if (space == "alpha") { | ||
alpha = vals; | ||
} | ||
else if (vals.length) { | ||
// [10, 10, 10] | ||
values[space] = vals.slice(0, space.length); | ||
alpha = vals[space.length]; | ||
} | ||
else if (vals[space[0]] !== undefined) { | ||
// {r: 10, g: 10, b: 10} | ||
for (var i = 0; i < space.length; i++) { | ||
values[space][i] = vals[space[i]]; | ||
} | ||
alpha = vals.a; | ||
} | ||
else if (vals[spaces[space][0]] !== undefined) { | ||
// {red: 10, green: 10, blue: 10} | ||
var chans = spaces[space]; | ||
for (var i = 0; i < space.length; i++) { | ||
values[space][i] = vals[chans[i]]; | ||
} | ||
alpha = vals.alpha; | ||
} | ||
values.alpha = Math.max(0, Math.min(1, alpha || values.alpha)); | ||
if (space == "alpha") { | ||
return; | ||
} | ||
// convert to all the other color spaces | ||
for (var sname in spaces) { | ||
if (sname != space) { | ||
values[sname] = convert[space][sname](values[space]) | ||
} | ||
// cap values | ||
for (var i = 0; i < sname.length; i++) { | ||
values[sname][i] = Math.round(Math.max(0, Math.min(maxes[sname][i], values[sname][i]))); | ||
} | ||
} | ||
} | ||
} | ||
// parse Color() argument | ||
@@ -90,6 +24,6 @@ if (typeof cssString == "string") { | ||
if (vals) { | ||
setValues("rgb", vals); | ||
this.setValues("rgb", vals); | ||
} | ||
else if(vals = string.getHsla(cssString)) { | ||
setValues("hsl", vals); | ||
this.setValues("hsl", vals); | ||
} | ||
@@ -100,263 +34,337 @@ } | ||
if(vals["r"] !== undefined || vals["red"] !== undefined) { | ||
setValues("rgb", vals) | ||
this.setValues("rgb", vals) | ||
} | ||
else if(vals["l"] !== undefined || vals["lightness"] !== undefined) { | ||
setValues("hsl", vals) | ||
this.setValues("hsl", vals) | ||
} | ||
else if(vals["v"] !== undefined || vals["value"] !== undefined) { | ||
setValues("hsv", vals) | ||
this.setValues("hsv", vals) | ||
} | ||
else if(vals["c"] !== undefined || vals["cyan"] !== undefined) { | ||
setValues("cmyk", vals) | ||
this.setValues("cmyk", vals) | ||
} | ||
} | ||
} | ||
Color.prototype = { | ||
rgb: function (vals) { | ||
return this.setSpace("rgb", arguments); | ||
}, | ||
hsl: function(vals) { | ||
return this.setSpace("hsl", arguments); | ||
}, | ||
hsv: function(vals) { | ||
return this.setSpace("hsv", arguments); | ||
}, | ||
cmyk: function(vals) { | ||
return this.setSpace("cmyk", arguments); | ||
}, | ||
function setSpace(space, args) { | ||
var vals = args[0]; | ||
if (vals === undefined) { | ||
// color.rgb() | ||
return getValues(space); | ||
rgbArray: function() { | ||
return this.values.rgb; | ||
}, | ||
hslArray: function() { | ||
return this.values.hsl; | ||
}, | ||
hsvArray: function() { | ||
return this.values.hsv; | ||
}, | ||
cmykArray: function() { | ||
return this.values.cmyk; | ||
}, | ||
rgbaArray: function() { | ||
var rgb = this.values.rgb; | ||
rgb.push(this.values.alpha); | ||
return rgb; | ||
}, | ||
hslaArray: function() { | ||
var hsl = this.values.hsl; | ||
hsl.push(this.values.alpha); | ||
return hsl; | ||
}, | ||
alpha: function(val) { | ||
if (val === undefined) { | ||
return this.values.alpha; | ||
} | ||
// color.rgb(10, 10, 10) | ||
if (typeof vals == "number") { | ||
vals = Array.prototype.slice.call(args); | ||
this.setValues("alpha", val); | ||
return this; | ||
}, | ||
red: function(val) { | ||
return this.setChannel("rgb", 0, val); | ||
}, | ||
green: function(val) { | ||
return this.setChannel("rgb", 1, val); | ||
}, | ||
blue: function(val) { | ||
return this.setChannel("rgb", 2, val); | ||
}, | ||
hue: function(val) { | ||
return this.setChannel("hsl", 0, val); | ||
}, | ||
saturation: function(val) { | ||
return this.setChannel("hsl", 1, val); | ||
}, | ||
lightness: function(val) { | ||
return this.setChannel("hsl", 2, val); | ||
}, | ||
saturationv: function(val) { | ||
return this.setChannel("hsv", 1, val); | ||
}, | ||
value: function(val) { | ||
return this.setChannel("hsv", 2, val); | ||
}, | ||
cyan: function(val) { | ||
return this.setChannel("cmyk", 0, val); | ||
}, | ||
magenta: function(val) { | ||
return this.setChannel("cmyk", 1, val); | ||
}, | ||
yellow: function(val) { | ||
return this.setChannel("cmyk", 2, val); | ||
}, | ||
black: function(val) { | ||
return this.setChannel("cmyk", 3, val); | ||
}, | ||
hexString: function() { | ||
return string.hexString(this.values.rgb); | ||
}, | ||
rgbString: function() { | ||
return string.rgbString(this.values.rgb, this.values.alpha); | ||
}, | ||
rgbaString: function() { | ||
return string.rgbaString(this.values.rgb, this.values.alpha); | ||
}, | ||
percentString: function() { | ||
return string.percentString(this.values.rgb, this.values.alpha); | ||
}, | ||
hslString: function() { | ||
return string.hslString(this.values.hsl, this.values.alpha); | ||
}, | ||
hslaString: function() { | ||
return string.hslaString(this.values.hsl, this.values.alpha); | ||
}, | ||
keyword: function() { | ||
return string.keyword(this.values.rgb, this.values.alpha); | ||
}, | ||
luminosity: function() { | ||
// http://www.w3.org/TR/WCAG20/#relativeluminancedef | ||
var rgb = this.values.rgb; | ||
for (var i = 0; i < rgb.length; i++) { | ||
var chan = rgb[i] / 255; | ||
rgb[i] = (chan <= 0.03928) ? chan / 12.92 | ||
: Math.pow(((chan + 0.055) / 1.055), 2.4) | ||
} | ||
setValues(space, vals); | ||
return color; | ||
} | ||
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]; | ||
}, | ||
function setChannel(space, index, val) { | ||
if (val === undefined) { | ||
// color.red() | ||
return values[space][index]; | ||
contrast: function(color2) { | ||
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef | ||
var lum1 = this.luminosity(); | ||
var lum2 = color2.luminosity(); | ||
if (lum1 > lum2) { | ||
return (lum1 + 0.05) / (lum2 + 0.05) | ||
}; | ||
return (lum2 + 0.05) / (lum1 + 0.05); | ||
}, | ||
dark: function() { | ||
// YIQ equation from http://24ways.org/2010/calculating-color-contrast | ||
var rgb = this.values.rgb, | ||
yiq = rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114 / 1000; | ||
return yiq < 128; | ||
}, | ||
light: function() { | ||
return !this.dark(); | ||
}, | ||
negate: function() { | ||
var rgb = [] | ||
for (var i = 0; i < 3; i++) { | ||
rgb[i] = 255 - this.values.rgb[i]; | ||
} | ||
// color.red(100) | ||
values[space][index] = val; | ||
setValues(space, values[space]); | ||
return color; | ||
} | ||
this.setValues("rgb", rgb); | ||
return this; | ||
}, | ||
var color = { | ||
rgb: function (vals) { | ||
return setSpace("rgb", arguments); | ||
}, | ||
hsl: function(vals) { | ||
return setSpace("hsl", arguments); | ||
}, | ||
hsv: function(vals) { | ||
return setSpace("hsv", arguments); | ||
}, | ||
cmyk: function(vals) { | ||
return setSpace("cmyk", arguments); | ||
}, | ||
rgbArray: function() { | ||
return values.rgb; | ||
}, | ||
hslArray: function() { | ||
return values.hsl; | ||
}, | ||
hsvArray: function() { | ||
return values.hsv; | ||
}, | ||
cmykArray: function() { | ||
return values.cmyk; | ||
}, | ||
rgbaArray: function() { | ||
var rgb = values.rgb; | ||
rgb.push(values.alpha); | ||
return rgb; | ||
}, | ||
hslaArray: function() { | ||
var hsl = values.hsl; | ||
hsl.push(values.alpha); | ||
return hsl; | ||
}, | ||
alpha: function(val) { | ||
if (val === undefined) { | ||
return values.alpha; | ||
} | ||
setValues("alpha", val); | ||
return color; | ||
}, | ||
lighten: function(ratio) { | ||
this.values.hsl[2] += this.values.hsl[2] * ratio; | ||
this.setValues("hsl", this.values.hsl); | ||
return this; | ||
}, | ||
red: function(val) { | ||
return setChannel("rgb", 0, val); | ||
}, | ||
green: function(val) { | ||
return setChannel("rgb", 1, val); | ||
}, | ||
blue: function(val) { | ||
return setChannel("rgb", 2, val); | ||
}, | ||
hue: function(val) { | ||
return setChannel("hsl", 0, val); | ||
}, | ||
saturation: function(val) { | ||
return setChannel("hsl", 1, val); | ||
}, | ||
lightness: function(val) { | ||
return setChannel("hsl", 2, val); | ||
}, | ||
saturationv: function(val) { | ||
return setChannel("hsv", 1, val); | ||
}, | ||
value: function(val) { | ||
return setChannel("hsv", 2, val); | ||
}, | ||
cyan: function(val) { | ||
return setChannel("cmyk", 0, val); | ||
}, | ||
magenta: function(val) { | ||
return setChannel("cmyk", 1, val); | ||
}, | ||
yellow: function(val) { | ||
return setChannel("cmyk", 2, val); | ||
}, | ||
black: function(val) { | ||
return setChannel("cmyk", 3, val); | ||
}, | ||
darken: function(ratio) { | ||
this.values.hsl[2] -= this.values.hsl[2] * ratio; | ||
this.setValues("hsl", this.values.hsl); | ||
return this; | ||
}, | ||
saturate: function(ratio) { | ||
this.values.hsl[1] += this.values.hsl[1] * ratio; | ||
this.setValues("hsl", this.values.hsl); | ||
return this; | ||
}, | ||
hexString: function() { | ||
return string.hexString(values.rgb); | ||
}, | ||
rgbString: function() { | ||
return string.rgbString(values.rgb, values.alpha); | ||
}, | ||
rgbaString: function() { | ||
return string.rgbaString(values.rgb, values.alpha); | ||
}, | ||
percentString: function() { | ||
return string.percentString(values.rgb, values.alpha); | ||
}, | ||
hslString: function() { | ||
return string.hslString(values.hsl, values.alpha); | ||
}, | ||
hslaString: function() { | ||
return string.hslaString(values.hsl, values.alpha); | ||
}, | ||
keyword: function() { | ||
return string.keyword(values.rgb, values.alpha); | ||
}, | ||
desaturate: function(ratio) { | ||
this.values.hsl[1] -= this.values.hsl[1] * ratio; | ||
this.setValues("hsl", this.values.hsl); | ||
return this; | ||
}, | ||
greyscale: function() { | ||
var rgb = this.values.rgb; | ||
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale | ||
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; | ||
this.setValues("rgb", [val, val, val]); | ||
return this; | ||
}, | ||
clearer: function(ratio) { | ||
this.setValues("alpha", this.values.alpha - (this.values.alpha * ratio)); | ||
return this; | ||
}, | ||
opaquer: function(ratio) { | ||
this.setValues("alpha", this.values.alpha + (this.values.alpha * ratio)); | ||
return this; | ||
}, | ||
rotate: function(degrees) { | ||
var hue = this.values.hsl[0]; | ||
hue = (hue + degrees) % 360; | ||
hue = hue < 0 ? 360 + hue : hue; | ||
this.values.hsl[0] = hue; | ||
this.setValues("hsl", this.values.hsl); | ||
return this; | ||
}, | ||
mix: function(color2, weight) { | ||
weight = 1 - (weight || 0.5); | ||
luminosity: function() { | ||
// http://www.w3.org/TR/WCAG20/#relativeluminancedef | ||
var rgb = values.rgb; | ||
for (var i = 0; i < rgb.length; i++) { | ||
var chan = rgb[i] / 255; | ||
rgb[i] = (chan <= 0.03928) ? chan / 12.92 | ||
: Math.pow(((chan + 0.055) / 1.055), 2.4) | ||
} | ||
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]; | ||
}, | ||
// algorithm from Sass's mix(). Ratio of first color in mix is | ||
// determined by the alphas of both colors and the weight | ||
var t1 = weight * 2 - 1, | ||
d = this.alpha() - color2.alpha(); | ||
var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2; | ||
var weight2 = 1 - weight1; | ||
contrast: function(color2) { | ||
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef | ||
var lum1 = color.luminosity(); | ||
var lum2 = color2.luminosity(); | ||
if (lum1 > lum2) { | ||
return (lum1 + 0.05) / (lum2 + 0.05) | ||
}; | ||
return (lum2 + 0.05) / (lum1 + 0.05); | ||
}, | ||
var rgb = this.rgbArray(); | ||
var rgb2 = color2.rgbArray(); | ||
for (var i = 0; i < rgb.length; i++) { | ||
rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2; | ||
} | ||
this.setValues("rgb", rgb); | ||
dark: function() { | ||
return color.contrast(Color("white")) > color.contrast(Color("black")); | ||
}, | ||
var alpha = this.alpha() * weight + color2.alpha() * (1 - weight); | ||
this.setValues("alpha", alpha); | ||
light: function() { | ||
return !color.dark(); | ||
}, | ||
negate: function() { | ||
var rgb = [] | ||
for (var i = 0; i < 3; i++) { | ||
rgb[i] = 255 - values.rgb[i]; | ||
} | ||
setValues("rgb", rgb); | ||
return color; | ||
}, | ||
return this; | ||
}, | ||
lighten: function(ratio) { | ||
values.hsl[2] += values.hsl[2] * ratio; | ||
setValues("hsl", values.hsl); | ||
return color; | ||
}, | ||
toJSON: function() { | ||
return this.rgb(); | ||
} | ||
} | ||
darken: function(ratio) { | ||
values.hsl[2] -= values.hsl[2] * ratio; | ||
setValues("hsl", values.hsl); | ||
return color; | ||
}, | ||
saturate: function(ratio) { | ||
values.hsl[1] += values.hsl[1] * ratio; | ||
setValues("hsl", values.hsl); | ||
return color; | ||
}, | ||
desaturate: function(ratio) { | ||
values.hsl[1] -= values.hsl[1] * ratio; | ||
setValues("hsl", values.hsl); | ||
return color; | ||
}, | ||
Color.prototype.getValues = function(space) { | ||
var vals = {}; | ||
for (var i = 0; i < space.length; i++) { | ||
vals[space[i]] = this.values[space][i]; | ||
} | ||
if (this.values.alpha != 1) { | ||
vals["a"] = this.values.alpha; | ||
} | ||
// {r: 255, g: 255, b: 255, a: 0.4} | ||
return vals; | ||
} | ||
Color.prototype.setValues = function(space, vals) { | ||
var spaces = { | ||
"rgb": ["red", "green", "blue"], | ||
"hsl": ["hue", "saturation", "lightness"], | ||
"hsv": ["hue", "saturation", "value"], | ||
"cmyk": ["cyan", "magenta", "yellow", "black"] | ||
}; | ||
var maxes = { | ||
"rgb": [255, 255, 255], | ||
"hsl": [360, 100, 100], | ||
"hsv": [360, 100, 100], | ||
"cmyk": [100, 100, 100, 100], | ||
}; | ||
var alpha = 1; | ||
if (space == "alpha") { | ||
alpha = vals; | ||
} | ||
else if (vals.length) { | ||
// [10, 10, 10] | ||
this.values[space] = vals.slice(0, space.length); | ||
alpha = vals[space.length]; | ||
} | ||
else if (vals[space[0]] !== undefined) { | ||
// {r: 10, g: 10, b: 10} | ||
for (var i = 0; i < space.length; i++) { | ||
this.values[space][i] = vals[space[i]]; | ||
} | ||
alpha = vals.a; | ||
} | ||
else if (vals[spaces[space][0]] !== undefined) { | ||
// {red: 10, green: 10, blue: 10} | ||
var chans = spaces[space]; | ||
for (var i = 0; i < space.length; i++) { | ||
this.values[space][i] = vals[chans[i]]; | ||
} | ||
alpha = vals.alpha; | ||
} | ||
this.values.alpha = Math.max(0, Math.min(1, alpha || this.values.alpha)); | ||
if (space == "alpha") { | ||
return; | ||
} | ||
greyscale: function() { | ||
var rgb = values.rgb; | ||
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale | ||
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; | ||
setValues("rgb", [val, val, val]); | ||
return color; | ||
}, | ||
clearer: function(ratio) { | ||
setValues("alpha", values.alpha - (values.alpha * ratio)); | ||
return color; | ||
}, | ||
opaquer: function(ratio) { | ||
setValues("alpha", values.alpha + (values.alpha * ratio)); | ||
return color; | ||
}, | ||
rotate: function(degrees) { | ||
var hue = values.hsl[0]; | ||
hue = (hue + degrees) % 360; | ||
hue = hue < 0 ? 360 + hue : hue; | ||
values.hsl[0] = hue; | ||
setValues("hsl", values.hsl); | ||
return color; | ||
}, | ||
// convert to all the other color spaces | ||
for (var sname in spaces) { | ||
if (sname != space) { | ||
this.values[sname] = convert[space][sname](this.values[space]) | ||
} | ||
mix: function(color2, weight) { | ||
weight = 1 - (weight || 0.5); | ||
// algorithm from Sass's mix(). Ratio of first color in mix is | ||
// determined by the alphas of both colors and the weight | ||
var t1 = weight * 2 - 1, | ||
d = color.alpha() - color2.alpha(); | ||
// cap values | ||
for (var i = 0; i < sname.length; i++) { | ||
var capped = Math.max(0, Math.min(maxes[sname][i], this.values[sname][i])); | ||
this.values[sname][i] = Math.round(capped); | ||
} | ||
} | ||
return true; | ||
} | ||
var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2; | ||
var weight2 = 1 - weight1; | ||
var rgb = color.rgbArray(); | ||
var rgb2 = color2.rgbArray(); | ||
Color.prototype.setSpace = function(space, args) { | ||
var vals = args[0]; | ||
if (vals === undefined) { | ||
// color.rgb() | ||
return this.getValues(space); | ||
} | ||
// color.rgb(10, 10, 10) | ||
if (typeof vals == "number") { | ||
vals = Array.prototype.slice.call(args); | ||
} | ||
this.setValues(space, vals); | ||
return this; | ||
} | ||
for (var i = 0; i < rgb.length; i++) { | ||
rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2; | ||
} | ||
setValues("rgb", rgb); | ||
var alpha = color.alpha() * weight + color2.alpha() * (1 - weight); | ||
setValues("alpha", alpha); | ||
return color; | ||
}, | ||
toJSON: function() { | ||
return color.rgb(); | ||
} | ||
} | ||
return color; | ||
} | ||
Color.prototype.setChannel = function(space, index, val) { | ||
if (val === undefined) { | ||
// color.red() | ||
return this.values[space][index]; | ||
} | ||
// color.red(100) | ||
this.values[space][index] = val; | ||
this.setValues(space, this.values[space]); | ||
return this; | ||
} |
@@ -23,3 +23,3 @@ /* | ||
}); | ||
source = "var Color = (function() {" + source + " return require('/color')})();" | ||
source = "/* MIT license */\nvar Color = (function() {" + source + " return require('/color')})();" | ||
fs.writeFileSync(dest, source); | ||
@@ -26,0 +26,0 @@ console.log("> " + dest); |
{ | ||
"name": "color", | ||
"description": "Color conversion and manipulation with CSS string support", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"author": "Heather Arthur <fayearthur@gmail.com>", | ||
@@ -6,0 +6,0 @@ "repository": { |
481
22417