You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1781 lines
59 KiB
1781 lines
59 KiB
import valueParser from 'postcss-values-parser'; |
|
import fs from 'fs'; |
|
import path from 'path'; |
|
import postcss from 'postcss'; |
|
import { rgb2hsl, rgb2hwb, hsl2rgb, hsl2hwb, hwb2rgb, hwb2hsl, rgb2hue } from '@csstools/convert-colors'; |
|
|
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { |
|
try { |
|
var info = gen[key](arg); |
|
var value = info.value; |
|
} catch (error) { |
|
reject(error); |
|
return; |
|
} |
|
|
|
if (info.done) { |
|
resolve(value); |
|
} else { |
|
Promise.resolve(value).then(_next, _throw); |
|
} |
|
} |
|
|
|
function _asyncToGenerator(fn) { |
|
return function () { |
|
var self = this, |
|
args = arguments; |
|
return new Promise(function (resolve, reject) { |
|
var gen = fn.apply(self, args); |
|
|
|
function _next(value) { |
|
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); |
|
} |
|
|
|
function _throw(err) { |
|
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); |
|
} |
|
|
|
_next(undefined); |
|
}); |
|
}; |
|
} |
|
|
|
function _defineProperty(obj, key, value) { |
|
if (key in obj) { |
|
Object.defineProperty(obj, key, { |
|
value: value, |
|
enumerable: true, |
|
configurable: true, |
|
writable: true |
|
}); |
|
} else { |
|
obj[key] = value; |
|
} |
|
|
|
return obj; |
|
} |
|
|
|
function _objectSpread(target) { |
|
for (var i = 1; i < arguments.length; i++) { |
|
var source = arguments[i] != null ? arguments[i] : {}; |
|
var ownKeys = Object.keys(source); |
|
|
|
if (typeof Object.getOwnPropertySymbols === 'function') { |
|
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { |
|
return Object.getOwnPropertyDescriptor(source, sym).enumerable; |
|
})); |
|
} |
|
|
|
ownKeys.forEach(function (key) { |
|
_defineProperty(target, key, source[key]); |
|
}); |
|
} |
|
|
|
return target; |
|
} |
|
|
|
function _slicedToArray(arr, i) { |
|
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); |
|
} |
|
|
|
function _toArray(arr) { |
|
return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest(); |
|
} |
|
|
|
function _arrayWithHoles(arr) { |
|
if (Array.isArray(arr)) return arr; |
|
} |
|
|
|
function _iterableToArray(iter) { |
|
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); |
|
} |
|
|
|
function _iterableToArrayLimit(arr, i) { |
|
var _arr = []; |
|
var _n = true; |
|
var _d = false; |
|
var _e = undefined; |
|
|
|
try { |
|
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { |
|
_arr.push(_s.value); |
|
|
|
if (i && _arr.length === i) break; |
|
} |
|
} catch (err) { |
|
_d = true; |
|
_e = err; |
|
} finally { |
|
try { |
|
if (!_n && _i["return"] != null) _i["return"](); |
|
} finally { |
|
if (_d) throw _e; |
|
} |
|
} |
|
|
|
return _arr; |
|
} |
|
|
|
function _nonIterableRest() { |
|
throw new TypeError("Invalid attempt to destructure non-iterable instance"); |
|
} |
|
|
|
function getCustomProperties(root, opts) { |
|
// initialize custom selectors |
|
const customPropertiesFromHtmlElement = {}; |
|
const customPropertiesFromRootPsuedo = {}; // for each html or :root rule |
|
|
|
root.nodes.slice().forEach(rule => { |
|
const customPropertiesObject = isHtmlRule(rule) ? customPropertiesFromHtmlElement : isRootRule(rule) ? customPropertiesFromRootPsuedo : null; // for each custom property |
|
|
|
if (customPropertiesObject) { |
|
rule.nodes.slice().forEach(decl => { |
|
if (isCustomDecl(decl)) { |
|
const prop = decl.prop; // write the parsed value to the custom property |
|
|
|
customPropertiesObject[prop] = valueParser(decl.value).parse(); // conditionally remove the custom property declaration |
|
|
|
if (!opts.preserve) { |
|
decl.remove(); |
|
} |
|
} |
|
}); // conditionally remove the empty html or :root rule |
|
|
|
if (!opts.preserve && isEmptyParent(rule)) { |
|
rule.remove(); |
|
} |
|
} |
|
}); // return all custom properties, preferring :root properties over html properties |
|
|
|
return _objectSpread({}, customPropertiesFromHtmlElement, customPropertiesFromRootPsuedo); |
|
} // match html and :root rules |
|
|
|
const htmlSelectorRegExp = /^html$/i; |
|
const rootSelectorRegExp = /^:root$/i; |
|
const customPropertyRegExp = /^--[A-z][\w-]*$/; // whether the node is an html or :root rule |
|
|
|
const isHtmlRule = node => node.type === 'rule' && htmlSelectorRegExp.test(node.selector) && Object(node.nodes).length; |
|
|
|
const isRootRule = node => node.type === 'rule' && rootSelectorRegExp.test(node.selector) && Object(node.nodes).length; // whether the node is an custom property |
|
|
|
|
|
const isCustomDecl = node => node.type === 'decl' && customPropertyRegExp.test(node.prop); // whether the node is a parent without children |
|
|
|
|
|
const isEmptyParent = node => Object(node.nodes).length === 0; |
|
|
|
/* Import Custom Properties from CSS AST |
|
/* ========================================================================== */ |
|
|
|
function importCustomPropertiesFromCSSAST(root) { |
|
return getCustomProperties(root, { |
|
preserve: true |
|
}); |
|
} |
|
/* Import Custom Properties from CSS File |
|
/* ========================================================================== */ |
|
|
|
|
|
function importCustomPropertiesFromCSSFile(_x) { |
|
return _importCustomPropertiesFromCSSFile.apply(this, arguments); |
|
} |
|
/* Import Custom Properties from Object |
|
/* ========================================================================== */ |
|
|
|
|
|
function _importCustomPropertiesFromCSSFile() { |
|
_importCustomPropertiesFromCSSFile = _asyncToGenerator(function* (from) { |
|
const css = yield readFile(from); |
|
const root = postcss.parse(css, { |
|
from |
|
}); |
|
return importCustomPropertiesFromCSSAST(root); |
|
}); |
|
return _importCustomPropertiesFromCSSFile.apply(this, arguments); |
|
} |
|
|
|
function importCustomPropertiesFromObject(object) { |
|
const customProperties = Object.assign({}, Object(object).customProperties || Object(object)['custom-properties']); |
|
|
|
for (const prop in customProperties) { |
|
customProperties[prop] = valueParser(customProperties[prop]).parse(); |
|
} |
|
|
|
return customProperties; |
|
} |
|
/* Import Custom Properties from JSON file |
|
/* ========================================================================== */ |
|
|
|
|
|
function importCustomPropertiesFromJSONFile(_x2) { |
|
return _importCustomPropertiesFromJSONFile.apply(this, arguments); |
|
} |
|
/* Import Custom Properties from JS file |
|
/* ========================================================================== */ |
|
|
|
|
|
function _importCustomPropertiesFromJSONFile() { |
|
_importCustomPropertiesFromJSONFile = _asyncToGenerator(function* (from) { |
|
const object = yield readJSON(from); |
|
return importCustomPropertiesFromObject(object); |
|
}); |
|
return _importCustomPropertiesFromJSONFile.apply(this, arguments); |
|
} |
|
|
|
function importCustomPropertiesFromJSFile(_x3) { |
|
return _importCustomPropertiesFromJSFile.apply(this, arguments); |
|
} |
|
/* Import Custom Properties from Sources |
|
/* ========================================================================== */ |
|
|
|
|
|
function _importCustomPropertiesFromJSFile() { |
|
_importCustomPropertiesFromJSFile = _asyncToGenerator(function* (from) { |
|
const object = yield import(from); |
|
return importCustomPropertiesFromObject(object); |
|
}); |
|
return _importCustomPropertiesFromJSFile.apply(this, arguments); |
|
} |
|
|
|
function importCustomPropertiesFromSources(sources) { |
|
return sources.map(source => { |
|
if (source instanceof Promise) { |
|
return source; |
|
} else if (source instanceof Function) { |
|
return source(); |
|
} // read the source as an object |
|
|
|
|
|
const opts = source === Object(source) ? source : { |
|
from: String(source) |
|
}; // skip objects with Custom Properties |
|
|
|
if (opts.customProperties || opts['custom-properties']) { |
|
return opts; |
|
} // source pathname |
|
|
|
|
|
const from = path.resolve(String(opts.from || '')); // type of file being read from |
|
|
|
const type = (opts.type || path.extname(from).slice(1)).toLowerCase(); |
|
return { |
|
type, |
|
from |
|
}; |
|
}).reduce( |
|
/*#__PURE__*/ |
|
function () { |
|
var _ref = _asyncToGenerator(function* (customProperties, source) { |
|
const _ref2 = yield source, |
|
type = _ref2.type, |
|
from = _ref2.from; |
|
|
|
if (type === 'ast') { |
|
return Object.assign((yield customProperties), importCustomPropertiesFromCSSAST(from)); |
|
} |
|
|
|
if (type === 'css') { |
|
return Object.assign((yield customProperties), (yield importCustomPropertiesFromCSSFile(from))); |
|
} |
|
|
|
if (type === 'js') { |
|
return Object.assign((yield customProperties), (yield importCustomPropertiesFromJSFile(from))); |
|
} |
|
|
|
if (type === 'json') { |
|
return Object.assign((yield customProperties), (yield importCustomPropertiesFromJSONFile(from))); |
|
} |
|
|
|
return Object.assign((yield customProperties), (yield importCustomPropertiesFromObject((yield source)))); |
|
}); |
|
|
|
return function (_x4, _x5) { |
|
return _ref.apply(this, arguments); |
|
}; |
|
}(), {}); |
|
} |
|
/* Helper utilities |
|
/* ========================================================================== */ |
|
|
|
const readFile = from => new Promise((resolve, reject) => { |
|
fs.readFile(from, 'utf8', (error, result) => { |
|
if (error) { |
|
reject(error); |
|
} else { |
|
resolve(result); |
|
} |
|
}); |
|
}); |
|
|
|
const readJSON = |
|
/*#__PURE__*/ |
|
function () { |
|
var _ref3 = _asyncToGenerator(function* (from) { |
|
return JSON.parse((yield readFile(from))); |
|
}); |
|
|
|
return function readJSON(_x6) { |
|
return _ref3.apply(this, arguments); |
|
}; |
|
}(); |
|
|
|
/* Convert Degree to Hue Degree |
|
/* ========================================================================== */ |
|
function convertDtoD(deg) { |
|
return deg % 360; |
|
} |
|
/* Convert Gradian to Hue Degree |
|
/* ========================================================================== */ |
|
|
|
function convertGtoD(grad) { |
|
return grad * 0.9 % 360; |
|
} |
|
/* Convert Radian to Hue Degree |
|
/* ========================================================================== */ |
|
|
|
function convertRtoD(rad) { |
|
return rad * 180 / Math.PI % 360; |
|
} |
|
/* Convert Turn to Hue Degree |
|
/* ========================================================================== */ |
|
|
|
function convertTtoD(turn) { |
|
return turn * 360 % 360; |
|
} |
|
/* Convert a Name to Red/Green/Blue |
|
/* ========================================================================== */ |
|
|
|
function convertNtoRGB(name) { |
|
const names = { |
|
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], |
|
transparent: [0, 0, 0], |
|
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] |
|
}; |
|
return names[name] && names[name].map(c => c / 2.55); |
|
} |
|
/* Convert a Hex to Red/Green/Blue |
|
/* ========================================================================== */ |
|
|
|
function convertHtoRGB(hex) { |
|
// #<hex-color>{3,4,6,8} |
|
const _slice = (hex.match(hexColorMatch) || []).slice(1), |
|
_slice2 = _slicedToArray(_slice, 8), |
|
r = _slice2[0], |
|
g = _slice2[1], |
|
b = _slice2[2], |
|
a = _slice2[3], |
|
rr = _slice2[4], |
|
gg = _slice2[5], |
|
bb = _slice2[6], |
|
aa = _slice2[7]; |
|
|
|
if (rr !== undefined || r !== undefined) { |
|
const red = rr !== undefined ? parseInt(rr, 16) : r !== undefined ? parseInt(r + r, 16) : 0; |
|
const green = gg !== undefined ? parseInt(gg, 16) : g !== undefined ? parseInt(g + g, 16) : 0; |
|
const blue = bb !== undefined ? parseInt(bb, 16) : b !== undefined ? parseInt(b + b, 16) : 0; |
|
const alpha = aa !== undefined ? parseInt(aa, 16) : a !== undefined ? parseInt(a + a, 16) : 255; |
|
return [red, green, blue, alpha].map(c => c / 2.55); |
|
} |
|
|
|
return undefined; |
|
} |
|
const hexColorMatch = /^#(?:([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?|([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?)$/i; |
|
|
|
class Color { |
|
constructor(color) { |
|
this.color = Object(Object(color).color || color); |
|
this.color.colorspace = this.color.colorspace ? this.color.colorspace : 'red' in color && 'green' in color && 'blue' in color ? 'rgb' : 'hue' in color && 'saturation' in color && 'lightness' in color ? 'hsl' : 'hue' in color && 'whiteness' in color && 'blackness' in color ? 'hwb' : 'unknown'; |
|
|
|
if (color.colorspace === 'rgb') { |
|
this.color.hue = rgb2hue(color.red, color.green, color.blue, color.hue || 0); |
|
} |
|
} |
|
|
|
alpha(alpha) { |
|
const color = this.color; |
|
return alpha === undefined ? color.alpha : new Color(assign(color, { |
|
alpha |
|
})); |
|
} |
|
|
|
blackness(blackness) { |
|
const hwb = color2hwb(this.color); |
|
return blackness === undefined ? hwb.blackness : new Color(assign(hwb, { |
|
blackness |
|
})); |
|
} |
|
|
|
blend(color, percentage, colorspace = 'rgb') { |
|
const base = this.color; |
|
return new Color(blend(base, color, percentage, colorspace)); |
|
} |
|
|
|
blenda(color, percentage, colorspace = 'rgb') { |
|
const base = this.color; |
|
return new Color(blend(base, color, percentage, colorspace, true)); |
|
} |
|
|
|
blue(blue) { |
|
const rgb = color2rgb(this.color); |
|
return blue === undefined ? rgb.blue : new Color(assign(rgb, { |
|
blue |
|
})); |
|
} |
|
|
|
contrast(percentage) { |
|
const base = this.color; |
|
return new Color(contrast(base, percentage)); |
|
} |
|
|
|
green(green) { |
|
const rgb = color2rgb(this.color); |
|
return green === undefined ? rgb.green : new Color(assign(rgb, { |
|
green |
|
})); |
|
} |
|
|
|
hue(hue) { |
|
const hsl = color2hsl(this.color); |
|
return hue === undefined ? hsl.hue : new Color(assign(hsl, { |
|
hue |
|
})); |
|
} |
|
|
|
lightness(lightness) { |
|
const hsl = color2hsl(this.color); |
|
return lightness === undefined ? hsl.lightness : new Color(assign(hsl, { |
|
lightness |
|
})); |
|
} |
|
|
|
red(red) { |
|
const rgb = color2rgb(this.color); |
|
return red === undefined ? rgb.red : new Color(assign(rgb, { |
|
red |
|
})); |
|
} |
|
|
|
rgb(red, green, blue) { |
|
const rgb = color2rgb(this.color); |
|
return new Color(assign(rgb, { |
|
red, |
|
green, |
|
blue |
|
})); |
|
} |
|
|
|
saturation(saturation) { |
|
const hsl = color2hsl(this.color); |
|
return saturation === undefined ? hsl.saturation : new Color(assign(hsl, { |
|
saturation |
|
})); |
|
} |
|
|
|
shade(percentage) { |
|
const hwb = color2hwb(this.color); |
|
const shade = { |
|
hue: 0, |
|
whiteness: 0, |
|
blackness: 100, |
|
colorspace: 'hwb' |
|
}; |
|
const colorspace = 'rgb'; |
|
return percentage === undefined ? hwb.blackness : new Color(blend(hwb, shade, percentage, colorspace)); |
|
} |
|
|
|
tint(percentage) { |
|
const hwb = color2hwb(this.color); |
|
const tint = { |
|
hue: 0, |
|
whiteness: 100, |
|
blackness: 0, |
|
colorspace: 'hwb' |
|
}; |
|
const colorspace = 'rgb'; |
|
return percentage === undefined ? hwb.blackness : new Color(blend(hwb, tint, percentage, colorspace)); |
|
} |
|
|
|
whiteness(whiteness) { |
|
const hwb = color2hwb(this.color); |
|
return whiteness === undefined ? hwb.whiteness : new Color(assign(hwb, { |
|
whiteness |
|
})); |
|
} |
|
|
|
toHSL() { |
|
return color2hslString(this.color); |
|
} |
|
|
|
toHWB() { |
|
return color2hwbString(this.color); |
|
} |
|
|
|
toLegacy() { |
|
return color2legacyString(this.color); |
|
} |
|
|
|
toRGB() { |
|
return color2rgbString(this.color); |
|
} |
|
|
|
toRGBLegacy() { |
|
return color2rgbLegacyString(this.color); |
|
} |
|
|
|
toString() { |
|
return color2string(this.color); |
|
} |
|
|
|
} |
|
/* Blending |
|
/* ========================================================================== */ |
|
|
|
function blend(base, color, percentage, colorspace, isBlendingAlpha) { |
|
const addition = percentage / 100; |
|
const subtraction = 1 - addition; |
|
|
|
if (colorspace === 'hsl') { |
|
const _color2hsl = color2hsl(base), |
|
h1 = _color2hsl.hue, |
|
s1 = _color2hsl.saturation, |
|
l1 = _color2hsl.lightness, |
|
a1 = _color2hsl.alpha; |
|
|
|
const _color2hsl2 = color2hsl(color), |
|
h2 = _color2hsl2.hue, |
|
s2 = _color2hsl2.saturation, |
|
l2 = _color2hsl2.lightness, |
|
a2 = _color2hsl2.alpha; |
|
|
|
const hue = h1 * subtraction + h2 * addition, |
|
saturation = s1 * subtraction + s2 * addition, |
|
lightness = l1 * subtraction + l2 * addition, |
|
alpha = isBlendingAlpha ? a1 * subtraction + a2 * addition : a1; |
|
return { |
|
hue, |
|
saturation, |
|
lightness, |
|
alpha, |
|
colorspace: 'hsl' |
|
}; |
|
} else if (colorspace === 'hwb') { |
|
const _color2hwb = color2hwb(base), |
|
h1 = _color2hwb.hue, |
|
w1 = _color2hwb.whiteness, |
|
b1 = _color2hwb.blackness, |
|
a1 = _color2hwb.alpha; |
|
|
|
const _color2hwb2 = color2hwb(color), |
|
h2 = _color2hwb2.hue, |
|
w2 = _color2hwb2.whiteness, |
|
b2 = _color2hwb2.blackness, |
|
a2 = _color2hwb2.alpha; |
|
|
|
const hue = h1 * subtraction + h2 * addition, |
|
whiteness = w1 * subtraction + w2 * addition, |
|
blackness = b1 * subtraction + b2 * addition, |
|
alpha = isBlendingAlpha ? a1 * subtraction + a2 * addition : a1; |
|
return { |
|
hue, |
|
whiteness, |
|
blackness, |
|
alpha, |
|
colorspace: 'hwb' |
|
}; |
|
} else { |
|
const _color2rgb = color2rgb(base), |
|
r1 = _color2rgb.red, |
|
g1 = _color2rgb.green, |
|
b1 = _color2rgb.blue, |
|
a1 = _color2rgb.alpha; |
|
|
|
const _color2rgb2 = color2rgb(color), |
|
r2 = _color2rgb2.red, |
|
g2 = _color2rgb2.green, |
|
b2 = _color2rgb2.blue, |
|
a2 = _color2rgb2.alpha; |
|
|
|
const red = r1 * subtraction + r2 * addition, |
|
green = g1 * subtraction + g2 * addition, |
|
blue = b1 * subtraction + b2 * addition, |
|
alpha = isBlendingAlpha ? a1 * subtraction + a2 * addition : a1; |
|
return { |
|
red, |
|
green, |
|
blue, |
|
alpha, |
|
colorspace: 'rgb' |
|
}; |
|
} |
|
} |
|
/* Assign channels to a new instance of a base color |
|
/* ========================================================================== */ |
|
|
|
|
|
function assign(base, channels) { |
|
const color = Object.assign({}, base); |
|
Object.keys(channels).forEach(channel => { |
|
// detect channel |
|
const isHue = channel === 'hue'; |
|
const isRGB = !isHue && blueGreenRedMatch.test(channel); // normalized value of the channel |
|
|
|
const value = normalize(channels[channel], channel); // assign channel to new object |
|
|
|
color[channel] = value; |
|
|
|
if (isRGB) { |
|
// conditionally preserve the hue |
|
color.hue = rgb2hue(color.red, color.green, color.blue, base.hue || 0); |
|
} |
|
}); |
|
return color; |
|
} |
|
|
|
function normalize(value, channel) { |
|
// detect channel |
|
const isHue = channel === 'hue'; // value limitations |
|
|
|
const min = 0; |
|
const max = isHue ? 360 : 100; |
|
const normalizedValue = Math.min(Math.max(isHue ? value % 360 : value, min), max); |
|
return normalizedValue; |
|
} |
|
/* Convert colors |
|
/* ========================================================================== */ |
|
|
|
|
|
function color2rgb(color) { |
|
const _ref = color.colorspace === 'hsl' ? hsl2rgb(color.hue, color.saturation, color.lightness) : color.colorspace === 'hwb' ? hwb2rgb(color.hue, color.whiteness, color.blackness) : [color.red, color.green, color.blue], |
|
_ref2 = _slicedToArray(_ref, 3), |
|
red = _ref2[0], |
|
green = _ref2[1], |
|
blue = _ref2[2]; |
|
|
|
return { |
|
red, |
|
green, |
|
blue, |
|
hue: color.hue, |
|
alpha: color.alpha, |
|
colorspace: 'rgb' |
|
}; |
|
} |
|
|
|
function color2hsl(color) { |
|
const _ref3 = color.colorspace === 'rgb' ? rgb2hsl(color.red, color.green, color.blue, color.hue) : color.colorspace === 'hwb' ? hwb2hsl(color.hue, color.whiteness, color.blackness) : [color.hue, color.saturation, color.lightness], |
|
_ref4 = _slicedToArray(_ref3, 3), |
|
hue = _ref4[0], |
|
saturation = _ref4[1], |
|
lightness = _ref4[2]; |
|
|
|
return { |
|
hue, |
|
saturation, |
|
lightness, |
|
alpha: color.alpha, |
|
colorspace: 'hsl' |
|
}; |
|
} |
|
|
|
function color2hwb(color) { |
|
const _ref5 = color.colorspace === 'rgb' ? rgb2hwb(color.red, color.green, color.blue, color.hue) : color.colorspace === 'hsl' ? hsl2hwb(color.hue, color.saturation, color.lightness) : [color.hue, color.whiteness, color.blackness], |
|
_ref6 = _slicedToArray(_ref5, 3), |
|
hue = _ref6[0], |
|
whiteness = _ref6[1], |
|
blackness = _ref6[2]; |
|
|
|
return { |
|
hue, |
|
whiteness, |
|
blackness, |
|
alpha: color.alpha, |
|
colorspace: 'hwb' |
|
}; |
|
} |
|
/* Contrast functions |
|
/* ========================================================================== */ |
|
|
|
|
|
function contrast(color, percentage) { |
|
// https://drafts.csswg.org/css-color/#contrast-adjuster |
|
const hwb = color2hwb(color); |
|
const rgb = color2rgb(color); // compute the luminance of the color. |
|
|
|
const luminance = rgb2luminance(rgb.red, rgb.green, rgb.blue); // the maximum-contrast color, if it is less than .5 |
|
|
|
const maxContrastColor = luminance < 0.5 // hwb(X, 100%, 0%), where X is the hue angle of the color |
|
? { |
|
hue: hwb.hue, |
|
whiteness: 100, |
|
blackness: 0, |
|
alpha: hwb.alpha, |
|
colorspace: 'hwb' // otherwise, hwb(X, 0%, 100%), where X is the hue angle of the color |
|
|
|
} : { |
|
hue: hwb.hue, |
|
whiteness: 0, |
|
blackness: 100, |
|
alpha: hwb.alpha, |
|
colorspace: 'hwb' |
|
}; // contrast ratio |
|
|
|
const contrastRatio = colors2contrast(color, maxContrastColor); |
|
const minContrastColor = contrastRatio > 4.5 // the color with the smallest contrast ratio with the base color that is greater than 4.5 |
|
? colors2contrastRatioColor(hwb, maxContrastColor) // otherwise, the maximum-contrast color |
|
: maxContrastColor; // color(maximum-contrast blend(minimum-contrast <percentage> hwb))); |
|
|
|
return blend(maxContrastColor, minContrastColor, percentage, 'hwb', false); |
|
} |
|
|
|
function colors2contrast(color1, color2) { |
|
// https://drafts.csswg.org/css-color/#contrast-ratio |
|
const rgb1 = color2rgb(color1); |
|
const rgb2 = color2rgb(color2); |
|
const l1 = rgb2luminance(rgb1.red, rgb1.green, rgb1.blue); |
|
const l2 = rgb2luminance(rgb2.red, rgb2.green, rgb2.blue); |
|
return l1 > l2 // if l1 is the relative luminance of the lighter of the colors |
|
? (l1 + 0.05) / (l2 + 0.05) // otherwise, if l2 is the relative luminance of the lighter of the colors |
|
: (l2 + 0.05) / (l1 + 0.05); |
|
} |
|
|
|
function rgb2luminance(red, green, blue) { |
|
const _ref7 = [channel2luminance(red), channel2luminance(green), channel2luminance(blue)], |
|
redLuminance = _ref7[0], |
|
greenLuminance = _ref7[1], |
|
blueLuminance = _ref7[2]; // https://drafts.csswg.org/css-color/#luminance |
|
|
|
const luminance = 0.2126 * redLuminance + 0.7152 * greenLuminance + 0.0722 * blueLuminance; |
|
return luminance; |
|
} |
|
|
|
function channel2luminance(value) { |
|
// https://drafts.csswg.org/css-color/#luminance |
|
const luminance = value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4); |
|
return luminance; |
|
} // return the smallest contrast ratio from a color and a maximum contrast (credit: @thetalecrafter) |
|
|
|
|
|
function colors2contrastRatioColor(hwb, maxHWB) { |
|
const modifiedHWB = Object.assign({}, hwb); // values to be used for linear interpolations in HWB space |
|
|
|
let minW = hwb.whiteness; |
|
let minB = hwb.blackness; |
|
let maxW = maxHWB.whiteness; |
|
let maxB = maxHWB.blackness; // find the color with the smallest contrast ratio with the base color that is greater than 4.5 |
|
|
|
while (Math.abs(minW - maxW) > 100 || Math.abs(minB - maxB) > 100) { |
|
const midW = Math.round((maxW + minW) / 2); |
|
const midB = Math.round((maxB + minB) / 2); |
|
modifiedHWB.whiteness = midW; |
|
modifiedHWB.blackness = midB; |
|
|
|
if (colors2contrast(modifiedHWB, hwb) > 4.5) { |
|
maxW = midW; |
|
maxB = midB; |
|
} else { |
|
minW = midW; |
|
minB = midB; |
|
} |
|
} |
|
|
|
return modifiedHWB; |
|
} |
|
/* Match |
|
/* ========================================================================== */ |
|
|
|
|
|
const blueGreenRedMatch = /^(blue|green|red)$/i; |
|
/* Stringifiers |
|
/* ========================================================================== */ |
|
|
|
function color2string(color) { |
|
return color.colorspace === 'hsl' ? color2hslString(color) : color.colorspace === 'hwb' ? color2hwbString(color) : color2rgbString(color); |
|
} |
|
|
|
function color2hslString(color) { |
|
const hsl = color2hsl(color); |
|
const isOpaque = hsl.alpha === 100; |
|
const hue = hsl.hue; |
|
const saturation = Math.round(hsl.saturation * 10000000000) / 10000000000; |
|
const lightness = Math.round(hsl.lightness * 10000000000) / 10000000000; |
|
const alpha = Math.round(hsl.alpha * 10000000000) / 10000000000; |
|
return `hsl(${hue} ${saturation}% ${lightness}%${isOpaque ? '' : ` / ${alpha}%`})`; |
|
} |
|
|
|
function color2hwbString(color) { |
|
const hwb = color2hwb(color); |
|
const isOpaque = hwb.alpha === 100; |
|
const hue = hwb.hue; |
|
const whiteness = Math.round(hwb.whiteness * 10000000000) / 10000000000; |
|
const blackness = Math.round(hwb.blackness * 10000000000) / 10000000000; |
|
const alpha = Math.round(hwb.alpha * 10000000000) / 10000000000; |
|
return `hwb(${hue} ${whiteness}% ${blackness}%${isOpaque ? '' : ` / ${alpha}%`})`; |
|
} |
|
|
|
function color2rgbString(color) { |
|
const rgb = color2rgb(color); |
|
const isOpaque = rgb.alpha === 100; |
|
const red = Math.round(rgb.red * 10000000000) / 10000000000; |
|
const green = Math.round(rgb.green * 10000000000) / 10000000000; |
|
const blue = Math.round(rgb.blue * 10000000000) / 10000000000; |
|
const alpha = Math.round(rgb.alpha * 10000000000) / 10000000000; |
|
return `rgb(${red}% ${green}% ${blue}%${isOpaque ? '' : ` / ${alpha}%`})`; |
|
} |
|
|
|
function color2legacyString(color) { |
|
return color.colorspace === 'hsl' ? color2hslLegacyString(color) : color2rgbLegacyString(color); |
|
} |
|
|
|
function color2rgbLegacyString(color) { |
|
const rgb = color2rgb(color); |
|
const isOpaque = rgb.alpha === 100; |
|
const name = isOpaque ? 'rgb' : 'rgba'; |
|
const red = Math.round(rgb.red * 255 / 100); |
|
const green = Math.round(rgb.green * 255 / 100); |
|
const blue = Math.round(rgb.blue * 255 / 100); |
|
const alpha = Math.round(rgb.alpha / 100 * 10000000000) / 10000000000; |
|
return `${name}(${red}, ${green}, ${blue}${isOpaque ? '' : `, ${alpha}`})`; |
|
} |
|
|
|
function color2hslLegacyString(color) { |
|
const hsl = color2hsl(color); |
|
const isOpaque = hsl.alpha === 100; |
|
const name = isOpaque ? 'hsl' : 'hsla'; |
|
const hue = hsl.hue; |
|
const saturation = Math.round(hsl.saturation * 10000000000) / 10000000000; |
|
const lightness = Math.round(hsl.lightness * 10000000000) / 10000000000; |
|
const alpha = Math.round(hsl.alpha / 100 * 10000000000) / 10000000000; |
|
return `${name}(${hue}, ${saturation}%, ${lightness}%${isOpaque ? '' : `, ${alpha}`})`; |
|
} |
|
|
|
function manageUnresolved(node, opts, word, message) { |
|
if ('warn' === opts.unresolved) { |
|
opts.decl.warn(opts.result, message, { |
|
word |
|
}); |
|
} else if ('ignore' !== opts.unresolved) { |
|
throw opts.decl.error(message, { |
|
word |
|
}); |
|
} |
|
} |
|
|
|
/* Transform AST |
|
/* ========================================================================== */ |
|
|
|
function transformAST(node, opts) { |
|
node.nodes.slice(0).forEach(child => { |
|
if (isColorModFunction(child)) { |
|
// transform any variables within the color-mod() function |
|
if (opts.transformVars) { |
|
transformVariables(child, opts); |
|
} // transform any color-mod() functions |
|
|
|
|
|
const color = transformColorModFunction(child, opts); |
|
|
|
if (color) { |
|
// update the color-mod() function with the transformed value |
|
child.replaceWith(valueParser.word({ |
|
raws: child.raws, |
|
value: opts.stringifier(color) |
|
})); |
|
} |
|
} else if (child.nodes && Object(child.nodes).length) { |
|
transformAST(child, opts); |
|
} |
|
}); |
|
} |
|
/* Transform <var> functions |
|
/* ========================================================================== */ |
|
|
|
function transformVariables(node, opts) { |
|
walk(node, child => { |
|
if (isVariable(child)) { |
|
// get the custom property and fallback value from var() |
|
const _transformArgsByParam = transformArgsByParams(child, [// <value> , [ <fallback> ]? |
|
[transformWord, isComma, transformNode]]), |
|
_transformArgsByParam2 = _slicedToArray(_transformArgsByParam, 2), |
|
prop = _transformArgsByParam2[0], |
|
fallbackNode = _transformArgsByParam2[1]; // if the custom property is known |
|
|
|
|
|
if (prop in opts.customProperties) { |
|
let customPropertyValue = opts.customProperties[prop]; // follow custom properties referencing custom properties |
|
|
|
if (looseVarMatch.test(customPropertyValue)) { |
|
const rootChildAST = customPropertyValue.clone(); |
|
transformVariables(rootChildAST, opts); |
|
customPropertyValue = rootChildAST; |
|
} // replace var() with the custom property value |
|
|
|
|
|
if (customPropertyValue.nodes.length === 1 && customPropertyValue.nodes[0].nodes.length) { |
|
customPropertyValue.nodes[0].nodes.forEach(customPropertyChild => { |
|
child.parent.insertBefore(child, customPropertyChild); |
|
}); |
|
} |
|
|
|
child.remove(); |
|
} else if (fallbackNode && fallbackNode.nodes.length === 1 && fallbackNode.nodes[0].nodes.length) { |
|
// otherwise, replace var() with the fallback value |
|
transformVariables(fallbackNode, opts); |
|
child.replaceWith(...fallbackNode.nodes[0].nodes[0]); |
|
} |
|
} |
|
}); |
|
} |
|
/* Transform <color> functions |
|
/* ========================================================================== */ |
|
|
|
|
|
function transformColor(node, opts) { |
|
if (isRGBFunction(node)) { |
|
return transformRGBFunction(node, opts); |
|
} else if (isHSLFunction(node)) { |
|
return transformHSLFunction(node, opts); |
|
} else if (isHWBFunction(node)) { |
|
return transformHWBFunction(node, opts); |
|
} else if (isColorModFunction(node)) { |
|
return transformColorModFunction(node, opts); |
|
} else if (isHexColor(node)) { |
|
return transformHexColor(node, opts); |
|
} else if (isNamedColor(node)) { |
|
return transformNamedColor(node, opts); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a color`); |
|
} |
|
} // return a transformed rgb/rgba color function |
|
|
|
|
|
function transformRGBFunction(node, opts) { |
|
const _transformArgsByParam3 = transformArgsByParams(node, [// <percentage> <percentage> <percentage> [ , <alpha-value> ]? |
|
[transformPercentage, transformPercentage, transformPercentage, isSlash, transformAlpha], // <number> <number> <number> [ , <alpha-value> ]? |
|
[transformRGBNumber, transformRGBNumber, transformRGBNumber, isSlash, transformAlpha], // <percentage> , <percentage> , <percentage> [ , <alpha-value> ]? |
|
[transformPercentage, isComma, transformPercentage, isComma, transformPercentage, isComma, transformAlpha], // <number> , <number> , <number> [ , <alpha-value> ]? |
|
[transformRGBNumber, isComma, transformRGBNumber, isComma, transformRGBNumber, isComma, transformAlpha]]), |
|
_transformArgsByParam4 = _slicedToArray(_transformArgsByParam3, 4), |
|
red = _transformArgsByParam4[0], |
|
green = _transformArgsByParam4[1], |
|
blue = _transformArgsByParam4[2], |
|
_transformArgsByParam5 = _transformArgsByParam4[3], |
|
alpha = _transformArgsByParam5 === void 0 ? 100 : _transformArgsByParam5; |
|
|
|
if (red !== undefined) { |
|
const color = new Color({ |
|
red, |
|
green, |
|
blue, |
|
alpha, |
|
colorspace: 'rgb' |
|
}); |
|
return color; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid rgb() function`); |
|
} |
|
} // return a transformed hsl/hsla color function |
|
|
|
|
|
function transformHSLFunction(node, opts) { |
|
const _transformArgsByParam6 = transformArgsByParams(node, [// <hue> <percentage> <percentage> [ / <alpha-value> ]? |
|
[transformHue, transformPercentage, transformPercentage, isSlash, transformAlpha], // <hue> , <percentage> , <percentage> [ , <alpha-value> ]? |
|
[transformHue, isComma, transformPercentage, isComma, transformPercentage, isComma, transformAlpha]]), |
|
_transformArgsByParam7 = _slicedToArray(_transformArgsByParam6, 4), |
|
hue = _transformArgsByParam7[0], |
|
saturation = _transformArgsByParam7[1], |
|
lightness = _transformArgsByParam7[2], |
|
_transformArgsByParam8 = _transformArgsByParam7[3], |
|
alpha = _transformArgsByParam8 === void 0 ? 100 : _transformArgsByParam8; |
|
|
|
if (lightness !== undefined) { |
|
const color = new Color({ |
|
hue, |
|
saturation, |
|
lightness, |
|
alpha, |
|
colorspace: 'hsl' |
|
}); |
|
return color; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hsl() function`); |
|
} |
|
} // return a transformed hwb color function |
|
|
|
|
|
function transformHWBFunction(node, opts) { |
|
const _transformArgsByParam9 = transformArgsByParams(node, [// <hue> <percentage> <percentage> [ / <alpha-value> ]? |
|
[transformHue, transformPercentage, transformPercentage, isSlash, transformAlpha]]), |
|
_transformArgsByParam10 = _slicedToArray(_transformArgsByParam9, 4), |
|
hue = _transformArgsByParam10[0], |
|
whiteness = _transformArgsByParam10[1], |
|
blackness = _transformArgsByParam10[2], |
|
_transformArgsByParam11 = _transformArgsByParam10[3], |
|
alpha = _transformArgsByParam11 === void 0 ? 100 : _transformArgsByParam11; |
|
|
|
if (blackness !== undefined) { |
|
const color = new Color({ |
|
hue, |
|
whiteness, |
|
blackness, |
|
alpha, |
|
colorspace: 'hwb' |
|
}); |
|
return color; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hwb() function`); |
|
} |
|
} // return a transformed color-mod color function |
|
|
|
|
|
function transformColorModFunction(node, opts) { |
|
// [ <color> | <hue> ] <color-adjuster>* |
|
const _ref = (node.nodes || []).slice(1, -1) || [], |
|
_ref2 = _toArray(_ref), |
|
colorOrHueNode = _ref2[0], |
|
adjusterNodes = _ref2.slice(1); |
|
|
|
if (colorOrHueNode !== undefined) { |
|
const color = isHue(colorOrHueNode) ? new Color({ |
|
hue: transformHue(colorOrHueNode, opts), |
|
saturation: 100, |
|
lightness: 50, |
|
alpha: 100, |
|
colorspace: 'hsl' |
|
}) : transformColor(colorOrHueNode, opts); |
|
|
|
if (color) { |
|
const adjustedColor = transformColorByAdjusters(color, adjusterNodes, opts); |
|
return adjustedColor; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid color`); |
|
} |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid color-mod() function`); |
|
} |
|
} // return a transformed hex color |
|
|
|
|
|
function transformHexColor(node, opts) { |
|
if (hexColorMatch$1.test(node.value)) { |
|
// #<hex-color>{3,4,6,8} |
|
const _convertHtoRGB = convertHtoRGB(node.value), |
|
_convertHtoRGB2 = _slicedToArray(_convertHtoRGB, 4), |
|
red = _convertHtoRGB2[0], |
|
green = _convertHtoRGB2[1], |
|
blue = _convertHtoRGB2[2], |
|
alpha = _convertHtoRGB2[3]; |
|
|
|
const color = new Color({ |
|
red, |
|
green, |
|
blue, |
|
alpha |
|
}); |
|
return color; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hex color`); |
|
} |
|
} // return a transformed named-color |
|
|
|
|
|
function transformNamedColor(node, opts) { |
|
if (isNamedColor(node)) { |
|
// <named-color> |
|
const _convertNtoRGB = convertNtoRGB(node.value), |
|
_convertNtoRGB2 = _slicedToArray(_convertNtoRGB, 3), |
|
red = _convertNtoRGB2[0], |
|
green = _convertNtoRGB2[1], |
|
blue = _convertNtoRGB2[2]; |
|
|
|
const color = new Color({ |
|
red, |
|
green, |
|
blue, |
|
alpha: 100, |
|
colorspace: 'rgb' |
|
}); |
|
return color; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid named-color`); |
|
} |
|
} |
|
/* Transform <color-adjuster> functions |
|
/* ========================================================================== */ |
|
// return a transformed color using adjustments |
|
|
|
|
|
function transformColorByAdjusters(color, adjusterNodes, opts) { |
|
const adjustedColor = adjusterNodes.reduce((base, node) => { |
|
if (isAlphaBlueGreenRedAdjuster(node)) { |
|
return transformAlphaBlueGreenRedAdjuster(base, node, opts); |
|
} else if (isRGBAdjuster(node)) { |
|
return transformRGBAdjuster(base, node, opts); |
|
} else if (isHueAdjuster(node)) { |
|
return transformHueAdjuster(base, node, opts); |
|
} else if (isBlacknessLightnessSaturationWhitenessAdjuster(node)) { |
|
return transformBlacknessLightnessSaturationWhitenessAdjuster(base, node, opts); |
|
} else if (isShadeTintAdjuster(node)) { |
|
return transformShadeTintAdjuster(base, node, opts); |
|
} else if (isBlendAdjuster(node)) { |
|
return transformBlendAdjuster(base, node, node.value === 'blenda', opts); |
|
} else if (isContrastAdjuster(node)) { |
|
return transformContrastAdjuster(base, node, opts); |
|
} else { |
|
manageUnresolved(node, opts, node.value, `Expected a valid color adjuster`); |
|
return base; |
|
} |
|
}, color); |
|
return adjustedColor; |
|
} // return a transformed color using a/alpha/blue/green/red adjustments |
|
|
|
|
|
function transformAlphaBlueGreenRedAdjuster(base, node, opts) { |
|
const _transformArgsByParam12 = transformArgsByParams(node, alphaMatch.test(node.value) // a/alpha adjustments |
|
? [// [ + | - ] <alpha-value> |
|
[transformMinusPlusOperator, transformAlpha], // * <percentage> |
|
[transformTimesOperator, transformPercentage], // <alpha-value> |
|
[transformAlpha]] // blue/green/red adjustments |
|
: [// [ + | - ] <percentage> |
|
[transformMinusPlusOperator, transformPercentage], // [ + | - ] <number> |
|
[transformMinusPlusOperator, transformRGBNumber], // * <percentage> |
|
[transformTimesOperator, transformPercentage], // <percentage> |
|
[transformPercentage], // <number> |
|
[transformRGBNumber]]), |
|
_transformArgsByParam13 = _slicedToArray(_transformArgsByParam12, 2), |
|
operatorOrValue = _transformArgsByParam13[0], |
|
adjustment = _transformArgsByParam13[1]; |
|
|
|
if (operatorOrValue !== undefined) { |
|
// normalized channel name |
|
const channel = node.value.toLowerCase().replace(alphaMatch, 'alpha'); |
|
const existingValue = base[channel](); |
|
const modifiedValue = adjustment !== undefined ? operatorOrValue === '+' ? existingValue + Number(adjustment) : operatorOrValue === '-' ? existingValue - Number(adjustment) : operatorOrValue === '*' ? existingValue * Number(adjustment) : Number(adjustment) : Number(operatorOrValue); |
|
const modifiedColor = base[channel](modifiedValue); |
|
return modifiedColor; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid modifier()`); |
|
} |
|
} // return a transformed color using an rgb adjustment |
|
|
|
|
|
function transformRGBAdjuster(base, node, opts) { |
|
const _transformArgsByParam14 = transformArgsByParams(node, [// [ + | - ] <percentage> <percentage> <percentage> |
|
[transformMinusPlusOperator, transformPercentage, transformPercentage, transformPercentage], // [ + | - ] <number> <number> <number> |
|
[transformMinusPlusOperator, transformRGBNumber, transformRGBNumber, transformRGBNumber], // [ + | - ] <hash-token> |
|
[transformMinusPlusOperator, transformHexColor], // [ * ] <percentage> |
|
[transformTimesOperator, transformPercentage]]), |
|
_transformArgsByParam15 = _slicedToArray(_transformArgsByParam14, 4), |
|
arg1 = _transformArgsByParam15[0], |
|
arg2 = _transformArgsByParam15[1], |
|
arg3 = _transformArgsByParam15[2], |
|
arg4 = _transformArgsByParam15[3]; |
|
|
|
if (arg2 !== undefined && arg2.color) { |
|
const modifiedColor = base.rgb(arg1 === '+' ? base.red() + arg2.red() : base.red() - arg2.red(), arg1 === '+' ? base.green() + arg2.green() : base.green() - arg2.green(), arg1 === '+' ? base.blue() + arg2.blue() : base.blue() - arg2.blue()); |
|
return modifiedColor; |
|
} else if (arg1 !== undefined && minusPlusMatch.test(arg1)) { |
|
const modifiedColor = base.rgb(arg1 === '+' ? base.red() + arg2 : base.red() - arg2, arg1 === '+' ? base.green() + arg3 : base.green() - arg3, arg1 === '+' ? base.blue() + arg4 : base.blue() - arg4); |
|
return modifiedColor; |
|
} else if (arg1 !== undefined && arg2 !== undefined) { |
|
const modifiedColor = base.rgb(base.red() * arg2, base.green() * arg2, base.blue() * arg2); |
|
return modifiedColor; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid rgb() adjuster`); |
|
} |
|
} // return a transformed color using a blend/blenda adjustment |
|
|
|
|
|
function transformBlendAdjuster(base, node, isAlphaBlend, opts) { |
|
const _transformArgsByParam16 = transformArgsByParams(node, [[transformColor, transformPercentage, transformColorSpace]]), |
|
_transformArgsByParam17 = _slicedToArray(_transformArgsByParam16, 3), |
|
color = _transformArgsByParam17[0], |
|
percentage = _transformArgsByParam17[1], |
|
_transformArgsByParam18 = _transformArgsByParam17[2], |
|
colorspace = _transformArgsByParam18 === void 0 ? 'rgb' : _transformArgsByParam18; |
|
|
|
if (percentage !== undefined) { |
|
const modifiedColor = isAlphaBlend ? base.blenda(color.color, percentage, colorspace) : base.blend(color.color, percentage, colorspace); |
|
return modifiedColor; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid blend() adjuster)`); |
|
} |
|
} // return a transformed color using a contrast adjustment |
|
|
|
|
|
function transformContrastAdjuster(base, node, opts) { |
|
const _transformArgsByParam19 = transformArgsByParams(node, [// <percentage> |
|
[transformPercentage]]), |
|
_transformArgsByParam20 = _slicedToArray(_transformArgsByParam19, 1), |
|
percentage = _transformArgsByParam20[0]; |
|
|
|
if (percentage !== undefined) { |
|
const modifiedColor = base.contrast(percentage); |
|
return modifiedColor; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid contrast() adjuster)`); |
|
} |
|
} // return a transformed color using a hue adjustment |
|
|
|
|
|
function transformHueAdjuster(base, node, opts) { |
|
const _transformArgsByParam21 = transformArgsByParams(node, [// [ + | - | * ] <angle> |
|
[transformMinusPlusTimesOperator, transformHue], // <angle> |
|
[transformHue]]), |
|
_transformArgsByParam22 = _slicedToArray(_transformArgsByParam21, 2), |
|
operatorOrHue = _transformArgsByParam22[0], |
|
adjustment = _transformArgsByParam22[1]; |
|
|
|
if (operatorOrHue !== undefined) { |
|
const existingHue = base.hue(); |
|
const modifiedValue = adjustment !== undefined ? operatorOrHue === '+' ? existingHue + Number(adjustment) : operatorOrHue === '-' ? existingHue - Number(adjustment) : operatorOrHue === '*' ? existingHue * Number(adjustment) : Number(adjustment) : Number(operatorOrHue); |
|
return base.hue(modifiedValue); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hue() function)`); |
|
} |
|
} // [ b | blackness | l | lightness | s | saturation | w | whiteness ]( [ + | - | * ]? <percentage> ) |
|
|
|
|
|
function transformBlacknessLightnessSaturationWhitenessAdjuster(base, node, opts) { |
|
const channel = node.value.toLowerCase().replace(/^b$/, 'blackness').replace(/^l$/, 'lightness').replace(/^s$/, 'saturation').replace(/^w$/, 'whiteness'); |
|
|
|
const _transformArgsByParam23 = transformArgsByParams(node, [[transformMinusPlusTimesOperator, transformPercentage], [transformPercentage]]), |
|
_transformArgsByParam24 = _slicedToArray(_transformArgsByParam23, 2), |
|
operatorOrValue = _transformArgsByParam24[0], |
|
adjustment = _transformArgsByParam24[1]; |
|
|
|
if (operatorOrValue !== undefined) { |
|
const existingValue = base[channel](); |
|
const modifiedValue = adjustment !== undefined ? operatorOrValue === '+' ? existingValue + Number(adjustment) : operatorOrValue === '-' ? existingValue - Number(adjustment) : operatorOrValue === '*' ? existingValue * Number(adjustment) : Number(adjustment) : Number(operatorOrValue); |
|
return base[channel](modifiedValue); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid ${channel}() function)`); |
|
} |
|
} // return a transformed color using shade/tint adjustments |
|
|
|
|
|
function transformShadeTintAdjuster(base, node, opts) { |
|
const channel = node.value.toLowerCase(); |
|
|
|
const _transformArgsByParam25 = transformArgsByParams(node, [// [ shade | tint ]( <percentage> ) |
|
[transformPercentage]]), |
|
_transformArgsByParam26 = _slicedToArray(_transformArgsByParam25, 1), |
|
percentage = _transformArgsByParam26[0]; |
|
|
|
if (percentage !== undefined) { |
|
const modifiedValue = Number(percentage); |
|
return base[channel](modifiedValue); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected valid ${channel}() arguments`); |
|
} |
|
} |
|
/* Argument Transforms |
|
/* ========================================================================== */ |
|
// return a transformed color space |
|
|
|
|
|
function transformColorSpace(node, opts) { |
|
if (isColorSpace(node)) { |
|
// [ hsl | hwb | rgb ] |
|
return node.value; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid color space)`); |
|
} |
|
} // return a transformed alpha value |
|
|
|
|
|
function transformAlpha(node, opts) { |
|
if (isNumber(node)) { |
|
// <number> |
|
return node.value * 100; |
|
} else if (isPercentage(node)) { |
|
// <percentage> |
|
return transformPercentage(node, opts); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid alpha value)`); |
|
} |
|
} // return a transformed rgb number |
|
|
|
|
|
function transformRGBNumber(node, opts) { |
|
if (isNumber(node)) { |
|
// <number> |
|
return node.value / 2.55; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid RGB value)`); |
|
} |
|
} // return a transformed hue |
|
|
|
|
|
function transformHue(node, opts) { |
|
if (isHue(node)) { |
|
// <hue> = <number> | <angle> |
|
const unit = node.unit.toLowerCase(); |
|
|
|
if (unit === 'grad') { |
|
// if <angle> = <gradian> (400 per circle) |
|
return convertGtoD(node.value); |
|
} else if (unit === 'rad') { |
|
// if <angle> = <radian> (2π per circle) |
|
return convertRtoD(node.value); |
|
} else if (unit === 'turn') { |
|
// if <angle> = <turn> (1 per circle) |
|
return convertTtoD(node.value); |
|
} else { |
|
// if <angle> = [ <degree> | <number> ] (360 per circle) |
|
return convertDtoD(node.value); |
|
} |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hue`); |
|
} |
|
} // return a transformed percentage |
|
|
|
|
|
function transformPercentage(node, opts) { |
|
if (isPercentage(node)) { |
|
// <percentage> |
|
return Number(node.value); |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid hue`); |
|
} |
|
} // return a transformed minus-plus operator |
|
|
|
|
|
function transformMinusPlusOperator(node, opts) { |
|
if (isMinusPlusOperator(node)) { |
|
// [ - | + ] |
|
return node.value; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a plus or minus operator`); |
|
} |
|
} // return a transformed times operator |
|
|
|
|
|
function transformTimesOperator(node, opts) { |
|
if (isTimesOperator(node)) { |
|
// [ * ] |
|
return node.value; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a times operator`); |
|
} |
|
} // return a transformed minus-plus-times operator |
|
|
|
|
|
function transformMinusPlusTimesOperator(node, opts) { |
|
if (isMinusPlusTimesOperator(node)) { |
|
// [ - | + | * ] |
|
return node.value; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a plus, minus, or times operator`); |
|
} |
|
} |
|
/* Additional transforms |
|
/* ========================================================================== */ |
|
|
|
|
|
function transformWord(node, opts) { |
|
if (isWord(node)) { |
|
return node.value; |
|
} else { |
|
return manageUnresolved(node, opts, node.value, `Expected a valid word`); |
|
} |
|
} |
|
|
|
function transformNode(node) { |
|
return Object(node); |
|
} |
|
/* Transform helper |
|
/* ========================================================================== */ |
|
// return the first set of transformed arguments allowable by the parameters |
|
|
|
|
|
function transformArgsByParams(node, params) { |
|
const nodes = (node.nodes || []).slice(1, -1); |
|
const opts = { |
|
unresolved: 'ignore' |
|
}; |
|
return params.map(param => nodes.map((childNode, index) => typeof param[index] === 'function' ? param[index](childNode, opts) : undefined).filter(child => typeof child !== 'boolean')).filter(param => param.every(result => result !== undefined))[0] || []; |
|
} |
|
/* Walk helper (required because the default walker is affected by mutations) |
|
/* ========================================================================== */ |
|
// run a function over each node and hen walk each child node of that node |
|
|
|
|
|
function walk(node, fn) { |
|
fn(node); |
|
|
|
if (Object(node.nodes).length) { |
|
node.nodes.slice().forEach(childNode => { |
|
walk(childNode, fn); |
|
}); |
|
} |
|
} |
|
/* Variable validators |
|
/* ========================================================================== */ |
|
// return whether the node is a var function |
|
|
|
|
|
function isVariable(node) { |
|
// var() |
|
return Object(node).type === 'func' && varMatch.test(node.value); |
|
} |
|
/* Adjustment validators |
|
/* ========================================================================== */ |
|
// return whether the node is an a/alpha/blue/green/red adjuster |
|
|
|
|
|
function isAlphaBlueGreenRedAdjuster(node) { |
|
// [ a(), alpha(), blue(), green(), red() ] |
|
return Object(node).type === 'func' && alphaBlueGreenRedMatch.test(node.value); |
|
} // return whether the node is an rgb adjuster |
|
|
|
|
|
function isRGBAdjuster(node) { |
|
return Object(node).type === 'func' && rgbMatch.test(node.value); |
|
} // return whether the node is a hue adjuster |
|
|
|
|
|
function isHueAdjuster(node) { |
|
// [ h() | hue() ] |
|
return Object(node).type === 'func' && hueMatch.test(node.value); |
|
} // return whether the node is a blackness/lightness/saturation/whiteness adjuster |
|
|
|
|
|
function isBlacknessLightnessSaturationWhitenessAdjuster(node) { |
|
// [ b() | blackness() | l() | lightness() | s() | saturation() | w() | whiteness() ] |
|
return Object(node).type === 'func' && blacknessLightnessSaturationWhitenessMatch.test(node.value); |
|
} // return whether the node is a shade/tint adjuster |
|
|
|
|
|
function isShadeTintAdjuster(node) { |
|
// [ shade() | tint() ] |
|
return Object(node).type === 'func' && shadeTintMatch.test(node.value); |
|
} // return whether the node is a blend adjuster |
|
|
|
|
|
function isBlendAdjuster(node) { |
|
// [ blend(), blenda() ] |
|
return Object(node).type === 'func' && blendMatch.test(node.value); |
|
} // return whether the node is a contrast adjuster |
|
|
|
|
|
function isContrastAdjuster(node) { |
|
// [ contrast() ] |
|
return Object(node).type === 'func' && contrastMatch.test(node.value); |
|
} |
|
/* Color validators |
|
/* ========================================================================== */ |
|
// return whether the node is an rgb/rgba color function |
|
|
|
|
|
function isRGBFunction(node) { |
|
// [ rgb(), rgba() ] |
|
return Object(node).type === 'func' && rgbaMatch.test(node.value); |
|
} // return whether the node is an hsl color function |
|
|
|
|
|
function isHSLFunction(node) { |
|
// [ hsl(), hsla() ] |
|
return Object(node).type === 'func' && hslaMatch.test(node.value); |
|
} // return whether the node is an hwb color function |
|
|
|
|
|
function isHWBFunction(node) { |
|
// hwb() |
|
return Object(node).type === 'func' && hwbMatch.test(node.value); |
|
} // return whether the node is a color-mod function |
|
|
|
|
|
function isColorModFunction(node) { |
|
// color-mod() |
|
return Object(node).type === 'func' && colorModMatch.test(node.value); |
|
} // return whether the node is a valid named-color |
|
|
|
|
|
function isNamedColor(node) { |
|
return Object(node).type === 'word' && Boolean(convertNtoRGB(node.value)); |
|
} // return whether the node is a valid hex color |
|
|
|
|
|
function isHexColor(node) { |
|
// #<hex-color>{3,4,6,8} |
|
return Object(node).type === 'word' && hexColorMatch$1.test(node.value); |
|
} // return whether the node is a valid color space |
|
|
|
|
|
function isColorSpace(node) { |
|
// [ hsl | hwb | rgb ] |
|
return Object(node).type === 'word' && colorSpaceMatch.test(node.value); |
|
} |
|
/* Additional validators |
|
/* ========================================================================== */ |
|
// return whether the hue value is valid |
|
|
|
|
|
function isHue(node) { |
|
return Object(node).type === 'number' && hueUnitMatch.test(node.unit); |
|
} // return whether the comma is valid |
|
|
|
|
|
function isComma(node) { |
|
return Object(node).type === 'comma'; |
|
} // return whether the slash operator is valid |
|
|
|
|
|
function isSlash(node) { |
|
return Object(node).type === 'operator' && node.value === '/'; |
|
} // return whether the number is valid |
|
|
|
|
|
function isNumber(node) { |
|
return Object(node).type === 'number' && node.unit === ''; |
|
} // return whether the mind-plus operator is valid |
|
|
|
|
|
function isMinusPlusOperator(node) { |
|
return Object(node).type === 'operator' && minusPlusMatch.test(node.value); |
|
} // return whether the minus-plus-times operator is valid |
|
|
|
|
|
function isMinusPlusTimesOperator(node) { |
|
return Object(node).type === 'operator' && minusPlusTimesMatch.test(node.value); |
|
} // return whether the times operator is valid |
|
|
|
|
|
function isTimesOperator(node) { |
|
return Object(node).type === 'operator' && timesMatch.test(node.value); |
|
} // return whether the percentage is valid |
|
|
|
|
|
function isPercentage(node) { |
|
return Object(node).type === 'number' && (node.unit === '%' || node.value === '0'); |
|
} // return whether the node is a word |
|
|
|
|
|
function isWord(node) { |
|
// <word> |
|
return Object(node).type === 'word'; |
|
} |
|
/* Matchers |
|
/* ========================================================================== */ |
|
|
|
|
|
const alphaMatch = /^a(lpha)?$/i; |
|
const alphaBlueGreenRedMatch = /^(a(lpha)?|blue|green|red)$/i; |
|
const blacknessLightnessSaturationWhitenessMatch = /^(b(lackness)?|l(ightness)?|s(aturation)?|w(hiteness)?)$/i; |
|
const blendMatch = /^blenda?$/i; |
|
const colorModMatch = /^color-mod$/i; |
|
const colorSpaceMatch = /^(hsl|hwb|rgb)$/i; |
|
const contrastMatch = /^contrast$/i; |
|
const hexColorMatch$1 = /^#(?:([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?|([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?)$/i; |
|
const hslaMatch = /^hsla?$/i; |
|
const hueUnitMatch = /^(deg|grad|rad|turn)?$/i; |
|
const hueMatch = /^h(ue)?$/i; |
|
const hwbMatch = /^hwb$/i; |
|
const minusPlusMatch = /^[+-]$/; |
|
const minusPlusTimesMatch = /^[*+-]$/; |
|
const rgbMatch = /^rgb$/i; |
|
const rgbaMatch = /^rgba?$/i; |
|
const shadeTintMatch = /^(shade|tint)$/i; |
|
const varMatch = /^var$/i; |
|
const looseVarMatch = /(^|[^\w-])var\(/i; |
|
const timesMatch = /^[*]$/; |
|
|
|
var index = postcss.plugin('postcss-color-mod-function', opts => { |
|
// how unresolved functions and arguments should be handled (default: "throw") |
|
const unresolvedOpt = String(Object(opts).unresolved || 'throw').toLowerCase(); // how transformed colors will be produced in CSS |
|
|
|
const stringifierOpt = Object(opts).stringifier || (color => color.toLegacy()); // sources to import custom selectors from |
|
|
|
|
|
const importFrom = [].concat(Object(opts).importFrom || []); // whether var() within color-mod() should use Custom Properties or var() fallback |
|
|
|
const transformVarsOpt = 'transformVars' in Object(opts) ? opts.transformVars : true; // promise any custom selectors are imported |
|
|
|
const customPropertiesPromise = importCustomPropertiesFromSources(importFrom); |
|
return ( |
|
/*#__PURE__*/ |
|
function () { |
|
var _ref = _asyncToGenerator(function* (root, result) { |
|
const customProperties = Object.assign((yield customPropertiesPromise), getCustomProperties(root, { |
|
preserve: true |
|
})); |
|
root.walkDecls(decl => { |
|
const originalValue = decl.value; |
|
|
|
if (colorModFunctionMatch.test(originalValue)) { |
|
const ast = valueParser(originalValue, { |
|
loose: true |
|
}).parse(); |
|
transformAST(ast, { |
|
unresolved: unresolvedOpt, |
|
stringifier: stringifierOpt, |
|
transformVars: transformVarsOpt, |
|
decl, |
|
result, |
|
customProperties |
|
}); |
|
const modifiedValue = ast.toString(); |
|
|
|
if (originalValue !== modifiedValue) { |
|
decl.value = modifiedValue; |
|
} |
|
} |
|
}); |
|
}); |
|
|
|
return function (_x, _x2) { |
|
return _ref.apply(this, arguments); |
|
}; |
|
}() |
|
); |
|
}); |
|
const colorModFunctionMatch = /(^|[^\w-])color-mod\(/i; |
|
|
|
export default index; |
|
//# sourceMappingURL=index.es.mjs.map
|
|
|