| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- /* *
- *
- * (c) 2010-2020 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- 'use strict';
- import H from '../Core/Globals.js';
- import Point from '../Core/Series/Point.js';
- import U from '../Core/Utilities.js';
- var defined = U.defined, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick, seriesType = U.seriesType;
- import '../Core/Options.js';
- import '../Core/Series/Series.js';
- var noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes, seriesProto = Series.prototype, pointProto = Point.prototype;
- /**
- * The area range series is a carteseian series with higher and lower values for
- * each point along an X axis, where the area between the values is shaded.
- *
- * @sample {highcharts} highcharts/demo/arearange/
- * Area range chart
- * @sample {highstock} stock/demo/arearange/
- * Area range chart
- *
- * @extends plotOptions.area
- * @product highcharts highstock
- * @excluding stack, stacking
- * @requires highcharts-more
- * @optionparent plotOptions.arearange
- */
- seriesType('arearange', 'area', {
- /**
- * Whether to apply a drop shadow to the graph line. Since 2.3 the shadow
- * can be an object configuration containing `color`, `offsetX`, `offsetY`,
- * `opacity` and `width`.
- *
- * @type {boolean|Highcharts.ShadowOptionsObject}
- * @product highcharts
- * @apioption plotOptions.arearange.shadow
- */
- /**
- * @default low
- * @apioption plotOptions.arearange.colorKey
- */
- /**
- * Pixel width of the arearange graph line.
- *
- * @since 2.3.0
- *
- * @private
- */
- lineWidth: 1,
- threshold: null,
- tooltip: {
- pointFormat: '<span style="color:{series.color}">\u25CF</span> ' +
- '{series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
- },
- /**
- * Whether the whole area or just the line should respond to mouseover
- * tooltips and other mouse or touch events.
- *
- * @since 2.3.0
- *
- * @private
- */
- trackByArea: true,
- /**
- * Extended data labels for range series types. Range series data labels use
- * no `x` and `y` options. Instead, they have `xLow`, `xHigh`, `yLow` and
- * `yHigh` options to allow the higher and lower data label sets
- * individually.
- *
- * @declare Highcharts.SeriesAreaRangeDataLabelsOptionsObject
- * @exclude x, y
- * @since 2.3.0
- * @product highcharts highstock
- *
- * @private
- */
- dataLabels: {
- align: void 0,
- verticalAlign: void 0,
- /**
- * X offset of the lower data labels relative to the point value.
- *
- * @sample highcharts/plotoptions/arearange-datalabels/
- * Data labels on range series
- * @sample highcharts/plotoptions/arearange-datalabels/
- * Data labels on range series
- */
- xLow: 0,
- /**
- * X offset of the higher data labels relative to the point value.
- *
- * @sample highcharts/plotoptions/arearange-datalabels/
- * Data labels on range series
- */
- xHigh: 0,
- /**
- * Y offset of the lower data labels relative to the point value.
- *
- * @sample highcharts/plotoptions/arearange-datalabels/
- * Data labels on range series
- */
- yLow: 0,
- /**
- * Y offset of the higher data labels relative to the point value.
- *
- * @sample highcharts/plotoptions/arearange-datalabels/
- * Data labels on range series
- */
- yHigh: 0
- }
- // Prototype members
- }, {
- pointArrayMap: ['low', 'high'],
- pointValKey: 'low',
- deferTranslatePolar: true,
- /* eslint-disable valid-jsdoc */
- /**
- * @private
- */
- toYData: function (point) {
- return [point.low, point.high];
- },
- /**
- * Translate a point's plotHigh from the internal angle and radius measures
- * to true plotHigh coordinates. This is an addition of the toXY method
- * found in Polar.js, because it runs too early for arearanges to be
- * considered (#3419).
- * @private
- */
- highToXY: function (point) {
- // Find the polar plotX and plotY
- var chart = this.chart, xy = this.xAxis.postTranslate(point.rectPlotX, this.yAxis.len - point.plotHigh);
- point.plotHighX = xy.x - chart.plotLeft;
- point.plotHigh = xy.y - chart.plotTop;
- point.plotLowX = point.plotX;
- },
- /**
- * Translate data points from raw values x and y to plotX and plotY.
- * @private
- */
- translate: function () {
- var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue;
- seriesTypes.area.prototype.translate.apply(series);
- // Set plotLow and plotHigh
- series.points.forEach(function (point) {
- var high = point.high, plotY = point.plotY;
- if (point.isNull) {
- point.plotY = null;
- }
- else {
- point.plotLow = plotY;
- point.plotHigh = yAxis.translate(hasModifyValue ?
- series.modifyValue(high, point) :
- high, 0, 1, 0, 1);
- if (hasModifyValue) {
- point.yBottom = point.plotHigh;
- }
- }
- });
- // Postprocess plotHigh
- if (this.chart.polar) {
- this.points.forEach(function (point) {
- series.highToXY(point);
- point.tooltipPos = [
- (point.plotHighX + point.plotLowX) / 2,
- (point.plotHigh + point.plotLow) / 2
- ];
- });
- }
- },
- /**
- * Extend the line series' getSegmentPath method by applying the segment
- * path to both lower and higher values of the range.
- * @private
- */
- getGraphPath: function (points) {
- var highPoints = [], highAreaPoints = [], i, getGraphPath = seriesTypes.area.prototype.getGraphPath, point, pointShim, linePath, lowerPath, options = this.options, polar = this.chart.polar, connectEnds = polar && options.connectEnds !== false, connectNulls = options.connectNulls, step = options.step, higherPath, higherAreaPath;
- points = points || this.points;
- // Create the top line and the top part of the area fill. The area fill
- // compensates for null points by drawing down to the lower graph,
- // moving across the null gap and starting again at the lower graph.
- i = points.length;
- while (i--) {
- point = points[i];
- // Support for polar
- var highAreaPoint = polar ? {
- plotX: point.rectPlotX,
- plotY: point.yBottom,
- doCurve: false // #5186, gaps in areasplinerange fill
- } : {
- plotX: point.plotX,
- plotY: point.plotY,
- doCurve: false // #5186, gaps in areasplinerange fill
- };
- if (!point.isNull &&
- !connectEnds &&
- !connectNulls &&
- (!points[i + 1] || points[i + 1].isNull)) {
- highAreaPoints.push(highAreaPoint);
- }
- pointShim = {
- polarPlotY: point.polarPlotY,
- rectPlotX: point.rectPlotX,
- yBottom: point.yBottom,
- // plotHighX is for polar charts
- plotX: pick(point.plotHighX, point.plotX),
- plotY: point.plotHigh,
- isNull: point.isNull
- };
- highAreaPoints.push(pointShim);
- highPoints.push(pointShim);
- if (!point.isNull &&
- !connectEnds &&
- !connectNulls &&
- (!points[i - 1] || points[i - 1].isNull)) {
- highAreaPoints.push(highAreaPoint);
- }
- }
- // Get the paths
- lowerPath = getGraphPath.call(this, points);
- if (step) {
- if (step === true) {
- step = 'left';
- }
- options.step = {
- left: 'right',
- center: 'center',
- right: 'left'
- }[step]; // swap for reading in getGraphPath
- }
- higherPath = getGraphPath.call(this, highPoints);
- higherAreaPath = getGraphPath.call(this, highAreaPoints);
- options.step = step;
- // Create a line on both top and bottom of the range
- linePath = []
- .concat(lowerPath, higherPath);
- // For the area path, we need to change the 'move' statement
- // into 'lineTo'
- if (!this.chart.polar && higherAreaPath[0] && higherAreaPath[0][0] === 'M') {
- // This probably doesn't work for spline
- higherAreaPath[0] = ['L', higherAreaPath[0][1], higherAreaPath[0][2]];
- }
- this.graphPath = linePath;
- this.areaPath = lowerPath.concat(higherAreaPath);
- // Prepare for sideways animation
- linePath.isArea = true;
- linePath.xMap = lowerPath.xMap;
- this.areaPath.xMap = lowerPath.xMap;
- return linePath;
- },
- /**
- * Extend the basic drawDataLabels method by running it for both lower and
- * higher values.
- * @private
- */
- drawDataLabels: function () {
- var data = this.points, length = data.length, i, originalDataLabels = [], dataLabelOptions = this.options.dataLabels, point, up, inverted = this.chart.inverted, upperDataLabelOptions, lowerDataLabelOptions;
- // Split into upper and lower options. If data labels is an array, the
- // first element is the upper label, the second is the lower.
- //
- // TODO: We want to change this and allow multiple labels for both upper
- // and lower values in the future - introducing some options for which
- // point value to use as Y for the dataLabel, so that this could be
- // handled in Series.drawDataLabels. This would also improve performance
- // since we now have to loop over all the points multiple times to work
- // around the data label logic.
- if (isArray(dataLabelOptions)) {
- if (dataLabelOptions.length > 1) {
- upperDataLabelOptions = dataLabelOptions[0];
- lowerDataLabelOptions = dataLabelOptions[1];
- }
- else {
- upperDataLabelOptions = dataLabelOptions[0];
- lowerDataLabelOptions = { enabled: false };
- }
- }
- else {
- // Make copies
- upperDataLabelOptions = extend({}, dataLabelOptions);
- upperDataLabelOptions.x = dataLabelOptions.xHigh;
- upperDataLabelOptions.y = dataLabelOptions.yHigh;
- lowerDataLabelOptions = extend({}, dataLabelOptions);
- lowerDataLabelOptions.x = dataLabelOptions.xLow;
- lowerDataLabelOptions.y = dataLabelOptions.yLow;
- }
- // Draw upper labels
- if (upperDataLabelOptions.enabled || this._hasPointLabels) {
- // Set preliminary values for plotY and dataLabel
- // and draw the upper labels
- i = length;
- while (i--) {
- point = data[i];
- if (point) {
- up = upperDataLabelOptions.inside ?
- point.plotHigh < point.plotLow :
- point.plotHigh > point.plotLow;
- point.y = point.high;
- point._plotY = point.plotY;
- point.plotY = point.plotHigh;
- // Store original data labels and set preliminary label
- // objects to be picked up in the uber method
- originalDataLabels[i] = point.dataLabel;
- point.dataLabel = point.dataLabelUpper;
- // Set the default offset
- point.below = up;
- if (inverted) {
- if (!upperDataLabelOptions.align) {
- upperDataLabelOptions.align = up ? 'right' : 'left';
- }
- }
- else {
- if (!upperDataLabelOptions.verticalAlign) {
- upperDataLabelOptions.verticalAlign = up ?
- 'top' :
- 'bottom';
- }
- }
- }
- }
- this.options.dataLabels = upperDataLabelOptions;
- if (seriesProto.drawDataLabels) {
- // #1209:
- seriesProto.drawDataLabels.apply(this, arguments);
- }
- // Reset state after the upper labels were created. Move
- // it to point.dataLabelUpper and reassign the originals.
- // We do this here to support not drawing a lower label.
- i = length;
- while (i--) {
- point = data[i];
- if (point) {
- point.dataLabelUpper = point.dataLabel;
- point.dataLabel = originalDataLabels[i];
- delete point.dataLabels;
- point.y = point.low;
- point.plotY = point._plotY;
- }
- }
- }
- // Draw lower labels
- if (lowerDataLabelOptions.enabled || this._hasPointLabels) {
- i = length;
- while (i--) {
- point = data[i];
- if (point) {
- up = lowerDataLabelOptions.inside ?
- point.plotHigh < point.plotLow :
- point.plotHigh > point.plotLow;
- // Set the default offset
- point.below = !up;
- if (inverted) {
- if (!lowerDataLabelOptions.align) {
- lowerDataLabelOptions.align = up ? 'left' : 'right';
- }
- }
- else {
- if (!lowerDataLabelOptions.verticalAlign) {
- lowerDataLabelOptions.verticalAlign = up ?
- 'bottom' :
- 'top';
- }
- }
- }
- }
- this.options.dataLabels = lowerDataLabelOptions;
- if (seriesProto.drawDataLabels) {
- seriesProto.drawDataLabels.apply(this, arguments);
- }
- }
- // Merge upper and lower into point.dataLabels for later destroying
- if (upperDataLabelOptions.enabled) {
- i = length;
- while (i--) {
- point = data[i];
- if (point) {
- point.dataLabels = [
- point.dataLabelUpper,
- point.dataLabel
- ].filter(function (label) {
- return !!label;
- });
- }
- }
- }
- // Reset options
- this.options.dataLabels = dataLabelOptions;
- },
- alignDataLabel: function () {
- seriesTypes.column.prototype.alignDataLabel
- .apply(this, arguments);
- },
- drawPoints: function () {
- var series = this, pointLength = series.points.length, point, i;
- // Draw bottom points
- seriesProto.drawPoints
- .apply(series, arguments);
- // Prepare drawing top points
- i = 0;
- while (i < pointLength) {
- point = series.points[i];
- // Save original props to be overridden by temporary props for top
- // points
- point.origProps = {
- plotY: point.plotY,
- plotX: point.plotX,
- isInside: point.isInside,
- negative: point.negative,
- zone: point.zone,
- y: point.y
- };
- point.lowerGraphic = point.graphic;
- point.graphic = point.upperGraphic;
- point.plotY = point.plotHigh;
- if (defined(point.plotHighX)) {
- point.plotX = point.plotHighX;
- }
- point.y = point.high;
- point.negative = point.high < (series.options.threshold || 0);
- point.zone = (series.zones.length && point.getZone());
- if (!series.chart.polar) {
- point.isInside = point.isTopInside = (typeof point.plotY !== 'undefined' &&
- point.plotY >= 0 &&
- point.plotY <= series.yAxis.len && // #3519
- point.plotX >= 0 &&
- point.plotX <= series.xAxis.len);
- }
- i++;
- }
- // Draw top points
- seriesProto.drawPoints.apply(series, arguments);
- // Reset top points preliminary modifications
- i = 0;
- while (i < pointLength) {
- point = series.points[i];
- point.upperGraphic = point.graphic;
- point.graphic = point.lowerGraphic;
- extend(point, point.origProps);
- delete point.origProps;
- i++;
- }
- },
- /* eslint-enable valid-jsdoc */
- setStackedPoints: noop
- }, {
- /**
- * Range series only. The high or maximum value for each data point.
- * @name Highcharts.Point#high
- * @type {number|undefined}
- */
- /**
- * Range series only. The low or minimum value for each data point.
- * @name Highcharts.Point#low
- * @type {number|undefined}
- */
- /* eslint-disable valid-jsdoc */
- /**
- * @private
- */
- setState: function () {
- var prevState = this.state, series = this.series, isPolar = series.chart.polar;
- if (!defined(this.plotHigh)) {
- // Boost doesn't calculate plotHigh
- this.plotHigh = series.yAxis.toPixels(this.high, true);
- }
- if (!defined(this.plotLow)) {
- // Boost doesn't calculate plotLow
- this.plotLow = this.plotY = series.yAxis.toPixels(this.low, true);
- }
- if (series.stateMarkerGraphic) {
- series.lowerStateMarkerGraphic = series.stateMarkerGraphic;
- series.stateMarkerGraphic = series.upperStateMarkerGraphic;
- }
- // Change state also for the top marker
- this.graphic = this.upperGraphic;
- this.plotY = this.plotHigh;
- if (isPolar) {
- this.plotX = this.plotHighX;
- }
- // Top state:
- pointProto.setState.apply(this, arguments);
- this.state = prevState;
- // Now restore defaults
- this.plotY = this.plotLow;
- this.graphic = this.lowerGraphic;
- if (isPolar) {
- this.plotX = this.plotLowX;
- }
- if (series.stateMarkerGraphic) {
- series.upperStateMarkerGraphic = series.stateMarkerGraphic;
- series.stateMarkerGraphic = series.lowerStateMarkerGraphic;
- // Lower marker is stored at stateMarkerGraphic
- // to avoid reference duplication (#7021)
- series.lowerStateMarkerGraphic = void 0;
- }
- pointProto.setState.apply(this, arguments);
- },
- haloPath: function () {
- var isPolar = this.series.chart.polar, path = [];
- // Bottom halo
- this.plotY = this.plotLow;
- if (isPolar) {
- this.plotX = this.plotLowX;
- }
- if (this.isInside) {
- path = pointProto.haloPath.apply(this, arguments);
- }
- // Top halo
- this.plotY = this.plotHigh;
- if (isPolar) {
- this.plotX = this.plotHighX;
- }
- if (this.isTopInside) {
- path = path.concat(pointProto.haloPath.apply(this, arguments));
- }
- return path;
- },
- destroyElements: function () {
- var graphics = ['lowerGraphic', 'upperGraphic'];
- graphics.forEach(function (graphicName) {
- if (this[graphicName]) {
- this[graphicName] =
- this[graphicName].destroy();
- }
- }, this);
- // Clear graphic for states, removed in the above each:
- this.graphic = null;
- return pointProto.destroyElements.apply(this, arguments);
- },
- isValid: function () {
- return isNumber(this.low) && isNumber(this.high);
- }
- /* eslint-enable valid-jsdoc */
- });
- /**
- * A `arearange` series. If the [type](#series.arearange.type) option is not
- * specified, it is inherited from [chart.type](#chart.type).
- *
- *
- * @extends series,plotOptions.arearange
- * @excluding dataParser, dataURL, stack, stacking
- * @product highcharts highstock
- * @requires highcharts-more
- * @apioption series.arearange
- */
- /**
- * An array of data points for the series. For the `arearange` series type,
- * points can be given in the following ways:
- *
- * 1. An array of arrays with 3 or 2 values. In this case, the values
- * correspond to `x,low,high`. If the first value is a string, it is
- * applied as the name of the point, and the `x` value is inferred.
- * The `x` value can also be omitted, in which case the inner arrays
- * should be of length 2\. Then the `x` value is automatically calculated,
- * either starting at 0 and incremented by 1, or from `pointStart`
- * and `pointInterval` given in the series options.
- * ```js
- * data: [
- * [0, 8, 3],
- * [1, 1, 1],
- * [2, 6, 8]
- * ]
- * ```
- *
- * 2. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.arearange.turboThreshold),
- * this option is not available.
- * ```js
- * data: [{
- * x: 1,
- * low: 9,
- * high: 0,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * low: 3,
- * high: 4,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<Array<(number|string),number>|Array<(number|string),number,number>|*>}
- * @extends series.line.data
- * @excluding marker, y
- * @product highcharts highstock
- * @apioption series.arearange.data
- */
- /**
- * @extends series.arearange.dataLabels
- * @product highcharts highstock
- * @apioption series.arearange.data.dataLabels
- */
- /**
- * The high or maximum value for each data point.
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.arearange.data.high
- */
- /**
- * The low or minimum value for each data point.
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.arearange.data.low
- */
- ''; // adds doclets above to tranpiled file
|