|
// Set seriesData.previous to hold data series of the previous period |
|
// so that we may compute deltas (e.g. +10%). |
|
// We also ensure that all the series, previous and current, have the same |
|
// number of data points, adding fake ones if necessary, so that the delta-computation |
|
// code <tbd:add reference> doesn't blow up. There may be some data missing |
|
// at the end of a period, for example because months have different numbers of days. |
|
// The fake elements and also any other undefined values will default to [0,0], which |
|
// should be OK with all (line,area,..) graphs and tables. |
|
// E.x.: seriesData.series[0].data = [ [1,"12%"],[2,"22%"],..,[31,"18%"] ], |
|
// the previous month had only 30 days so we add a fake element for day 31; |
|
// See the class Series for more information. |
|
augmentSeriesWithPreviousValues:function (seriesData, previousSeriesData) { |
|
var series = seriesData.series; |
|
seriesData.previous = []; |
|
var previousSeries = previousSeriesData.series; |
|
|
|
… // checks, computing maxRows, maxColumns, previousCategories |
|
// that maps category name to series index (id) |
|
var matcher = new SeriesMatcher(seriesData, previousCategories); // a helper |
|
|
|
// The current and previous series must have data for the same unit (e.g. day 15 if period=month), |
|
// only at the end of the period there may be missing entries (shorter month etc.) |
|
assertMatchingDaysInCurrentAndPrevious(series, previousSeries); |
|
|
|
for (var seriesId = 0; seriesId < series.length; seriesId++) { |
|
var previousSeriesId = matcher.previousSeriesIdFor(seriesId) |
|
for (var rowId = 0; rowId < maxRows; rowId++) { |
|
for (var columnId = 1; columnId < maxColumns; columnId++) { |
|
|
|
createDataPointIfMissing(series, seriesId, rowId); |
|
replaceNaNWithDefaultOf(0, series, seriesId, rowId, columnId); |
|
|
|
createDataPointIfMissing(seriesData.previous, seriesId, rowId); |
|
copyPreviousValueIfDefined(seriesData.previous[seriesId], previousSeries[previousSeriesId], rowId, columnId); |
|
replaceNaNWithDefaultOf(0, seriesData.previous, seriesId, rowId, columnId); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Make sure the 3D array series has the element [seriesId][rowId]; |
|
// it's assumed that [seriesId-1][rowId-1] exist, unless 0. |
|
function createDataPointIfMissing(series, seriesId, rowId) { |
|
var array = series; |
|
[seriesId, rowId].forEach(function(idx){ |
|
if (!array[idx]) { |
|
array[idx] = []; |
|
} |
|
array = array[idx]; |
|
}); |
|
return series; |
|
} |
|
|
|
// If x or y coordinate of the data point series[seriesId][rowId] is undefined, |
|
// set it to the given default value |
|
function replaceNaNWithDefaultOf(default, series, seriesId, rowId, columnId) { |
|
var value = series[seriesId][rowId][columnId]; |
|
if (isNaN(value)) { |
|
series[seriesId][rowId][columnId] = default; |
|
} |
|
} |
|
|
|
// Copy the given data point's coordinate from source (if defined) to target |
|
function copyPreviousValueIfDefined(target, source, rowId, columnId) { |
|
if (source && source[rowId] && source[rowId][columnId]) { |
|
target[rowId][columnId] = source[rowId][columnId]; |
|
} |
|
} |
|
|
|
… // implementation of other helper functions, such as assertNoMissing, not shown |