UNPKG

@pagyew/safolor

Version:

Convert colors to 8-bit web-safe colors

116 lines (112 loc) 3.87 kB
'use strict'; 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;