diff --git a/src/color.js b/src/color.js index 036d9f4..75d8372 100644 --- a/src/color.js +++ b/src/color.js @@ -5,16 +5,9 @@ export function Color() {} export var darker = 0.7; export var brighter = 1 / darker; -var reI = "\\s*([+-]?\\d+)\\s*", - reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*", - reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*", - reHex = /^#([0-9a-f]{3,8})$/, - reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"), - reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"), - reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"), - reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"), - reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"), - reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); +var reHex = /^#([0-9a-f]{3,8})$/, + reRgb = /^rgb[a]?\(([^)]*)\)$/, + reHsl = /^hsl[a]?\(([^)]*)\)$/; var named = { aliceblue: 0xf0f8ff, @@ -193,6 +186,57 @@ function color_formatRgb() { return this.rgb().formatRgb(); } +function color_isPercent(s) { + return s.endsWith("%"); +} + +function color_parsePercent(s, k = 1) { + return /\d%$/.test(s) ? s.slice(0, -1) * k / 100 : NaN; +} + +function color_parseNumber(s) { + return s.endsWith(".") ? NaN : +s; +} + +function color_parseAlpha(s) { + return s === undefined ? 1 : (color_isPercent(s) ? color_parsePercent : color_parseNumber)(s); +} + +function color_parseRgb(S) { + S = S.split(",").map(s => s.trim()); + if (S.length < 3 || S.length > 4) return null; + let [r, g, b, a] = S; + if (color_isPercent(r) || color_isPercent(g) || color_isPercent(g)) { + r = color_parsePercent(r, 255); + g = color_parsePercent(g, 255); + b = color_parsePercent(b, 255); + } else { + r = color_parseNumber(r); + g = color_parseNumber(g); + b = color_parseNumber(b); + } + a = color_parseAlpha(a); + if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) return null; + return rgba(r, g, b, a); +} + +function color_parseHsl(S) { + S = S.split(",").map(s => s.trim()); + if (S.length < 3 || S.length > 4) return null; + let [h, s, l, a] = S; + h = color_parseNumber(h); + if (color_isPercent(s) || color_isPercent(l)) { + s = color_parsePercent(s); + l = color_parsePercent(l); + } else { + s = color_parseNumber(s); + l = color_parseNumber(l); + } + a = color_parseAlpha(a); + if (isNaN(h) || isNaN(s) || isNaN(l) || isNaN(a)) return null; + return hsla(h, s, l, a); +} + export default function color(format) { var m, l; format = (format + "").trim().toLowerCase(); @@ -201,12 +245,8 @@ export default function color(format) { : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000 : l === 4 ? rgba((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000 : null) // invalid hex - : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) - : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) - : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) - : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) - : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) - : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) + : (m = reRgb.exec(format)) ? color_parseRgb(m[1]) // rgb(255, 0, 0) + : (m = reHsl.exec(format)) ? color_parseHsl(m[1]) // hsl(120, 50%, 50%) : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null; diff --git a/test/color-test.js b/test/color-test.js index 2c44313..95ade59 100644 --- a/test/color-test.js +++ b/test/color-test.js @@ -127,8 +127,8 @@ tape("color(format) allows exponential format for hue, opacity and percentages", test.end(); }); -tape("color(format) does not allow decimals for integer values", function(test) { - test.equal(color.color("rgb(120.5,30,50)"), null); +tape("color(format) allows decimals for integer values", function(test) { + test.rgbEqual(color.color("rgb(120.5,30,50)"), 120.5, 30, 50, 1); test.end(); });