| /* MIT license */ |
| var colorNames = require('color-name'); |
| var swizzle = require('simple-swizzle'); |
| |
| var reverseNames = {}; |
| |
| // create a list of reverse color names |
| for (var name in colorNames) { |
| if (colorNames.hasOwnProperty(name)) { |
| reverseNames[colorNames[name]] = name; |
| } |
| } |
| |
| var cs = module.exports = { |
| to: {}, |
| get: {} |
| }; |
| |
| cs.get = function (string) { |
| var prefix = string.substring(0, 3).toLowerCase(); |
| var val; |
| var model; |
| switch (prefix) { |
| case 'hsl': |
| val = cs.get.hsl(string); |
| model = 'hsl'; |
| break; |
| case 'hwb': |
| val = cs.get.hwb(string); |
| model = 'hwb'; |
| break; |
| default: |
| val = cs.get.rgb(string); |
| model = 'rgb'; |
| break; |
| } |
| |
| if (!val) { |
| return null; |
| } |
| |
| return {model: model, value: val}; |
| }; |
| |
| cs.get.rgb = function (string) { |
| if (!string) { |
| return null; |
| } |
| |
| var abbr = /^#([a-f0-9]{3,4})$/i; |
| var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i; |
| var rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; |
| var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; |
| var keyword = /(\D+)/; |
| |
| var rgb = [0, 0, 0, 1]; |
| var match; |
| var i; |
| var hexAlpha; |
| |
| if (match = string.match(hex)) { |
| hexAlpha = match[2]; |
| match = match[1]; |
| |
| for (i = 0; i < 3; i++) { |
| // https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19 |
| var i2 = i * 2; |
| rgb[i] = parseInt(match.slice(i2, i2 + 2), 16); |
| } |
| |
| if (hexAlpha) { |
| rgb[3] = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; |
| } |
| } else if (match = string.match(abbr)) { |
| match = match[1]; |
| hexAlpha = match[3]; |
| |
| for (i = 0; i < 3; i++) { |
| rgb[i] = parseInt(match[i] + match[i], 16); |
| } |
| |
| if (hexAlpha) { |
| rgb[3] = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; |
| } |
| } else if (match = string.match(rgba)) { |
| for (i = 0; i < 3; i++) { |
| rgb[i] = parseInt(match[i + 1], 0); |
| } |
| |
| if (match[4]) { |
| rgb[3] = parseFloat(match[4]); |
| } |
| } else if (match = string.match(per)) { |
| for (i = 0; i < 3; i++) { |
| rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); |
| } |
| |
| if (match[4]) { |
| rgb[3] = parseFloat(match[4]); |
| } |
| } else if (match = string.match(keyword)) { |
| if (match[1] === 'transparent') { |
| return [0, 0, 0, 0]; |
| } |
| |
| rgb = colorNames[match[1]]; |
| |
| if (!rgb) { |
| return null; |
| } |
| |
| rgb[3] = 1; |
| |
| return rgb; |
| } else { |
| return null; |
| } |
| |
| for (i = 0; i < 3; i++) { |
| rgb[i] = clamp(rgb[i], 0, 255); |
| } |
| rgb[3] = clamp(rgb[3], 0, 1); |
| |
| return rgb; |
| }; |
| |
| cs.get.hsl = function (string) { |
| if (!string) { |
| return null; |
| } |
| |
| var hsl = /^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; |
| var match = string.match(hsl); |
| |
| if (match) { |
| var alpha = parseFloat(match[4]); |
| var h = (parseFloat(match[1]) + 360) % 360; |
| var s = clamp(parseFloat(match[2]), 0, 100); |
| var l = clamp(parseFloat(match[3]), 0, 100); |
| var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1); |
| |
| return [h, s, l, a]; |
| } |
| |
| return null; |
| }; |
| |
| cs.get.hwb = function (string) { |
| if (!string) { |
| return null; |
| } |
| |
| var hwb = /^hwb\(\s*([+-]?\d*[\.]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; |
| var match = string.match(hwb); |
| |
| if (match) { |
| var alpha = parseFloat(match[4]); |
| var h = ((parseFloat(match[1]) % 360) + 360) % 360; |
| var w = clamp(parseFloat(match[2]), 0, 100); |
| var b = clamp(parseFloat(match[3]), 0, 100); |
| var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1); |
| return [h, w, b, a]; |
| } |
| |
| return null; |
| }; |
| |
| cs.to.hex = function () { |
| var rgba = swizzle(arguments); |
| |
| return ( |
| '#' + |
| hexDouble(rgba[0]) + |
| hexDouble(rgba[1]) + |
| hexDouble(rgba[2]) + |
| (rgba[3] < 1 |
| ? (hexDouble(Math.round(rgba[3] * 255))) |
| : '') |
| ); |
| }; |
| |
| cs.to.rgb = function () { |
| var rgba = swizzle(arguments); |
| |
| return rgba.length < 4 || rgba[3] === 1 |
| ? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')' |
| : 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')'; |
| }; |
| |
| cs.to.rgb.percent = function () { |
| var rgba = swizzle(arguments); |
| |
| var r = Math.round(rgba[0] / 255 * 100); |
| var g = Math.round(rgba[1] / 255 * 100); |
| var b = Math.round(rgba[2] / 255 * 100); |
| |
| return rgba.length < 4 || rgba[3] === 1 |
| ? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)' |
| : 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')'; |
| }; |
| |
| cs.to.hsl = function () { |
| var hsla = swizzle(arguments); |
| return hsla.length < 4 || hsla[3] === 1 |
| ? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)' |
| : 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')'; |
| }; |
| |
| // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax |
| // (hwb have alpha optional & 1 is default value) |
| cs.to.hwb = function () { |
| var hwba = swizzle(arguments); |
| |
| var a = ''; |
| if (hwba.length >= 4 && hwba[3] !== 1) { |
| a = ', ' + hwba[3]; |
| } |
| |
| return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')'; |
| }; |
| |
| cs.to.keyword = function (rgb) { |
| return reverseNames[rgb.slice(0, 3)]; |
| }; |
| |
| // helpers |
| function clamp(num, min, max) { |
| return Math.min(Math.max(min, num), max); |
| } |
| |
| function hexDouble(num) { |
| var str = num.toString(16).toUpperCase(); |
| return (str.length < 2) ? '0' + str : str; |
| } |