forked from plotly/plotly.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
183 lines (146 loc) · 5.62 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
173
174
175
176
177
178
179
180
181
182
183
'use strict';
var tinycolor = require('tinycolor2');
var isNumeric = require('fast-isnumeric');
var isTypedArray = require('../../lib/array').isTypedArray;
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();
};
/*
* Linearly interpolate between two colors at a normalized interpolation position (0 to 1).
*
* Ignores alpha channel values.
* The resulting color is computed as: factor * first + (1 - factor) * second.
*/
color.interpolate = function(first, second, factor) {
var fc = tinycolor(first).toRgb();
var sc = tinycolor(second).toRgb();
var ic = {
r: factor * fc.r + (1 - factor) * sc.r,
g: factor * fc.g + (1 - factor) * sc.g,
b: factor * fc.b + (1 - factor) * sc.b,
};
return tinycolor(ic).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' && !isTypedArray(val)) 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 + ')';
}