Skip to content

Commit 54d8b58

Browse files
Merge remote-tracking branch 'upstream/master' into rangeslider-allow-zoom-on-oppaxis
2 parents 149ef70 + 4bcde5f commit 54d8b58

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2446
-1559
lines changed

package-lock.json

Lines changed: 827 additions & 1286 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"gl-select-box": "^1.0.2",
8484
"gl-spikes2d": "^1.0.1",
8585
"gl-surface3d": "^1.3.4",
86-
"glslify": "^6.1.0",
86+
"glslify": "^6.1.1",
8787
"has-hover": "^1.0.1",
8888
"has-passive-events": "^1.0.0",
8989
"kdgrass": "^1.0.1",
@@ -99,8 +99,8 @@
9999
"polybooljs": "^1.2.0",
100100
"regl": "^1.3.1",
101101
"regl-error2d": "^2.0.3",
102-
"regl-line2d": "^2.1.4",
103-
"regl-scatter2d": "^2.1.13",
102+
"regl-line2d": "^2.1.5",
103+
"regl-scatter2d": "^2.1.17",
104104
"right-now": "^1.0.0",
105105
"robust-orientation": "^1.1.3",
106106
"sane-topojson": "^2.0.0",
@@ -119,18 +119,18 @@
119119
"check-node-version": "^3.2.0",
120120
"deep-equal": "^1.0.1",
121121
"ecstatic": "^3.2.0",
122-
"eslint": "^4.17.0",
122+
"eslint": "^4.18.0",
123123
"falafel": "^2.0.0",
124124
"fs-extra": "^2.0.0",
125125
"fuse.js": "^3.2.0",
126126
"glob": "^7.0.0",
127127
"gzip-size": "^4.1.0",
128128
"image-size": "^0.6.2",
129129
"into-stream": "^3.1.0",
130-
"jasmine-core": "^2.4.1",
130+
"jasmine-core": "^2.99.1",
131131
"jsdom": "^11.6.2",
132132
"karma": "^2.0.0",
133-
"karma-browserify": "^5.1.1",
133+
"karma-browserify": "^5.2.0",
134134
"karma-chrome-launcher": "^2.0.0",
135135
"karma-coverage": "^1.0.0",
136136
"karma-firefox-launcher": "^1.0.1",

src/lib/coerce.js

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,19 +257,56 @@ exports.valObjectMeta = {
257257
'An {array} of plot information.'
258258
].join(' '),
259259
requiredOpts: ['items'],
260-
otherOpts: ['dflt', 'freeLength'],
260+
// set dimensions=2 for a 2D array
261+
// `items` may be a single object instead of an array, in which case
262+
// `freeLength` must be true.
263+
otherOpts: ['dflt', 'freeLength', 'dimensions'],
261264
coerceFunction: function(v, propOut, dflt, opts) {
265+
266+
// simplified coerce function just for array items
267+
function coercePart(v, opts, dflt) {
268+
var out;
269+
var propPart = {set: function(v) { out = v; }};
270+
271+
if(dflt === undefined) dflt = opts.dflt;
272+
273+
exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
274+
275+
return out;
276+
}
277+
278+
var twoD = opts.dimensions === 2;
279+
262280
if(!Array.isArray(v)) {
263281
propOut.set(dflt);
264282
return;
265283
}
266284

267-
var items = opts.items,
268-
vOut = [];
285+
var items = opts.items;
286+
var vOut = [];
287+
var arrayItems = Array.isArray(items);
288+
var len = arrayItems ? items.length : v.length;
289+
290+
var i, j, len2, vNew;
291+
269292
dflt = Array.isArray(dflt) ? dflt : [];
270293

271-
for(var i = 0; i < items.length; i++) {
272-
exports.coerce(v, vOut, items, '[' + i + ']', dflt[i]);
294+
if(twoD) {
295+
for(i = 0; i < len; i++) {
296+
vOut[i] = [];
297+
var row = Array.isArray(v[i]) ? v[i] : [];
298+
len2 = arrayItems ? items[i].length : row.length;
299+
for(j = 0; j < len2; j++) {
300+
vNew = coercePart(row[j], arrayItems ? items[i][j] : items, (dflt[i] || [])[j]);
301+
if(vNew !== undefined) vOut[i][j] = vNew;
302+
}
303+
}
304+
}
305+
else {
306+
for(i = 0; i < len; i++) {
307+
vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
308+
if(vNew !== undefined) vOut[i] = vNew;
309+
}
273310
}
274311

275312
propOut.set(vOut);
@@ -278,15 +315,25 @@ exports.valObjectMeta = {
278315
if(!Array.isArray(v)) return false;
279316

280317
var items = opts.items;
318+
var arrayItems = Array.isArray(items);
319+
var twoD = opts.dimensions === 2;
281320

282321
// when free length is off, input and declared lengths must match
283322
if(!opts.freeLength && v.length !== items.length) return false;
284323

285324
// valid when all input items are valid
286325
for(var i = 0; i < v.length; i++) {
287-
var isItemValid = exports.validate(v[i], opts.items[i]);
288-
289-
if(!isItemValid) return false;
326+
if(twoD) {
327+
if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
328+
return false;
329+
}
330+
for(var j = 0; j < v[i].length; j++) {
331+
if(!exports.validate(v[i][j], arrayItems ? items[i][j] : items)) {
332+
return false;
333+
}
334+
}
335+
}
336+
else if(!exports.validate(v[i], arrayItems ? items[i] : items)) return false;
290337
}
291338

292339
return true;

src/lib/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,3 +876,19 @@ lib.subplotSort = function(a, b) {
876876
}
877877
return numB - numA;
878878
};
879+
880+
// repeatable pseudorandom generator
881+
var randSeed = 2000000000;
882+
883+
lib.seedPseudoRandom = function() {
884+
randSeed = 2000000000;
885+
};
886+
887+
lib.pseudoRandom = function() {
888+
var lastVal = randSeed;
889+
randSeed = (69069 * randSeed + 1) % 4294967296;
890+
// don't let consecutive vals be too close together
891+
// gets away from really trying to be random, in favor of better local uniformity
892+
if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
893+
return randSeed / 4294967296;
894+
};

src/lib/regex.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88

99
'use strict';
1010

11-
// Simple helper functions
12-
// none of these need any external deps
13-
1411
/*
1512
* make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10...
16-
* eg: regexCounter('x')
17-
* tail is an optional piece after the id
18-
* eg regexCounter('scene', '.annotations') for scene2.annotations etc.
13+
*
14+
* @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc.
15+
* 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc
16+
* @param {Optional(string)} tail: a fixed piece after the id
17+
* eg counterRegex('scene', '.annotations') for scene2.annotations etc.
18+
* @param {boolean} openEnded: if true, the string may continue past the match.
1919
*/
2020
exports.counter = function(head, tail, openEnded) {
21-
return new RegExp('^' + head + '([2-9]|[1-9][0-9]+)?' +
22-
(tail || '') + (openEnded ? '' : '$'));
21+
var fullTail = (tail || '') + (openEnded ? '' : '$');
22+
if(head === 'xy') {
23+
return new RegExp('^x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail);
24+
}
25+
return new RegExp('^' + head + '([2-9]|[1-9][0-9]+)?' + fullTail);
2326
};

src/lib/stats.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var isNumeric = require('fast-isnumeric');
2828
exports.aggNums = function(f, v, a, len) {
2929
var i,
3030
b;
31-
if(!len) len = a.length;
31+
if(!len || len > a.length) len = a.length;
3232
if(!isNumeric(v)) v = false;
3333
if(Array.isArray(a[0])) {
3434
b = new Array(len);

src/plot_api/plot_schema.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ function recurseIntoValObject(valObject, parts, i) {
383383
}
384384

385385
// now recurse as far as we can. Occasionally we have an attribute
386-
// setting an internal part below what's
386+
// setting an internal part below what's in the schema; just return
387+
// the innermost schema item we find.
387388
for(; i < parts.length; i++) {
388389
var newValObject = valObject[parts[i]];
389390
if(Lib.isPlainObject(newValObject)) valObject = newValObject;
@@ -398,8 +399,23 @@ function recurseIntoValObject(valObject, parts, i) {
398399
else if(valObject.valType === 'info_array') {
399400
i++;
400401
var index = parts[i];
401-
if(!isIndex(index) || index >= valObject.items.length) return false;
402-
valObject = valObject.items[index];
402+
if(!isIndex(index)) return false;
403+
404+
var items = valObject.items;
405+
if(Array.isArray(items)) {
406+
if(index >= items.length) return false;
407+
if(valObject.dimensions === 2) {
408+
i++;
409+
if(parts.length === i) return valObject;
410+
var index2 = parts[i];
411+
if(!isIndex(index2)) return false;
412+
valObject = items[index][index2];
413+
}
414+
else valObject = items[index];
415+
}
416+
else {
417+
valObject = items;
418+
}
403419
}
404420
}
405421

src/plot_api/subroutines.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ exports.lsInner = function(gd) {
272272
* -----
273273
* x2
274274
*/
275+
var xPath = 'M0,0';
275276
if(shouldShowLinesOrTicks(xa, subplot)) {
276277
leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList);
277278
xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0);
@@ -288,17 +289,17 @@ exports.lsInner = function(gd) {
288289
xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
289290
}
290291

291-
var xPath = mainPath(xa, xLinePath, xLinePathFree);
292+
xPath = mainPath(xa, xLinePath, xLinePathFree);
292293
if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) {
293294
xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop);
294295
}
295296

296297
plotinfo.xlines
297-
.attr('d', xPath || 'M0,0')
298298
.style('stroke-width', xa._lw + 'px')
299299
.call(Color.stroke, xa.showline ?
300300
xa.linecolor : 'rgba(0,0,0,0)');
301301
}
302+
plotinfo.xlines.attr('d', xPath);
302303

303304
/*
304305
* y lines that meet x axes get longer only by margin.pad, because
@@ -311,6 +312,7 @@ exports.lsInner = function(gd) {
311312
* |
312313
* +-----
313314
*/
315+
var yPath = 'M0,0';
314316
if(shouldShowLinesOrTicks(ya, subplot)) {
315317
connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList);
316318
yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0);
@@ -324,17 +326,17 @@ exports.lsInner = function(gd) {
324326
ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
325327
}
326328

327-
var yPath = mainPath(ya, yLinePath, yLinePathFree);
329+
yPath = mainPath(ya, yLinePath, yLinePathFree);
328330
if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) {
329331
yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight);
330332
}
331333

332334
plotinfo.ylines
333-
.attr('d', yPath || 'M0,0')
334335
.style('stroke-width', ya._lw + 'px')
335336
.call(Color.stroke, ya.showline ?
336337
ya.linecolor : 'rgba(0,0,0,0)');
337338
}
339+
plotinfo.ylines.attr('d', yPath);
338340
});
339341

340342
Plotly.Axes.makeClipPaths(gd);

src/plot_api/validate.js

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,65 @@ function crawl(objIn, objOut, schema, list, base, path) {
164164
var valIn = objIn[k],
165165
valOut = objOut[k];
166166

167-
var nestedSchema = getNestedSchema(schema, k),
168-
isInfoArray = (nestedSchema || {}).valType === 'info_array',
169-
isColorscale = (nestedSchema || {}).valType === 'colorscale';
167+
var nestedSchema = getNestedSchema(schema, k);
168+
var isInfoArray = (nestedSchema || {}).valType === 'info_array';
169+
var isColorscale = (nestedSchema || {}).valType === 'colorscale';
170+
var items = (nestedSchema || {}).items;
170171

171172
if(!isInSchema(schema, k)) {
172173
list.push(format('schema', base, p));
173174
}
174175
else if(isPlainObject(valIn) && isPlainObject(valOut)) {
175176
crawl(valIn, valOut, nestedSchema, list, base, p);
176177
}
178+
else if(isInfoArray && isArray(valIn)) {
179+
if(valIn.length > valOut.length) {
180+
list.push(format('unused', base, p.concat(valOut.length)));
181+
}
182+
var len = valOut.length;
183+
var arrayItems = Array.isArray(items);
184+
if(arrayItems) len = Math.min(len, items.length);
185+
var m, n, item, valInPart, valOutPart;
186+
if(nestedSchema.dimensions === 2) {
187+
for(n = 0; n < len; n++) {
188+
if(isArray(valIn[n])) {
189+
if(valIn[n].length > valOut[n].length) {
190+
list.push(format('unused', base, p.concat(n, valOut[n].length)));
191+
}
192+
var len2 = valOut[n].length;
193+
for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) {
194+
item = arrayItems ? items[n][m] : items;
195+
valInPart = valIn[n][m];
196+
valOutPart = valOut[n][m];
197+
if(!Lib.validate(valInPart, item)) {
198+
list.push(format('value', base, p.concat(n, m), valInPart));
199+
}
200+
else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
201+
list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart));
202+
}
203+
}
204+
}
205+
else {
206+
list.push(format('array', base, p.concat(n), valIn[n]));
207+
}
208+
}
209+
}
210+
else {
211+
for(n = 0; n < len; n++) {
212+
item = arrayItems ? items[n] : items;
213+
valInPart = valIn[n];
214+
valOutPart = valOut[n];
215+
if(!Lib.validate(valInPart, item)) {
216+
list.push(format('value', base, p.concat(n), valInPart));
217+
}
218+
else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
219+
list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart));
220+
}
221+
}
222+
}
223+
}
177224
else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
178-
var items = nestedSchema.items,
179-
_nestedSchema = items[Object.keys(items)[0]],
225+
var _nestedSchema = items[Object.keys(items)[0]],
180226
indexList = [];
181227

182228
var j, _p;

0 commit comments

Comments
 (0)