Spaces:
Runtime error
Runtime error
| /* | |
| Flot plugin for stacking data sets, i.e. putting them on top of each | |
| other, for accumulative graphs. | |
| The plugin assumes the data is sorted on x (or y if stacking | |
| horizontally). For line charts, it is assumed that if a line has an | |
| undefined gap (from a null point), then the line above it should have | |
| the same gap - insert zeros instead of "null" if you want another | |
| behaviour. This also holds for the start and end of the chart. Note | |
| that stacking a mix of positive and negative values in most instances | |
| doesn't make sense (so it looks weird). | |
| Two or more series are stacked when their "stack" attribute is set to | |
| the same key (which can be any number or string or just "true"). To | |
| specify the default stack, you can set | |
| series: { | |
| stack: null or true or key (number/string) | |
| } | |
| or specify it for a specific series | |
| $.plot($("#placeholder"), [{ data: [ ... ], stack: true }]) | |
| The stacking order is determined by the order of the data series in | |
| the array (later series end up on top of the previous). | |
| Internally, the plugin modifies the datapoints in each series, adding | |
| an offset to the y value. For line series, extra data points are | |
| inserted through interpolation. If there's a second y value, it's also | |
| adjusted (e.g for bar charts or filled areas). | |
| */ | |
| (function ($) { | |
| var options = { | |
| series: { stack: null } // or number/string | |
| }; | |
| function init(plot) { | |
| function findMatchingSeries(s, allseries) { | |
| var res = null | |
| for (var i = 0; i < allseries.length; ++i) { | |
| if (s == allseries[i]) | |
| break; | |
| if (allseries[i].stack == s.stack) | |
| res = allseries[i]; | |
| } | |
| return res; | |
| } | |
| function stackData(plot, s, datapoints) { | |
| if (s.stack == null) | |
| return; | |
| var other = findMatchingSeries(s, plot.getData()); | |
| if (!other) | |
| return; | |
| var ps = datapoints.pointsize, | |
| points = datapoints.points, | |
| otherps = other.datapoints.pointsize, | |
| otherpoints = other.datapoints.points, | |
| newpoints = [], | |
| px, py, intery, qx, qy, bottom, | |
| withlines = s.lines.show, | |
| horizontal = s.bars.horizontal, | |
| withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), | |
| withsteps = withlines && s.lines.steps, | |
| fromgap = true, | |
| keyOffset = horizontal ? 1 : 0, | |
| accumulateOffset = horizontal ? 0 : 1, | |
| i = 0, j = 0, l; | |
| while (true) { | |
| if (i >= points.length) | |
| break; | |
| l = newpoints.length; | |
| if (points[i] == null) { | |
| // copy gaps | |
| for (m = 0; m < ps; ++m) | |
| newpoints.push(points[i + m]); | |
| i += ps; | |
| } | |
| else if (j >= otherpoints.length) { | |
| // for lines, we can't use the rest of the points | |
| if (!withlines) { | |
| for (m = 0; m < ps; ++m) | |
| newpoints.push(points[i + m]); | |
| } | |
| i += ps; | |
| } | |
| else if (otherpoints[j] == null) { | |
| // oops, got a gap | |
| for (m = 0; m < ps; ++m) | |
| newpoints.push(null); | |
| fromgap = true; | |
| j += otherps; | |
| } | |
| else { | |
| // cases where we actually got two points | |
| px = points[i + keyOffset]; | |
| py = points[i + accumulateOffset]; | |
| qx = otherpoints[j + keyOffset]; | |
| qy = otherpoints[j + accumulateOffset]; | |
| bottom = 0; | |
| if (px == qx) { | |
| for (m = 0; m < ps; ++m) | |
| newpoints.push(points[i + m]); | |
| newpoints[l + accumulateOffset] += qy; | |
| bottom = qy; | |
| i += ps; | |
| j += otherps; | |
| } | |
| else if (px > qx) { | |
| // we got past point below, might need to | |
| // insert interpolated extra point | |
| if (withlines && i > 0 && points[i - ps] != null) { | |
| intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); | |
| newpoints.push(qx); | |
| newpoints.push(intery + qy); | |
| for (m = 2; m < ps; ++m) | |
| newpoints.push(points[i + m]); | |
| bottom = qy; | |
| } | |
| j += otherps; | |
| } | |
| else { // px < qx | |
| if (fromgap && withlines) { | |
| // if we come from a gap, we just skip this point | |
| i += ps; | |
| continue; | |
| } | |
| for (m = 0; m < ps; ++m) | |
| newpoints.push(points[i + m]); | |
| // we might be able to interpolate a point below, | |
| // this can give us a better y | |
| if (withlines && j > 0 && otherpoints[j - otherps] != null) | |
| bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); | |
| newpoints[l + accumulateOffset] += bottom; | |
| i += ps; | |
| } | |
| fromgap = false; | |
| if (l != newpoints.length && withbottom) | |
| newpoints[l + 2] += bottom; | |
| } | |
| // maintain the line steps invariant | |
| if (withsteps && l != newpoints.length && l > 0 | |
| && newpoints[l] != null | |
| && newpoints[l] != newpoints[l - ps] | |
| && newpoints[l + 1] != newpoints[l - ps + 1]) { | |
| for (m = 0; m < ps; ++m) | |
| newpoints[l + ps + m] = newpoints[l + m]; | |
| newpoints[l + 1] = newpoints[l - ps + 1]; | |
| } | |
| } | |
| datapoints.points = newpoints; | |
| } | |
| plot.hooks.processDatapoints.push(stackData); | |
| } | |
| $.plot.plugins.push({ | |
| init: init, | |
| options: options, | |
| name: 'stack', | |
| version: '1.2' | |
| }); | |
| })(jQuery); | |