8000 In the middle of a weird bugfix · generhim/plotly.js@020b2e4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 020b2e4

Browse files
committed
In the middle of a weird bugfix
1 parent fca1ac0 commit 020b2e4

File tree

8 files changed

+581
-133
lines changed

8 files changed

+581
-133
lines changed

src/plot_api/extend_traces.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright 2012-2015, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
var Queue = require('../lib/queue');
10+
11+
var Helpers = require('./helpers');
12+
var redraw = require('./redraw');
13+
var prependTraces = require('./prepend_traces');
14+
15+
16+
module.exports = extendTraces;
17+
18+
19+
/**
20+
* extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
21+
*
22+
* Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend
23+
* inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints
24+
* from the head, whereas Extend truncates the head of the array, counting backward maxPoints
25+
* from the tail.
26+
*
27+
* If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no
28+
* truncation / windowing will be performed. If its zero, well the whole trace is truncated.
29+
*
30+
* @param {Object|HTMLDivElement} gd The graph div
31+
* @param {Object} update The key:array map of target attributes to extend
32+
* @param {Number|Number[]} indices The locations of traces to be extended
33+
* @param {Number|Object} [maxPoints] Number of points for trace window after lengthening.
34+
*
35+
*/
36+
// function extendTraces (gd, update, indices, maxPoints) {
37+
// gd = Helpers.getGraphDiv(gd);
38+
39+
40+
// function lengthen(target, insert) {
41+
// return target.concat(insert);
42+
// };
43+
44+
// function spliceArray(target, maxPts) {
45+
// return target.splice(0, target.length - maxPts);
46+
// };
47+
48+
// var undo = Helpers.spliceTraces(gd, update, indices, maxPoints, lengthen, spliceArray),
49+
// promise = redraw(gd),
50+
// undoArgs = [gd, undo.update, indices, undo.maxPoints];
51+
52+
// if (Queue) {
53+
// Queue.add(gd, prependTraces, undoArgs, extendTraces, arguments);
54+
// }
55+
56+
// return promise;
57+
// };
58+
59+
function extendTraces (gd, update, indices, maxPoints) {
60+
console.log(gd);
61+
gd = Helpers.getGraphDiv(gd);
62+
console.log(gd);
63+
64+
var undo = Helpers.spliceTraces(gd, update, indices, maxPoints,
65+
66+
/*
67+
* The Lengthen operation extends trace from end with insert
68+
*/
69+
function(target, insert) {
70+
return target.concat(insert);
71+
},
72+
73+
/*
74+
* Window the trace keeping maxPoints, counting back from the end
75+
*/
76+
function(target, maxPoints) {
77+
return target.splice(0, target.length - maxPoints);
78+
});
79+
80+
var promise = redraw(gd);
81+
82+
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
83+
if (Queue) {
84+
Queue.add(gd, prependTraces, undoArgs, extendTraces, arguments);
85+
}
86+
87+
return promise;
88+
}

src/plot_api/helpers.js

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/**
2+
* Copyright 2012-2015, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
// Vendor
12+
var isNumeric = require('fast-isnumeric');
13+
14+
// Plotly things
15+
var Lib = require('../lib');
16+
17+
exports.getGraphDiv = getGraphDiv;
18+
exports.spliceTraces = spliceTraces;
19+
// exports.positivifyIndices = positivifyIndices;
20+
21+
function getGraphDiv(gd) {
22+
var gdElement;
23+
24+
if(typeof gd === 'string') {
25+
gdElement = document.getElementById(gd);
26+
27+
if(gdElement === null) {
28+
throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
29+
}
30+
31+
return gdElement;
32+
}
33+
else if(gd === null || gd === undefined) {
34+
throw new Error('DOM element provided is null or undefined');
35+
}
36+
37+
return gd;
38+
}
39+
40+
41+
/**
42+
* A private function to key Extend and Prepend traces DRY
43+
*
44+
* @param {Object|HTMLDivElement} gd
45+
* @param {Object} update
46+
* @param {Number[]} indices
47+
* @param {Number||Object} maxPoints
48+
* @param {Function} lengthenArray
49+
* @param {Function} spliceArray
50+
* @return {Object}
51+
*/
52+
function spliceTraces (gd, update, indices, maxPoints, lengthenArray, spliceArray) {
53+
54+
assertExtendTracesArgs(gd, update, indices, maxPoints);
55+
56+
var updateProps = getExtendProperties(gd, update, indices, maxPoints),
57+
remainder = [],
58+
undoUpdate = {},
59+
undoPoints = {},
60+
target,
61+
prop,
62+
maxp;
63+
64+
for (var i = 0; i < updateProps.length; i++) {
65+
66+
// prop is the object returned by Lib.nestedProperties
67+
prop = updateProps[i].prop;
68+
maxp = updateProps[i].maxp;
69+
70+
target = lengthenArray(updateProps[i].target, updateProps[i].insert);
71+
72+
/*
73+
* If maxp is set within post-extension trace.length, splice to maxp length.
74+
* Otherwise skip function call as splice op will have no effect anyway.
75+
*/
76+
if (maxp >= 0 && maxp < target.length) remainder = spliceArray(target, maxp);
77+
78+
/*
79+
* to reverse this operation we need the size of the original trace as the reverse
80+
* operation will need to window out any lengthening operation performed in this pass.
81+
*/
82+
maxp = updateProps[i].target.length;
83+
84+
// Magic happens here! update gd.data.trace[key] with new array data.
85+
prop.set(target);
86+
87+
if (!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
88+
if (!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
89+
90+
// build the inverse update object for the undo operation
91+
undoUpdate[prop.astr].push(remainder);
92+
93+
// build the matching maxPoints undo object containing original trace lengths.
94+
undoPoints[prop.astr].push(maxp);
95+
}
96+
97+
return { update: undoUpdate, maxPoints: undoPoints };
98+
}
99+
100+
101+
/**
102+
* A private function to reduce the type checking clutter in spliceTraces.
103+
* Get all update Properties from gd.data. Validate inputs and outputs.
104+
* Used by prependTrace and extendTraces
105+
*
106+
* @param gd
107+
* @param update
108+
* @param indices
109+
* @param maxPoints
110+
*/
111+
function assertExtendTracesArgs(gd, update, indices, maxPoints) {
112+
113+
var maxPointsIsObject = Lib.isPlainObject(maxPoints);
114+
115+
if (!Array.isArray(gd.data)) {
116+
throw new Error('gd.data must be an array');
117+
}
118+
if (!Lib.isPlainObject(update)) {
119+
throw new Error('update must be a key:value object');
120+
}
121+
122+
if (typeof indices === 'undefined') {
123+
throw new Error('indices must be an integer or array of integers');
124+
}
125+
126+
assertIndexArray(gd, indices, 'indices');
127+
128+
for (var key in update) {
129+
130+
/*
131+
* Verify that the attribute to be updated contains as many trace updates
132+
* as indices. Failure must result in throw and no-op
133+
*/
134+
if (!Array.isArray(update[key]) || update[key].length !== indices.length) {
135+
throw new Error('attribute ' + key + ' must be an array of length equal to indices array length');
136+
}
137+
138+
/*
139+
* if maxPoints is an object it must match keys and array lengths of 'update' 1:1
140+
*/
141+
if (maxPointsIsObject &&
142+
(!(key in maxPoints) || !Array.isArray(maxPoints[key]) ||
143+
maxPoints[key].length !== update[key].length )) {
144+
throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' +
145+
'corrispondence with the keys and number of traces in the update object');
146+
}
147+
}
148+
}
149+
150+
151+
/**
152+
* Ensures that an index array for manipulating gd.data is valid.
153+
*
154+
* Intended for use with addTraces, deleteTraces, and moveTraces.
155+
*
156+
* @param gd
157+
* @param indices
158+
* @param arrayName
159+
*/
160+
function assertIndexArray(gd, indices, arrayName) {
161+
var i,
162+
index;
163+
164+
for (i = 0; i < indices.length; i++) {
165+
index = indices[i];
166+
167+
// validate that indices are indeed integers
168+
if (index !== parseInt(index, 10)) {
169+
throw new Error('all values in ' + arrayName + ' must be integers');
170+
}
171+
172+
// check that all indices are in bounds for given gd.data array length
173+
if (index >= gd.data.length || index < -gd.data.length) {
174+
throw new Error(arrayName + ' must be valid indices for gd.data.');
175+
}
176+
177+
// check that indices aren't repeated
178+
if (indices.indexOf(index, i + 1) > -1 ||
179+
index >= 0 && indices.indexOf(-gd.data.length + index) > -1 ||
180+
index < 0 && indices.indexOf(gd.data.length + index) > -1) {
181+
throw new Error('each index in ' + arrayName + ' must be unique.');
182+
}
183+
}
184+
}
185+
186+
187+
/**
188+
* A private function to reduce the type checking clutter in spliceTraces.
189+
*
190+
* @param {Object|HTMLDivElement} gd
191+
* @param {Object} update
192+
* @param {Number[]} indices
193+
* @param {Number||Object} maxPoints
194+
* @return {Object[]}
195+
*/
196+
function getExtendProperties (gd, update, indices, maxPoints) {
197+
198+
var maxPointsIsObject = Lib.isPlainObject(maxPoints),
199+
updateProps = [],
200+
trace,
201+
target,
202+
prop,
203+
insert,
204+
maxp;
205+
206+
// allow scalar index to represent a single trace position
207+
if (!Array.isArray(indices)) indices = [indices];
208+
209+
// negative indices are wrapped around to their positive value. Equivalent to python indexing.
210+
indices = positivifyIndices(indices, gd.data.length - 1);
211+
212+
// loop through all update keys and traces and harvest validated data.
213+
for (var key in update) {
214+
215+
for (var j = 0; j < indices.length; j++) {
216+
217+
/*
218+
* Choose the trace indexed by the indices map argument and get the prop setter-getter
219+
* instance that references the key and value for this particular trace.
220+
*/
221+
trace = gd.data[indices[j]];
222+
prop = Lib.nestedProperty(trace, key);
223+
224+
/*
225+
* Target is the existing gd.data.trace.dataArray value like "x" or "marker.size"
226+
* Target must exist as an Array to allow the extend operation to be performed.
227+
*/
228+
target = prop.get();
229+
insert = update[key][j];
230+
231+
if (!Array.isArray(insert)) {
232+
throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
233+
}
234+
if (!Array.isArray(target)) {
235+
throw new Error('cannot extend missing or non-array attribute: ' + key);
236+
}
237+
238+
/*
239+
* maxPoints may be an object map or a scalar. If object select the key:value, else
240+
* Use the scalar maxPoints for all key and trace combinations.
241+
*/
242+
maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints;
243+
244+
// could have chosen null here, -1 just tells us to not take a window
245+
if (!isNumeric(maxp)) maxp = -1;
246+
247+
/*
248+
* Wrap the nestedProperty in an object containing required data
249+
* for lengthening and windowing this particular trace - key combination.
250+
* Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function.
251+
*/
252+
updateProps.push({
253+
prop: prop,
254+
target: target,
255+
insert: insert,
256+
maxp: Math.floor(maxp)
257+
});
258+
}
259+
}
260+
261+
// all target and insertion data now validated
262+
return updateProps;
263+
}
264+
265+
266+
/**
267+
* Wrap negative indicies to their positive counterparts.
268+
*
269+
* @param {Number[]} indices An array of indices
270+
* @param {Number} maxIndex The maximum index allowable (arr.length - 1)
271+
*/
272+
function positivifyIndices(indices, maxIndex) {
273+
var parentLength = maxIndex + 1,
274+
positiveIndices = [],
275+
i,
276+
index;
277+
278+
for (i = 0; i < indices.length; i++) {
279+
index = indices[i];
280+
if (index < 0) {
281+
positiveIndices.push(parentLength + index);
282+
} else {
283+
positiveIndices.push(index);
284+
}
285+
}
286+
return positiveIndices;
287+
}

0 commit comments

Comments
 (0)
0