/* * * * (c) 2010-2024 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import Axis from '../Core/Axis/Axis.js'; import Point from '../Core/Series/Point.js'; const { tooltipFormatter: pointTooltipFormatter } = Point.prototype; import Series from '../Core/Series/Series.js'; import U from '../Core/Utilities.js'; const { addEvent, arrayMax, arrayMin, correctFloat, defined, isArray, isNumber, isString, pick } = U; /* * * * Composition * * */ var DataModifyComposition; (function (DataModifyComposition) { /* * * * Declarations * * */ /* * * * Functions * * */ /** * Extends the series, axis and point classes with * compare and cumulative support. * * @private * * @param SeriesClass * Series class to use. * * @param AxisClass * Axis class to extend. * * @param PointClass * Point class to use. */ function compose(SeriesClass, AxisClass, PointClass) { const axisProto = AxisClass.prototype, pointProto = PointClass.prototype, seriesProto = SeriesClass.prototype; if (!seriesProto.setCompare) { seriesProto.setCompare = seriesSetCompare; seriesProto.setCumulative = seriesSetCumulative; addEvent(SeriesClass, 'afterInit', afterInit); addEvent(SeriesClass, 'afterGetExtremes', afterGetExtremes); addEvent(SeriesClass, 'afterProcessData', afterProcessData); } if (!axisProto.setCompare) { axisProto.setCompare = axisSetCompare; axisProto.setModifier = setModifier; axisProto.setCumulative = axisSetCumulative; pointProto.tooltipFormatter = tooltipFormatter; } return SeriesClass; } DataModifyComposition.compose = compose; /* ********************************************************************** * * Start shared compare and cumulative logic * * ********************************************************************** */ /** * Shared code for the axis.setCompare() and the axis.setCumulative() * methods. Inits the 'compare' or the 'cumulative' mode. * @private */ function setModifier(mode, modeState, redraw) { if (!this.isXAxis) { this.series.forEach(function (series) { if (mode === 'compare' && typeof modeState !== 'boolean') { series.setCompare(modeState, false); } else if (mode === 'cumulative' && !isString(modeState)) { series.setCumulative(modeState, false); } }); if (pick(redraw, true)) { this.chart.redraw(); } } } /** * Extend the tooltip formatter by adding support for the point.change * variable as well as the changeDecimals option. * * @ignore * @function Highcharts.Point#tooltipFormatter * * @param {string} pointFormat */ function tooltipFormatter(pointFormat) { const point = this, { numberFormatter } = point.series.chart, replace = function (value) { pointFormat = pointFormat.replace('{point.' + value + '}', (point[value] > 0 && value === 'change' ? '+' : '') + numberFormatter(point[value], pick(point.series.tooltipOptions.changeDecimals, 2))); }; if (defined(point.change)) { replace('change'); } if (defined(point.cumulativeSum)) { replace('cumulativeSum'); } return pointTooltipFormatter.apply(this, [pointFormat]); } /** * Extend series.init by adding a methods to modify the y values used * for plotting on the y axis. For compare mode, this method is called both * from the axis when finding dataMin and dataMax, * and from the series.translate method. * * @ignore * @function Highcharts.Series#init */ function afterInit() { const compare = this.options.compare; let dataModify; if (compare === 'percent' || compare === 'value' || this.options.cumulative) { dataModify = new Additions(this); if (compare === 'percent' || compare === 'value') { // Set comparison mode dataModify.initCompare(compare); } else { // Set Cumulative Sum mode dataModify.initCumulative(); } } this.dataModify = dataModify; } /** * Adjust the extremes (compare and cumulative modify the data). * @private */ function afterGetExtremes(e) { const dataExtremes = e.dataExtremes, activeYData = dataExtremes.activeYData; if (this.dataModify && dataExtremes) { let extremes; if (this.options.compare) { extremes = [ this.dataModify.modifyValue(dataExtremes.dataMin), this.dataModify.modifyValue(dataExtremes.dataMax) ]; } else if (this.options.cumulative && isArray(activeYData) && // If only one y visible, sum doesn't change // so no need to change extremes activeYData.length >= 2) { extremes = Additions.getCumulativeExtremes(activeYData); } if (extremes) { dataExtremes.dataMin = arrayMin(extremes); dataExtremes.dataMax = arrayMax(extremes); } } } /* ********************************************************************** * * End shared compare and cumulative logic * * ********************************************************************** */ /* ********************************************************************** * * Start value compare logic * * ********************************************************************** */ /** * Highcharts Stock only. Set the * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare) * mode of the series after render time. * In most cases it is more useful running * {@link Axis#setCompare} on the X axis to update all its series. * * @function Highcharts.Series#setCompare * * @param {string|null} [compare] * Can be one of `undefined` (default), `null`, `"percent"` * or `"value"`. * * @param {boolean} [redraw=true] * Whether to redraw the chart or to wait for a later call to * {@link Chart#redraw}. */ function seriesSetCompare(compare, redraw) { // Survive to export, #5485 (and for options generally) this.options.compare = this.userOptions.compare = compare; // Fire series.init() that will set or delete series.dataModify this.update({}, pick(redraw, true)); if (this.dataModify && (compare === 'value' || compare === 'percent')) { this.dataModify.initCompare(compare); } else { // When disabling, clear the points this.points.forEach((point) => { delete point.change; }); } } /** * Extend series.processData by finding the first y value in the plot area, * used for comparing the following values * * @ignore * @function Highcharts.Series#processData */ function afterProcessData() { const series = this; if (series.xAxis && // Not pies series.processedYData && series.dataModify) { const processedXData = series.processedXData, processedYData = series.processedYData, length = processedYData.length, compareStart = series.options.compareStart === true ? 0 : 1; let keyIndex = -1, i; // For series with more than one value (range, OHLC etc), compare // against close or the pointValKey (#4922, #3112, #9854) if (series.pointArrayMap) { keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y'); } // Find the first value for comparison for (i = 0; i < length - compareStart; i++) { const compareValue = processedYData[i] && keyIndex > -1 ? processedYData[i][keyIndex] : processedYData[i]; if (isNumber(compareValue) && compareValue !== 0 && processedXData[i + compareStart] >= (series.xAxis.min || 0)) { series.dataModify.compareValue = compareValue; break; } } } } /** * Highcharts Stock only. Set the compare mode on all series * belonging to a Y axis. * * @see [plotOptions.series.compare](https://api.highcharts.com/highstock/plotOptions.series.compare) * * @sample stock/members/axis-setcompare/ * Set compare * * @function Highcharts.Axis#setCompare * * @param {string|null} [compare] * The compare mode. Can be one of `undefined` (default), `null`, * `"value"` or `"percent"`. * * @param {boolean} [redraw=true] * Whether to redraw the chart or to wait for a later call to * {@link Chart#redraw}. */ function axisSetCompare(compare, redraw) { this.setModifier('compare', compare, redraw); } /* ********************************************************************** * * End value compare logic * * ********************************************************************** */ /* ********************************************************************** * * Start Cumulative Sum logic, author: Rafal Sebestjanski * * ********************************************************************** */ /** * Highcharts Stock only. Set the * [cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative) * mode of the series after render time. * In most cases it is more useful running * {@link Axis#setCumulative} on the Y axis to update all its series. * * @function Highcharts.Series#setCumulative * * @param {boolean} [cumulative=false] * Either enable or disable Cumulative Sum mode. * Can be one of `false` (default) or `true`. * * @param {boolean} [redraw=true] * Whether to redraw the chart or to wait for a later call to * {@link Chart#redraw}. */ function seriesSetCumulative(cumulative, redraw) { // Set default value to false cumulative = pick(cumulative, false); // Survive to export, #5485 (and for options generally) this.options.cumulative = this.userOptions.cumulative = cumulative; // Fire series.init() that will set or delete series.dataModify this.update({}, pick(redraw, true)); // If should, turn on the Cumulative Sum mode if (this.dataModify) { this.dataModify.initCumulative(); } else { // When disabling, clear the points this.points.forEach((point) => { delete point.cumulativeSum; }); } } /** * Highcharts Stock only. Set the cumulative mode on all series * belonging to a Y axis. * * @see [plotOptions.series.cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative) * * @sample stock/members/axis-setcumulative/ * Set cumulative * * @function Highcharts.Axis#setCumulative * * @param {boolean} [cumulative] * Whether to disable or enable the cumulative mode. * Can be one of `undefined` (default, treated as `false`), * `false` or `true`. * * @param {boolean} [redraw=true] * Whether to redraw the chart or to wait for a later call to * {@link Chart#redraw}. */ function axisSetCumulative(cumulative, redraw) { this.setModifier('cumulative', cumulative, redraw); } /* * * * Classes * * */ /** * @private */ class Additions { /* * * * Constructors * * */ /** * @private */ constructor(series) { this.series = series; } /* * * * Functions * * */ /** * @private */ modifyValue() { return 0; } /** * @ignore * @function Highcharts.Series#getCumulativeExtremes * * @param {Array} [activeYData] * An array cointaining all the points' y values * in a visible range. */ static getCumulativeExtremes(activeYData) { let cumulativeDataMin = Infinity, cumulativeDataMax = -Infinity; activeYData.reduce((prev, cur) => { const sum = prev + cur; cumulativeDataMin = Math.min(cumulativeDataMin, sum, prev); cumulativeDataMax = Math.max(cumulativeDataMax, sum, prev); return sum; }); return [cumulativeDataMin, cumulativeDataMax]; } /** * @ignore * @function Highcharts.Series#initCompare * * @param {string} [compare] * Can be one of `"percent"` or `"value"`. */ initCompare(compare) { // Set the modifyValue method this.modifyValue = function (value, index) { if (value === null) { value = 0; } const compareValue = this.compareValue; if (typeof value !== 'undefined' && typeof compareValue !== 'undefined') { // #2601, #5814 // Get the modified value if (compare === 'value') { value -= compareValue; // Compare percent } else { const compareBase = this.series.options.compareBase; value = 100 * (value / compareValue) - (compareBase === 100 ? 0 : 100); } // Record for tooltip etc. if (typeof index !== 'undefined') { const point = this.series.points[index]; if (point) { point.change = value; } } return value; } return 0; }; } /** * @ignore * @function Highcharts.Series#initCumulative */ initCumulative() { // Set the modifyValue method this.modifyValue = function (value, index) { if (value === null) { value = 0; } if (value !== void 0 && index !== void 0) { const prevPoint = index > 0 ? this.series.points[index - 1] : null; // Get the modified value if (prevPoint && prevPoint.cumulativeSum) { value = correctFloat(prevPoint.cumulativeSum + value); } // Record for tooltip etc. const point = this.series.points[index]; if (point) { point.cumulativeSum = value; } return value; } return 0; }; } } DataModifyComposition.Additions = Additions; })(DataModifyComposition || (DataModifyComposition = {})); /* * * * Default Export * * */ export default DataModifyComposition; /* * * * API Options * * */ /** * Compare the values of the series against the first non-null, non- * zero value in the visible range. The y axis will show percentage * or absolute change depending on whether `compare` is set to `"percent"` * or `"value"`. When this is applied to multiple series, it allows * comparing the development of the series against each other. Adds * a `change` field to every point object. * * @see [compareBase](#plotOptions.series.compareBase) * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare) * @see [Series.setCompare()](/class-reference/Highcharts.Series#setCompare) * * @sample {highstock} stock/plotoptions/series-compare-percent/ * Percent * @sample {highstock} stock/plotoptions/series-compare-value/ * Value * * @type {string} * @since 1.0.1 * @product highstock * @validvalue ["percent", "value"] * @apioption plotOptions.series.compare */ /** * Defines if comparison should start from the first point within the visible * range or should start from the first point **before** the range. * * In other words, this flag determines if first point within the visible range * will have 0% (`compareStart=true`) or should have been already calculated * according to the previous point (`compareStart=false`). * * @sample {highstock} stock/plotoptions/series-comparestart/ * Calculate compare within visible range * * @type {boolean} * @default false * @since 6.0.0 * @product highstock * @apioption plotOptions.series.compareStart */ /** * When [compare](#plotOptions.series.compare) is `percent`, this option * dictates whether to use 0 or 100 as the base of comparison. * * @sample {highstock} stock/plotoptions/series-comparebase/ * Compare base is 100 * * @type {number} * @default 0 * @since 5.0.6 * @product highstock * @validvalue [0, 100] * @apioption plotOptions.series.compareBase */ /** * Cumulative Sum feature replaces points' values with the following formula: * `sum of all previous points' values + current point's value`. * Works only for points in a visible range. * Adds the `cumulativeSum` field to each point object that can be accessed * e.g. in the [tooltip.pointFormat](https://api.highcharts.com/highstock/tooltip.pointFormat). * * With `dataGrouping` enabled, default grouping approximation is set to `sum`. * * @see [Axis.setCumulative()](/class-reference/Highcharts.Axis#setCumulative) * @see [Series.setCumulative()](/class-reference/Highcharts.Series#setCumulative) * * @sample {highstock} stock/plotoptions/series-cumulative-sum/ * Cumulative Sum * * @type {boolean} * @default false * @since 9.3.0 * @product highstock * @apioption plotOptions.series.cumulative */ ''; // Keeps doclets above in transpiled file