Spaces:
Running
on
Zero
Running
on
Zero
| // #region patterns | |
| const float = '-?\\d*(?:\\.\\d+)'; | |
| export const number = `(${float}?)`; | |
| export const percentage = `(${float}?%)`; | |
| export const numberOrPercentage = `(${float}?%?)`; | |
| const clamp = (num, min, max) => Math.min(Math.max(min, num), max); | |
| const hexCharacters = 'a-f\\d'; | |
| const match3or4Hex = `#?[${hexCharacters}]{3}[${hexCharacters}]?`; | |
| const match6or8Hex = `#?[${hexCharacters}]{6}([${hexCharacters}]{2})?`; | |
| const nonHexChars = new RegExp(`[^#${hexCharacters}]`, 'gi'); | |
| const validHexSize = new RegExp(`^${match3or4Hex}$|^${match6or8Hex}$`, 'i'); | |
| export const hex_pattern = new RegExp(/^#([a-f0-9]{3,4}|[a-f0-9]{4}(?:[a-f0-9]{2}){1,2})\b$/, "i"); | |
| export const hsl3_pattern = new RegExp(`^ | |
| hsla?\\( | |
| \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s*, | |
| \\s*${percentage}\\s*, | |
| \\s*${percentage}\\s* | |
| (?:,\\s*${numberOrPercentage}\\s*)? | |
| \\) | |
| $ | |
| `.replace(/\n|\s/g, '')) | |
| export const hsl4_pattern = new RegExp(`^ | |
| hsla?\\( | |
| \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s* | |
| \\s+${percentage} | |
| \\s+${percentage} | |
| \\s*(?:\\s*\\/\\s*${numberOrPercentage}\\s*)? | |
| \\) | |
| $ | |
| `.replace(/\n|\s/g, '')) | |
| export const rgb3_pattern = new RegExp(`^ | |
| rgba?\\( | |
| \\s*${number}\\s*, | |
| \\s*${number}\\s*, | |
| \\s*${number}\\s* | |
| (?:,\\s*${numberOrPercentage}\\s*)? | |
| \\) | |
| $ | |
| `.replace(/\n|\s/g, '')) | |
| export const rgb4_pattern = new RegExp(`^ | |
| rgba?\\( | |
| \\s*${number} | |
| \\s+${number} | |
| \\s+${number} | |
| \\s*(?:\\s*\\/\\s*${numberOrPercentage}\\s*)? | |
| \\) | |
| $ | |
| `.replace(/\n|\s/g, '')); | |
| export const transparent_pattern = new RegExp(/^transparent$/, 'i'); | |
| // #endregion | |
| // #region utils | |
| /* 500 => 255, -10 => 0, 128 => 128 */ | |
| const parseRGB = (num) => { | |
| let n = num; | |
| if (typeof n !== 'number') { | |
| n = n.endsWith('%') ? (parseFloat(n) * 255) / 100 : parseFloat(n); | |
| } | |
| return clamp(Math.round(n), 0, 255); | |
| }; | |
| /* 200 => 100, -100 => 0, 50 => 50 */ | |
| const parsePercentage = (percentage) => clamp(parseFloat(percentage), 0, 100); | |
| /* '50%' => 5.0, 200 => 1, -10 => 0 */ | |
| function parseAlpha(alpha) { | |
| let a = alpha; | |
| if (typeof a !== 'number') { | |
| a = a.endsWith('%') ? parseFloat(a) / 100 : parseFloat(a); | |
| } | |
| return clamp(a, 0, 1); | |
| } | |
| export function getHEX(hex) { | |
| const [r, g, b, a] = hex2Rgb(hex, { format: 'array' }); | |
| return getRGB([null, ...[r, g, b, a]]); | |
| } | |
| export function getHSL([, h, s, l, a = 1]) { | |
| let hh = h; | |
| if (hh.endsWith('turn')) { | |
| hh = (parseFloat(hh) * 360) / 1; | |
| } else if (hh.endsWith('rad')) { | |
| hh = Math.round((parseFloat(hh) * 180) / Math.PI); | |
| } else { | |
| hh = parseFloat(hh); | |
| } | |
| return { | |
| type: 'hsl', | |
| values: [hh, parsePercentage(s), parsePercentage(l)], | |
| alpha: parseAlpha(a === null ? 1 : a) | |
| }; | |
| } | |
| export function getRGB([, r, g, b, a = 1]) { | |
| return { | |
| type: 'rgb', | |
| values: [r, g, b].map(parseRGB), | |
| alpha: parseAlpha(a === null ? 1 : a) | |
| }; | |
| } | |
| export function hex2Rgb(hex, options = {}) { | |
| if (typeof hex !== 'string' || nonHexChars.test(hex) || !validHexSize.test(hex)) { | |
| throw new TypeError('Expected a valid hex string'); | |
| } | |
| hex = hex.replace(/^#/, ''); | |
| let alphaFromHex = 1; | |
| if (hex.length === 8) { | |
| alphaFromHex = Number.parseInt(hex.slice(6, 8), 16) / 255; | |
| hex = hex.slice(0, 6); | |
| } | |
| if (hex.length === 4) { | |
| alphaFromHex = Number.parseInt(hex.slice(3, 4).repeat(2), 16) / 255; | |
| hex = hex.slice(0, 3); | |
| } | |
| if (hex.length === 3) { | |
| hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; | |
| } | |
| const number = Number.parseInt(hex, 16); | |
| const red = number >> 16; | |
| const green = (number >> 8) & 255; | |
| const blue = number & 255; | |
| const alpha = typeof options.alpha === 'number' ? options.alpha : alphaFromHex; | |
| if (options.format === 'array') { | |
| return [red, green, blue, alpha]; | |
| } | |
| if (options.format === 'css') { | |
| const alphaString = alpha === 1 ? '' : ` / ${Number((alpha * 100).toFixed(2))}%`; | |
| return `rgb(${red} ${green} ${blue}${alphaString})`; | |
| } | |
| return {red, green, blue, alpha}; | |
| } | |
| // #endregion | |
| // #region colorNames | |
| export const colorName = { | |
| aliceblue: [240, 248, 255], | |
| antiquewhite: [250, 235, 215], | |
| aqua: [0, 255, 255], | |
| aquamarine: [127, 255, 212], | |
| azure: [240, 255, 255], | |
| beige: [245, 245, 220], | |
| bisque: [255, 228, 196], | |
| black: [0, 0, 0], | |
| blanchedalmond: [255, 235, 205], | |
| blue: [0, 0, 255], | |
| blueviolet: [138, 43, 226], | |
| brown: [165, 42, 42], | |
| burlywood: [222, 184, 135], | |
| cadetblue: [95, 158, 160], | |
| chartreuse: [127, 255, 0], | |
| chocolate: [210, 105, 30], | |
| coral: [255, 127, 80], | |
| cornflowerblue: [100, 149, 237], | |
| cornsilk: [255, 248, 220], | |
| crimson: [220, 20, 60], | |
| cyan: [0, 255, 255], | |
| darkblue: [0, 0, 139], | |
| darkcyan: [0, 139, 139], | |
| darkgoldenrod: [184, 134, 11], | |
| darkgray: [169, 169, 169], | |
| darkgreen: [0, 100, 0], | |
| darkgrey: [169, 169, 169], | |
| darkkhaki: [189, 183, 107], | |
| darkmagenta: [139, 0, 139], | |
| darkolivegreen: [85, 107, 47], | |
| darkorange: [255, 140, 0], | |
| darkorchid: [153, 50, 204], | |
| darkred: [139, 0, 0], | |
| darksalmon: [233, 150, 122], | |
| darkseagreen: [143, 188, 143], | |
| darkslateblue: [72, 61, 139], | |
| darkslategray: [47, 79, 79], | |
| darkslategrey: [47, 79, 79], | |
| darkturquoise: [0, 206, 209], | |
| darkviolet: [148, 0, 211], | |
| deeppink: [255, 20, 147], | |
| deepskyblue: [0, 191, 255], | |
| dimgray: [105, 105, 105], | |
| dimgrey: [105, 105, 105], | |
| dodgerblue: [30, 144, 255], | |
| firebrick: [178, 34, 34], | |
| floralwhite: [255, 250, 240], | |
| forestgreen: [34, 139, 34], | |
| fuchsia: [255, 0, 255], | |
| gainsboro: [220, 220, 220], | |
| ghostwhite: [248, 248, 255], | |
| gold: [255, 215, 0], | |
| goldenrod: [218, 165, 32], | |
| gray: [128, 128, 128], | |
| green: [0, 128, 0], | |
| greenyellow: [173, 255, 47], | |
| grey: [128, 128, 128], | |
| honeydew: [240, 255, 240], | |
| hotpink: [255, 105, 180], | |
| indianred: [205, 92, 92], | |
| indigo: [75, 0, 130], | |
| ivory: [255, 255, 240], | |
| khaki: [240, 230, 140], | |
| lavender: [230, 230, 250], | |
| lavenderblush: [255, 240, 245], | |
| lawngreen: [124, 252, 0], | |
| lemonchiffon: [255, 250, 205], | |
| lightblue: [173, 216, 230], | |
| lightcoral: [240, 128, 128], | |
| lightcyan: [224, 255, 255], | |
| lightgoldenrodyellow: [250, 250, 210], | |
| lightgray: [211, 211, 211], | |
| lightgreen: [144, 238, 144], | |
| lightgrey: [211, 211, 211], | |
| lightpink: [255, 182, 193], | |
| lightsalmon: [255, 160, 122], | |
| lightseagreen: [32, 178, 170], | |
| lightskyblue: [135, 206, 250], | |
| lightslategray: [119, 136, 153], | |
| lightslategrey: [119, 136, 153], | |
| lightsteelblue: [176, 196, 222], | |
| lightyellow: [255, 255, 224], | |
| lime: [0, 255, 0], | |
| limegreen: [50, 205, 50], | |
| linen: [250, 240, 230], | |
| magenta: [255, 0, 255], | |
| maroon: [128, 0, 0], | |
| mediumaquamarine: [102, 205, 170], | |
| mediumblue: [0, 0, 205], | |
| mediumorchid: [186, 85, 211], | |
| mediumpurple: [147, 112, 219], | |
| mediumseagreen: [60, 179, 113], | |
| mediumslateblue: [123, 104, 238], | |
| mediumspringgreen: [0, 250, 154], | |
| mediumturquoise: [72, 209, 204], | |
| mediumvioletred: [199, 21, 133], | |
| midnightblue: [25, 25, 112], | |
| mintcream: [245, 255, 250], | |
| mistyrose: [255, 228, 225], | |
| moccasin: [255, 228, 181], | |
| navajowhite: [255, 222, 173], | |
| navy: [0, 0, 128], | |
| oldlace: [253, 245, 230], | |
| olive: [128, 128, 0], | |
| olivedrab: [107, 142, 35], | |
| orange: [255, 165, 0], | |
| orangered: [255, 69, 0], | |
| orchid: [218, 112, 214], | |
| palegoldenrod: [238, 232, 170], | |
| palegreen: [152, 251, 152], | |
| paleturquoise: [175, 238, 238], | |
| palevioletred: [219, 112, 147], | |
| papayawhip: [255, 239, 213], | |
| peachpuff: [255, 218, 185], | |
| peru: [205, 133, 63], | |
| pink: [255, 192, 203], | |
| plum: [221, 160, 221], | |
| powderblue: [176, 224, 230], | |
| purple: [128, 0, 128], | |
| rebeccapurple: [102, 51, 153], | |
| red: [255, 0, 0], | |
| rosybrown: [188, 143, 143], | |
| royalblue: [65, 105, 225], | |
| saddlebrown: [139, 69, 19], | |
| salmon: [250, 128, 114], | |
| sandybrown: [244, 164, 96], | |
| seagreen: [46, 139, 87], | |
| seashell: [255, 245, 238], | |
| sienna: [160, 82, 45], | |
| silver: [192, 192, 192], | |
| skyblue: [135, 206, 235], | |
| slateblue: [106, 90, 205], | |
| slategray: [112, 128, 144], | |
| slategrey: [112, 128, 144], | |
| snow: [255, 250, 250], | |
| springgreen: [0, 255, 127], | |
| steelblue: [70, 130, 180], | |
| tan: [210, 180, 140], | |
| teal: [0, 128, 128], | |
| thistle: [216, 191, 216], | |
| tomato: [255, 99, 71], | |
| turquoise: [64, 224, 208], | |
| violet: [238, 130, 238], | |
| wheat: [245, 222, 179], | |
| white: [255, 255, 255], | |
| whitesmoke: [245, 245, 245], | |
| yellow: [255, 255, 0], | |
| yellowgreen: [154, 205, 50] | |
| } | |
| // #endregion | |
| export const parseCSSColor = (str, debug=false) => { | |
| if (typeof str !== 'string') { | |
| console.error(`parseCSSColor: expected a string found ${typeof str}`,str); | |
| return null; | |
| } | |
| const hex = hex_pattern.exec(str); | |
| if (hex) { | |
| if (debug){ | |
| console.debug('parseCSSColor: hex', hex); | |
| } | |
| return getHEX(hex[0]); | |
| } | |
| const hsl = hsl4_pattern.exec(str) || hsl3_pattern.exec(str); | |
| if (hsl) { | |
| if (debug){ | |
| console.debug('parseCSSColor: hsl', hsl); | |
| } | |
| return getHSL(hsl); | |
| } | |
| const rgb = | |
| rgb4_pattern.exec(str) || | |
| rgb3_pattern.exec(str) | |
| if (rgb) { | |
| if (debug){ | |
| console.debug('parseCSSColor: rgb', rgb); | |
| } | |
| return getRGB(rgb); | |
| } | |
| if (transparent_pattern.exec(str)) { | |
| if (debug){ | |
| console.debug('parseCSSColor: transparent'); | |
| } | |
| return getRGB([null, 0, 0, 0, 0]); | |
| } | |
| const cn = colorName[str.toLowerCase()]; | |
| if (cn) { | |
| if (debug){ | |
| console.debug('parseCSSColor: colorName', cn); | |
| } | |
| return getRGB([null, cn[0], cn[1], cn[2], 1]); | |
| } | |
| console.error('parseCSSColor: unknown color', str); | |
| return null; | |
| }; | |
| export default parseCSSColor; | |