Skip to content

Commit df06911

Browse files
committed
break lib.js into smaller lib modules:
- coerce.js, dates.js, matrix.js nested_property.js, notifier.js, search.js, stats.js
1 parent edae224 commit df06911

File tree

8 files changed

+1294
-1224
lines changed

8 files changed

+1294
-1224
lines changed

src/lib/coerce.js

+334
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
'use strict';
2+
3+
var Plotly = require('../plotly');
4+
var isNumeric = require('fast-isnumeric');
5+
var tinycolor = require('tinycolor2');
6+
var nestedProperty = require('./nested_property');
7+
8+
var colorscaleNames = Object.keys(require('../components/colorscale/scales'));
9+
10+
11+
exports.valObjects = {
12+
data_array: {
13+
// You can use *dflt=[] to force said array to exist though.
14+
description: [
15+
'An {array} of data.',
16+
'The value MUST be an {array}, or we ignore it.'
17+
].join(' '),
18+
requiredOpts: [],
19+
otherOpts: ['dflt'],
20+
coerceFunction: function(v, propOut, dflt) {
21+
if(Array.isArray(v)) propOut.set(v);
22+
else if(dflt!==undefined) propOut.set(dflt);
23+
}
24+
},
25+
enumerated: {
26+
description: [
27+
'Enumerated value type. The available values are listed',
28+
'in `values`.'
29+
].join(' '),
30+
requiredOpts: ['values'],
31+
otherOpts: ['dflt', 'coerceNumber', 'arrayOk'],
32+
coerceFunction: function(v, propOut, dflt, opts) {
33+
if(opts.coerceNumber) v = +v;
34+
if(opts.values.indexOf(v)===-1) propOut.set(dflt);
35+
else propOut.set(v);
36+
}
37+
},
38+
'boolean': {
39+
description: 'A boolean (true/false) value.',
40+
requiredOpts: [],
41+
otherOpts: ['dflt'],
42+
coerceFunction: function(v, propOut, dflt) {
43+
if(v===true || v===false) propOut.set(v);
44+
else propOut.set(dflt);
45+
}
46+
},
47+
number: {
48+
description: [
49+
'A number or a numeric value',
50+
'(e.g. a number inside a string).',
51+
'When applicable, values greater (less) than `max` (`min`)',
52+
'are coerced to the `dflt`.'
53+
].join(' '),
54+
requiredOpts: [],
55+
otherOpts: ['dflt', 'min', 'max', 'arrayOk'],
56+
coerceFunction: function(v, propOut, dflt, opts) {
57+
if(!isNumeric(v) ||
58+
(opts.min!==undefined && v<opts.min) ||
59+
(opts.max!==undefined && v>opts.max)) {
60+
propOut.set(dflt);
61+
}
62+
else propOut.set(+v);
63+
}
64+
},
65+
integer: {
66+
description: [
67+
'An integer or an integer inside a string.',
68+
'When applicable, values greater (less) than `max` (`min`)',
69+
'are coerced to the `dflt`.'
70+
].join(' '),
71+
requiredOpts: [],
72+
otherOpts: ['dflt', 'min', 'max'],
73+
coerceFunction: function(v, propOut, dflt, opts) {
74+
if(v%1 || !isNumeric(v) ||
75+
(opts.min!==undefined && v<opts.min) ||
76+
(opts.max!==undefined && v>opts.max)) {
77+
propOut.set(dflt);
78+
}
79+
else propOut.set(+v);
80+
}
81+
},
82+
string: {
83+
description: [
84+
'A string value.',
85+
'Numbers are converted to strings except for attributes with',
86+
'`strict` set to true.'
87+
].join(' '),
88+
requiredOpts: [],
89+
// TODO 'values shouldn't be in there (edge case: 'dash' in Scatter)
90+
otherOpts: ['dflt', 'noBlank', 'strict', 'arrayOk', 'values'],
91+
coerceFunction: function(v, propOut, dflt, opts) {
92+
if(opts.strict===true && typeof v !== 'string') {
93+
propOut.set(dflt);
94+
return;
95+
}
96+
97+
var s = String(v);
98+
if(v===undefined || (opts.noBlank===true && !s)) {
99+
propOut.set(dflt);
100+
}
101+
else propOut.set(s);
102+
}
103+
},
104+
color: {
105+
description: [
106+
'A string describing color.',
107+
'Supported formats:',
108+
'- hex (e.g. \'#d3d3d3\')',
109+
'- rgb (e.g. \'rgb(255, 0, 0)\')',
110+
'- rgba (e.g. \'rgb(255, 0, 0, 0.5)\')',
111+
'- hsl (e.g. \'hsl(0, 100%, 50%)\')',
112+
'- hsv (e.g. \'hsv(0, 100%, 100%)\')',
113+
'- named colors (full list: http://www.w3.org/TR/css3-color/#svg-color)'
114+
].join(' '),
115+
requiredOpts: [],
116+
otherOpts: ['dflt', 'arrayOk'],
117+
coerceFunction: function(v, propOut, dflt) {
118+
if(tinycolor(v).isValid()) propOut.set(v);
119+
else propOut.set(dflt);
120+
}
121+
},
122+
colorscale: {
123+
description: [
124+
'A Plotly colorscale either picked by a name:',
125+
'(any of', colorscaleNames.join(', '), ')',
126+
'customized as an {array} of 2-element {arrays} where',
127+
'the first element is the normalized color level value',
128+
'(starting at *0* and ending at *1*),',
129+
'and the second item is a valid color string.'
130+
].join(' '),
131+
requiredOpts: [],
132+
otherOpts: ['dflt'],
133+
coerceFunction: function(v, propOut, dflt) {
134+
propOut.set(Plotly.Colorscale.getScale(v, dflt));
135+
}
136+
},
137+
angle: {
138+
description: [
139+
'A number (in degree) between -180 and 180.'
140+
].join(' '),
141+
requiredOpts: [],
142+
otherOpts: ['dflt'],
143+
coerceFunction: function(v, propOut, dflt) {
144+
if(v==='auto') propOut.set('auto');
145+
else if(!isNumeric(v)) propOut.set(dflt);
146+
else {
147+
if(Math.abs(v)>180) v -= Math.round(v/360)*360;
148+
propOut.set(+v);
149+
}
150+
}
151+
},
152+
axisid: {
153+
description: [
154+
'An axis id string (e.g. \'x\', \'x2\', \'x3\', ...).'
155+
].join(' '),
156+
requiredOpts: [],
157+
otherOpts: ['dflt'],
158+
coerceFunction: function(v, propOut, dflt) {
159+
if(typeof v === 'string' && v.charAt(0)===dflt) {
160+
var axnum = Number(v.substr(1));
161+
if(axnum%1 === 0 && axnum>1) {
162+
propOut.set(v);
163+
return;
164+
}
165+
}
166+
propOut.set(dflt);
167+
}
168+
},
169+
sceneid: {
170+
description: [
171+
'A scene id string (e.g. \'scene\', \'scene2\', \'scene3\', ...).'
172+
].join(' '),
173+
requiredOpts: [],
174+
otherOpts: ['dflt'],
175+
coerceFunction: function(v, propOut, dflt) {
176+
if(typeof v === 'string' && v.substr(0,5)===dflt) {
177+
var scenenum = Number(v.substr(5));
178+
if(scenenum%1 === 0 && scenenum>1) {
179+
propOut.set(v);
180+
return;
181+
}
182+
}
183+
propOut.set(dflt);
184+
}
185+
},
186+
geoid: {
187+
description: [
188+
'A geo id string (e.g. \'geo\', \'geo2\', \'geo3\', ...).'
189+
].join(' '),
190+
requiredOpts: [],
191+
otherOpts: ['dflt'],
192+
coerceFunction: function(v, propOut, dflt) {
193+
if(typeof v === 'string' && v.substr(0,3)===dflt) {
194+
var geonum = Number(v.substr(3));
195+
if(geonum%1 === 0 && geonum>1) {
196+
propOut.set(v);
197+
return;
198+
}
199+
}
200+
propOut.set(dflt);
201+
}
202+
},
203+
flaglist: {
204+
description: [
205+
'A string representing a combination of flags',
206+
'(order does not matter here).',
207+
'Combine any of the available `flags` with *+*.',
208+
'(e.g. (\'lines+markers\')).',
209+
'Values in `extras` cannot be combined.'
210+
].join(' '),
211+
requiredOpts: ['flags'],
212+
otherOpts: ['dflt', 'extras'],
213+
coerceFunction: function(v, propOut, dflt, opts) {
214+
if(typeof v !== 'string') {
215+
propOut.set(dflt);
216+
return;
217+
}
218+
if(opts.extras.indexOf(v)!==-1) {
219+
propOut.set(v);
220+
return;
221+
}
222+
var vParts = v.split('+'),
223+
i = 0;
224+
while(i<vParts.length) {
225+
var vi = vParts[i];
226+
if(opts.flags.indexOf(vi)===-1 || vParts.indexOf(vi)<i) {
227+
vParts.splice(i,1);
228+
}
229+
else i++;
230+
}
231+
if(!vParts.length) propOut.set(dflt);
232+
else propOut.set(vParts.join('+'));
233+
}
234+
},
235+
any: {
236+
description: 'Any type.',
237+
requiredOpts: [],
238+
otherOpts: ['dflt'],
239+
coerceFunction: function(v, propOut, dflt) {
240+
if(v===undefined) propOut.set(dflt);
241+
else propOut.set(v);
242+
}
243+
},
244+
info_array: {
245+
description: [
246+
'An {array} of plot information.'
247+
].join(' '),
248+
requiredOpts: ['items'],
249+
otherOpts: ['dflt'],
250+
coerceFunction: function(v, propOut, dflt, opts) {
251+
if(!Array.isArray(v)) {
252+
propOut.set(dflt);
253+
return;
254+
}
255+
256+
var items = opts.items,
257+
vOut = [];
258+
dflt = Array.isArray(dflt) ? dflt : [];
259+
260+
for(var i = 0; i < items.length; i++) {
261+
exports.coerce(v, vOut, items, '[' + i + ']', dflt[i]);
262+
}
263+
264+
propOut.set(vOut);
265+
}
266+
}
267+
};
268+
269+
/**
270+
* Ensures that container[attribute] has a valid value.
271+
*
272+
* attributes[attribute] is an object with possible keys:
273+
* - valType: data_array, enumerated, boolean, ... as in valObjects
274+
* - values: (enumerated only) array of allowed vals
275+
* - min, max: (number, integer only) inclusive bounds on allowed vals
276+
* either or both may be omitted
277+
* - dflt: if attribute is invalid or missing, use this default
278+
* if dflt is provided as an argument to lib.coerce it takes precedence
279+
* as a convenience, returns the value it finally set
280+
*/
281+
exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
282+
var opts = nestedProperty(attributes, attribute).get(),
283+
propIn = nestedProperty(containerIn, attribute),
284+
propOut = nestedProperty(containerOut, attribute),
285+
v = propIn.get();
286+
287+
if(dflt===undefined) dflt = opts.dflt;
288+
289+
/**
290+
* arrayOk: value MAY be an array, then we do no value checking
291+
* at this point, because it can be more complicated than the
292+
* individual form (eg. some array vals can be numbers, even if the
293+
* single values must be color strings)
294+
*/
295+
if(opts.arrayOk && Array.isArray(v)) {
296+
propOut.set(v);
297+
return v;
298+
}
299+
300+
exports.valObjects[opts.valType].coerceFunction(v, propOut, dflt, opts);
301+
302+
return propOut.get();
303+
};
304+
305+
/**
306+
* Variation on coerce
307+
*
308+
* Uses coerce to get attribute value if user input is valid,
309+
* returns attribute default if user input it not valid or
310+
* returns false if there is no user input.
311+
*/
312+
exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) {
313+
var propIn = nestedProperty(containerIn, attribute),
314+
propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt);
315+
316+
return propIn.get() ? propOut : false;
317+
};
318+
319+
/*
320+
* Shortcut to coerce the three font attributes
321+
*
322+
* 'coerce' is a lib.coerce wrapper with implied first three arguments
323+
*/
324+
exports.coerceFont = function(coerce, attr, dfltObj) {
325+
var out = {};
326+
327+
dfltObj = dfltObj || {};
328+
329+
out.family = coerce(attr + '.family', dfltObj.family);
330+
out.size = coerce(attr + '.size', dfltObj.size);
331+
out.color = coerce(attr + '.color', dfltObj.color);
332+
333+
return out;
334+
};

0 commit comments

Comments
 (0)