diff --git a/lib/color.js b/lib/color.js index bb01518..4b6e6e6 100644 --- a/lib/color.js +++ b/lib/color.js @@ -2,24 +2,33 @@ var installedColorSpaces = []; var undef = function (obj) { return typeof obj === 'undefined'; }; -var channelRegExp = /\s*(\.\d+|\d+(?:\.\d+)?)(%)?\s*/; +var channelRegExp = /\s*(\.\d+|\d+(?:\.\d+)?)(%|deg)?\s*/; var percentageChannelRegExp = /\s*(\.\d+|100|\d?\d(?:\.\d+)?)%\s*/; -var alphaChannelRegExp = /\s*(\.\d+|\d+(?:\.\d+)?)\s*/; var cssColorRegExp = new RegExp( '^(rgb|hsl|hsv)a?' + '\\(' + channelRegExp.source + - ',' + + '[, ]' + channelRegExp.source + - ',' + + '[, ]' + + channelRegExp.source + + '(?:[,/]' + channelRegExp.source + - '(?:,' + - alphaChannelRegExp.source + ')?' + '\\)$', 'i' ); +function divisor(unit, channelNumber, hasHue) { + if (unit === '%') { + return 100; + } else if (unit === 'deg' || (hasHue && channelNumber === 0)) { + return 360; + } else if (!unit) { + return 255; + } +} + function color(obj) { if (Array.isArray(obj)) { if (typeof obj[0] === 'string' && typeof color[obj[0]] === 'function') { @@ -46,37 +55,38 @@ function color(obj) { var matchCssSyntax = obj.match(cssColorRegExp); if (matchCssSyntax) { var colorSpaceName = matchCssSyntax[1].toUpperCase(); - var alpha = undef(matchCssSyntax[8]) - ? matchCssSyntax[8] - : parseFloat(matchCssSyntax[8]); var hasHue = colorSpaceName[0] === 'H'; - var firstChannelDivisor = matchCssSyntax[3] ? 100 : hasHue ? 360 : 255; - var secondChannelDivisor = matchCssSyntax[5] || hasHue ? 100 : 255; - var thirdChannelDivisor = matchCssSyntax[7] || hasHue ? 100 : 255; if (undef(color[colorSpaceName])) { throw new Error('color.' + colorSpaceName + ' is not installed.'); } return new color[colorSpaceName]( - parseFloat(matchCssSyntax[2]) / firstChannelDivisor, - parseFloat(matchCssSyntax[4]) / secondChannelDivisor, - parseFloat(matchCssSyntax[6]) / thirdChannelDivisor, - alpha + parseFloat(matchCssSyntax[2]) / divisor(matchCssSyntax[3], 0, hasHue), + parseFloat(matchCssSyntax[4]) / divisor(matchCssSyntax[5], 1, hasHue), + parseFloat(matchCssSyntax[6]) / divisor(matchCssSyntax[7], 2, hasHue), + undef(matchCssSyntax[8]) + ? matchCssSyntax[8] + : parseFloat(matchCssSyntax[8]) / (matchCssSyntax[9] ? 100 : 255) ); } // Assume hex syntax if (obj.length < 6) { // Allow CSS shorthand - obj = obj.replace(/^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, '$1$1$2$2$3$3'); + obj = obj.replace( + /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i, + '$1$1$2$2$3$3$4$4' + ); } - // Split obj into red, green, and blue components + // Split obj into the red, green, blue, and optionally alpha component var hexMatch = obj.match( - /^#?([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$/i + /^#?([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])?$/i ); + if (hexMatch) { return new color.RGB( parseInt(hexMatch[1], 16) / 255, parseInt(hexMatch[2], 16) / 255, - parseInt(hexMatch[3], 16) / 255 + parseInt(hexMatch[3], 16) / 255, + hexMatch[4] ? parseInt(hexMatch[4], 16) / 255 : 1 ); } @@ -316,12 +326,7 @@ color.installColorSpace('RGB', ['red', 'green', 'blue', 'alpha'], { hexa: function () { var alphaString = Math.round(this._alpha * 255).toString(16); - return ( - '#' + - '00'.substr(0, 2 - alphaString.length) + - alphaString + - this.hex().substr(1, 6) - ); + return this.hex() + '00'.substr(0, 2 - alphaString.length) + alphaString; }, css: function () { diff --git a/test/parse.js b/test/parse.js index d5305f2..fdcf9a7 100644 --- a/test/parse.js +++ b/test/parse.js @@ -53,4 +53,128 @@ describe('parsing', function () { expect(color('cmyk(100,100%,100%)'), 'to be false'); }); }); + + describe('with #rrggbbaa', function () { + var instance = color('#00ff0080'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#00ff00'); + }); + + it('should have its alpha channel set correctly', function () { + expect(instance.alpha().toFixed(2), 'to equal', '0.50'); + }); + }); + + describe('with #rgba', function () { + var instance = color('#0f08'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#00ff00'); + }); + + it('should have its alpha channel set correctly', function () { + expect(instance.alpha().toFixed(2), 'to equal', '0.53'); + }); + }); + + describe('with rgb(r, g, b)', function () { + var instance = color('rgb(10, 20, 30)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#0a141e'); + }); + }); + + describe('with rgb(r,g,b)', function () { + var instance = color('rgb(10,20,30)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#0a141e'); + }); + }); + + describe('with rgb(r g b)', function () { + var instance = color('rgb(10 20 30)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#0a141e'); + }); + }); + + describe('with rgba(r g b / a)', function () { + var instance = color('rgba(10 20 30 / 50%)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#0a141e'); + }); + + it('should have its alpha channel set correctly', function () { + expect(instance.alpha().toFixed(2), 'to equal', '0.50'); + }); + }); + + describe('with rgba(r g b/a)', function () { + var instance = color('rgba(10 20 30/50%)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#0a141e'); + }); + + it('should have its alpha channel set correctly', function () { + expect(instance.alpha().toFixed(2), 'to equal', '0.50'); + }); + }); + + describe('with hsl(h s l) and the hue given as a percentage', function () { + var instance = color('hsl(10% 20% 30%)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#5c503d'); + }); + }); + + describe('with hsl(h s l) and the hue given as an angle', function () { + var instance = color('hsl(10deg 20% 30%)'); + + it('should return a color instance', function () { + expect(instance, 'to be a color instance'); + }); + + it('should be green', function () { + expect(instance.hex(), 'to equal', '#5c423d'); + }); + }); });