Skip to content

Commit

Permalink
updated test suite using the web-platform-tests suite
Browse files Browse the repository at this point in the history
  • Loading branch information
erikyo committed Dec 27, 2023
1 parent 14c6225 commit b93a058
Show file tree
Hide file tree
Showing 15 changed files with 4,293 additions and 402 deletions.
90 changes: 60 additions & 30 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ export const hexRegex: RegExp = /^#([\da-f]{6,}|[\da-f]{3,})/i;
export const rgbRegex: RegExp = /^rgba?\(([^)]+)\)/i;
/** A regex to match hsl colors */
export const hslRegex: RegExp = /^hsla?\(([^)]+)\)/i;
/** A regex to match strings that are only int numbers */
export const isNumeric: RegExp = /^[0-9]*$/;
/** A regex to match strings that are only valid numbers with and without decimals and the number can be negative and without the 0 in the beginning */
export const isNumeric: RegExp = /^-?\d*\.?\d+$/i;
/** Remove comments from string */
export const stripComments: RegExp = /(\/\*[^*]*\*\/)|(\/\/[^*]*)/g

/**
* This set is used in order to detect if the color is bright or dark
* This set is used to detect if the color is bright or dark
*
* @note the set has been corrected to get pure RGB values (eg. pure red, pure green) in the "bright" area
*/
Expand All @@ -27,7 +29,7 @@ export const BLACKANDWHITE: RGBCOLORDEF[] = [
];

/**
* This set is used in order to detect the nearest rgb color
* This set is used to detect the nearest rgb color
*/
export const RGBSET: RGBCOLORDEF[] = [
[255, 0, 0, "red"],
Expand All @@ -42,59 +44,87 @@ export const RGBSET: RGBCOLORDEF[] = [
*
* @param {string} rawValues - the value inside the rgb(.*) css color definition
*
* @return {Array} the array of rgb values finded inside the passed string
* @return {Array} the array of rgb values found inside the passed string
*/
export function splitValues(rawValues: string): string[] {
if (rawValues.includes(",")) {
return rawValues.split(/[,\\/]/).map((val) => val.trim());
}

return rawValues
.split(/[ \\/]/)
.map((val) => val.trim())
.filter(Boolean);
return rawValues.split(
rawValues.includes(",") ? "," : " "
).map(
s => s.trim()
);
}

/**
* If the value is an angle in degrees, convert it to the 0-360 range
*
* @param {string} value - the degrees to convert into a number
* @param {number} multiplier - the number that represent the maximum - default is 100
* @param {string} angle - the degrees to convert into a number
*
* @return {number} the converted value
*/
export function normalizeDegree(value: string, multiplier: number = 360): number {
let angle = parseFloat(value);
while (angle < 0) {
angle += 360;
export function normalizeDegrees(angle: string): number {
// Strip label and convert to degrees (if necessary)
let degAngle = parseFloat(angle) || 0;
if (angle.indexOf("deg") > -1) {
degAngle = parseFloat( angle.substring(0, angle.length - 3) );
}else if (angle.indexOf("rad") > -1) {
degAngle = Math.round(parseFloat( angle.substring(0, angle.length - 3)) * (180 / Math.PI));
}else if (angle.indexOf("turn") > -1) {
degAngle = Math.round(parseFloat( angle.substring(0, angle.length - 4)) * 360);
}
while (angle > 360) {
angle -= 360;

while (degAngle < 0) {
degAngle += 360;
}
return (angle / 360) * multiplier;

// Make sure it's a number between 0 and 360
if (degAngle >= 360)
degAngle %= 360;

return degAngle;
}

export function normalizePercentage(value: string, multiplier: number): number {
return (parseFloat(value) / 100) * multiplier
}

export function colorValueFallbacks(value: string, err?: string): number {
if (value === 'none') {
console.log(err ?? 'Invalid value ' + value )
}

return 0;
}

/**
* Takes a string with a css value that could be a number or percentage or an angle in degrees and returns the corresponding 8bit value
*
* @param {string} value - a valid value for the css color definition (like 255, "100%", "324deg", etc)
* @param {string} value - a valid value for the css color definition (like 255, "100%", "324deg", etc)
* @param {string} value - a valid value for the css color definition (like 255, "100%", "324deg", etc.) *
* @param multiplier - the number that represent the maximum - default is 255 decimal - 100 hex
*
* @example convertToInt8('100%'); // 255
*
* @return {string} the corresponding value in 8 bit format
* @return {string} the corresponding value in 8-bit format
*/
export function convertToInt8(value: string, multiplier: number = 255): number {
value = value.trim();
if (isNumeric.test(value)) {
// If the value is an int number return it as number
return parseInt(value, 10);
// limit the min and the max value
return Math.min(
Math.max(
Math.round(
parseFloat(value) || 0
),
0
), multiplier
);
} else if (value.endsWith("%")) {
// If the value is a percentage, divide it by 100 to get a value from 0 to 1
// and then multiply it by 255 to get a value from 0 to 255
return (parseFloat(value) / 100) * multiplier;
} else if (value.endsWith("deg")) {
return normalizeDegree(value, 255);
return normalizePercentage(value, multiplier) || 0;
} else if (value.endsWith("deg") || value.endsWith("rad") || value.endsWith("turn")) {
return normalizeDegrees(value);
} else if (value === "none") {
return 0
} else {
// If the value is not a percentage or an angle in degrees, it is invalid
throw new Error(`Invalid value: ${value}`);
Expand Down
71 changes: 27 additions & 44 deletions src/hsl-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hslRegex, normalizeDegree, splitValues } from "./common";
import {colorValueFallbacks, convertToInt8, hslRegex, normalizeDegrees, normalizePercentage, splitValues} from "./common";
import { HSLVALUE, RGBVALUE } from "./types";

/**
Expand All @@ -19,6 +19,8 @@ export function parseHsl(hslAsString: string): string[] {
throw new Error(`Can't parse hsl color: ${hslAsString}`);
}

const angleError = (value: string): string => `Invalid angle: ${value} - The none keyword is invalid in legacy color syntax `;

/**
* This function takes an array of strings and returns and object with the hsl values converted into INT8 (0-255)
*
Expand All @@ -28,63 +30,44 @@ export function parseHsl(hslAsString: string): string[] {
export function getHslValues(hsl: string[]): HSLVALUE {
if (hsl.length >= 2) {
return {
h: normalizeDegree(hsl[0]),
s: parseInt(hsl[1], 10),
l: parseInt(hsl[2], 10),
h: colorValueFallbacks(hsl[0], angleError(hsl[0]) ) || Math.round(normalizeDegrees(hsl[0])),
s: colorValueFallbacks(hsl[1]) || normalizePercentage(hsl[1], 100) || 0,
l: colorValueFallbacks(hsl[2]) || convertToInt8(hsl[2], 100),
};
}
throw new Error(`Invalid hsl color: ${hsl.join(", ")}`);
}

function getHue(c: number, x: number, h: number): [number, number, number] {
if (h < 60) return [c, x, 0];
if (h < 120) return [x, c, 0];
if (h < 180) return [0, c, x];
if (h < 240) return [0, x, c];
if (h < 300) return [x, 0, c];
return [c, 0, x];
}

/**
* Given the HSL color it convert the color into RGB
*
* @param {string[]} hslColor the HSL value to parse
* @return {Object} rgb value
*/
export function hslToRgb(hslColor: string[]): RGBVALUE {
if (hslColor === null || hslColor.length < 2) {
throw new Error(`Invalid hsl color: ${hslColor.join(", ")}`);
if (!hslColor || hslColor.length < 3) {
throw new Error(`Invalid HSL color: ${hslColor}`);
}

const hsl = getHslValues(hslColor);

// Must be fractions of 1
hsl.s /= 100;
hsl.l /= 100;

const c = (1 - Math.abs(2 * hsl.l - 1)) * hsl.s;
const x = c * (1 - Math.abs(((hsl.h / 60) % 2) - 1));
const m = hsl.l - c / 2;
let r = 0;
let g = 0;
let b = 0;

if (hsl.h >= 0 && hsl.h < 60) {
r = c;
g = x;
b = 0;
} else if (hsl.h >= 60 && hsl.h < 120) {
r = x;
g = c;
b = 0;
} else if (hsl.h >= 120 && hsl.h < 180) {
r = 0;
g = c;
b = x;
} else if (hsl.h >= 180 && hsl.h < 240) {
r = 0;
g = x;
b = c;
} else if (hsl.h >= 240 && hsl.h < 300) {
r = x;
g = 0;
b = c;
} else if (hsl.h >= 300 && hsl.h < 360) {
r = c;
g = 0;
b = x;
}
let hsl = getHslValues(hslColor),
s = hsl.s / 100,
l = hsl.l / 100;

const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((hsl.h / 60) % 2 - 1));
const m = l - c / 2;

let [r, g, b] = getHue(c, x, hsl.h);

r = Math.round((r + m) * 255);
g = Math.round((g + m) * 255);
b = Math.round((b + m) * 255);
Expand Down
15 changes: 1 addition & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,6 @@ function isDark(color: string): boolean {
return closest(color, BLACKANDWHITE).name === "black";
}

/**
* Given a color returns if the color is light or dark (by dark is meant more mathematically closer to black)
*
* @param {string} color - The color to check
*
* @returns {string} light when the color is close to white, dark otherwise
*
* @example isLightOrDark('#fff'); // 'light'
*/
function isLightOrDark(color: string): string {
return isLight(color) ? "light" : "dark";
}

/**
* Given a color returns if the color is closer to "red", "green" or "blue".
*
Expand Down Expand Up @@ -154,4 +141,4 @@ function rgbToHex(rgbString: string): HEX | Error {
throw new Error(`Invalid color: ${rgbString}`);
}

export { closest, rgbToHex, distance, isLight, isDark, isLightOrDark, closestRGB };
export { closest, rgbToHex, distance, isLight, isDark, closestRGB };
10 changes: 5 additions & 5 deletions src/rgb-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { convertToInt8, rgbRegex, splitValues } from "./common";
import {convertToInt8, rgbRegex, splitValues, stripComments} from "./common";
import { RGBVALUE } from "./types";

/**
Expand All @@ -9,7 +9,7 @@ import { RGBVALUE } from "./types";
* @return {Array} the values of the rgb string as Array of strings that represent the rgb color
*/
export function parseRgb(rgbAsString: string): string[] {
const rgbvalue = rgbAsString.match(rgbRegex);
const rgbvalue = rgbAsString.replace(stripComments, '').match(rgbRegex);
if (rgbvalue != null) {
const rgb: string[] = splitValues(rgbvalue[1]);

Expand All @@ -30,9 +30,9 @@ export function parseRgb(rgbAsString: string): string[] {
export function getRgbValues(rgb: string[]): RGBVALUE {
if (rgb.length >= 2) {
return {
r: convertToInt8(rgb[0]),
g: convertToInt8(rgb[1]),
b: convertToInt8(rgb[2]),
r: Math.round(convertToInt8(rgb[0])),
g: Math.round(convertToInt8(rgb[1])),
b: Math.round(convertToInt8(rgb[2])),
};
}
throw new Error(`Invalid rgb color: ${rgb.join(", ")}`);
Expand Down
19 changes: 10 additions & 9 deletions tests/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import b from 'benny'
import { colord, extend } from 'colord'
import namesPlugin from 'colord/plugins/names'
import {closest} from "../src";
import {closest} from "color-2-name";
import * as fs from "fs";

extend([namesPlugin])
Expand All @@ -26,11 +26,12 @@ b.suite(

b.cycle(),
b.complete()
)

// get the benchmark folder path
const path = process.cwd() + '/bench'
if (fs.existsSync(path)) {
console.log('Removing bench folder at ' + path)
fs.rmSync(path, {recursive: true})
}
).then( () => {
// get the benchmark folder path
const path = process.cwd() + '/bench'
if (fs.existsSync(path)) {
console.log('Removing bench folder at ' + path)
fs.rmSync(path, {recursive: true})
}
process.exit()
} )
Loading

0 comments on commit b93a058

Please sign in to comment.