diff --git a/README.md b/README.md index fff2dd8..effe3f0 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,10 @@ Color-convert is a color conversion library for JavaScript and node. It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s: ```js -var converter = require("color-convert")(); +var convert = require('color-convert'); -converter.rgb(140, 200, 100).hsl() // [96, 48, 59] - -converter.keyword("blue").rgb() // [0, 0, 255] +convert.rgb.hsl(140, 200, 100); // [96, 48, 59] +convert.keyword.rgb('blue'); // [0, 0, 255] ``` # Install @@ -21,41 +20,43 @@ $ npm install color-convert # API -Color-convert exports a converter object with getter/setter methods for each color space. It caches conversions: +Simply get the property of the _from_ and _to_ conversion that you're looking for. + +All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function. ```js -var converter = require("color-convert")(); +var convert = require('color-convert'); -converter.rgb(140, 200, 100).hsl() // [96, 48, 59] +// Hex to LAB +convert.hex.lab('DEADBF'); // [ 76, 21, -2 ] +convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ] -converter.rgb([140, 200, 100]) // args can be an array +// RGB to CMYK +convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ] +convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ] ``` -### Plain functions -Get direct conversion functions with no fancy objects: - -```js -require("color-convert").rgb2hsl([140, 200, 100]); // [96, 48, 59] -``` +### Arrays +All functions that accept multiple arguments also support passing an array. -### Unrounded -To get the unrounded conversion, append `Raw` to the function name: +Not that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.) ```js -convert.rgb2hslRaw([140, 200, 100]); // [95.99999999999999, 47.619047619047606, 58.82352941176471] -``` - -### Hash -There's also a hash of the conversion functions keyed first by the "from" color space, then by the "to" color space: +var convert = require('color-convert'); -```js -convert["hsl"]["hsv"]([160, 0, 20]) == convert.hsl2hsv([160, 0, 20]) +convert.rgb.hex(123, 45, 67); // '7B2D43' +convert.rgb.hex([123, 45, 67]); // '7B2D43' ``` -### Other spaces +## Routing -There are some conversions from rgb (sRGB) to XYZ and LAB too, available as `rgb2xyz()`, `rgb2lab()`, `xyz2rgb()`, and `xyz2lab()`. +Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex). + +Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js). # Contribute -Please fork, add conversions, figure out color profile stuff for XYZ, LAB, etc. This is meant to be a basic library that can be used by other libraries to wrap color calculations in some cool way. +If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request. + +# License +Copyright © 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE). diff --git a/conversions.js b/conversions.js index 61a0134..d0aa959 100644 --- a/conversions.js +++ b/conversions.js @@ -1,253 +1,9 @@ /* MIT license */ +var cssKeywords = require('./css-keywords'); -module.exports = { - rgb2hsl: rgb2hsl, - rgb2hsv: rgb2hsv, - rgb2hwb: rgb2hwb, - rgb2cmyk: rgb2cmyk, - rgb2keyword: rgb2keyword, - rgb2xyz: rgb2xyz, - rgb2lab: rgb2lab, - rgb2lch: rgb2lch, - rgb2ansi16: rgb2ansi16, - rgb2ansi: rgb2ansi, - rgb2hex: rgb2hex, - - hsl2rgb: hsl2rgb, - hsl2hsv: hsl2hsv, - hsl2hwb: hsl2hwb, - hsl2cmyk: hsl2cmyk, - hsl2keyword: hsl2keyword, - hsl2ansi16: hsl2ansi16, - hsl2ansi: hsl2ansi, - hsl2hex: hsl2hex, - - hsv2rgb: hsv2rgb, - hsv2hsl: hsv2hsl, - hsv2hwb: hsv2hwb, - hsv2cmyk: hsv2cmyk, - hsv2keyword: hsv2keyword, - hsv2ansi16: hsv2ansi16, - hsv2ansi: hsv2ansi, - hsv2hex: hsv2hex, - - hwb2rgb: hwb2rgb, - hwb2hsl: hwb2hsl, - hwb2hsv: hwb2hsv, - hwb2cmyk: hwb2cmyk, - hwb2keyword: hwb2keyword, - hwb2ansi16: hwb2ansi16, - hwb2ansi: hwb2ansi, - hwb2hex: hwb2hex, - - cmyk2rgb: cmyk2rgb, - cmyk2hsl: cmyk2hsl, - cmyk2hsv: cmyk2hsv, - cmyk2hwb: cmyk2hwb, - cmyk2keyword: cmyk2keyword, - cmyk2ansi16: cmyk2ansi16, - cmyk2ansi: cmyk2ansi, - cmyk2hex: cmyk2hex, - - keyword2rgb: keyword2rgb, - keyword2hsl: keyword2hsl, - keyword2hsv: keyword2hsv, - keyword2hwb: keyword2hwb, - keyword2cmyk: keyword2cmyk, - keyword2lab: keyword2lab, - keyword2xyz: keyword2xyz, - keyword2ansi16: keyword2ansi16, - keyword2ansi: keyword2ansi, - keyword2hex: keyword2hex, - - xyz2rgb: xyz2rgb, - xyz2lab: xyz2lab, - xyz2lch: xyz2lch, - - lab2xyz: lab2xyz, - lab2rgb: lab2rgb, - lab2lch: lab2lch, - - lch2lab: lch2lab, - lch2xyz: lch2xyz, - lch2rgb: lch2rgb, - - ansi162rgb: ansi162rgb, - ansi162hsl: ansi162hsl, - ansi162hsv: ansi162hsv, - ansi162hwb: ansi162hwb, - ansi162cmyk: ansi162cmyk, - ansi162keyword: ansi162keyword, - ansi162hex: ansi162hex, - - ansi2rgb: ansi2rgb, - ansi2hsl: ansi2hsl, - ansi2hsv: ansi2hsv, - ansi2hwb: ansi2hwb, - ansi2cmyk: ansi2cmyk, - ansi2keyword: ansi2keyword, - ansi2hex: ansi2hex, - - hex2rgb: hex2rgb, - hex2hsl: hex2hsl, - hex2hsv: hex2hsv, - hex2hwb: hex2hwb, - hex2cmyk: hex2cmyk, - hex2keyword: hex2keyword, - hex2ansi16: hex2ansi16, - hex2ansi: hex2ansi -}; - -var cssKeywords = { - aliceblue: [240, 248, 255], - antiquewhite: [250, 235, 215], - aqua: [0, 255, 255], - aquamarine: [127, 255, 212], - azure: [240, 255, 255], - beige: [245, 245, 220], - bisque: [255, 228, 196], - black: [0, 0, 0], - blanchedalmond: [255, 235, 205], - blue: [0, 0, 255], - blueviolet: [138, 43, 226], - brown: [165, 42, 42], - burlywood: [222, 184, 135], - cadetblue: [95, 158, 160], - chartreuse: [127, 255, 0], - chocolate: [210, 105, 30], - coral: [255, 127, 80], - cornflowerblue: [100, 149, 237], - cornsilk: [255, 248, 220], - crimson: [220, 20, 60], - cyan: [0, 255, 255], - darkblue: [0, 0, 139], - darkcyan: [0, 139, 139], - darkgoldenrod: [184, 134, 11], - darkgray: [169, 169, 169], - darkgreen: [0, 100, 0], - darkgrey: [169, 169, 169], - darkkhaki: [189, 183, 107], - darkmagenta: [139, 0, 139], - darkolivegreen: [85, 107, 47], - darkorange: [255, 140, 0], - darkorchid: [153, 50, 204], - darkred: [139, 0, 0], - darksalmon: [233, 150, 122], - darkseagreen: [143, 188, 143], - darkslateblue: [72, 61, 139], - darkslategray: [47, 79, 79], - darkslategrey: [47, 79, 79], - darkturquoise: [0, 206, 209], - darkviolet: [148, 0, 211], - deeppink: [255, 20, 147], - deepskyblue: [0, 191, 255], - dimgray: [105, 105, 105], - dimgrey: [105, 105, 105], - dodgerblue: [30, 144, 255], - firebrick: [178, 34, 34], - floralwhite: [255, 250, 240], - forestgreen: [34, 139, 34], - fuchsia: [255, 0, 255], - gainsboro: [220, 220, 220], - ghostwhite: [248, 248, 255], - gold: [255, 215, 0], - goldenrod: [218, 165, 32], - gray: [128, 128, 128], - green: [0, 128, 0], - greenyellow: [173, 255, 47], - grey: [128, 128, 128], - honeydew: [240, 255, 240], - hotpink: [255, 105, 180], - indianred: [205, 92, 92], - indigo: [75, 0, 130], - ivory: [255, 255, 240], - khaki: [240, 230, 140], - lavender: [230, 230, 250], - lavenderblush: [255, 240, 245], - lawngreen: [124, 252, 0], - lemonchiffon: [255, 250, 205], - lightblue: [173, 216, 230], - lightcoral: [240, 128, 128], - lightcyan: [224, 255, 255], - lightgoldenrodyellow: [250, 250, 210], - lightgray: [211, 211, 211], - lightgreen: [144, 238, 144], - lightgrey: [211, 211, 211], - lightpink: [255, 182, 193], - lightsalmon: [255, 160, 122], - lightseagreen: [32, 178, 170], - lightskyblue: [135, 206, 250], - lightslategray: [119, 136, 153], - lightslategrey: [119, 136, 153], - lightsteelblue: [176, 196, 222], - lightyellow: [255, 255, 224], - lime: [0, 255, 0], - limegreen: [50, 205, 50], - linen: [250, 240, 230], - magenta: [255, 0, 255], - maroon: [128, 0, 0], - mediumaquamarine: [102, 205, 170], - mediumblue: [0, 0, 205], - mediumorchid: [186, 85, 211], - mediumpurple: [147, 112, 219], - mediumseagreen: [60, 179, 113], - mediumslateblue: [123, 104, 238], - mediumspringgreen: [0, 250, 154], - mediumturquoise: [72, 209, 204], - mediumvioletred: [199, 21, 133], - midnightblue: [25, 25, 112], - mintcream: [245, 255, 250], - mistyrose: [255, 228, 225], - moccasin: [255, 228, 181], - navajowhite: [255, 222, 173], - navy: [0, 0, 128], - oldlace: [253, 245, 230], - olive: [128, 128, 0], - olivedrab: [107, 142, 35], - orange: [255, 165, 0], - orangered: [255, 69, 0], - orchid: [218, 112, 214], - palegoldenrod: [238, 232, 170], - palegreen: [152, 251, 152], - paleturquoise: [175, 238, 238], - palevioletred: [219, 112, 147], - papayawhip: [255, 239, 213], - peachpuff: [255, 218, 185], - peru: [205, 133, 63], - pink: [255, 192, 203], - plum: [221, 160, 221], - powderblue: [176, 224, 230], - purple: [128, 0, 128], - rebeccapurple: [102, 51, 153], - red: [255, 0, 0], - rosybrown: [188, 143, 143], - royalblue: [65, 105, 225], - saddlebrown: [139, 69, 19], - salmon: [250, 128, 114], - sandybrown: [244, 164, 96], - seagreen: [46, 139, 87], - seashell: [255, 245, 238], - sienna: [160, 82, 45], - silver: [192, 192, 192], - skyblue: [135, 206, 235], - slateblue: [106, 90, 205], - slategray: [112, 128, 144], - slategrey: [112, 128, 144], - snow: [255, 250, 250], - springgreen: [0, 255, 127], - steelblue: [70, 130, 180], - tan: [210, 180, 140], - teal: [0, 128, 128], - thistle: [216, 191, 216], - tomato: [255, 99, 71], - turquoise: [64, 224, 208], - violet: [238, 130, 238], - wheat: [245, 222, 179], - white: [255, 255, 255], - whitesmoke: [245, 245, 245], - yellow: [255, 255, 0], - yellowgreen: [154, 205, 50] -}; +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) var reverseKeywords = {}; for (var key in cssKeywords) { @@ -256,7 +12,22 @@ for (var key in cssKeywords) { } } -function rgb2hsl(rgb) { +var convert = module.exports = { + rgb: {}, + hsl: {}, + hsv: {}, + hwb: {}, + cmyk: {}, + xyz: {}, + lab: {}, + lch: {}, + hex: {}, + keyword: {}, + ansi16: {}, + ansi256: {} +}; + +convert.rgb.hsl = function (rgb) { var r = rgb[0] / 255; var g = rgb[1] / 255; var b = rgb[2] / 255; @@ -294,9 +65,9 @@ function rgb2hsl(rgb) { } return [h, s * 100, l * 100]; -} +}; -function rgb2hsv(rgb) { +convert.rgb.hsv = function (rgb) { var r = rgb[0]; var g = rgb[1]; var b = rgb[2]; @@ -332,21 +103,21 @@ function rgb2hsv(rgb) { v = ((max / 255) * 1000) / 10; return [h, s, v]; -} +}; -function rgb2hwb(rgb) { +convert.rgb.hwb = function (rgb) { var r = rgb[0]; var g = rgb[1]; var b = rgb[2]; - var h = rgb2hsl(rgb)[0]; + var h = convert.rgb.hsl(rgb)[0]; var w = 1 / 255 * Math.min(r, Math.min(g, b)); b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; -} +}; -function rgb2cmyk(rgb) { +convert.rgb.cmyk = function (rgb) { var r = rgb[0] / 255; var g = rgb[1] / 255; var b = rgb[2] / 255; @@ -361,13 +132,17 @@ function rgb2cmyk(rgb) { y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; -} +}; -function rgb2keyword(rgb) { +convert.rgb.keyword = function (rgb) { return reverseKeywords[rgb.join()]; -} +}; + +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; -function rgb2xyz(rgb) { +convert.rgb.xyz = function (rgb) { var r = rgb[0] / 255; var g = rgb[1] / 255; var b = rgb[2] / 255; @@ -382,10 +157,10 @@ function rgb2xyz(rgb) { var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y * 100, z * 100]; -} +}; -function rgb2lab(rgb) { - var xyz = rgb2xyz(rgb); +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); var x = xyz[0]; var y = xyz[1]; var z = xyz[2]; @@ -406,13 +181,9 @@ function rgb2lab(rgb) { b = 200 * (y - z); return [l, a, b]; -} - -function rgb2lch(args) { - return lab2lch(rgb2lab(args)); -} +}; -function hsl2rgb(hsl) { +convert.hsl.rgb = function (hsl) { var h = hsl[0] / 360; var s = hsl[1] / 100; var l = hsl[2] / 100; @@ -459,9 +230,9 @@ function hsl2rgb(hsl) { } return rgb; -} +}; -function hsl2hsv(hsl) { +convert.hsl.hsv = function (hsl) { var h = hsl[0]; var s = hsl[1] / 100; var l = hsl[2] / 100; @@ -480,21 +251,9 @@ function hsl2hsv(hsl) { sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; -} - -function hsl2hwb(args) { - return rgb2hwb(hsl2rgb(args)); -} - -function hsl2cmyk(args) { - return rgb2cmyk(hsl2rgb(args)); -} - -function hsl2keyword(args) { - return rgb2keyword(hsl2rgb(args)); -} +}; -function hsv2rgb(hsv) { +convert.hsv.rgb = function (hsv) { var h = hsv[0] / 60; var s = hsv[1] / 100; var v = hsv[2] / 100; @@ -520,9 +279,9 @@ function hsv2rgb(hsv) { case 5: return [v, p, q]; } -} +}; -function hsv2hsl(hsv) { +convert.hsv.hsl = function (hsv) { var h = hsv[0]; var s = hsv[1] / 100; var v = hsv[2] / 100; @@ -536,22 +295,10 @@ function hsv2hsl(hsv) { l /= 2; return [h, sl * 100, l * 100]; -} - -function hsv2hwb(args) { - return rgb2hwb(hsv2rgb(args)); -} - -function hsv2cmyk(args) { - return rgb2cmyk(hsv2rgb(args)); -} - -function hsv2keyword(args) { - return rgb2keyword(hsv2rgb(args)); -} +}; // http://dev.w3.org/csswg/css-color/#hwb-to-rgb -function hwb2rgb(hwb) { +convert.hwb.rgb = function (hwb) { var h = hwb[0] / 360; var wh = hwb[1] / 100; var bl = hwb[2] / 100; @@ -592,25 +339,9 @@ function hwb2rgb(hwb) { } return [r * 255, g * 255, b * 255]; -} - -function hwb2hsl(args) { - return rgb2hsl(hwb2rgb(args)); -} - -function hwb2hsv(args) { - return rgb2hsv(hwb2rgb(args)); -} - -function hwb2cmyk(args) { - return rgb2cmyk(hwb2rgb(args)); -} - -function hwb2keyword(args) { - return rgb2keyword(hwb2rgb(args)); -} +}; -function cmyk2rgb(cmyk) { +convert.cmyk.rgb = function (cmyk) { var c = cmyk[0] / 100; var m = cmyk[1] / 100; var y = cmyk[2] / 100; @@ -624,25 +355,9 @@ function cmyk2rgb(cmyk) { b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; -} - -function cmyk2hsl(args) { - return rgb2hsl(cmyk2rgb(args)); -} - -function cmyk2hsv(args) { - return rgb2hsv(cmyk2rgb(args)); -} - -function cmyk2hwb(args) { - return rgb2hwb(cmyk2rgb(args)); -} - -function cmyk2keyword(args) { - return rgb2keyword(cmyk2rgb(args)); -} +}; -function xyz2rgb(xyz) { +convert.xyz.rgb = function (xyz) { var x = xyz[0] / 100; var y = xyz[1] / 100; var z = xyz[2] / 100; @@ -672,9 +387,9 @@ function xyz2rgb(xyz) { b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; -} +}; -function xyz2lab(xyz) { +convert.xyz.lab = function (xyz) { var x = xyz[0]; var y = xyz[1]; var z = xyz[2]; @@ -695,13 +410,9 @@ function xyz2lab(xyz) { b = 200 * (y - z); return [l, a, b]; -} - -function xyz2lch(args) { - return lab2lch(xyz2lab(args)); -} +}; -function lab2xyz(lab) { +convert.lab.xyz = function (lab) { var l = lab[0]; var a = lab[1]; var b = lab[2]; @@ -726,9 +437,9 @@ function lab2xyz(lab) { : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; -} +}; -function lab2lch(lab) { +convert.lab.lch = function (lab) { var l = lab[0]; var a = lab[1]; var b = lab[2]; @@ -746,13 +457,9 @@ function lab2lch(lab) { c = Math.sqrt(a * a + b * b); return [l, c, h]; -} - -function lab2rgb(args) { - return xyz2rgb(lab2xyz(args)); -} +}; -function lch2lab(lch) { +convert.lch.lab = function (lch) { var l = lch[0]; var c = lch[1]; var h = lch[2]; @@ -765,49 +472,13 @@ function lch2lab(lch) { b = c * Math.sin(hr); return [l, a, b]; -} - -function lch2xyz(args) { - return lab2xyz(lch2lab(args)); -} - -function lch2rgb(args) { - return lab2rgb(lch2lab(args)); -} - -function keyword2rgb(keyword) { - return cssKeywords[keyword]; -} - -function keyword2hsl(args) { - return rgb2hsl(keyword2rgb(args)); -} - -function keyword2hsv(args) { - return rgb2hsv(keyword2rgb(args)); -} - -function keyword2hwb(args) { - return rgb2hwb(keyword2rgb(args)); -} - -function keyword2cmyk(args) { - return rgb2cmyk(keyword2rgb(args)); -} - -function keyword2lab(args) { - return rgb2lab(keyword2rgb(args)); -} - -function keyword2xyz(args) { - return rgb2xyz(keyword2rgb(args)); -} +}; -function rgb2ansi16(args) { +convert.rgb.ansi16 = function (args) { var r = args[0]; var g = args[1]; var b = args[2]; - var value = arguments[1] || rgb2hsv(args)[2]; // hsv2ansi16 optimization + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization value = Math.round(value / 50); @@ -825,9 +496,15 @@ function rgb2ansi16(args) { } return ansi; -} +}; -function rgb2ansi(args) { +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { var r = args[0]; var g = args[1]; var b = args[2]; @@ -852,49 +529,9 @@ function rgb2ansi(args) { + Math.round(b / 255 * 5); return ansi; -} - -function hsl2ansi16(args) { - return rgb2ansi16(hsl2rgb(args)); -} - -function hsl2ansi(args) { - return rgb2ansi(hsl2rgb(args)); -} - -function hsv2ansi16(args) { - return rgb2ansi16(hsv2rgb(args), args[2]); -} - -function hsv2ansi(args) { - return rgb2ansi(hsv2rgb(args)); -} - -function hwb2ansi16(args) { - return rgb2ansi16(hwb2rgb(args)); -} - -function hwb2ansi(args) { - return rgb2ansi(hwb2rgb(args)); -} - -function cmyk2ansi16(args) { - return rgb2ansi16(cmyk2rgb(args)); -} - -function cmyk2ansi(args) { - return rgb2ansi(cmyk2rgb(args)); -} - -function keyword2ansi16(args) { - return rgb2ansi16(keyword2rgb(args)); -} - -function keyword2ansi(args) { - return rgb2ansi(keyword2rgb(args)); -} +}; -function ansi162rgb(args) { +convert.ansi16.rgb = function (args) { var color = args % 10; // handle greyscale @@ -914,29 +551,9 @@ function ansi162rgb(args) { var b = (((color >> 2) & 1) * mult) * 255; return [r, g, b]; -} - -function ansi162hsl(args) { - return rgb2hsl(ansi162rgb(args)); -} - -function ansi162hsv(args) { - return rgb2hsv(ansi162rgb(args)); -} - -function ansi162hwb(args) { - return rgb2hwb(ansi162rgb(args)); -} - -function ansi162cmyk(args) { - return rgb2cmyk(ansi162rgb(args)); -} - -function ansi162keyword(args) { - return rgb2keyword(ansi162rgb(args)); -} +}; -function ansi2rgb(args) { +convert.ansi256.rgb = function (args) { // handle greyscale if (args >= 232) { var c = (args - 232) * 10 + 8; @@ -951,66 +568,18 @@ function ansi2rgb(args) { var b = (rem % 6) / 5 * 255; return [r, g, b]; -} - -function ansi2hsl(args) { - return rgb2hsl(ansi2rgb(args)); -} - -function ansi2hsv(args) { - return rgb2hsv(ansi2rgb(args)); -} - -function ansi2hwb(args) { - return rgb2hwb(ansi2rgb(args)); -} - -function ansi2cmyk(args) { - return rgb2cmyk(ansi2rgb(args)); -} - -function ansi2keyword(args) { - return rgb2keyword(ansi2rgb(args)); -} +}; -function rgb2hex(args) { +convert.rgb.hex = function (args) { var integer = ((Math.round(args[0]) & 0xFF) << 16) + ((Math.round(args[1]) & 0xFF) << 8) + (Math.round(args[2]) & 0xFF); var string = integer.toString(16).toUpperCase(); return '000000'.substring(string.length) + string; -} - -function hsl2hex(args) { - return rgb2hex(hsl2rgb(args)); -} - -function hsv2hex(args) { - return rgb2hex(hsv2rgb(args)); -} - -function hwb2hex(args) { - return rgb2hex(hwb2rgb(args)); -} - -function cmyk2hex(args) { - return rgb2hex(cmyk2rgb(args)); -} - -function keyword2hex(args) { - return rgb2hex(keyword2rgb(args)); -} - -function ansi162hex(args) { - return rgb2hex(ansi162rgb(args)); -} - -function ansi2hex(args) { - return rgb2hex(ansi2rgb(args)); -} +}; -function hex2rgb(args) { +convert.hex.rgb = function (args) { var match = args.toString(16).match(/[a-f0-9]{6}/i); if (!match) { return [0, 0, 0]; @@ -1022,32 +591,4 @@ function hex2rgb(args) { var b = integer & 0xFF; return [r, g, b]; -} - -function hex2hsl(args) { - return rgb2hsl(hex2rgb(args)); -} - -function hex2hsv(args) { - return rgb2hsv(hex2rgb(args)); -} - -function hex2hwb(args) { - return rgb2hwb(hex2rgb(args)); -} - -function hex2cmyk(args) { - return rgb2cmyk(hex2rgb(args)); -} - -function hex2keyword(args) { - return rgb2keyword(hex2rgb(args)); -} - -function hex2ansi16(args) { - return rgb2ansi16(hex2rgb(args)); -} - -function hex2ansi(args) { - return rgb2ansi(hex2rgb(args)); -} +}; diff --git a/css-keywords.js b/css-keywords.js new file mode 100644 index 0000000..495ca56 --- /dev/null +++ b/css-keywords.js @@ -0,0 +1,151 @@ +module.exports = { + aliceblue: [240, 248, 255], + antiquewhite: [250, 235, 215], + aqua: [0, 255, 255], + aquamarine: [127, 255, 212], + azure: [240, 255, 255], + beige: [245, 245, 220], + bisque: [255, 228, 196], + black: [0, 0, 0], + blanchedalmond: [255, 235, 205], + blue: [0, 0, 255], + blueviolet: [138, 43, 226], + brown: [165, 42, 42], + burlywood: [222, 184, 135], + cadetblue: [95, 158, 160], + chartreuse: [127, 255, 0], + chocolate: [210, 105, 30], + coral: [255, 127, 80], + cornflowerblue: [100, 149, 237], + cornsilk: [255, 248, 220], + crimson: [220, 20, 60], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgoldenrod: [184, 134, 11], + darkgray: [169, 169, 169], + darkgreen: [0, 100, 0], + darkgrey: [169, 169, 169], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkseagreen: [143, 188, 143], + darkslateblue: [72, 61, 139], + darkslategray: [47, 79, 79], + darkslategrey: [47, 79, 79], + darkturquoise: [0, 206, 209], + darkviolet: [148, 0, 211], + deeppink: [255, 20, 147], + deepskyblue: [0, 191, 255], + dimgray: [105, 105, 105], + dimgrey: [105, 105, 105], + dodgerblue: [30, 144, 255], + firebrick: [178, 34, 34], + floralwhite: [255, 250, 240], + forestgreen: [34, 139, 34], + fuchsia: [255, 0, 255], + gainsboro: [220, 220, 220], + ghostwhite: [248, 248, 255], + gold: [255, 215, 0], + goldenrod: [218, 165, 32], + gray: [128, 128, 128], + green: [0, 128, 0], + greenyellow: [173, 255, 47], + grey: [128, 128, 128], + honeydew: [240, 255, 240], + hotpink: [255, 105, 180], + indianred: [205, 92, 92], + indigo: [75, 0, 130], + ivory: [255, 255, 240], + khaki: [240, 230, 140], + lavender: [230, 230, 250], + lavenderblush: [255, 240, 245], + lawngreen: [124, 252, 0], + lemonchiffon: [255, 250, 205], + lightblue: [173, 216, 230], + lightcoral: [240, 128, 128], + lightcyan: [224, 255, 255], + lightgoldenrodyellow: [250, 250, 210], + lightgray: [211, 211, 211], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightsalmon: [255, 160, 122], + lightseagreen: [32, 178, 170], + lightskyblue: [135, 206, 250], + lightslategray: [119, 136, 153], + lightslategrey: [119, 136, 153], + lightsteelblue: [176, 196, 222], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + limegreen: [50, 205, 50], + linen: [250, 240, 230], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + mediumaquamarine: [102, 205, 170], + mediumblue: [0, 0, 205], + mediumorchid: [186, 85, 211], + mediumpurple: [147, 112, 219], + mediumseagreen: [60, 179, 113], + mediumslateblue: [123, 104, 238], + mediumspringgreen: [0, 250, 154], + mediumturquoise: [72, 209, 204], + mediumvioletred: [199, 21, 133], + midnightblue: [25, 25, 112], + mintcream: [245, 255, 250], + mistyrose: [255, 228, 225], + moccasin: [255, 228, 181], + navajowhite: [255, 222, 173], + navy: [0, 0, 128], + oldlace: [253, 245, 230], + olive: [128, 128, 0], + olivedrab: [107, 142, 35], + orange: [255, 165, 0], + orangered: [255, 69, 0], + orchid: [218, 112, 214], + palegoldenrod: [238, 232, 170], + palegreen: [152, 251, 152], + paleturquoise: [175, 238, 238], + palevioletred: [219, 112, 147], + papayawhip: [255, 239, 213], + peachpuff: [255, 218, 185], + peru: [205, 133, 63], + pink: [255, 192, 203], + plum: [221, 160, 221], + powderblue: [176, 224, 230], + purple: [128, 0, 128], + rebeccapurple: [102, 51, 153], + red: [255, 0, 0], + rosybrown: [188, 143, 143], + royalblue: [65, 105, 225], + saddlebrown: [139, 69, 19], + salmon: [250, 128, 114], + sandybrown: [244, 164, 96], + seagreen: [46, 139, 87], + seashell: [255, 245, 238], + sienna: [160, 82, 45], + silver: [192, 192, 192], + skyblue: [135, 206, 235], + slateblue: [106, 90, 205], + slategray: [112, 128, 144], + slategrey: [112, 128, 144], + snow: [255, 250, 250], + springgreen: [0, 255, 127], + steelblue: [70, 130, 180], + tan: [210, 180, 140], + teal: [0, 128, 128], + thistle: [216, 191, 216], + tomato: [255, 99, 71], + turquoise: [64, 224, 208], + violet: [238, 130, 238], + wheat: [245, 222, 179], + white: [255, 255, 255], + whitesmoke: [245, 245, 245], + yellow: [255, 255, 0], + yellowgreen: [154, 205, 50] +}; + diff --git a/index.js b/index.js index 9c2c746..b3418f6 100644 --- a/index.js +++ b/index.js @@ -1,108 +1,75 @@ var conversions = require('./conversions'); +var route = require('./route'); -var convert = function () { - return new Converter(); -}; +var convert = {}; -function wrapConverter(func) { - // accept array or plain args - return function (arg) { - if (typeof arg === 'number') { - arg = Array.prototype.slice.call(arguments); +var models = Object.keys(conversions); + +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); } - return conversions[func](arg); + return fn(args); }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; } -function wrapConverterQualified(func) { - return function (arg) { - if (typeof arg === 'number') { - arg = Array.prototype.slice.call(arguments); +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; } - var val = conversions[func](arg); - if (typeof val === 'string' || val === undefined) { - return val; // keyword + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); } - for (var i = 0; i < val.length; i++) { - val[i] = Math.round(val[i]); + var result = fn(args); + + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } } - return val; + return result; }; -} -for (var func in conversions) { - if (!conversions.hasOwnProperty(func)) { - continue; + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; } - // export Raw versions - convert[func + 'Raw'] = wrapConverter(func); - - var pair = /(\w+)2(\w+)/.exec(func); - var from = pair[1]; - var to = pair[2]; - - // export rgb2hsl and ["rgb"]["hsl"] - convert[from] = convert[from] || {}; - - convert[from][to] = convert[func] = wrapConverterQualified(func); + return wrappedFn; } -/* Converter does lazy conversion and caching */ -function Converter() { - this.convs = {}; -} +models.forEach(function (fromModel) { + convert[fromModel] = {}; -/* Either get the values for a space or - set the values for a space, depending on args */ -Converter.prototype.routeSpace = function (space, args) { - var values = args[0]; - if (values === undefined) { - // color.rgb() - return this.getValues(space); - } + var routes = route(fromModel); + var routeModels = Object.keys(routes); - // color.rgb(10, 10, 10) - if (typeof values === 'number') { - values = Array.prototype.slice.call(args); - } + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; - return this.setValues(space, values); -}; - -/* Set the values for a space, invalidating cache */ -Converter.prototype.setValues = function (space, values) { - this.space = space; - this.convs = {}; - this.convs[space] = values; - return this; -}; - -/* Get the values for a space. If there's already - a conversion for the space, fetch it, otherwise - compute it */ -Converter.prototype.getValues = function (space) { - var vals = this.convs[space]; - - if (!vals) { - var fspace = this.space; - var from = this.convs[fspace]; - - vals = convert[fspace][space](from); - this.convs[space] = vals; - } - - return vals; -}; - -['rgb', 'hsl', 'hsv', 'cmyk', 'keyword'].forEach(function (space) { - Converter.prototype[space] = function () { - return this.routeSpace(space, arguments); - }; + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); }); module.exports = convert; diff --git a/package.json b/package.json index 6f79c90..b2ba58d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ ], "files": [ "index.js", - "conversions.js" + "conversions.js", + "css-keywords.js" ], "xo": { "rules": { @@ -35,6 +36,7 @@ } }, "devDependencies": { + "chalk": "^1.1.1", "xo": "^0.11.2" } } diff --git a/route.js b/route.js new file mode 100644 index 0000000..c365e1e --- /dev/null +++ b/route.js @@ -0,0 +1,98 @@ +var conversions = require('./conversions'); + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +// https://jsperf.com/object-keys-vs-for-in-with-closure/3 +var models = Object.keys(conversions); + +function buildGraph() { + var graph = {}; + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + diff --git a/test/basic.js b/test/basic.js index 3d997b1..de3827c 100644 --- a/test/basic.js +++ b/test/basic.js @@ -1,79 +1,104 @@ /* eslint-disable dot-notation */ -var convert = require('../index'); var assert = require('assert'); +var chalk = require('chalk'); +var convert = require('../index'); +var conversions = require('../conversions'); + +var models = Object.keys(conversions); +for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + for (var j = 0; j < len; j++) { + var fromModel = models[j]; + + if (toModel === fromModel) { + continue; + } + + var fn = convert[toModel][fromModel]; + if (fn) { + var path = (fn.conversion || [fromModel, toModel]).slice(); + path[0] = chalk.bold.cyan(path[0]); + path[path.length - 1] = chalk.bold.cyan(path[path.length - 1]); + + console.log(path.join(chalk.bold.black('->'))); + } else { + console.log(chalk.red([fromModel, toModel].join('->')), chalk.red('(no conversion)')); + } + } +} -assert.deepEqual(convert.rgb2hsl([140, 200, 100]), [96, 48, 59]); -assert.deepEqual(convert.rgb2hsv([140, 200, 100]), [96, 50, 78]); -assert.deepEqual(convert.rgb2hwb([140, 200, 100]), [96, 39, 22]); -assert.deepEqual(convert.rgb2cmyk([140, 200, 100]), [30, 0, 50, 22]); -assert.deepEqual(convert.rgb2cmyk([0, 0, 0, 1]), [0, 0, 0, 100]); -assert.deepEqual(convert.rgb2keyword([255, 228, 196]), 'bisque'); -assert.deepEqual(convert.rgb2xyz([92, 191, 84]), [25, 40, 15]); -assert.deepEqual(convert.rgb2lab([92, 191, 84]), [70, -50, 45]); -assert.deepEqual(convert.rgb2lch([92, 191, 84]), [70, 67, 138]); -assert.deepEqual(convert.rgb2ansi16([92, 191, 84]), 32); -assert.deepEqual(convert.rgb2ansi([92, 191, 84]), 114); -assert.deepEqual(convert.rgb2hex([92, 191, 84]), '5CBF54'); - -assert.deepEqual(convert.hsl2rgb([96, 48, 59]), [140, 201, 100]); -assert.deepEqual(convert.hsl2hsv([96, 48, 59]), [96, 50, 79]); // colorpicker says [96,50,79] -assert.deepEqual(convert.hsl2hwb([96, 48, 59]), [96, 39, 21]); // computer round to 21, should be 22 -assert.deepEqual(convert.hsl2cmyk([96, 48, 59]), [30, 0, 50, 21]); -assert.deepEqual(convert.hsl2keyword([240, 100, 50]), 'blue'); -assert.deepEqual(convert.hsl2ansi16([240, 100, 50]), 94); -assert.deepEqual(convert.hsl2ansi([240, 100, 50]), 21); -assert.deepEqual(convert.hsl2hex([240, 100, 50]), '0000FF'); - -assert.deepEqual(convert.hsv2rgb([96, 50, 78]), [139, 199, 99]); -assert.deepEqual(convert.hsv2hsl([96, 50, 78]), [96, 47, 59]); -assert.deepEqual(convert.hsv2hsl([0, 0, 0]), [0, 0, 0]); -assert.deepEqual(convert.hsv2hwb([96, 50, 78]), [96, 39, 22]); -assert.deepEqual(convert.hsv2cmyk([96, 50, 78]), [30, 0, 50, 22]); -assert.deepEqual(convert.hsv2keyword([240, 100, 100]), 'blue'); -assert.deepEqual(convert.hsv2ansi16([240, 100, 100]), 94); -assert.deepEqual(convert.hsv2ansi([240, 100, 100]), 21); -assert.deepEqual(convert.hsv2hex([251, 80, 42]), '25156B'); - -assert.deepEqual(convert.cmyk2rgb([30, 0, 50, 22]), [139, 199, 99]); -assert.deepEqual(convert.cmyk2hsl([30, 0, 50, 22]), [96, 47, 59]); -assert.deepEqual(convert.cmyk2hsv([30, 0, 50, 22]), [96, 50, 78]); -assert.deepEqual(convert.cmyk2hwb([30, 0, 50, 22]), [96, 39, 22]); -assert.deepEqual(convert.cmyk2keyword([100, 100, 0, 0]), 'blue'); -assert.deepEqual(convert.cmyk2ansi16([30, 0, 50, 22]), 93); -assert.deepEqual(convert.cmyk2ansi([30, 0, 50, 22]), 150); -assert.deepEqual(convert.cmyk2hex([30, 0, 50, 22]), '8BC763'); - -assert.deepEqual(convert.keyword2rgb('blue'), [0, 0, 255]); -assert.deepEqual(convert.keyword2hsl('blue'), [240, 100, 50]); -assert.deepEqual(convert.keyword2hsv('blue'), [240, 100, 100]); -assert.deepEqual(convert.keyword2hwb('blue'), [240, 0, 0]); -assert.deepEqual(convert.keyword2cmyk('blue'), [100, 100, 0, 0]); -assert.deepEqual(convert.keyword2lab('blue'), [32, 79, -108]); -assert.deepEqual(convert.keyword2xyz('blue'), [18, 7, 95]); -assert.deepEqual(convert.keyword2ansi16('purple'), 35); -assert.deepEqual(convert.keyword2ansi('purple'), 127); -assert.deepEqual(convert.keyword2hex('blue'), '0000FF'); - -assert.deepEqual(convert.xyz2rgb([25, 40, 15]), [97, 190, 85]); -assert.deepEqual(convert.xyz2rgb([50, 100, 100]), [0, 255, 241]); -assert.deepEqual(convert.xyz2lab([25, 40, 15]), [69, -48, 44]); -assert.deepEqual(convert.xyz2lch([25, 40, 15]), [69, 65, 137]); - -assert.deepEqual(convert.lab2xyz([69, -48, 44]), [25, 39, 15]); -assert.deepEqual(convert.lab2rgb([75, 20, -30]), [194, 175, 240]); -assert.deepEqual(convert.lab2lch([69, -48, 44]), [69, 65, 137]); - -assert.deepEqual(convert.lch2lab([69, 65, 137]), [69, -48, 44]); -assert.deepEqual(convert.lch2xyz([69, 65, 137]), [25, 39, 15]); -assert.deepEqual(convert.lch2rgb([69, 65, 137]), [98, 188, 83]); - -assert.deepEqual(convert.ansi162rgb(103), [255, 255, 0]); -assert.deepEqual(convert.ansi2rgb(175), [204, 102, 153]); - -assert.deepEqual(convert.hex2rgb('ABCDEF'), [171, 205, 239]); +assert.deepEqual(convert.rgb.hsl([140, 200, 100]), [96, 48, 59]); +assert.deepEqual(convert.rgb.hsv([140, 200, 100]), [96, 50, 78]); +assert.deepEqual(convert.rgb.hwb([140, 200, 100]), [96, 39, 22]); +assert.deepEqual(convert.rgb.cmyk([140, 200, 100]), [30, 0, 50, 22]); +assert.deepEqual(convert.rgb.cmyk([0, 0, 0, 1]), [0, 0, 0, 100]); +assert.deepEqual(convert.rgb.keyword([255, 228, 196]), 'bisque'); +assert.deepEqual(convert.rgb.xyz([92, 191, 84]), [25, 40, 15]); +assert.deepEqual(convert.rgb.lab([92, 191, 84]), [70, -50, 45]); +assert.deepEqual(convert.rgb.lch([92, 191, 84]), [70, 67, 138]); +assert.deepEqual(convert.rgb.ansi16([92, 191, 84]), 32); +assert.deepEqual(convert.rgb.ansi256([92, 191, 84]), 114); +assert.deepEqual(convert.rgb.hex([92, 191, 84]), '5CBF54'); + +assert.deepEqual(convert.hsl.rgb([96, 48, 59]), [140, 201, 100]); +assert.deepEqual(convert.hsl.hsv([96, 48, 59]), [96, 50, 79]); // colorpicker says [96,50,79] +assert.deepEqual(convert.hsl.hwb([96, 48, 59]), [96, 39, 21]); // computer round to 21, should be 22 +assert.deepEqual(convert.hsl.cmyk([96, 48, 59]), [30, 0, 50, 21]); +assert.deepEqual(convert.hsl.keyword([240, 100, 50]), 'blue'); +assert.deepEqual(convert.hsl.ansi16([240, 100, 50]), 94); +assert.deepEqual(convert.hsl.ansi256([240, 100, 50]), 21); +assert.deepEqual(convert.hsl.hex([240, 100, 50]), '0000FF'); + +assert.deepEqual(convert.hsv.rgb([96, 50, 78]), [139, 199, 99]); +assert.deepEqual(convert.hsv.hsl([96, 50, 78]), [96, 47, 59]); +assert.deepEqual(convert.hsv.hsl([0, 0, 0]), [0, 0, 0]); +assert.deepEqual(convert.hsv.hwb([96, 50, 78]), [96, 39, 22]); +assert.deepEqual(convert.hsv.cmyk([96, 50, 78]), [30, 0, 50, 22]); +assert.deepEqual(convert.hsv.keyword([240, 100, 100]), 'blue'); +assert.deepEqual(convert.hsv.ansi16([240, 100, 100]), 94); +assert.deepEqual(convert.hsv.ansi256([240, 100, 100]), 21); +assert.deepEqual(convert.hsv.hex([251, 80, 42]), '25156B'); + +assert.deepEqual(convert.cmyk.rgb([30, 0, 50, 22]), [139, 199, 99]); +assert.deepEqual(convert.cmyk.hsl([30, 0, 50, 22]), [96, 47, 59]); +assert.deepEqual(convert.cmyk.hsv([30, 0, 50, 22]), [96, 50, 78]); +assert.deepEqual(convert.cmyk.hwb([30, 0, 50, 22]), [96, 39, 22]); +assert.deepEqual(convert.cmyk.keyword([100, 100, 0, 0]), 'blue'); +assert.deepEqual(convert.cmyk.ansi16([30, 0, 50, 22]), 93); +assert.deepEqual(convert.cmyk.ansi256([30, 0, 50, 22]), 150); +assert.deepEqual(convert.cmyk.hex([30, 0, 50, 22]), '8BC763'); + +assert.deepEqual(convert.keyword.rgb('blue'), [0, 0, 255]); +assert.deepEqual(convert.keyword.hsl('blue'), [240, 100, 50]); +assert.deepEqual(convert.keyword.hsv('blue'), [240, 100, 100]); +assert.deepEqual(convert.keyword.hwb('blue'), [240, 0, 0]); +assert.deepEqual(convert.keyword.cmyk('blue'), [100, 100, 0, 0]); +assert.deepEqual(convert.keyword.lab('blue'), [32, 79, -108]); +assert.deepEqual(convert.keyword.xyz('blue'), [18, 7, 95]); +assert.deepEqual(convert.keyword.ansi16('purple'), 35); +assert.deepEqual(convert.keyword.ansi256('purple'), 127); +assert.deepEqual(convert.keyword.hex('blue'), '0000FF'); + +assert.deepEqual(convert.xyz.rgb([25, 40, 15]), [97, 190, 85]); +assert.deepEqual(convert.xyz.rgb([50, 100, 100]), [0, 255, 241]); +assert.deepEqual(convert.xyz.lab([25, 40, 15]), [69, -48, 44]); +assert.deepEqual(convert.xyz.lch([25, 40, 15]), [69, 65, 137]); + +assert.deepEqual(convert.lab.xyz([69, -48, 44]), [25, 39, 15]); +assert.deepEqual(convert.lab.rgb([75, 20, -30]), [194, 175, 240]); +assert.deepEqual(convert.lab.lch([69, -48, 44]), [69, 65, 137]); + +assert.deepEqual(convert.lch.lab([69, 65, 137]), [69, -48, 44]); +assert.deepEqual(convert.lch.xyz([69, 65, 137]), [25, 39, 15]); +assert.deepEqual(convert.lch.rgb([69, 65, 137]), [98, 188, 83]); + +assert.deepEqual(convert.ansi16.rgb(103), [255, 255, 0]); +assert.deepEqual(convert.ansi256.rgb(175), [204, 102, 153]); + +assert.deepEqual(convert.hex.rgb('ABCDEF'), [171, 205, 239]); // non-array arguments -assert.deepEqual(convert.hsl2rgb(96, 48, 59), [140, 201, 100]); +assert.deepEqual(convert.hsl.rgb(96, 48, 59), [140, 201, 100]); // raw functions function round(vals) { @@ -84,126 +109,60 @@ function round(vals) { return vals; } -assert.deepEqual(round(convert.hsl2rgbRaw([96, 48, 59])), [140.4, 200.6, 100.3]); -assert.deepEqual(round(convert.rgb2hslRaw([140, 200, 100])), [96, 47.6, 58.8]); - -assert.deepEqual(round(convert.hsv2rgbRaw([96, 50, 78])), [139.2, 198.9, 99.5]); -assert.deepEqual(round(convert.rgb2hsvRaw([140, 200, 100])), [96, 50, 78.4]); - -assert.deepEqual(round(convert.hwb2rgbRaw([96, 39, 22])), [139.2, 198.9, 99.5]); -assert.deepEqual(round(convert.rgb2hwbRaw([140, 200, 100])), [96, 39.2, 21.6]); - -assert.deepEqual(round(convert.cmyk2rgbRaw([30, 0, 50, 22])), [139.2, 198.9, 99.5]); -assert.deepEqual(round(convert.rgb2cmykRaw([140, 200, 100])), [30, 0, 50, 21.6]); - -assert.deepEqual(round(convert.keyword2rgbRaw('blue')), [0, 0, 255]); -assert.deepEqual(convert.rgb2keywordRaw([255, 228, 196]), 'bisque'); - -assert.deepEqual(round(convert.hsv2hslRaw([96, 50, 78])), [96, 47, 58.5]); -assert.deepEqual(round(convert.hsl2hsvRaw([96, 48, 59])), [96, 50, 78.7]); - -assert.deepEqual(round(convert.hsl2hsvRaw([96, 48, 59])), [96, 50, 78.7]); - -assert.deepEqual(round(convert.xyz2rgbRaw([25, 40, 15])), [97.4, 189.9, 85]); -assert.deepEqual(round(convert.rgb2xyzRaw([92, 191, 84])), [24.6, 40.2, 14.8]); - -assert.deepEqual(round(convert.rgb2labRaw([92, 191, 84])), [69.6, -50.1, 44.6]); - -// hashed -var val = [140, 200, 100]; -assert.deepEqual(convert['rgb']['hsl'](val), convert.rgb2hsl(val)); -assert.deepEqual(convert['rgb']['hsv'](val), convert.rgb2hsv(val)); -assert.deepEqual(convert['rgb']['hwb'](val), convert.rgb2hwb(val)); -assert.deepEqual(convert['rgb']['cmyk'](val), convert.rgb2cmyk(val)); -assert.deepEqual(convert['rgb']['xyz'](val), convert.rgb2xyz(val)); -assert.deepEqual(convert['rgb']['lab'](val), convert.rgb2lab(val)); -assert.deepEqual(convert['rgb']['keyword']([255, 228, 196]), 'bisque'); - -val = [96, 48, 59]; -assert.deepEqual(convert['hsl']['rgb'](val), convert.hsl2rgb(val)); -assert.deepEqual(convert['hsl']['hsv'](val), convert.hsl2hsv(val)); -assert.deepEqual(convert['hsl']['hwb'](val), convert.hsl2hwb(val)); -assert.deepEqual(convert['hsl']['cmyk'](val), convert.hsl2cmyk(val)); -assert.deepEqual(convert['hsl']['keyword'](val), convert.hsl2keyword(val)); - -val = [96, 50, 78]; -assert.deepEqual(convert['hsv']['rgb'](val), convert.hsv2rgb(val)); -assert.deepEqual(convert['hsv']['hsl'](val), convert.hsv2hsl(val)); -assert.deepEqual(convert['hsv']['hwb'](val), convert.hsv2hwb(val)); -assert.deepEqual(convert['hsv']['cmyk'](val), convert.hsv2cmyk(val)); -assert.deepEqual(convert['hsv']['keyword'](val), convert.hsv2keyword(val)); - -val = [30, 0, 50, 22]; -assert.deepEqual(convert['cmyk']['rgb'](val), convert.cmyk2rgb(val)); -assert.deepEqual(convert['cmyk']['hsl'](val), convert.cmyk2hsl(val)); -assert.deepEqual(convert['cmyk']['hsv'](val), convert.cmyk2hsv(val)); -assert.deepEqual(convert['cmyk']['hwb'](val), convert.cmyk2hwb(val)); -assert.deepEqual(convert['cmyk']['keyword'](val), convert.cmyk2keyword(val)); - -val = 'blue'; -assert.deepEqual(convert['keyword']['rgb'](val), convert.keyword2rgb(val)); -assert.deepEqual(convert['keyword']['hsl'](val), convert.keyword2hsl(val)); -assert.deepEqual(convert['keyword']['hsv'](val), convert.keyword2hsv(val)); -assert.deepEqual(convert['keyword']['hwb'](val), convert.keyword2hwb(val)); -assert.deepEqual(convert['keyword']['cmyk'](val), convert.keyword2cmyk(val)); -assert.deepEqual(convert['keyword']['lab'](val), convert.keyword2lab(val)); -assert.deepEqual(convert['keyword']['xyz'](val), convert.keyword2xyz(val)); - -val = [25, 40, 15]; -assert.deepEqual(convert['xyz']['rgb'](val), convert.xyz2rgb(val)); -assert.deepEqual(convert['xyz']['lab'](val), convert.xyz2lab(val)); - -val = [69, -48, 44]; -assert.deepEqual(convert['lab']['xyz'](val), [25, 39, 15]); - -// converter -var converter = convert(); - -var vals = [140, 200, 100]; -converter.rgb(140, 200, 100); - -assert.deepEqual(converter.hsl(), convert.rgb2hsl(vals)); -assert.deepEqual(converter.hsv(), convert.rgb2hsv(vals)); -assert.deepEqual(converter.cmyk(), convert.rgb2cmyk(vals)); -assert.deepEqual(converter.rgb(), vals); -assert.deepEqual(converter.rgb([255, 228, 196]).keyword(), 'bisque'); - -vals = [96, 48, 59]; -converter.hsl(vals); -assert.deepEqual(converter.rgb(), convert.hsl2rgb(vals)); -assert.deepEqual(converter.hsv(), convert.hsl2hsv(vals)); -assert.deepEqual(converter.cmyk(), convert.hsl2cmyk(vals)); -assert.deepEqual(converter.keyword(), convert.hsl2keyword(vals)); +assert.deepEqual(round(convert.hsl.rgb.raw([96, 48, 59])), [140.4, 200.6, 100.3]); +assert.deepEqual(round(convert.rgb.hsl.raw([140, 200, 100])), [96, 47.6, 58.8]); + +assert.deepEqual(round(convert.hsv.rgb.raw([96, 50, 78])), [139.2, 198.9, 99.5]); +assert.deepEqual(round(convert.rgb.hsv.raw([140, 200, 100])), [96, 50, 78.4]); + +assert.deepEqual(round(convert.hwb.rgb.raw([96, 39, 22])), [139.2, 198.9, 99.5]); +assert.deepEqual(round(convert.rgb.hwb.raw([140, 200, 100])), [96, 39.2, 21.6]); + +assert.deepEqual(round(convert.cmyk.rgb.raw([30, 0, 50, 22])), [139.2, 198.9, 99.5]); +assert.deepEqual(round(convert.rgb.cmyk.raw([140, 200, 100])), [30, 0, 50, 21.6]); + +assert.deepEqual(round(convert.keyword.rgb.raw('blue')), [0, 0, 255]); +assert.deepEqual(convert.rgb.keyword.raw([255, 228, 196]), 'bisque'); + +assert.deepEqual(round(convert.hsv.hsl.raw([96, 50, 78])), [96, 47, 58.5]); +assert.deepEqual(round(convert.hsl.hsv.raw([96, 48, 59])), [96, 50, 78.7]); + +assert.deepEqual(round(convert.hsl.hsv.raw([96, 48, 59])), [96, 50, 78.7]); + +assert.deepEqual(round(convert.xyz.rgb.raw([25, 40, 15])), [97.4, 189.9, 85]); +assert.deepEqual(round(convert.rgb.xyz.raw([92, 191, 84])), [24.6, 40.2, 14.8]); + +assert.deepEqual(round(convert.rgb.lab.raw([92, 191, 84])), [69.6, -50.1, 44.6]); // hwb // http://dev.w3.org/csswg/css-color/#hwb-examples // all extrem value should give black, white or grey for (var angle = 0; angle <= 360; angle++) { - assert.deepEqual(convert.hwb2rgb([angle, 0, 100]), [0, 0, 0]); - assert.deepEqual(convert.hwb2rgb([angle, 100, 0]), [255, 255, 255]); - assert.deepEqual(convert.hwb2rgb([angle, 100, 100]), [128, 128, 128]); + assert.deepEqual(convert.hwb.rgb([angle, 0, 100]), [0, 0, 0]); + assert.deepEqual(convert.hwb.rgb([angle, 100, 0]), [255, 255, 255]); + assert.deepEqual(convert.hwb.rgb([angle, 100, 100]), [128, 128, 128]); } -assert.deepEqual(convert.hwb2rgb([0, 0, 0]), [255, 0, 0]); -assert.deepEqual(convert.hwb2rgb([0, 20, 40]), [153, 51, 51]); -assert.deepEqual(convert.hwb2rgb([0, 40, 40]), [153, 102, 102]); -assert.deepEqual(convert.hwb2rgb([0, 40, 20]), [204, 102, 102]); +assert.deepEqual(convert.hwb.rgb([0, 0, 0]), [255, 0, 0]); +assert.deepEqual(convert.hwb.rgb([0, 20, 40]), [153, 51, 51]); +assert.deepEqual(convert.hwb.rgb([0, 40, 40]), [153, 102, 102]); +assert.deepEqual(convert.hwb.rgb([0, 40, 20]), [204, 102, 102]); -assert.deepEqual(convert.hwb2rgb([120, 0, 0]), [0, 255, 0]); -assert.deepEqual(convert.hwb2rgb([120, 20, 40]), [51, 153, 51]); -assert.deepEqual(convert.hwb2rgb([120, 40, 40]), [102, 153, 102]); -assert.deepEqual(convert.hwb2rgb([120, 40, 20]), [102, 204, 102]); +assert.deepEqual(convert.hwb.rgb([120, 0, 0]), [0, 255, 0]); +assert.deepEqual(convert.hwb.rgb([120, 20, 40]), [51, 153, 51]); +assert.deepEqual(convert.hwb.rgb([120, 40, 40]), [102, 153, 102]); +assert.deepEqual(convert.hwb.rgb([120, 40, 20]), [102, 204, 102]); -assert.deepEqual(convert.hwb2rgb([240, 0, 0]), [0, 0, 255]); -assert.deepEqual(convert.hwb2rgb([240, 20, 40]), [51, 51, 153]); -assert.deepEqual(convert.hwb2rgb([240, 40, 40]), [102, 102, 153]); -assert.deepEqual(convert.hwb2rgb([240, 40, 20]), [102, 102, 204]); +assert.deepEqual(convert.hwb.rgb([240, 0, 0]), [0, 0, 255]); +assert.deepEqual(convert.hwb.rgb([240, 20, 40]), [51, 51, 153]); +assert.deepEqual(convert.hwb.rgb([240, 40, 40]), [102, 102, 153]); +assert.deepEqual(convert.hwb.rgb([240, 40, 20]), [102, 102, 204]); // black should always stay black -val = [0, 0, 0]; -assert.deepEqual(convert.hsl2hsv(val), val); -assert.deepEqual(convert.hsl2rgb(val), val); -assert.deepEqual(convert.hsl2hwb(val), [0, 0, 100]); -assert.deepEqual(convert.hsl2cmyk(val), [0, 0, 0, 100]); -assert.deepEqual(convert.hsl2hex(val), '000000'); +var val = [0, 0, 0]; +assert.deepEqual(convert.hsl.hsv(val), val); +assert.deepEqual(convert.hsl.rgb(val), val); +assert.deepEqual(convert.hsl.hwb(val), [0, 0, 100]); +assert.deepEqual(convert.hsl.cmyk(val), [0, 0, 0, 100]); +assert.deepEqual(convert.hsl.hex(val), '000000');