@pagyew/safolor
Version:
Convert colors to 8-bit web-safe colors
116 lines (112 loc) • 3.87 kB
JavaScript
;
const validHEXSymbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
function hexToInt(symbol) {
return Number.parseInt(symbol, 16);
}
function intToHex(number) {
const str = number.toString(16);
return str.padEnd(2, str);
}
function normalizePct(number) {
return Math.max(0, Math.min(100, number));
}
function normalizeRGBValue(number) {
return Math.max(0, Math.min(255, number));
}
function pctToInt(pct) {
return normalizePct(Number.parseFloat(pct)) / 100;
}
function rgbValueToInt(value) {
return normalizeRGBValue(Number.parseFloat(value));
}
function parseHEX(color) {
if (color.toLowerCase().split("").slice(1).some((l) => !validHEXSymbols.includes(l)))
throw new SyntaxError("For HEX format expected only HEX symbols");
const hex = color.slice(1);
let r;
let g;
let b;
let a;
switch (hex.length) {
case 3:
case 4:
r = hexToInt(hex[0] + hex[0]);
g = hexToInt(hex[1] + hex[1]);
b = hexToInt(hex[2] + hex[2]);
hex[3] && (a = hexToInt(hex[3] + hex[3]));
break;
case 6:
case 8:
r = hexToInt(hex[0] + hex[1]);
g = hexToInt(hex[2] + hex[3]);
b = hexToInt(hex[4] + hex[5]);
hex[6] && (a = hexToInt(hex[6] + hex[7]));
break;
default:
throw new SyntaxError("For HEX format expected 3, 4, 6, or 8 symbols");
}
return { r, g, b, a };
}
function parseRGB(color) {
const rgbLegacyPct = color.match(/^rgba?\(\s*(\d*\.?\d+%)\s*,\s*(\d*\.?\d+%)\s*,\s*(\d*\.?\d+%)\s*(,\s*(\d*\.?\d+%?)\s*)?\)$/);
const rgbLegacyInt = color.match(/^rgba?\(\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\s*(,\s*(\d*\.?\d+%?)\s*)?\)$/);
const rgbModernPct = color.match(/^rgba?\(\s*(\d*\.?\d+%|none)\s+(\d*\.?\d+%|none)\s+(\d*\.?\d+%|none)\s*(?:\/\s*(\d*\.?\d+%?|none)\s*)?\)$/);
const rgbModernInt = color.match(/^rgba?\(\s*(\d*\.?\d+|none)\s+(\d*\.?\d+|none)\s+(\d*\.?\d+|none)\s*(?:\/\s*(\d*\.?\d+%?|none)\s*)?\)$/);
const rgb = rgbLegacyPct || rgbLegacyInt || rgbModernPct || rgbModernInt;
if (rgb === null)
throw new SyntaxError("Invalid RGB format, expected legacy or modern syntax with 3 or 4 values");
const [_, R, G, B, A = "1"] = rgb;
const r = R === "none" ? 0 : R.endsWith("%") ? pctToInt(R) * 255 : rgbValueToInt(R);
const g = G === "none" ? 0 : G.endsWith("%") ? pctToInt(G) * 255 : rgbValueToInt(G);
const b = B === "none" ? 0 : B.endsWith("%") ? pctToInt(B) * 255 : rgbValueToInt(B);
const a = A === "none" ? 0 : A.endsWith("%") ? pctToInt(A) : Math.max(0, Math.min(1, Number.parseFloat(A)));
return { r, g, b, a };
}
function parse(color) {
if (typeof color !== "string")
throw new TypeError("Expected a string");
if (!(color.startsWith("#") || color.startsWith("rgb")))
throw new SyntaxError("Expected a HEX or RGB format");
let rgbObject = { r: 0, g: 0, b: 0, a: 1 };
if (color.startsWith("#"))
rgbObject = parseHEX(color);
else if (color.startsWith("rgb"))
rgbObject = parseRGB(color);
return rgbObject;
}
function safe(color) {
const { r, g, b } = color;
const nR = Math.round(r / 51) * 51;
const nG = Math.round(g / 51) * 51;
const nB = Math.round(b / 51) * 51;
return { r: nR, g: nG, b: nB };
}
const safolor = (() => {
let color;
let safeColor;
function exec(original) {
color = parse(original);
safeColor = safe(color);
}
function hex(color2) {
exec(color2);
const { r, g, b } = safeColor;
return `#${intToHex(r)}${intToHex(g)}${intToHex(b)}`;
}
function rgb(color2) {
const { r, g, b } = call.rgbObj(color2);
return `rgb(${r}, ${g}, ${b})`;
}
function rgbObj(color2) {
exec(color2);
return safeColor;
}
function call(color2) {
return call.hex(color2);
}
call.hex = hex;
call.rgb = rgb;
call.rgbObj = rgbObj;
return call;
})();
exports.safolor = safolor;