-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathindex.js
172 lines (136 loc) · 5.18 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var tinycolor = require('tinycolor2');
var isNumeric = require('fast-isnumeric');
var color = module.exports = {};
var colorAttrs = require('./attributes');
color.defaults = colorAttrs.defaults;
var defaultLine = color.defaultLine = colorAttrs.defaultLine;
color.lightLine = colorAttrs.lightLine;
var background = color.background = colorAttrs.background;
/*
* tinyRGB: turn a tinycolor into an rgb string, but
* unlike the built-in tinycolor.toRgbString this never includes alpha
*/
color.tinyRGB = function(tc) {
var c = tc.toRgb();
return 'rgb(' + Math.round(c.r) + ', ' +
Math.round(c.g) + ', ' + Math.round(c.b) + ')';
};
color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); };
color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; };
color.addOpacity = function(cstr, op) {
var c = tinycolor(cstr).toRgb();
return 'rgba(' + Math.round(c.r) + ', ' +
Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')';
};
// combine two colors into one apparent color
// if back has transparency or is missing,
// color.background is assumed behind it
color.combine = function(front, back) {
var fc = tinycolor(front).toRgb();
if(fc.a === 1) return tinycolor(front).toRgbString();
var bc = tinycolor(back || background).toRgb();
var bcflat = bc.a === 1 ? bc : {
r: 255 * (1 - bc.a) + bc.r * bc.a,
g: 255 * (1 - bc.a) + bc.g * bc.a,
b: 255 * (1 - bc.a) + bc.b * bc.a
};
var fcflat = {
r: bcflat.r * (1 - fc.a) + fc.r * fc.a,
g: bcflat.g * (1 - fc.a) + fc.g * fc.a,
b: bcflat.b * (1 - fc.a) + fc.b * fc.a
};
return tinycolor(fcflat).toRgbString();
};
/*
* Create a color that contrasts with cstr.
*
* If cstr is a dark color, we lighten it; if it's light, we darken.
*
* If lightAmount / darkAmount are used, we adjust by these percentages,
* otherwise we go all the way to white or black.
*/
color.contrast = function(cstr, lightAmount, darkAmount) {
var tc = tinycolor(cstr);
if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background));
var newColor = tc.isDark() ?
(lightAmount ? tc.lighten(lightAmount) : background) :
(darkAmount ? tc.darken(darkAmount) : defaultLine);
return newColor.toString();
};
color.stroke = function(s, c) {
var tc = tinycolor(c);
s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()});
};
color.fill = function(s, c) {
var tc = tinycolor(c);
s.style({
'fill': color.tinyRGB(tc),
'fill-opacity': tc.getAlpha()
});
};
// search container for colors with the deprecated rgb(fractions) format
// and convert them to rgb(0-255 values)
color.clean = function(container) {
if(!container || typeof container !== 'object') return;
var keys = Object.keys(container);
var i, j, key, val;
for(i = 0; i < keys.length; i++) {
key = keys[i];
val = container[key];
if(key.substr(key.length - 5) === 'color') {
// only sanitize keys that end in "color" or "colorscale"
if(Array.isArray(val)) {
for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]);
} else container[key] = cleanOne(val);
} else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) {
// colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]]
for(j = 0; j < val.length; j++) {
if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]);
}
} else if(Array.isArray(val)) {
// recurse into arrays of objects, and plain objects
var el0 = val[0];
if(!Array.isArray(el0) && el0 && typeof el0 === 'object') {
for(j = 0; j < val.length; j++) color.clean(val[j]);
}
} else if(val && typeof val === 'object') color.clean(val);
}
};
function cleanOne(val) {
if(isNumeric(val) || typeof val !== 'string') return val;
var valTrim = val.trim();
if(valTrim.substr(0, 3) !== 'rgb') return val;
var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/);
if(!match) return val;
var parts = match[1].trim().split(/\s*[\s,]\s*/);
var rgba = valTrim.charAt(3) === 'a' && parts.length === 4;
if(!rgba && parts.length !== 3) return val;
for(var i = 0; i < parts.length; i++) {
if(!parts[i].length) return val;
parts[i] = Number(parts[i]);
if(!(parts[i] >= 0)) {
// all parts must be non-negative numbers
return val;
}
if(i === 3) {
// alpha>1 gets clipped to 1
if(parts[i] > 1) parts[i] = 1;
} else if(parts[i] >= 1) {
// r, g, b must be < 1 (ie 1 itself is not allowed)
return val;
}
}
var rgbStr = Math.round(parts[0] * 255) + ', ' +
Math.round(parts[1] * 255) + ', ' +
Math.round(parts[2] * 255);
if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')';
return 'rgb(' + rgbStr + ')';
}