305 lines
9.0 KiB
TypeScript
305 lines
9.0 KiB
TypeScript
import {CSSValue, nonFunctionArgSeparator} from '../syntax/parser';
|
|
import {TokenType} from '../syntax/tokenizer';
|
|
import {ITypeDescriptor} from '../ITypeDescriptor';
|
|
import {angle, deg} from './angle';
|
|
import {getAbsoluteValue, isLengthPercentage} from './length-percentage';
|
|
export type Color = number;
|
|
|
|
export const color: ITypeDescriptor<Color> = {
|
|
name: 'color',
|
|
parse: (value: CSSValue): Color => {
|
|
if (value.type === TokenType.FUNCTION) {
|
|
const colorFunction = SUPPORTED_COLOR_FUNCTIONS[value.name];
|
|
if (typeof colorFunction === 'undefined') {
|
|
throw new Error(`Attempting to parse an unsupported color function "${value.name}"`);
|
|
}
|
|
return colorFunction(value.values);
|
|
}
|
|
|
|
if (value.type === TokenType.HASH_TOKEN) {
|
|
if (value.value.length === 3) {
|
|
const r = value.value.substring(0, 1);
|
|
const g = value.value.substring(1, 2);
|
|
const b = value.value.substring(2, 3);
|
|
return pack(parseInt(r + r, 16), parseInt(g + g, 16), parseInt(b + b, 16), 1);
|
|
}
|
|
|
|
if (value.value.length === 4) {
|
|
const r = value.value.substring(0, 1);
|
|
const g = value.value.substring(1, 2);
|
|
const b = value.value.substring(2, 3);
|
|
const a = value.value.substring(3, 4);
|
|
return pack(parseInt(r + r, 16), parseInt(g + g, 16), parseInt(b + b, 16), parseInt(a + a, 16) / 255);
|
|
}
|
|
|
|
if (value.value.length === 6) {
|
|
const r = value.value.substring(0, 2);
|
|
const g = value.value.substring(2, 4);
|
|
const b = value.value.substring(4, 6);
|
|
return pack(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1);
|
|
}
|
|
|
|
if (value.value.length === 8) {
|
|
const r = value.value.substring(0, 2);
|
|
const g = value.value.substring(2, 4);
|
|
const b = value.value.substring(4, 6);
|
|
const a = value.value.substring(6, 8);
|
|
return pack(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), parseInt(a, 16) / 255);
|
|
}
|
|
}
|
|
|
|
if (value.type === TokenType.IDENT_TOKEN) {
|
|
const namedColor = COLORS[value.value.toUpperCase()];
|
|
if (typeof namedColor !== 'undefined') {
|
|
return namedColor;
|
|
}
|
|
}
|
|
|
|
return COLORS.TRANSPARENT;
|
|
}
|
|
};
|
|
|
|
export const isTransparent = (color: Color): boolean => (0xff & color) === 0;
|
|
|
|
export const asString = (color: Color): string => {
|
|
const alpha = 0xff & color;
|
|
const blue = 0xff & (color >> 8);
|
|
const green = 0xff & (color >> 16);
|
|
const red = 0xff & (color >> 24);
|
|
return alpha < 255 ? `rgba(${red},${green},${blue},${alpha / 255})` : `rgb(${red},${green},${blue})`;
|
|
};
|
|
|
|
export const pack = (r: number, g: number, b: number, a: number): Color =>
|
|
((r << 24) | (g << 16) | (b << 8) | (Math.round(a * 255) << 0)) >>> 0;
|
|
|
|
const getTokenColorValue = (token: CSSValue, i: number): number => {
|
|
if (token.type === TokenType.NUMBER_TOKEN) {
|
|
return token.number;
|
|
}
|
|
|
|
if (token.type === TokenType.PERCENTAGE_TOKEN) {
|
|
const max = i === 3 ? 1 : 255;
|
|
return i === 3 ? (token.number / 100) * max : Math.round((token.number / 100) * max);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
const rgb = (args: CSSValue[]): number => {
|
|
const tokens = args.filter(nonFunctionArgSeparator);
|
|
|
|
if (tokens.length === 3) {
|
|
const [r, g, b] = tokens.map(getTokenColorValue);
|
|
return pack(r, g, b, 1);
|
|
}
|
|
|
|
if (tokens.length === 4) {
|
|
const [r, g, b, a] = tokens.map(getTokenColorValue);
|
|
return pack(r, g, b, a);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
function hue2rgb(t1: number, t2: number, hue: number): number {
|
|
if (hue < 0) {
|
|
hue += 1;
|
|
}
|
|
if (hue >= 1) {
|
|
hue -= 1;
|
|
}
|
|
|
|
if (hue < 1 / 6) {
|
|
return (t2 - t1) * hue * 6 + t1;
|
|
} else if (hue < 1 / 2) {
|
|
return t2;
|
|
} else if (hue < 2 / 3) {
|
|
return (t2 - t1) * 6 * (2 / 3 - hue) + t1;
|
|
} else {
|
|
return t1;
|
|
}
|
|
}
|
|
|
|
const hsl = (args: CSSValue[]): number => {
|
|
const tokens = args.filter(nonFunctionArgSeparator);
|
|
const [hue, saturation, lightness, alpha] = tokens;
|
|
|
|
const h = (hue.type === TokenType.NUMBER_TOKEN ? deg(hue.number) : angle.parse(hue)) / (Math.PI * 2);
|
|
const s = isLengthPercentage(saturation) ? saturation.number / 100 : 0;
|
|
const l = isLengthPercentage(lightness) ? lightness.number / 100 : 0;
|
|
const a = typeof alpha !== 'undefined' && isLengthPercentage(alpha) ? getAbsoluteValue(alpha, 1) : 1;
|
|
|
|
if (s === 0) {
|
|
return pack(l * 255, l * 255, l * 255, 1);
|
|
}
|
|
|
|
const t2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
|
|
|
|
const t1 = l * 2 - t2;
|
|
const r = hue2rgb(t1, t2, h + 1 / 3);
|
|
const g = hue2rgb(t1, t2, h);
|
|
const b = hue2rgb(t1, t2, h - 1 / 3);
|
|
return pack(r * 255, g * 255, b * 255, a);
|
|
};
|
|
|
|
const SUPPORTED_COLOR_FUNCTIONS: {
|
|
[key: string]: (args: CSSValue[]) => number;
|
|
} = {
|
|
hsl: hsl,
|
|
hsla: hsl,
|
|
rgb: rgb,
|
|
rgba: rgb
|
|
};
|
|
|
|
export const COLORS: {[key: string]: Color} = {
|
|
ALICEBLUE: 0xf0f8ffff,
|
|
ANTIQUEWHITE: 0xfaebd7ff,
|
|
AQUA: 0x00ffffff,
|
|
AQUAMARINE: 0x7fffd4ff,
|
|
AZURE: 0xf0ffffff,
|
|
BEIGE: 0xf5f5dcff,
|
|
BISQUE: 0xffe4c4ff,
|
|
BLACK: 0x000000ff,
|
|
BLANCHEDALMOND: 0xffebcdff,
|
|
BLUE: 0x0000ffff,
|
|
BLUEVIOLET: 0x8a2be2ff,
|
|
BROWN: 0xa52a2aff,
|
|
BURLYWOOD: 0xdeb887ff,
|
|
CADETBLUE: 0x5f9ea0ff,
|
|
CHARTREUSE: 0x7fff00ff,
|
|
CHOCOLATE: 0xd2691eff,
|
|
CORAL: 0xff7f50ff,
|
|
CORNFLOWERBLUE: 0x6495edff,
|
|
CORNSILK: 0xfff8dcff,
|
|
CRIMSON: 0xdc143cff,
|
|
CYAN: 0x00ffffff,
|
|
DARKBLUE: 0x00008bff,
|
|
DARKCYAN: 0x008b8bff,
|
|
DARKGOLDENROD: 0xb886bbff,
|
|
DARKGRAY: 0xa9a9a9ff,
|
|
DARKGREEN: 0x006400ff,
|
|
DARKGREY: 0xa9a9a9ff,
|
|
DARKKHAKI: 0xbdb76bff,
|
|
DARKMAGENTA: 0x8b008bff,
|
|
DARKOLIVEGREEN: 0x556b2fff,
|
|
DARKORANGE: 0xff8c00ff,
|
|
DARKORCHID: 0x9932ccff,
|
|
DARKRED: 0x8b0000ff,
|
|
DARKSALMON: 0xe9967aff,
|
|
DARKSEAGREEN: 0x8fbc8fff,
|
|
DARKSLATEBLUE: 0x483d8bff,
|
|
DARKSLATEGRAY: 0x2f4f4fff,
|
|
DARKSLATEGREY: 0x2f4f4fff,
|
|
DARKTURQUOISE: 0x00ced1ff,
|
|
DARKVIOLET: 0x9400d3ff,
|
|
DEEPPINK: 0xff1493ff,
|
|
DEEPSKYBLUE: 0x00bfffff,
|
|
DIMGRAY: 0x696969ff,
|
|
DIMGREY: 0x696969ff,
|
|
DODGERBLUE: 0x1e90ffff,
|
|
FIREBRICK: 0xb22222ff,
|
|
FLORALWHITE: 0xfffaf0ff,
|
|
FORESTGREEN: 0x228b22ff,
|
|
FUCHSIA: 0xff00ffff,
|
|
GAINSBORO: 0xdcdcdcff,
|
|
GHOSTWHITE: 0xf8f8ffff,
|
|
GOLD: 0xffd700ff,
|
|
GOLDENROD: 0xdaa520ff,
|
|
GRAY: 0x808080ff,
|
|
GREEN: 0x008000ff,
|
|
GREENYELLOW: 0xadff2fff,
|
|
GREY: 0x808080ff,
|
|
HONEYDEW: 0xf0fff0ff,
|
|
HOTPINK: 0xff69b4ff,
|
|
INDIANRED: 0xcd5c5cff,
|
|
INDIGO: 0x4b0082ff,
|
|
IVORY: 0xfffff0ff,
|
|
KHAKI: 0xf0e68cff,
|
|
LAVENDER: 0xe6e6faff,
|
|
LAVENDERBLUSH: 0xfff0f5ff,
|
|
LAWNGREEN: 0x7cfc00ff,
|
|
LEMONCHIFFON: 0xfffacdff,
|
|
LIGHTBLUE: 0xadd8e6ff,
|
|
LIGHTCORAL: 0xf08080ff,
|
|
LIGHTCYAN: 0xe0ffffff,
|
|
LIGHTGOLDENRODYELLOW: 0xfafad2ff,
|
|
LIGHTGRAY: 0xd3d3d3ff,
|
|
LIGHTGREEN: 0x90ee90ff,
|
|
LIGHTGREY: 0xd3d3d3ff,
|
|
LIGHTPINK: 0xffb6c1ff,
|
|
LIGHTSALMON: 0xffa07aff,
|
|
LIGHTSEAGREEN: 0x20b2aaff,
|
|
LIGHTSKYBLUE: 0x87cefaff,
|
|
LIGHTSLATEGRAY: 0x778899ff,
|
|
LIGHTSLATEGREY: 0x778899ff,
|
|
LIGHTSTEELBLUE: 0xb0c4deff,
|
|
LIGHTYELLOW: 0xffffe0ff,
|
|
LIME: 0x00ff00ff,
|
|
LIMEGREEN: 0x32cd32ff,
|
|
LINEN: 0xfaf0e6ff,
|
|
MAGENTA: 0xff00ffff,
|
|
MAROON: 0x800000ff,
|
|
MEDIUMAQUAMARINE: 0x66cdaaff,
|
|
MEDIUMBLUE: 0x0000cdff,
|
|
MEDIUMORCHID: 0xba55d3ff,
|
|
MEDIUMPURPLE: 0x9370dbff,
|
|
MEDIUMSEAGREEN: 0x3cb371ff,
|
|
MEDIUMSLATEBLUE: 0x7b68eeff,
|
|
MEDIUMSPRINGGREEN: 0x00fa9aff,
|
|
MEDIUMTURQUOISE: 0x48d1ccff,
|
|
MEDIUMVIOLETRED: 0xc71585ff,
|
|
MIDNIGHTBLUE: 0x191970ff,
|
|
MINTCREAM: 0xf5fffaff,
|
|
MISTYROSE: 0xffe4e1ff,
|
|
MOCCASIN: 0xffe4b5ff,
|
|
NAVAJOWHITE: 0xffdeadff,
|
|
NAVY: 0x000080ff,
|
|
OLDLACE: 0xfdf5e6ff,
|
|
OLIVE: 0x808000ff,
|
|
OLIVEDRAB: 0x6b8e23ff,
|
|
ORANGE: 0xffa500ff,
|
|
ORANGERED: 0xff4500ff,
|
|
ORCHID: 0xda70d6ff,
|
|
PALEGOLDENROD: 0xeee8aaff,
|
|
PALEGREEN: 0x98fb98ff,
|
|
PALETURQUOISE: 0xafeeeeff,
|
|
PALEVIOLETRED: 0xdb7093ff,
|
|
PAPAYAWHIP: 0xffefd5ff,
|
|
PEACHPUFF: 0xffdab9ff,
|
|
PERU: 0xcd853fff,
|
|
PINK: 0xffc0cbff,
|
|
PLUM: 0xdda0ddff,
|
|
POWDERBLUE: 0xb0e0e6ff,
|
|
PURPLE: 0x800080ff,
|
|
REBECCAPURPLE: 0x663399ff,
|
|
RED: 0xff0000ff,
|
|
ROSYBROWN: 0xbc8f8fff,
|
|
ROYALBLUE: 0x4169e1ff,
|
|
SADDLEBROWN: 0x8b4513ff,
|
|
SALMON: 0xfa8072ff,
|
|
SANDYBROWN: 0xf4a460ff,
|
|
SEAGREEN: 0x2e8b57ff,
|
|
SEASHELL: 0xfff5eeff,
|
|
SIENNA: 0xa0522dff,
|
|
SILVER: 0xc0c0c0ff,
|
|
SKYBLUE: 0x87ceebff,
|
|
SLATEBLUE: 0x6a5acdff,
|
|
SLATEGRAY: 0x708090ff,
|
|
SLATEGREY: 0x708090ff,
|
|
SNOW: 0xfffafaff,
|
|
SPRINGGREEN: 0x00ff7fff,
|
|
STEELBLUE: 0x4682b4ff,
|
|
TAN: 0xd2b48cff,
|
|
TEAL: 0x008080ff,
|
|
THISTLE: 0xd8bfd8ff,
|
|
TOMATO: 0xff6347ff,
|
|
TRANSPARENT: 0x00000000,
|
|
TURQUOISE: 0x40e0d0ff,
|
|
VIOLET: 0xee82eeff,
|
|
WHEAT: 0xf5deb3ff,
|
|
WHITE: 0xffffffff,
|
|
WHITESMOKE: 0xf5f5f5ff,
|
|
YELLOW: 0xffff00ff,
|
|
YELLOWGREEN: 0x9acd32ff
|
|
};
|