tupali_fork/librerias/gantt/code/es-modules/parts/Series.js
2020-05-23 15:45:54 -05:00

5490 lines
214 KiB
JavaScript

/* *
*
* (c) 2010-2020 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import LegendSymbolMixin from '../mixins/legend-symbol.js';
import H from './Globals.js';
import './Options.js';
import Point from './Point.js';
import './SvgRenderer.js';
import U from './Utilities.js';
/**
* This is a placeholder type of the possible series options for
* [Highcharts](../highcharts/series), [Highstock](../highstock/series),
* [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
*
* In TypeScript is this dynamically generated to reference all possible types
* of series options.
*
* @ignore-declaration
* @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
*/
/**
* Options for `dataSorting`.
*
* @interface Highcharts.DataSortingOptionsObject
* @since 8.0.0
*/ /**
* Enable or disable data sorting for the series.
* @name Highcharts.DataSortingOptionsObject#enabled
* @type {boolean|undefined}
*/ /**
* Whether to allow matching points by name in an update.
* @name Highcharts.DataSortingOptionsObject#matchByName
* @type {boolean|undefined}
*/ /**
* Determines what data value should be used to sort by.
* @name Highcharts.DataSortingOptionsObject#sortKey
* @type {string|undefined}
*/
/**
* Function callback when a series has been animated.
*
* @callback Highcharts.SeriesAfterAnimateCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occured.
*
* @param {Highcharts.SeriesAfterAnimateEventObject} event
* Event arguments.
*/
/**
* Event information regarding completed animation of a series.
*
* @interface Highcharts.SeriesAfterAnimateEventObject
*/ /**
* Animated series.
* @name Highcharts.SeriesAfterAnimateEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesAfterAnimateEventObject#type
* @type {"afterAnimate"}
*/
/**
* Function callback when the checkbox next to the series' name in the legend is
* clicked.
*
* @callback Highcharts.SeriesCheckboxClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occured.
*
* @param {Highcharts.SeriesCheckboxClickEventObject} event
* Event arguments.
*/
/**
* Event information regarding check of a series box.
*
* @interface Highcharts.SeriesCheckboxClickEventObject
*/ /**
* Whether the box has been checked.
* @name Highcharts.SeriesCheckboxClickEventObject#checked
* @type {boolean}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#item
* @type {Highcharts.Series}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesCheckboxClickEventObject#type
* @type {"checkboxClick"}
*/
/**
* Function callback when a series is clicked. Return false to cancel toogle
* actions.
*
* @callback Highcharts.SeriesClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occured.
*
* @param {Highcharts.SeriesClickEventObject} event
* Event arguments.
*/
/**
* Common information for a click event on a series.
*
* @interface Highcharts.SeriesClickEventObject
* @extends global.Event
*/ /**
* Nearest point on the graph.
* @name Highcharts.SeriesClickEventObject#point
* @type {Highcharts.Point}
*/
/**
* Gets fired when the series is hidden after chart generation time, either by
* clicking the legend item or by calling `.hide()`.
*
* @callback Highcharts.SeriesHideCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occured.
*
* @param {global.Event} event
* The event that occured.
*/
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
* graph.
*
* @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
*/
/**
* Gets fired when the legend item belonging to the series is clicked. The
* default action is to toggle the visibility of the series. This can be
* prevented by returning `false` or calling `event.preventDefault()`.
*
* @callback Highcharts.SeriesLegendItemClickCallbackFunction
*
* @param {Highcharts.Series} this
* The series where the event occured.
*
* @param {Highcharts.SeriesLegendItemClickEventObject} event
* The event that occured.
*/
/**
* Information about the event.
*
* @interface Highcharts.SeriesLegendItemClickEventObject
*/ /**
* Related browser event.
* @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
* @type {global.PointerEvent}
*/ /**
* Prevent the default action of toggle the visibility of the series.
* @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
* @type {Function}
*/ /**
* Related series.
* @name Highcharts.SeriesCheckboxClickEventObject#target
* @type {Highcharts.Series}
*/ /**
* Event type.
* @name Highcharts.SeriesCheckboxClickEventObject#type
* @type {"checkboxClick"}
*/
/**
* Gets fired when the mouse leaves the graph.
*
* @callback Highcharts.SeriesMouseOutCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occured.
*
* @param {global.PointerEvent} event
* Event that occured.
*/
/**
* Gets fired when the mouse enters the graph.
*
* @callback Highcharts.SeriesMouseOverCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occured.
*
* @param {global.PointerEvent} event
* Event that occured.
*/
/**
* Translation and scale for the plot area of a series.
*
* @interface Highcharts.SeriesPlotBoxObject
*/ /**
* @name Highcharts.SeriesPlotBoxObject#scaleX
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#scaleY
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#translateX
* @type {number}
*/ /**
* @name Highcharts.SeriesPlotBoxObject#translateY
* @type {number}
*/
/**
* Gets fired when the series is shown after chart generation time, either by
* clicking the legend item or by calling `.show()`.
*
* @callback Highcharts.SeriesShowCallbackFunction
*
* @param {Highcharts.Series} this
* Series where the event occured.
*
* @param {global.Event} event
* Event that occured.
*/
/**
* Possible key values for the series state options.
*
* @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
*/
''; // detach doclets above
var addEvent = U.addEvent, animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, erase = U.erase, error = U.error, extend = U.extend, find = U.find, fireEvent = U.fireEvent, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isString = U.isString, merge = U.merge, objectEach = U.objectEach, pick = U.pick, removeEvent = U.removeEvent, seriesType = U.seriesType, splat = U.splat, syncTimeout = U.syncTimeout;
var defaultOptions = H.defaultOptions, defaultPlotOptions = H.defaultPlotOptions, seriesTypes = H.seriesTypes, SVGElement = H.SVGElement, win = H.win;
/**
* This is the base series prototype that all other series types inherit from.
* A new series is initialized either through the
* [series](https://api.highcharts.com/highcharts/series)
* option structure, or after the chart is initialized, through
* {@link Highcharts.Chart#addSeries}.
*
* The object can be accessed in a number of ways. All series and point event
* handlers give a reference to the `series` object. The chart object has a
* {@link Highcharts.Chart#series|series} property that is a collection of all
* the chart's series. The point objects and axis objects also have the same
* reference.
*
* Another way to reference the series programmatically is by `id`. Add an id
* in the series configuration options, and get the series object by
* {@link Highcharts.Chart#get}.
*
* Configuration options for the series are given in three levels. Options for
* all series in a chart are given in the
* [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
* object. Then options for all series of a specific type
* are given in the plotOptions of that type, for example `plotOptions.line`.
* Next, options for one single series are given in the series array, or as
* arguments to `chart.addSeries`.
*
* The data in the series is stored in various arrays.
*
* - First, `series.options.data` contains all the original config options for
* each point whether added by options or methods like `series.addPoint`.
*
* - Next, `series.data` contains those values converted to points, but in case
* the series data length exceeds the `cropThreshold`, or if the data is
* grouped, `series.data` doesn't contain all the points. It only contains the
* points that have been created on demand.
*
* - Then there's `series.points` that contains all currently visible point
* objects. In case of cropping, the cropped-away points are not part of this
* array. The `series.points` array starts at `series.cropStart` compared to
* `series.data` and `series.options.data`. If however the series data is
* grouped, these can't be correlated one to one.
*
* - `series.xData` and `series.processedXData` contain clean x values,
* equivalent to `series.data` and `series.points`.
*
* - `series.yData` and `series.processedYData` contain clean y values,
* equivalent to `series.data` and `series.points`.
*
* @class
* @name Highcharts.Series
*
* @param {Highcharts.Chart} chart
* The chart instance.
*
* @param {Highcharts.SeriesOptionsType|object} options
* The series options.
*/ /**
* The line series is the base type and is therefor the series base prototype.
*
* @private
* @class
* @name Highcharts.seriesTypes.line
*
* @augments Highcharts.Series
*/
H.Series = seriesType('line',
/**
* Series options for specific data and the data itself. In TypeScript you
* have to cast the series options to specific series types, to get all
* possible options for a series.
*
* @example
* // TypeScript example
* Highcharts.chart('container', {
* series: [{
* color: '#06C',
* data: [[0, 1], [2, 3]]
* } as Highcharts.SeriesLineOptions ]
* });
*
* @type {Array<*>}
* @apioption series
*/
/**
* An id for the series. This can be used after render time to get a pointer
* to the series object through `chart.get()`.
*
* @sample {highcharts} highcharts/plotoptions/series-id/
* Get series by id
*
* @type {string}
* @since 1.2.0
* @apioption series.id
*/
/**
* The index of the series in the chart, affecting the internal index in the
* `chart.series` array, the visible Z index as well as the order in the
* legend.
*
* @type {number}
* @since 2.3.0
* @apioption series.index
*/
/**
* The sequential index of the series in the legend.
*
* @see [legend.reversed](#legend.reversed),
* [yAxis.reversedStacks](#yAxis.reversedStacks)
*
* @sample {highcharts|highstock} highcharts/series/legendindex/
* Legend in opposite order
*
* @type {number}
* @apioption series.legendIndex
*/
/**
* The name of the series as shown in the legend, tooltip etc.
*
* @sample {highcharts} highcharts/series/name/
* Series name
* @sample {highmaps} maps/demo/category-map/
* Series name
*
* @type {string}
* @apioption series.name
*/
/**
* This option allows grouping series in a stacked chart. The stack option
* can be a string or anything else, as long as the grouped series' stack
* options match each other after conversion into a string.
*
* @sample {highcharts} highcharts/series/stack/
* Stacked and grouped columns
*
* @type {number|string}
* @since 2.1
* @product highcharts highstock
* @apioption series.stack
*/
/**
* The type of series, for example `line` or `column`. By default, the
* series type is inherited from [chart.type](#chart.type), so unless the
* chart is a combination of series types, there is no need to set it on the
* series level.
*
* @sample {highcharts} highcharts/series/type/
* Line and column in the same chart
* @sample highcharts/series/type-dynamic/
* Dynamic types with button selector
* @sample {highmaps} maps/demo/mapline-mappoint/
* Multiple types in the same map
*
* @type {string}
* @apioption series.type
*/
/**
* When using dual or multiple x axes, this number defines which xAxis the
* particular series is connected to. It refers to either the
* {@link #xAxis.id|axis id}
* or the index of the axis in the xAxis array, with 0 being the first.
*
* @type {number|string}
* @default 0
* @product highcharts highstock
* @apioption series.xAxis
*/
/**
* When using dual or multiple y axes, this number defines which yAxis the
* particular series is connected to. It refers to either the
* {@link #yAxis.id|axis id}
* or the index of the axis in the yAxis array, with 0 being the first.
*
* @sample {highcharts} highcharts/series/yaxis/
* Apply the column series to the secondary Y axis
*
* @type {number|string}
* @default 0
* @product highcharts highstock
* @apioption series.yAxis
*/
/**
* Define the visual z index of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-zindex-default/
* With no z index, the series defined last are on top
* @sample {highcharts} highcharts/plotoptions/series-zindex/
* With a z index, the series with the highest z index is on top
* @sample {highstock} highcharts/plotoptions/series-zindex-default/
* With no z index, the series defined last are on top
* @sample {highstock} highcharts/plotoptions/series-zindex/
* With a z index, the series with the highest z index is on top
*
* @type {number}
* @product highcharts highstock
* @apioption series.zIndex
*/
null,
/**
* General options for all series types.
*
* @optionparent plotOptions.series
*/
{
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin`
* of a line graph. Round means that lines are rounded in the ends and
* bends.
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @since 3.0.7
* @apioption plotOptions.line.linecap
*/
/**
* Pixel width of the graph line.
*
* @see In styled mode, the line stroke-width can be set with the
* `.highcharts-graph` class name.
*
* @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
* On all series
* @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
* On one single series
*
* @product highcharts highstock
*
* @private
*/
lineWidth: 2,
/**
* For some series, there is a limit that shuts down initial animation
* by default when the total number of points in the chart is too high.
* For example, for a column chart and its derivatives, animation does
* not run if there is more than 250 points totally. To disable this
* cap, set `animationLimit` to `Infinity`.
*
* @type {number}
* @apioption plotOptions.series.animationLimit
*/
/**
* Allow this series' points to be selected by clicking on the graphic
* (columns, point markers, pie slices, map areas etc).
*
* The selected points can be handled by point select and unselect
* events, or collectively by the [getSelectedPoints
* ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
*
* And alternative way of selecting points is through dragging.
*
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
* Pie
* @sample {highcharts} highcharts/chart/events-selection-points/
* Select a range of points through a drag selection
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Map area
* @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
* Map bubble
*
* @since 1.2.0
*
* @private
*/
allowPointSelect: false,
/**
* When true, each point or column edge is rounded to its nearest pixel
* in order to render sharp on screen. In some cases, when there are a
* lot of densely packed columns, this leads to visible difference
* in column widths or distance between columns. In these cases,
* setting `crisp` to `false` may look better, even though each column
* is rendered blurry.
*
* @sample {highcharts} highcharts/plotoptions/column-crisp-false/
* Crisp is false
*
* @since 5.0.10
* @product highcharts highstock gantt
*
* @private
*/
crisp: true,
/**
* If true, a checkbox is displayed next to the legend item to allow
* selecting the series. The state of the checkbox is determined by
* the `selected` option.
*
* @productdesc {highmaps}
* Note that if a `colorAxis` is defined, the color axis is represented
* in the legend, not the series.
*
* @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
* Show select box
*
* @since 1.2.0
*
* @private
*/
showCheckbox: false,
/**
* Enable or disable the initial animation when a series is displayed.
* The animation can also be set as a configuration object. Please
* note that this option only applies to the initial animation of the
* series itself. For other animations, see [chart.animation](
* #chart.animation) and the animation parameter under the API methods.
* The following properties are supported:
*
* - `duration`: The duration of the animation in milliseconds.
*
* - `easing`: Can be a string reference to an easing function set on
* the `Math` object or a function. See the _Custom easing function_
* demo below.
*
* Due to poor performance, animation is disabled in old IE browsers
* for several chart types.
*
* @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
* Animation disabled
* @sample {highcharts} highcharts/plotoptions/series-animation-slower/
* Slower animation
* @sample {highcharts} highcharts/plotoptions/series-animation-easing/
* Custom easing function
* @sample {highstock} stock/plotoptions/animation-slower/
* Slower animation
* @sample {highstock} stock/plotoptions/animation-easing/
* Custom easing function
* @sample {highmaps} maps/plotoptions/series-animation-true/
* Animation enabled on map series
* @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
* Disabled on mapbubble series
*
* @type {boolean|Highcharts.AnimationOptionsObject}
* @default {highcharts} true
* @default {highstock} true
* @default {highmaps} false
*
* @private
*/
animation: {
/** @internal */
duration: 1000
},
/**
* An additional class name to apply to the series' graphical elements.
* This option does not replace default class names of the graphical
* element.
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.className
*/
/**
* Disable this option to allow series rendering in the whole plotting
* area.
*
* **Note:** Clipping should be always enabled when
* [chart.zoomType](#chart.zoomType) is set
*
* @sample {highcharts} highcharts/plotoptions/series-clip/
* Disabled clipping
*
* @default true
* @type {boolean}
* @since 3.0.0
* @apioption plotOptions.series.clip
*/
/**
* The main color of the series. In line type series it applies to the
* line and the point markers unless otherwise specified. In bar type
* series it applies to the bars unless a color is specified per point.
* The default value is pulled from the `options.colors` array.
*
* In styled mode, the color can be defined by the
* [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
* color can be set with the `.highcharts-series`,
* `.highcharts-color-{n}`, `.highcharts-{type}-series` or
* `.highcharts-series-{n}` class, or individual classes given by the
* `className` option.
*
* @productdesc {highmaps}
* In maps, the series color is rarely used, as most choropleth maps use
* the color to denote the value of each point. The series color can
* however be used in a map with multiple series holding categorized
* data.
*
* @sample {highcharts} highcharts/plotoptions/series-color-general/
* General plot option
* @sample {highcharts} highcharts/plotoptions/series-color-specific/
* One specific series
* @sample {highcharts} highcharts/plotoptions/series-color-area/
* Area color
* @sample {highcharts} highcharts/series/infographic/
* Pattern fill
* @sample {highmaps} maps/demo/category-map/
* Category map by multiple series
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.color
*/
/**
* Styled mode only. A specific color index to use for the series, so
* its graphic representations are given the class name
* `highcharts-color-{n}`.
*
* @type {number}
* @since 5.0.0
* @apioption plotOptions.series.colorIndex
*/
/**
* Whether to connect a graph line across null points, or render a gap
* between the two points on either side of the null.
*
* @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
* False by default
* @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
* True
*
* @type {boolean}
* @default false
* @product highcharts highstock
* @apioption plotOptions.series.connectNulls
*/
/**
* You can set the cursor to "pointer" if you have click events attached
* to the series, to signal to the user that the points and lines can
* be clicked.
*
* In styled mode, the series cursor can be set with the same classes
* as listed under [series.color](#plotOptions.series.color).
*
* @sample {highcharts} highcharts/plotoptions/series-cursor-line/
* On line graph
* @sample {highcharts} highcharts/plotoptions/series-cursor-column/
* On columns
* @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
* On scatter markers
* @sample {highstock} stock/plotoptions/cursor/
* Pointer on a line graph
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Map area
* @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
* Map bubble
*
* @type {string|Highcharts.CursorValue}
* @apioption plotOptions.series.cursor
*/
/**
* A reserved subspace to store options and values for customized
* functionality. Here you can add additional data for your own event
* callbacks and formatter callbacks.
*
* @sample {highcharts} highcharts/point/custom/
* Point and series with custom data
*
* @type {Highcharts.Dictionary<*>}
* @apioption plotOptions.series.custom
*/
/**
* Name of the dash style to use for the graph, or for some series types
* the outline of each shape.
*
* In styled mode, the
* [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
* can be set with the same classes as listed under
* [series.color](#plotOptions.series.color).
*
* @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highcharts} highcharts/plotoptions/series-dashstyle/
* Chart suitable for printing in black and white
* @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
* Possible values demonstrated
* @sample {highmaps} maps/plotoptions/series-dashstyle/
* Dotted borders on a map
*
* @type {Highcharts.DashStyleValue}
* @default Solid
* @since 2.1
* @apioption plotOptions.series.dashStyle
*/
/**
* A description of the series to add to the screen reader information
* about the series.
*
* @type {string}
* @since 5.0.0
* @requires modules/accessibility
* @apioption plotOptions.series.description
*/
/**
* Options for the series data sorting.
*
* @type {Highcharts.DataSortingOptionsObject}
* @since 8.0.0
* @product highcharts highstock
* @apioption plotOptions.series.dataSorting
*/
/**
* Enable or disable data sorting for the series. Use [xAxis.reversed](
* #xAxis.reversed) to change the sorting order.
*
* @sample {highcharts} highcharts/datasorting/animation/
* Data sorting in scatter-3d
* @sample {highcharts} highcharts/datasorting/labels-animation/
* Axis labels animation
* @sample {highcharts} highcharts/datasorting/dependent-sorting/
* Dependent series sorting
* @sample {highcharts} highcharts/datasorting/independent-sorting/
* Independent series sorting
*
* @type {boolean}
* @since 8.0.0
* @apioption plotOptions.series.dataSorting.enabled
*/
/**
* Whether to allow matching points by name in an update. If this option
* is disabled, points will be matched by order.
*
* @sample {highcharts} highcharts/datasorting/match-by-name/
* Enabled match by name
*
* @type {boolean}
* @since 8.0.0
* @apioption plotOptions.series.dataSorting.matchByName
*/
/**
* Determines what data value should be used to sort by.
*
* @sample {highcharts} highcharts/datasorting/sort-key/
* Sort key as `z` value
*
* @type {string}
* @since 8.0.0
* @default y
* @apioption plotOptions.series.dataSorting.sortKey
*/
/**
* Enable or disable the mouse tracking for a specific series. This
* includes point tooltips and click events on graphs and points. For
* large datasets it improves performance.
*
* @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
* No mouse tracking
* @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
* No mouse tracking
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.enableMouseTracking
*/
/**
* Whether to use the Y extremes of the total chart width or only the
* zoomed area when zooming in on parts of the X axis. By default, the
* Y axis adjusts to the min and max of the visible data. Cartesian
* series only.
*
* @type {boolean}
* @default false
* @since 4.1.6
* @product highcharts highstock gantt
* @apioption plotOptions.series.getExtremesFromAll
*/
/**
* An array specifying which option maps to which key in the data point
* array. This makes it convenient to work with unstructured data arrays
* from different sources.
*
* @see [series.data](#series.line.data)
*
* @sample {highcharts|highstock} highcharts/series/data-keys/
* An extended data array with keys
* @sample {highcharts|highstock} highcharts/series/data-nested-keys/
* Nested keys used to access object properties
*
* @type {Array<string>}
* @since 4.1.6
* @apioption plotOptions.series.keys
*/
/**
* The line cap used for line ends and line joins on the graph.
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @product highcharts highstock
* @apioption plotOptions.series.linecap
*/
/**
* The [id](#series.id) of another series to link to. Additionally,
* the value can be ":previous" to link to the previous series. When
* two series are linked, only the first one appears in the legend.
* Toggling the visibility of this also toggles the linked series.
*
* If master series uses data sorting and linked series does not have
* its own sorting definition, the linked series will be sorted in the
* same order as the master one.
*
* @sample {highcharts|highstock} highcharts/demo/arearange-line/
* Linked series
*
* @type {string}
* @since 3.0
* @product highcharts highstock gantt
* @apioption plotOptions.series.linkedTo
*/
/**
* Options for the corresponding navigator series if `showInNavigator`
* is `true` for this series. Available options are the same as any
* series, documented at [plotOptions](#plotOptions.series) and
* [series](#series).
*
* These options are merged with options in [navigator.series](
* #navigator.series), and will take precedence if the same option is
* defined both places.
*
* @see [navigator.series](#navigator.series)
*
* @type {Highcharts.PlotSeriesOptions}
* @since 5.0.0
* @product highstock
* @apioption plotOptions.series.navigatorOptions
*/
/**
* The color for the parts of the graph or points that are below the
* [threshold](#plotOptions.series.threshold). Note that `zones` takes
* precedence over the negative color. Using `negativeColor` is
* equivalent to applying a zone with value of 0.
*
* @see In styled mode, a negative color is applied by setting this option
* to `true` combined with the `.highcharts-negative` class name.
*
* @sample {highcharts} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
* Arearange
* @sample {highcharts} highcharts/css/series-negative-color/
* Styled mode
* @sample {highstock} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
* Arearange
* @sample {highmaps} highcharts/plotoptions/series-negative-color/
* Spline, area and column
* @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
* Arearange
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 3.0
* @apioption plotOptions.series.negativeColor
*/
/**
* Same as
* [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
* but for an individual series. Overrides the chart wide configuration.
*
* @type {Function}
* @since 5.0.12
* @apioption plotOptions.series.pointDescriptionFormatter
*/
/**
* If no x values are given for the points in a series, `pointInterval`
* defines the interval of the x values. For example, if a series
* contains one value every decade starting from year 0, set
* `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
* is set in milliseconds.
*
* It can be also be combined with `pointIntervalUnit` to draw irregular
* time intervals.
*
* Please note that this options applies to the _series data_, not the
* interval of the axis ticks, which is independent.
*
* @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
* Datetime X axis
* @sample {highstock} stock/plotoptions/pointinterval-pointstart/
* Using pointStart and pointInterval
*
* @type {number}
* @default 1
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointInterval
*/
/**
* On datetime series, this allows for setting the
* [pointInterval](#plotOptions.series.pointInterval) to irregular time
* units, `day`, `month` and `year`. A day is usually the same as 24
* hours, but `pointIntervalUnit` also takes the DST crossover into
* consideration when dealing with local time. Combine this option with
* `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
*
* Please note that this options applies to the _series data_, not the
* interval of the axis ticks, which is independent.
*
* @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
* One point a month
* @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
* One point a month
*
* @type {string}
* @since 4.1.0
* @product highcharts highstock gantt
* @validvalue ["day", "month", "year"]
* @apioption plotOptions.series.pointIntervalUnit
*/
/**
* Possible values: `"on"`, `"between"`, `number`.
*
* In a column chart, when pointPlacement is `"on"`, the point will not
* create any padding of the X axis. In a polar column chart this means
* that the first column points directly north. If the pointPlacement is
* `"between"`, the columns will be laid out between ticks. This is
* useful for example for visualising an amount between two points in
* time or in a certain sector of a polar chart.
*
* Since Highcharts 3.0.2, the point placement can also be numeric,
* where 0 is on the axis value, -0.5 is between this value and the
* previous, and 0.5 is between this value and the next. Unlike the
* textual options, numeric point placement options won't affect axis
* padding.
*
* Note that pointPlacement needs a [pointRange](
* #plotOptions.series.pointRange) to work. For column series this is
* computed, but for line-type series it needs to be set.
*
* For the `xrange` series type and gantt charts, if the Y axis is a
* category axis, the `pointPlacement` applies to the Y axis rather than
* the (typically datetime) X axis.
*
* Defaults to `undefined` in cartesian charts, `"between"` in polar
* charts.
*
* @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
*
* @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
* Between in a column chart
* @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
* Numeric placement for custom layout
* @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
* Placement in heatmap
*
* @type {string|number}
* @since 2.3.0
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointPlacement
*/
/**
* If no x values are given for the points in a series, pointStart
* defines on what value to start. For example, if a series contains one
* yearly value starting from 1945, set pointStart to 1945.
*
* @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
* Linear
* @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
* Datetime
* @sample {highstock} stock/plotoptions/pointinterval-pointstart/
* Using pointStart and pointInterval
*
* @type {number}
* @default 0
* @product highcharts highstock gantt
* @apioption plotOptions.series.pointStart
*/
/**
* Whether to select the series initially. If `showCheckbox` is true,
* the checkbox next to the series name in the legend will be checked
* for a selected series.
*
* @sample {highcharts} highcharts/plotoptions/series-selected/
* One out of two series selected
*
* @type {boolean}
* @default false
* @since 1.2.0
* @apioption plotOptions.series.selected
*/
/**
* 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`.
*
* @sample {highcharts} highcharts/plotoptions/series-shadow/
* Shadow enabled
*
* @type {boolean|Highcharts.ShadowOptionsObject}
* @default false
* @apioption plotOptions.series.shadow
*/
/**
* Whether to display this particular series or series type in the
* legend. Standalone series are shown in legend by default, and linked
* series are not. Since v7.2.0 it is possible to show series that use
* colorAxis by setting this option to `true`.
*
* @sample {highcharts} highcharts/plotoptions/series-showinlegend/
* One series in the legend, one hidden
*
* @type {boolean}
* @apioption plotOptions.series.showInLegend
*/
/**
* Whether or not to show the series in the navigator. Takes precedence
* over [navigator.baseSeries](#navigator.baseSeries) if defined.
*
* @type {boolean}
* @since 5.0.0
* @product highstock
* @apioption plotOptions.series.showInNavigator
*/
/**
* If set to `true`, the accessibility module will skip past the points
* in this series for keyboard navigation.
*
* @type {boolean}
* @since 5.0.12
* @apioption plotOptions.series.skipKeyboardNavigation
*/
/**
* Whether to stack the values of each series on top of each other.
* Possible values are `undefined` to disable, `"normal"` to stack by
* value or `"percent"`. When stacking is enabled, data must be sorted
* in ascending X order. A special stacking option is with the
* streamgraph series type, where the stacking option is set to
* `"stream"`. The second one is `"overlap"`, which only applies to
* waterfall series.
*
* @see [yAxis.reversedStacks](#yAxis.reversedStacks)
*
* @sample {highcharts} highcharts/plotoptions/series-stacking-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-stacking-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
* Bar
* @sample {highcharts} highcharts/plotoptions/series-stacking-area/
* Area
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
* Line
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
* Bar
* @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
* Area
* @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
* Waterfall with normal stacking
* @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
* Waterfall with overlap stacking
* @sample {highstock} stock/plotoptions/stacking/
* Area
*
* @type {string}
* @product highcharts highstock
* @validvalue ["normal", "overlap", "percent", "stream"]
* @apioption plotOptions.series.stacking
*/
/**
* Whether to apply steps to the line. Possible values are `left`,
* `center` and `right`.
*
* @sample {highcharts} highcharts/plotoptions/line-step/
* Different step line options
* @sample {highcharts} highcharts/plotoptions/area-step/
* Stepped, stacked area
* @sample {highstock} stock/plotoptions/line-step/
* Step line
*
* @type {string}
* @since 1.2.5
* @product highcharts highstock
* @validvalue ["left", "center", "right"]
* @apioption plotOptions.series.step
*/
/**
* The threshold, also called zero level or base level. For line type
* series this is only used in conjunction with
* [negativeColor](#plotOptions.series.negativeColor).
*
* @see [softThreshold](#plotOptions.series.softThreshold).
*
* @type {number}
* @default 0
* @since 3.0
* @product highcharts highstock
* @apioption plotOptions.series.threshold
*/
/**
* Set the initial visibility of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-visible/
* Two series, one hidden and one visible
* @sample {highstock} stock/plotoptions/series-visibility/
* Hidden series
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.visible
*/
/**
* Defines the Axis on which the zones are applied.
*
* @see [zones](#plotOptions.series.zones)
*
* @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
* Zones on the X-Axis
* @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
* Zones on the X-Axis
*
* @type {string}
* @default y
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zoneAxis
*/
/**
* General event handlers for the series items. These event hooks can
* also be attached to the series at run time using the
* `Highcharts.addEvent` function.
*
* @declare Highcharts.SeriesEventsOptionsObject
*
* @private
*/
events: {},
/**
* Fires after the series has finished its initial animation, or in case
* animation is disabled, immediately as the series is displayed.
*
* @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
* Show label after animate
* @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
* Show label after animate
*
* @type {Highcharts.SeriesAfterAnimateCallbackFunction}
* @since 4.0
* @product highcharts highstock gantt
* @context Highcharts.Series
* @apioption plotOptions.series.events.afterAnimate
*/
/**
* Fires when the checkbox next to the series' name in the legend is
* clicked. One parameter, `event`, is passed to the function. The state
* of the checkbox is found by `event.checked`. The checked item is
* found by `event.item`. Return `false` to prevent the default action
* which is to toggle the select state of the series.
*
* @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
* Alert checkbox status
*
* @type {Highcharts.SeriesCheckboxClickCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.checkboxClick
*/
/**
* Fires when the series is clicked. One parameter, `event`, is passed
* to the function, containing common event information. Additionally,
* `event.point` holds a pointer to the nearest point on the graph.
*
* @sample {highcharts} highcharts/plotoptions/series-events-click/
* Alert click info
* @sample {highstock} stock/plotoptions/series-events-click/
* Alert click info
* @sample {highmaps} maps/plotoptions/series-events-click/
* Display click info in subtitle
*
* @type {Highcharts.SeriesClickCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.click
*/
/**
* Fires when the series is hidden after chart generation time, either
* by clicking the legend item or by calling `.hide()`.
*
* @sample {highcharts} highcharts/plotoptions/series-events-hide/
* Alert when the series is hidden by clicking the legend item
*
* @type {Highcharts.SeriesHideCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.hide
*/
/**
* Fires when the legend item belonging to the series is clicked. One
* parameter, `event`, is passed to the function. The default action
* is to toggle the visibility of the series. This can be prevented
* by returning `false` or calling `event.preventDefault()`.
*
* @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
* Confirm hiding and showing
*
* @type {Highcharts.SeriesLegendItemClickCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.legendItemClick
*/
/**
* Fires when the mouse leaves the graph. One parameter, `event`, is
* passed to the function, containing common event information. If the
* [stickyTracking](#plotOptions.series) option is true, `mouseOut`
* doesn't happen before the mouse enters another graph or leaves the
* plot area.
*
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
* With sticky tracking by default
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
* Without sticky tracking
*
* @type {Highcharts.SeriesMouseOutCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.mouseOut
*/
/**
* Fires when the mouse enters the graph. One parameter, `event`, is
* passed to the function, containing common event information.
*
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
* With sticky tracking by default
* @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
* Without sticky tracking
*
* @type {Highcharts.SeriesMouseOverCallbackFunction}
* @context Highcharts.Series
* @apioption plotOptions.series.events.mouseOver
*/
/**
* Fires when the series is shown after chart generation time, either
* by clicking the legend item or by calling `.show()`.
*
* @sample {highcharts} highcharts/plotoptions/series-events-show/
* Alert when the series is shown by clicking the legend item.
*
* @type {Highcharts.SeriesShowCallbackFunction}
* @since 1.2.0
* @context Highcharts.Series
* @apioption plotOptions.series.events.show
*/
/**
* Options for the point markers of line-like series. Properties like
* `fillColor`, `lineColor` and `lineWidth` define the visual appearance
* of the markers. Other series types, like column series, don't have
* markers, but have visual options on the series level instead.
*
* In styled mode, the markers can be styled with the
* `.highcharts-point`, `.highcharts-point-hover` and
* `.highcharts-point-select` class names.
*
* @declare Highcharts.PointMarkerOptionsObject
*
* @private
*/
marker: {
/**
* Enable or disable the point marker. If `undefined`, the markers
* are hidden when the data is dense, and shown for more widespread
* data points.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
* Disabled markers
* @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
* Disabled in normal state but enabled on hover
* @sample {highstock} stock/plotoptions/series-marker/
* Enabled markers
*
* @type {boolean}
* @default {highcharts} undefined
* @default {highstock} false
* @apioption plotOptions.series.marker.enabled
*/
/**
* The threshold for how dense the point markers should be before
* they are hidden, given that `enabled` is not defined. The number
* indicates the horizontal distance between the two closest points
* in the series, as multiples of the `marker.radius`. In other
* words, the default value of 2 means points are hidden if
* overlapping horizontally.
*
* @sample highcharts/plotoptions/series-marker-enabledthreshold
* A higher threshold
*
* @since 6.0.5
*/
enabledThreshold: 2,
/**
* The fill color of the point marker. When `undefined`, the series'
* or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* White fill
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.fillColor
*/
/**
* Image markers only. Set the image width explicitly. When using
* this option, a `width` must also be set.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
* @sample {highstock} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
*
* @type {number}
* @since 4.0.4
* @apioption plotOptions.series.marker.height
*/
/**
* The color of the point marker's outline. When `undefined`, the
* series' or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* Inherit from series color (undefined)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
lineColor: '#ffffff',
/**
* The width of the point marker's outline.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
* 2px blue marker
*/
lineWidth: 0,
/**
* The radius of the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-radius/
* Bigger markers
*
* @default {highstock} 2
*/
radius: 4,
/**
* A predefined shape or symbol for the marker. When undefined, the
* symbol is pulled from options.symbols. Other possible values are
* `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
* `'triangle-down'`.
*
* Additionally, the URL to a graphic can be given on this form:
* `'url(graphic.png)'`. Note that for the image to be applied to
* exported charts, its URL needs to be accessible by the export
* server.
*
* Custom callbacks for symbol path generation can also be added to
* `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
* used by its method name, as shown in the demo.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
* Predefined, graphic and custom markers
* @sample {highstock} highcharts/plotoptions/series-marker-symbol/
* Predefined, graphic and custom markers
*
* @type {string}
* @apioption plotOptions.series.marker.symbol
*/
/**
* Image markers only. Set the image width explicitly. When using
* this option, a `height` must also be set.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
* @sample {highstock} highcharts/plotoptions/series-marker-width-height/
* Fixed width and height
*
* @type {number}
* @since 4.0.4
* @apioption plotOptions.series.marker.width
*/
/**
* States for a single point marker.
*
* @declare Highcharts.PointStatesOptionsObject
*/
states: {
/**
* The normal state of a single point marker. Currently only
* used for setting animation when returning to normal state
* from hover.
*
* @declare Highcharts.PointStatesNormalOptionsObject
*/
normal: {
/**
* Animation when returning to normal state after hovering.
*
* @type {boolean|Highcharts.AnimationOptionsObject}
*/
animation: true
},
/**
* The hover state for a single point marker.
*
* @declare Highcharts.PointStatesHoverOptionsObject
*/
hover: {
/**
* Animation when hovering over the marker.
*
* @type {boolean|Highcharts.AnimationOptionsObject}
*/
animation: {
/** @internal */
duration: 50
},
/**
* Enable or disable the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
* Disabled hover state
*/
enabled: true,
/**
* The fill color of the marker in hover state. When
* `undefined`, the series' or point's fillColor for normal
* state is used.
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.states.hover.fillColor
*/
/**
* The color of the point marker's outline. When
* `undefined`, the series' or point's lineColor for normal
* state is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
* White fill color, black line color
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.marker.states.hover.lineColor
*/
/**
* The width of the point marker's outline. When
* `undefined`, the series' or point's lineWidth for normal
* state is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
* 3px line width
*
* @type {number}
* @apioption plotOptions.series.marker.states.hover.lineWidth
*/
/**
* The radius of the point marker. In hover state, it
* defaults to the normal state's radius + 2 as per the
* [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
* option.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
* 10px radius
*
* @type {number}
* @apioption plotOptions.series.marker.states.hover.radius
*/
/**
* The number of pixels to increase the radius of the
* hovered point.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels greater radius on hover
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels greater radius on hover
*
* @since 4.0.3
*/
radiusPlus: 2,
/**
* The additional line width for a hovered point.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 2 pixels wider on hover
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 2 pixels wider on hover
*
* @since 4.0.3
*/
lineWidthPlus: 1
},
/**
* The appearance of the point marker when selected. In order to
* allow a point to be selected, set the
* `series.allowPointSelect` option to true.
*
* @declare Highcharts.PointStatesSelectOptionsObject
*/
select: {
/**
* Enable or disable visible feedback for selection.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
* Disabled select state
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.marker.states.select.enabled
*/
/**
* The radius of the point marker. In hover state, it
* defaults to the normal state's radius + 2.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
* 10px radius for selected points
*
* @type {number}
* @apioption plotOptions.series.marker.states.select.radius
*/
/**
* The fill color of the point marker.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
* Solid red discs for selected points
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
fillColor: '#cccccc',
/**
* The color of the point marker's outline. When
* `undefined`, the series' or point's color is used.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
* Red line color for selected points
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
*/
lineColor: '#000000',
/**
* The width of the point marker's outline.
*
* @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
* 3px line width for selected points
*/
lineWidth: 2
}
}
},
/**
* Properties for each single point.
*
* @declare Highcharts.PlotSeriesPointOptions
*
* @private
*/
point: {
/**
* Fires when a point is clicked. One parameter, `event`, is passed
* to the function, containing common event information.
*
* If the `series.allowPointSelect` option is true, the default
* action for the point's click event is to toggle the point's
* select state. Returning `false` cancels this action.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-click/
* Click marker to alert values
* @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
* Click column
* @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
* Go to URL
* @sample {highmaps} maps/plotoptions/series-point-events-click/
* Click marker to display values
* @sample {highmaps} maps/plotoptions/series-point-events-click-url/
* Go to URL
*
* @type {Highcharts.PointClickCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.click
*/
/**
* Fires when the mouse leaves the area close to the point. One
* parameter, `event`, is passed to the function, containing common
* event information.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
* Show values in the chart's corner on mouse over
*
* @type {Highcharts.PointMouseOutCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.mouseOut
*/
/**
* Fires when the mouse enters the area close to the point. One
* parameter, `event`, is passed to the function, containing common
* event information.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
* Show values in the chart's corner on mouse over
*
* @type {Highcharts.PointMouseOverCallbackFunction}
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.mouseOver
*/
/**
* Fires when the point is removed using the `.remove()` method. One
* parameter, `event`, is passed to the function. Returning `false`
* cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
* Remove point and confirm
*
* @type {Highcharts.PointRemoveCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.remove
*/
/**
* Fires when the point is selected either programmatically or
* following a click on the point. One parameter, `event`, is passed
* to the function. Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-select/
* Report the last selected point
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Report select and unselect
*
* @type {Highcharts.PointSelectCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.select
*/
/**
* Fires when the point is unselected either programmatically or
* following a click on the point. One parameter, `event`, is passed
* to the function.
* Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
* Report the last unselected point
* @sample {highmaps} maps/plotoptions/series-allowpointselect/
* Report select and unselect
*
* @type {Highcharts.PointUnselectCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.unselect
*/
/**
* Fires when the point is updated programmatically through the
* `.update()` method. One parameter, `event`, is passed to the
* function. The new point options can be accessed through
* `event.options`. Returning `false` cancels the operation.
*
* @sample {highcharts} highcharts/plotoptions/series-point-events-update/
* Confirm point updating
*
* @type {Highcharts.PointUpdateCallbackFunction}
* @since 1.2.0
* @context Highcharts.Point
* @apioption plotOptions.series.point.events.update
*/
/**
* Events for each single point.
*
* @declare Highcharts.PointEventsOptionsObject
*/
events: {}
},
/**
* Options for the series data labels, appearing next to each data
* point.
*
* Since v6.2.0, multiple data labels can be applied to each single
* point by defining them as an array of configs.
*
* In styled mode, the data labels can be styled with the
* `.highcharts-data-label-box` and `.highcharts-data-label` class names
* ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
* Data labels enabled
* @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
* Multiple data labels on a bar series
* @sample {highcharts} highcharts/css/series-datalabels
* Style mode example
*
* @type {*|Array<*>}
* @product highcharts highstock highmaps gantt
*
* @private
*/
dataLabels: {
/**
* The alignment of the data label compared to the point. If
* `right`, the right side of the label should be touching the
* point. For points with an extent, like columns, the alignments
* also dictates how to align it inside the box, as given with the
* [inside](#plotOptions.column.dataLabels.inside)
* option. Can be one of `left`, `center` or `right`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
* Left aligned
* @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
* Data labels inside the bar
*
* @type {Highcharts.AlignValue|null}
*/
align: 'center',
/**
* Whether to allow data labels to overlap. To make the labels less
* sensitive for overlapping, the
* [dataLabels.padding](#plotOptions.series.dataLabels.padding)
* can be set to 0.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
* Don't allow overlap
*
* @type {boolean}
* @default false
* @since 4.1.0
* @apioption plotOptions.series.dataLabels.allowOverlap
*/
/**
* The background color or gradient for the data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.backgroundColor
*/
/**
* The border color for the data label. Defaults to `undefined`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderColor
*/
/**
* The border radius in pixels for the data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {number}
* @default 0
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderRadius
*/
/**
* The border width in pixels for the data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {number}
* @default 0
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.borderWidth
*/
/**
* A class name for the data label. Particularly in styled mode,
* this can be used to give each series' or point's data label
* unique styling. In addition to this option, a default color class
* name is added so that we can give the labels a contrast text
* shadow.
*
* @sample {highcharts} highcharts/css/data-label-contrast/
* Contrast text shadow
* @sample {highcharts} highcharts/css/series-datalabels/
* Styling by CSS
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.dataLabels.className
*/
/**
* The text color for the data labels. Defaults to `undefined`. For
* certain series types, like column or map, the data labels can be
* drawn inside the points. In this case the data label will be
* drawn with maximum contrast by default. Additionally, it will be
* given a `text-outline` style with the opposite color, to further
* increase the contrast. This can be overridden by setting the
* `text-outline` style to `none` in the `dataLabels.style` option.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
* Red data labels
* @sample {highmaps} maps/demo/color-axis/
* White data labels
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @apioption plotOptions.series.dataLabels.color
*/
/**
* Whether to hide data labels that are outside the plot area. By
* default, the data label is moved inside the plot area according
* to the
* [overflow](#plotOptions.series.dataLabels.overflow)
* option.
*
* @type {boolean}
* @default true
* @since 2.3.3
* @apioption plotOptions.series.dataLabels.crop
*/
/**
* Whether to defer displaying the data labels until the initial
* series animation has finished.
*
* @type {boolean}
* @default true
* @since 4.0.0
* @product highcharts highstock gantt
* @apioption plotOptions.series.dataLabels.defer
*/
/**
* Enable or disable the data labels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
* Data labels enabled
* @sample {highmaps} maps/demo/color-axis/
* Data labels enabled
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataLabels.enabled
*/
/**
* A declarative filter to control of which data labels to display.
* The declarative filter is designed for use when callback
* functions are not available, like when the chart options require
* a pure JSON structure or for use with graphical editors. For
* programmatic control, use the `formatter` instead, and return
* `undefined` to disable a single data label.
*
* @example
* filter: {
* property: 'percentage',
* operator: '>',
* value: 4
* }
*
* @sample {highcharts} highcharts/demo/pie-monochrome
* Data labels filtered by percentage
*
* @declare Highcharts.DataLabelsFilterOptionsObject
* @since 6.0.3
* @apioption plotOptions.series.dataLabels.filter
*/
/**
* The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
* `==`, and `===`.
*
* @type {string}
* @validvalue [">", "<", ">=", "<=", "==", "==="]
* @apioption plotOptions.series.dataLabels.filter.operator
*/
/**
* The point property to filter by. Point options are passed
* directly to properties, additionally there are `y` value,
* `percentage` and others listed under {@link Highcharts.Point}
* members.
*
* @type {string}
* @apioption plotOptions.series.dataLabels.filter.property
*/
/**
* The value to compare against.
*
* @type {number}
* @apioption plotOptions.series.dataLabels.filter.value
*/
/**
* A
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* for the data label. Available variables are the same as for
* `formatter`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
* Add a unit
* @sample {highmaps} maps/plotoptions/series-datalabels-format/
* Formatted value in the data label
*
* @type {string}
* @default y
* @default point.value
* @since 3.0
* @apioption plotOptions.series.dataLabels.format
*/
// eslint-disable-next-line valid-jsdoc
/**
* Callback JavaScript function to format the data label. Note that
* if a `format` is defined, the format takes precedence and the
* formatter is ignored.
*
* @sample {highmaps} maps/plotoptions/series-datalabels-format/
* Formatted value
*
* @type {Highcharts.DataLabelsFormatterCallbackFunction}
*/
formatter: function () {
var numberFormatter = this.series.chart.numberFormatter;
return typeof this.y !== 'number' ? '' : numberFormatter(this.y, -1);
},
/**
* For points with an extent, like columns or map areas, whether to
* align the data label inside the box or to the actual value point.
* Defaults to `false` in most cases, `true` in stacked columns.
*
* @type {boolean}
* @since 3.0
* @apioption plotOptions.series.dataLabels.inside
*/
/**
* Format for points with the value of null. Works analogously to
* [format](#plotOptions.series.dataLabels.format). `nullFormat` can
* be applied only to series which support displaying null points.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
* Format data label and tooltip for null point.
*
* @type {boolean|string}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.nullFormat
*/
/**
* Callback JavaScript function that defines formatting for points
* with the value of null. Works analogously to
* [formatter](#plotOptions.series.dataLabels.formatter).
* `nullPointFormatter` can be applied only to series which support
* displaying null points.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
* Format data label and tooltip for null point.
*
* @type {Highcharts.DataLabelsFormatterCallbackFunction}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.nullFormatter
*/
/**
* How to handle data labels that flow outside the plot area. The
* default is `"justify"`, which aligns them inside the plot area.
* For columns and bars, this means it will be moved inside the bar.
* To display data labels outside the plot area, set `crop` to
* `false` and `overflow` to `"allow"`.
*
* @type {Highcharts.DataLabelsOverflowValue}
* @default justify
* @since 3.0.6
* @apioption plotOptions.series.dataLabels.overflow
*/
/**
* When either the `borderWidth` or the `backgroundColor` is set,
* this is the padding within the box.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
* @sample {highmaps} maps/plotoptions/series-datalabels-box/
* Data labels box options
*
* @since 2.2.1
*/
padding: 5,
/**
* Aligns data labels relative to points. If `center` alignment is
* not possible, it defaults to `right`.
*
* @type {Highcharts.AlignValue}
* @default center
* @apioption plotOptions.series.dataLabels.position
*/
/**
* Text rotation in degrees. Note that due to a more complex
* structure, backgrounds, borders and padding will be lost on a
* rotated data label.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical labels
*
* @type {number}
* @default 0
* @apioption plotOptions.series.dataLabels.rotation
*/
/**
* The shadow of the box. Works best with `borderWidth` or
* `backgroundColor`. Since 2.3 the shadow can be an object
* configuration containing `color`, `offsetX`, `offsetY`, `opacity`
* and `width`.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
* Data labels box options
*
* @type {boolean|Highcharts.ShadowOptionsObject}
* @default false
* @since 2.2.1
* @apioption plotOptions.series.dataLabels.shadow
*/
/**
* The name of a symbol to use for the border around the label.
* Symbols are predefined functions on the Renderer object.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
* A callout for annotations
*
* @type {string}
* @default square
* @since 4.1.2
* @apioption plotOptions.series.dataLabels.shape
*/
/**
* Styles for the label. The default `color` setting is
* `"contrast"`, which is a pseudo color that Highcharts picks up
* and applies the maximum contrast to the underlying point item,
* for example the bar in a bar chart.
*
* The `textOutline` is a pseudo property that applies an outline of
* the given width with the given color, which by default is the
* maximum contrast to the text. So a bright text color will result
* in a black text outline for maximum readability on a mixed
* background. In some cases, especially with grayscale text, the
* text outline doesn't work well, in which cases it can be disabled
* by setting it to `"none"`. When `useHTML` is true, the
* `textOutline` will not be picked up. In this, case, the same
* effect can be acheived through the `text-shadow` CSS property.
*
* For some series types, where each point has an extent, like for
* example tree maps, the data label may overflow the point. There
* are two strategies for handling overflow. By default, the text
* will wrap to multiple lines. The other strategy is to set
* `style.textOverflow` to `ellipsis`, which will keep the text on
* one line plus it will break inside long words.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
* Bold labels
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
* Long labels truncated with an ellipsis in a pie
* @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
* Long labels are wrapped in a pie
* @sample {highmaps} maps/demo/color-axis/
* Bold labels
*
* @type {Highcharts.CSSObject}
* @since 4.1.0
* @apioption plotOptions.series.dataLabels.style
*/
style: {
/** @internal */
fontSize: '11px',
/** @internal */
fontWeight: 'bold',
/** @internal */
color: 'contrast',
/** @internal */
textOutline: '1px contrast'
},
/**
* Options for a label text which should follow marker's shape.
* Border and background are disabled for a label that follows a
* path.
*
* **Note:** Only SVG-based renderer supports this option. Setting
* `useHTML` to true will disable this option.
*
* @declare Highcharts.DataLabelsTextPathOptionsObject
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath
*/
/**
* Presentation attributes for the text path.
*
* @type {Highcharts.SVGAttributes}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath.attributes
*/
/**
* Enable or disable `textPath` option for link's or marker's data
* labels.
*
* @type {boolean}
* @since 7.1.0
* @apioption plotOptions.series.dataLabels.textPath.enabled
*/
/**
* Whether to
* [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
* to render the labels.
*
* @type {boolean}
* @default false
* @apioption plotOptions.series.dataLabels.useHTML
*/
/**
* The vertical alignment of a data label. Can be one of `top`,
* `middle` or `bottom`. The default value depends on the data, for
* instance in a column chart, the label is above positive values
* and below negative values.
*
* @type {Highcharts.VerticalAlignValue|null}
* @since 2.3.3
*/
verticalAlign: 'bottom',
/**
* The x position offset of the label relative to the point in
* pixels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical and positioned
* @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
* Data labels inside the bar
*/
x: 0,
/**
* The Z index of the data labels. The default Z index puts it above
* the series. Use a Z index of 2 to display it behind the series.
*
* @type {number}
* @default 6
* @since 2.3.5
* @apioption plotOptions.series.dataLabels.z
*/
/**
* The y position offset of the label relative to the point in
* pixels.
*
* @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
* Vertical and positioned
*/
y: 0
},
/**
* When the series contains less points than the crop threshold, all
* points are drawn, even if the points fall outside the visible plot
* area at the current zoom. The advantage of drawing all points
* (including markers and columns), is that animation is performed on
* updates. On the other hand, when the series contains more points than
* the crop threshold, the series data is cropped to only contain points
* that fall within the plot area. The advantage of cropping away
* invisible points is to increase performance on large series.
*
* @since 2.2
* @product highcharts highstock
*
* @private
*/
cropThreshold: 300,
/**
* Opacity of a series parts: line, fill (e.g. area) and dataLabels.
*
* @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
*
* @since 7.1.0
*
* @private
*/
opacity: 1,
/**
* The width of each point on the x axis. For example in a column chart
* with one value each day, the pointRange would be 1 day (= 24 * 3600
* * 1000 milliseconds). This is normally computed automatically, but
* this option can be used to override the automatic value.
*
* @product highstock
*
* @private
*/
pointRange: 0,
/**
* When this is true, the series will not cause the Y axis to cross
* the zero plane (or [threshold](#plotOptions.series.threshold) option)
* unless the data actually crosses the plane.
*
* For example, if `softThreshold` is `false`, a series of 0, 1, 2,
* 3 will make the Y axis show negative values according to the
* `minPadding` option. If `softThreshold` is `true`, the Y axis starts
* at 0.
*
* @since 4.1.9
* @product highcharts highstock
*
* @private
*/
softThreshold: true,
/**
* @declare Highcharts.SeriesStatesOptionsObject
*
* @private
*/
states: {
/**
* The normal state of a series, or for point items in column, pie
* and similar series. Currently only used for setting animation
* when returning to normal state from hover.
*
* @declare Highcharts.SeriesStatesNormalOptionsObject
*/
normal: {
/**
* Animation when returning to normal state after hovering.
*
* @type {boolean|Highcharts.AnimationOptionsObject}
*/
animation: true
},
/**
* Options for the hovered series. These settings override the
* normal state options when a series is moused over or touched.
*
* @declare Highcharts.SeriesStatesHoverOptionsObject
*/
hover: {
/**
* Enable separate styles for the hovered series to visualize
* that the user hovers either the series itself or the legend.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
* Line
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
* Column
* @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
* Pie
*
* @type {boolean}
* @default true
* @since 1.2
* @apioption plotOptions.series.states.hover.enabled
*/
/**
* Animation setting for hovering the graph in line-type series.
*
* @type {boolean|Highcharts.AnimationOptionsObject}
* @since 5.0.8
* @product highcharts highstock
*/
animation: {
/**
* The duration of the hover animation in milliseconds. By
* default the hover state animates quickly in, and slowly
* back to normal.
*
* @internal
*/
duration: 50
},
/**
* Pixel width of the graph line. By default this property is
* undefined, and the `lineWidthPlus` property dictates how much
* to increase the linewidth from normal state.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
* 5px line on hover
*
* @type {number}
* @product highcharts highstock
* @apioption plotOptions.series.states.hover.lineWidth
*/
/**
* The additional line width for the graph of a hovered series.
*
* @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels wider
* @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
* 5 pixels wider
*
* @since 4.0.3
* @product highcharts highstock
*/
lineWidthPlus: 1,
/**
* In Highcharts 1.0, the appearance of all markers belonging
* to the hovered series. For settings on the hover state of the
* individual point, see
* [marker.states.hover](#plotOptions.series.marker.states.hover).
*
* @deprecated
*
* @extends plotOptions.series.marker
* @excluding states
* @product highcharts highstock
*/
marker: {
// lineWidth: base + 1,
// radius: base + 1
},
/**
* Options for the halo appearing around the hovered point in
* line-type series as well as outside the hovered slice in pie
* charts. By default the halo is filled by the current point or
* series color with an opacity of 0.25\. The halo can be
* disabled by setting the `halo` option to `null`.
*
* In styled mode, the halo is styled with the
* `.highcharts-halo` class, with colors inherited from
* `.highcharts-color-{n}`.
*
* @sample {highcharts} highcharts/plotoptions/halo/
* Halo options
* @sample {highstock} highcharts/plotoptions/halo/
* Halo options
*
* @declare Highcharts.SeriesStatesHoverHaloOptionsObject
* @type {null|*}
* @since 4.0
* @product highcharts highstock
*/
halo: {
/**
* A collection of SVG attributes to override the appearance
* of the halo, for example `fill`, `stroke` and
* `stroke-width`.
*
* @type {Highcharts.SVGAttributes}
* @since 4.0
* @product highcharts highstock
* @apioption plotOptions.series.states.hover.halo.attributes
*/
/**
* The pixel size of the halo. For point markers this is the
* radius of the halo. For pie slices it is the width of the
* halo outside the slice. For bubbles it defaults to 5 and
* is the width of the halo outside the bubble.
*
* @since 4.0
* @product highcharts highstock
*/
size: 10,
/**
* Opacity for the halo unless a specific fill is overridden
* using the `attributes` setting. Note that Highcharts is
* only able to apply opacity to colors of hex or rgb(a)
* formats.
*
* @since 4.0
* @product highcharts highstock
*/
opacity: 0.25
}
},
/**
* Specific options for point in selected states, after being
* selected by
* [allowPointSelect](#plotOptions.series.allowPointSelect)
* or programmatically.
*
* @sample maps/plotoptions/series-allowpointselect/
* Allow point select demo
*
* @declare Highcharts.SeriesStatesSelectOptionsObject
* @extends plotOptions.series.states.hover
* @excluding brightness
*/
select: {
animation: {
/** @internal */
duration: 0
}
},
/**
* The opposite state of a hover for series.
*
* @sample highcharts/plotoptions/series-states-inactive-disabled
* Disabled inactive state
*
* @declare Highcharts.SeriesStatesInactiveOptionsObject
*/
inactive: {
/**
* Enable or disable the inactive state for a series
*
* @sample highcharts/plotoptions/series-states-inactive-disabled
* Disabled inactive state
*
* @type {boolean}
* @default true
* @apioption plotOptions.series.states.inactive.enabled
*/
/**
* The animation for entering the inactive state.
*
* @type {boolean|Highcharts.AnimationOptionsObject}
*/
animation: {
/** @internal */
duration: 50
},
/**
* Opacity of series elements (dataLabels, line, area).
*
* @type {number}
*/
opacity: 0.2
}
},
/**
* Sticky tracking of mouse events. When true, the `mouseOut` event on a
* series isn't triggered until the mouse moves over another series, or
* out of the plot area. When false, the `mouseOut` event on a series is
* triggered when the mouse leaves the area around the series' graph or
* markers. This also implies the tooltip when not shared. When
* `stickyTracking` is false and `tooltip.shared` is false, the tooltip
* will be hidden when moving the mouse between series. Defaults to true
* for line and area type series, but to false for columns, pies etc.
*
* **Note:** The boost module will force this option because of
* technical limitations.
*
* @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
* True by default
* @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
* False
*
* @default {highcharts} true
* @default {highstock} true
* @default {highmaps} false
* @since 2.0
*
* @private
*/
stickyTracking: true,
/**
* A configuration object for the tooltip rendering of each single
* series. Properties are inherited from [tooltip](#tooltip), but only
* the following properties can be defined on a series level.
*
* @declare Highcharts.SeriesTooltipOptionsObject
* @since 2.3
* @extends tooltip
* @excluding animation, backgroundColor, borderColor, borderRadius,
* borderWidth, className, crosshairs, enabled, formatter,
* headerShape, hideDelay, outside, padding, positioner,
* shadow, shape, shared, snap, split, style, useHTML
* @apioption plotOptions.series.tooltip
*/
/**
* When a series contains a data array that is longer than this, only
* one dimensional arrays of numbers, or two dimensional arrays with
* x and y values are allowed. Also, only the first point is tested,
* and the rest are assumed to be the same format. This saves expensive
* data checking and indexing in long series. Set it to `0` disable.
*
* Note:
* In boost mode turbo threshold is forced. Only array of numbers or
* two dimensional arrays are allowed.
*
* @since 2.2
* @product highcharts highstock gantt
*
* @private
*/
turboThreshold: 1000,
/**
* An array defining zones within a series. Zones can be applied to the
* X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
* option. The zone definitions have to be in ascending order regarding
* to the value.
*
* In styled mode, the color zones are styled with the
* `.highcharts-zone-{n}` class, or custom classed from the `className`
* option
* ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
*
* @see [zoneAxis](#plotOptions.series.zoneAxis)
*
* @sample {highcharts} highcharts/series/color-zones-simple/
* Color zones
* @sample {highstock} highcharts/series/color-zones-simple/
* Color zones
*
* @declare Highcharts.SeriesZonesOptionsObject
* @type {Array<*>}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones
*/
/**
* Styled mode only. A custom class name for the zone.
*
* @sample highcharts/css/color-zones/
* Zones styled by class name
*
* @type {string}
* @since 5.0.0
* @apioption plotOptions.series.zones.className
*/
/**
* Defines the color of the series.
*
* @see [series color](#plotOptions.series.color)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.color
*/
/**
* A name for the dash style to use for the graph.
*
* @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
*
* @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
* Dashed line indicates prognosis
*
* @type {Highcharts.DashStyleValue}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.dashStyle
*/
/**
* Defines the fill color for the series (in area type series)
*
* @see [fillColor](#plotOptions.area.fillColor)
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.fillColor
*/
/**
* The value up to where the zone extends, if undefined the zones
* stretches to the last value in the series.
*
* @type {number}
* @since 4.1.0
* @product highcharts highstock
* @apioption plotOptions.series.zones.value
*/
/**
* When using dual or multiple color axes, this number defines which
* colorAxis the particular series is connected to. It refers to
* either the
* {@link #colorAxis.id|axis id}
* or the index of the axis in the colorAxis array, with 0 being the
* first. Set this option to false to prevent a series from connecting
* to the default color axis.
*
* Since v7.2.0 the option can also be an axis id or an axis index
* instead of a boolean flag.
*
* @sample highcharts/coloraxis/coloraxis-with-pie/
* Color axis with pie series
* @sample highcharts/coloraxis/multiple-coloraxis/
* Multiple color axis
*
* @type {number|string|boolean}
* @default 0
* @product highcharts highstock highmaps
* @apioption plotOptions.series.colorAxis
*/
/**
* Determines what data value should be used to calculate point color
* if `colorAxis` is used. Requires to set `min` and `max` if some
* custom point property is used or if approximation for data grouping
* is set to `'sum'`.
*
* @sample highcharts/coloraxis/custom-color-key/
* Custom color key
* @sample highcharts/coloraxis/changed-default-color-key/
* Changed default color key
*
* @type {string}
* @default y
* @since 7.2.0
* @product highcharts highstock highmaps
* @apioption plotOptions.series.colorKey
*/
/**
* Determines whether the series should look for the nearest point
* in both dimensions or just the x-dimension when hovering the series.
* Defaults to `'xy'` for scatter series and `'x'` for most other
* series. If the data has duplicate x-values, it is recommended to
* set this to `'xy'` to allow hovering over all points.
*
* Applies only to series types using nearest neighbor search (not
* direct hover) for tooltip.
*
* @sample {highcharts} highcharts/series/findnearestpointby/
* Different hover behaviors
* @sample {highstock} highcharts/series/findnearestpointby/
* Different hover behaviors
* @sample {highmaps} highcharts/series/findnearestpointby/
* Different hover behaviors
*
* @since 5.0.10
* @validvalue ["x", "xy"]
*
* @private
*/
findNearestPointBy: 'x'
},
/* eslint-disable no-invalid-this, valid-jsdoc */
/** @lends Highcharts.Series.prototype */
{
axisTypes: ['xAxis', 'yAxis'],
coll: 'series',
colorCounter: 0,
cropShoulder: 1,
directTouch: false,
eventsToUnbind: [],
isCartesian: true,
// each point's x and y values are stored in this.xData and this.yData
parallelArrays: ['x', 'y'],
pointClass: Point,
requireSorting: true,
sorted: true,
init: function (chart, options) {
fireEvent(this, 'init', { options: options });
var series = this, events, chartSeries = chart.series, lastSeries;
// A lookup over those events that are added by _options_ (not
// programmatically). These are updated through Series.update()
// (#10861).
this.eventOptions = this.eventOptions || {};
/**
* Read only. The chart that the series belongs to.
*
* @name Highcharts.Series#chart
* @type {Highcharts.Chart}
*/
series.chart = chart;
/**
* Read only. The series' type, like "line", "area", "column" etc.
* The type in the series options anc can be altered using
* {@link Series#update}.
*
* @name Highcharts.Series#type
* @type {string}
*/
/**
* Read only. The series' current options. To update, use
* {@link Series#update}.
*
* @name Highcharts.Series#options
* @type {Highcharts.SeriesOptionsType}
*/
series.options = options = series.setOptions(options);
series.linkedSeries = [];
// bind the axes
series.bindAxes();
// set some variables
extend(series, {
/**
* The series name as given in the options. Defaults to
* "Series {n}".
*
* @name Highcharts.Series#name
* @type {string}
*/
name: options.name,
state: '',
/**
* Read only. The series' visibility state as set by {@link
* Series#show}, {@link Series#hide}, or in the initial
* configuration.
*
* @name Highcharts.Series#visible
* @type {boolean}
*/
visible: options.visible !== false,
/**
* Read only. The series' selected state as set by {@link
* Highcharts.Series#select}.
*
* @name Highcharts.Series#selected
* @type {boolean}
*/
selected: options.selected === true // false by default
});
// Register event listeners
events = options.events;
objectEach(events, function (event, eventType) {
if (isFunction(event)) {
// If event does not exist, or is changed by Series.update
if (series.eventOptions[eventType] !== event) {
// Remove existing if set by option
if (isFunction(series.eventOptions[eventType])) {
removeEvent(series, eventType, series.eventOptions[eventType]);
}
series.eventOptions[eventType] = event;
addEvent(series, eventType, event);
}
}
});
if ((events && events.click) ||
(options.point &&
options.point.events &&
options.point.events.click) ||
options.allowPointSelect) {
chart.runTrackerClick = true;
}
series.getColor();
series.getSymbol();
// Initialize the parallel data arrays
series.parallelArrays.forEach(function (key) {
if (!series[key + 'Data']) {
series[key + 'Data'] = [];
}
});
// Mark cartesian
if (series.isCartesian) {
chart.hasCartesianSeries = true;
}
// Get the index and register the series in the chart. The index is
// one more than the current latest series index (#5960).
if (chartSeries.length) {
lastSeries = chartSeries[chartSeries.length - 1];
}
series._i = pick(lastSeries && lastSeries._i, -1) + 1;
// Insert the series and re-order all series above the insertion
// point.
chart.orderSeries(this.insert(chartSeries));
// Set options for series with sorting and set data later.
if (options.dataSorting && options.dataSorting.enabled) {
series.setDataSortingOptions();
}
else if (!series.points && !series.data) {
series.setData(options.data, false);
}
fireEvent(this, 'afterInit');
},
/**
* Check whether the series item is itself or inherits from a certain
* series type.
*
* @function Highcharts.Series#is
* @param {string} type The type of series to check for, can be either
* featured or custom series types. For example `column`, `pie`,
* `ohlc` etc.
*
* @return {boolean}
* True if this item is or inherits from the given type.
*/
is: function (type) {
return seriesTypes[type] && this instanceof seriesTypes[type];
},
/**
* Insert the series in a collection with other series, either the chart
* series or yAxis series, in the correct order according to the index
* option. Used internally when adding series.
*
* @private
* @function Highcharts.Series#insert
* @param {Array<Highcharts.Series>} collection
* A collection of series, like `chart.series` or `xAxis.series`.
* @return {number}
* The index of the series in the collection.
*/
insert: function (collection) {
var indexOption = this.options.index, i;
// Insert by index option
if (isNumber(indexOption)) {
i = collection.length;
while (i--) {
// Loop down until the interted element has higher index
if (indexOption >=
pick(collection[i].options.index, collection[i]._i)) {
collection.splice(i + 1, 0, this);
break;
}
}
if (i === -1) {
collection.unshift(this);
}
i = i + 1;
// Or just push it to the end
}
else {
collection.push(this);
}
return pick(i, collection.length - 1);
},
/**
* Set the xAxis and yAxis properties of cartesian series, and register
* the series in the `axis.series` array.
*
* @private
* @function Highcharts.Series#bindAxes
* @return {void}
* @exception 18
*/
bindAxes: function () {
var series = this, seriesOptions = series.options, chart = series.chart, axisOptions;
fireEvent(this, 'bindAxes', null, function () {
// repeat for xAxis and yAxis
(series.axisTypes || []).forEach(function (AXIS) {
// loop through the chart's axis objects
chart[AXIS].forEach(function (axis) {
axisOptions = axis.options;
// apply if the series xAxis or yAxis option mathches
// the number of the axis, or if undefined, use the
// first axis
if (seriesOptions[AXIS] ===
axisOptions.index ||
(typeof seriesOptions[AXIS] !==
'undefined' &&
seriesOptions[AXIS] === axisOptions.id) ||
(typeof seriesOptions[AXIS] ===
'undefined' &&
axisOptions.index === 0)) {
// register this series in the axis.series lookup
series.insert(axis.series);
// set this series.xAxis or series.yAxis reference
/**
* Read only. The unique xAxis object associated
* with the series.
*
* @name Highcharts.Series#xAxis
* @type {Highcharts.Axis}
*/
/**
* Read only. The unique yAxis object associated
* with the series.
*
* @name Highcharts.Series#yAxis
* @type {Highcharts.Axis}
*/
series[AXIS] = axis;
// mark dirty for redraw
axis.isDirty = true;
}
});
// The series needs an X and an Y axis
if (!series[AXIS] &&
series.optionalAxis !== AXIS) {
error(18, true, chart);
}
});
});
fireEvent(this, 'afterBindAxes');
},
/**
* For simple series types like line and column, the data values are
* held in arrays like xData and yData for quick lookup to find extremes
* and more. For multidimensional series like bubble and map, this can
* be extended with arrays like zData and valueData by adding to the
* `series.parallelArrays` array.
*
* @private
* @function Highcharts.Series#updateParallelArrays
* @param {Highcharts.Point} point
* @param {number|string} i
* @return {void}
*/
updateParallelArrays: function (point, i) {
var series = point.series, args = arguments, fn = isNumber(i) ?
// Insert the value in the given position
function (key) {
var val = key === 'y' && series.toYData ?
series.toYData(point) :
point[key];
series[key + 'Data'][i] = val;
} :
// Apply the method specified in i with the following
// arguments as arguments
function (key) {
Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
};
series.parallelArrays.forEach(fn);
},
/**
* Define hasData functions for series. These return true if there
* are data points on this series within the plot area.
*
* @private
* @function Highcharts.Series#hasData
* @return {boolean}
*/
hasData: function () {
return ((this.visible &&
typeof this.dataMax !== 'undefined' &&
typeof this.dataMin !== 'undefined') || ( // #3703
this.visible &&
this.yData &&
this.yData.length > 0) // #9758
);
},
/**
* Return an auto incremented x value based on the pointStart and
* pointInterval options. This is only used if an x value is not given
* for the point that calls autoIncrement.
*
* @private
* @function Highcharts.Series#autoIncrement
* @return {number}
*/
autoIncrement: function () {
var options = this.options, xIncrement = this.xIncrement, date, pointInterval, pointIntervalUnit = options.pointIntervalUnit, time = this.chart.time;
xIncrement = pick(xIncrement, options.pointStart, 0);
this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
// Added code for pointInterval strings
if (pointIntervalUnit) {
date = new time.Date(xIncrement);
if (pointIntervalUnit === 'day') {
time.set('Date', date, time.get('Date', date) + pointInterval);
}
else if (pointIntervalUnit === 'month') {
time.set('Month', date, time.get('Month', date) + pointInterval);
}
else if (pointIntervalUnit === 'year') {
time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
}
pointInterval = date.getTime() - xIncrement;
}
this.xIncrement = xIncrement + pointInterval;
return xIncrement;
},
/**
* Internal function to set properties for series if data sorting is
* enabled.
*
* @private
* @function Highcharts.Series#setDataSortingOptions
* @return {void}
*/
setDataSortingOptions: function () {
var options = this.options;
extend(this, {
requireSorting: false,
sorted: false,
enabledDataSorting: true,
allowDG: false
});
// To allow unsorted data for column series.
if (!defined(options.pointRange)) {
options.pointRange = 1;
}
},
/**
* Set the series options by merging from the options tree. Called
* internally on initializing and updating series. This function will
* not redraw the series. For API usage, use {@link Series#update}.
* @private
* @function Highcharts.Series#setOptions
* @param {Highcharts.SeriesOptionsType} itemOptions
* The series options.
* @return {Highcharts.SeriesOptionsType}
* @fires Highcharts.Series#event:afterSetOptions
*/
setOptions: function (itemOptions) {
var chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), options, zones, zone, styledMode = chart.styledMode, e = {
plotOptions: plotOptions,
userOptions: seriesUserOptions
};
fireEvent(this, 'setOptions', e);
// These may be modified by the event
var typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {});
// use copy to prevent undetected changes (#9762)
this.userOptions = e.userOptions;
options = merge(typeOptions, plotOptions.series,
// #3881, chart instance plotOptions[type] should trump
// plotOptions.series
userOptions.plotOptions &&
userOptions.plotOptions[this.type], seriesUserOptions);
// The tooltip options are merged between global and series specific
// options. Importance order asscendingly:
// globals: (1)tooltip, (2)plotOptions.series,
// (3)plotOptions[this.type]
// init userOptions with possible later updates: 4-6 like 1-3 and
// (7)this series options
this.tooltipOptions = merge(defaultOptions.tooltip, // 1
defaultOptions.plotOptions.series &&
defaultOptions.plotOptions.series.tooltip, // 2
defaultOptions.plotOptions[this.type].tooltip, // 3
chartOptions.tooltip.userOptions, // 4
plotOptions.series &&
plotOptions.series.tooltip, // 5
plotOptions[this.type].tooltip, // 6
seriesUserOptions.tooltip // 7
);
// When shared tooltip, stickyTracking is true by default,
// unless user says otherwise.
this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptions[this.type] &&
userPlotOptions[this.type].stickyTracking, userPlotOptions.series && userPlotOptions.series.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
true :
options.stickyTracking));
// Delete marker object if not allowed (#1125)
if (typeOptions.marker === null) {
delete options.marker;
}
// Handle color zones
this.zoneAxis = options.zoneAxis;
zones = this.zones = (options.zones || []).slice();
if ((options.negativeColor || options.negativeFillColor) &&
!options.zones) {
zone = {
value: options[this.zoneAxis + 'Threshold'] ||
options.threshold ||
0,
className: 'highcharts-negative'
};
if (!styledMode) {
zone.color = options.negativeColor;
zone.fillColor = options.negativeFillColor;
}
zones.push(zone);
}
if (zones.length) { // Push one extra zone for the rest
if (defined(zones[zones.length - 1].value)) {
zones.push(styledMode ? {} : {
color: this.color,
fillColor: this.fillColor
});
}
}
fireEvent(this, 'afterSetOptions', { options: options });
return options;
},
/**
* Return series name in "Series {Number}" format or the one defined by
* a user. This method can be simply overridden as series name format
* can vary (e.g. technical indicators).
*
* @function Highcharts.Series#getName
* @return {string}
* The series name.
*/
getName: function () {
// #4119
return pick(this.options.name, 'Series ' + (this.index + 1));
},
/**
* @private
* @function Highcharts.Series#getCyclic
* @param {string} prop
* @param {*} [value]
* @param {Highcharts.Dictionary<any>} [defaults]
* @return {void}
*/
getCyclic: function (prop, value, defaults) {
var i, chart = this.chart, userOptions = this.userOptions, indexName = prop + 'Index', counterName = prop + 'Counter', len = defaults ? defaults.length : pick(chart.options.chart[prop + 'Count'], chart[prop + 'Count']), setting;
if (!value) {
// Pick up either the colorIndex option, or the _colorIndex
// after Series.update()
setting = pick(userOptions[indexName], userOptions['_' + indexName]);
if (defined(setting)) { // after Series.update()
i = setting;
}
else {
// #6138
if (!chart.series.length) {
chart[counterName] = 0;
}
userOptions['_' + indexName] = i =
chart[counterName] % len;
chart[counterName] += 1;
}
if (defaults) {
value = defaults[i];
}
}
// Set the colorIndex
if (typeof i !== 'undefined') {
this[indexName] = i;
}
this[prop] = value;
},
/**
* Get the series' color based on either the options or pulled from
* global options.
*
* @private
* @function Highcharts.Series#getColor
* @return {void}
*/
getColor: function () {
if (this.chart.styledMode) {
this.getCyclic('color');
}
else if (this.options.colorByPoint) {
// #4359, selected slice got series.color even when colorByPoint
// was set.
this.options.color = null;
}
else {
this.getCyclic('color', this.options.color ||
defaultPlotOptions[this.type].color, this.chart.options.colors);
}
},
/**
* Get all points' instances created for this series.
*
* @private
* @function Highcharts.Series#getPointsCollection
* @return {Array<Highcharts.Point>}
*/
getPointsCollection: function () {
return (this.hasGroupedData ? this.points : this.data) || [];
},
/**
* Get the series' symbol based on either the options or pulled from
* global options.
*
* @private
* @function Highcharts.Series#getSymbol
* @return {void}
*/
getSymbol: function () {
var seriesMarkerOption = this.options.marker;
this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
},
/**
* Finds the index of an existing point that matches the given point
* options.
*
* @private
* @function Highcharts.Series#findPointIndex
* @param {Highcharts.PointOptionsObject} optionsObject
* The options of the point.
* @param {number} fromIndex
* The index to start searching from, used for optimizing
* series with required sorting.
* @returns {number|undefined}
* Returns the index of a matching point, or undefined if no
* match is found.
*/
findPointIndex: function (optionsObject, fromIndex) {
var id = optionsObject.id, x = optionsObject.x, oldData = this.points, matchingPoint, matchedById, pointIndex, matchKey, dataSorting = this.options.dataSorting;
if (id) {
matchingPoint = this.chart.get(id);
}
else if (this.linkedParent || this.enabledDataSorting) {
matchKey = (dataSorting && dataSorting.matchByName) ?
'name' : 'index';
matchingPoint = find(oldData, function (oldPoint) {
return !oldPoint.touched && oldPoint[matchKey] ===
optionsObject[matchKey];
});
// Add unmatched point as a new point
if (!matchingPoint) {
return void 0;
}
}
if (matchingPoint) {
pointIndex = matchingPoint && matchingPoint.index;
if (typeof pointIndex !== 'undefined') {
matchedById = true;
}
}
// Search for the same X in the existing data set
if (typeof pointIndex === 'undefined' && isNumber(x)) {
pointIndex = this.xData.indexOf(x, fromIndex);
}
// Reduce pointIndex if data is cropped
if (pointIndex !== -1 &&
typeof pointIndex !== 'undefined' &&
this.cropped) {
pointIndex = (pointIndex >= this.cropStart) ?
pointIndex - this.cropStart : pointIndex;
}
if (!matchedById &&
oldData[pointIndex] && oldData[pointIndex].touched) {
pointIndex = void 0;
}
return pointIndex;
},
/**
* @private
* @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
*/
drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
/**
* Internal function called from setData. If the point count is the same
* as is was, or if there are overlapping X values, just run
* Point.update which is cheaper, allows animation, and keeps references
* to points. This also allows adding or removing points if the X-es
* don't match.
*
* @private
* @function Highcharts.Series#updateData
*
* @param {Array<Highcharts.PointOptionsType>} data
*
* @return {boolean}
*/
updateData: function (data, animation) {
var options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], hasUpdatedByKey, i, point, lastIndex, requireSorting = this.requireSorting, equalLength = data.length === oldData.length, succeeded = true;
this.xIncrement = null;
// Iterate the new data
data.forEach(function (pointOptions, i) {
var id, x, pointIndex, optionsObject = (defined(pointOptions) &&
this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
// Get the x of the new data point
x = optionsObject.x;
id = optionsObject.id;
if (id || isNumber(x)) {
pointIndex = this.findPointIndex(optionsObject, lastIndex);
// Matching X not found
// or used already due to ununique x values (#8995),
// add point (but later)
if (pointIndex === -1 ||
typeof pointIndex === 'undefined') {
pointsToAdd.push(pointOptions);
// Matching X found, update
}
else if (oldData[pointIndex] &&
pointOptions !== options.data[pointIndex]) {
oldData[pointIndex].update(pointOptions, false, null, false);
// Mark it touched, below we will remove all points that
// are not touched.
oldData[pointIndex].touched = true;
// Speed optimize by only searching after last known
// index. Performs ~20% bettor on large data sets.
if (requireSorting) {
lastIndex = pointIndex + 1;
}
// Point exists, no changes, don't remove it
}
else if (oldData[pointIndex]) {
oldData[pointIndex].touched = true;
}
// If the length is equal and some of the nodes had a
// match in the same position, we don't want to remove
// non-matches.
if (!equalLength ||
i !== pointIndex ||
(dataSorting && dataSorting.enabled) ||
this.hasDerivedData) {
hasUpdatedByKey = true;
}
}
else {
// Gather all points that are not matched
pointsToAdd.push(pointOptions);
}
}, this);
// Remove points that don't exist in the updated data set
if (hasUpdatedByKey) {
i = oldData.length;
while (i--) {
point = oldData[i];
if (point && !point.touched && point.remove) {
point.remove(false, animation);
}
}
// If we did not find keys (ids or x-values), and the length is the
// same, update one-to-one
}
else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
data.forEach(function (point, i) {
// .update doesn't exist on a linked, hidden series (#3709)
// (#10187)
if (oldData[i].update && point !== oldData[i].y) {
oldData[i].update(point, false, null, false);
}
});
// Don't add new points since those configs are used above
pointsToAdd.length = 0;
// Did not succeed in updating data
}
else {
succeeded = false;
}
oldData.forEach(function (point) {
if (point) {
point.touched = false;
}
});
if (!succeeded) {
return false;
}
// Add new points
pointsToAdd.forEach(function (point) {
this.addPoint(point, false, null, null, false);
}, this);
if (this.xIncrement === null &&
this.xData &&
this.xData.length) {
this.xIncrement = arrayMax(this.xData);
this.autoIncrement();
}
return true;
},
/**
* Apply a new set of data to the series and optionally redraw it. The
* new data array is passed by reference (except in case of
* `updatePoints`), and may later be mutated when updating the chart
* data.
*
* Note the difference in behaviour when setting the same amount of
* points, or a different amount of points, as handled by the
* `updatePoints` parameter.
*
* @sample highcharts/members/series-setdata/
* Set new data from a button
* @sample highcharts/members/series-setdata-pie/
* Set data in a pie
* @sample stock/members/series-setdata/
* Set new data in Highstock
* @sample maps/members/series-setdata/
* Set new data in Highmaps
*
* @function Highcharts.Series#setData
*
* @param {Array<Highcharts.PointOptionsType>} data
* Takes an array of data in the same format as described under
* `series.{type}.data` for the given series type, for example a
* line series would take data in the form described under
* [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
*
* @param {boolean} [redraw=true]
* Whether to redraw the chart after the series is altered. If
* doing more operations on the chart, it is a good idea to set
* redraw to false and call {@link Chart#redraw} after.
*
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
* When the updated data is the same length as the existing data,
* points will be updated by default, and animation visualizes
* how the points are changed. Set false to disable animation, or
* a configuration object to set duration or easing.
*
* @param {boolean} [updatePoints=true]
* When this is true, points will be updated instead of replaced
* whenever possible. This occurs a) when the updated data is the
* same length as the existing data, b) when points are matched
* by their id's, or c) when points can be matched by X values.
* This allows updating with animation and performs better. In
* this case, the original array is not passed by reference. Set
* `false` to prevent.
*
* @return {void}
*/
setData: function (data, redraw, animation, updatePoints) {
var series = this, oldData = series.points, oldDataLength = (oldData && oldData.length) || 0, dataLength, options = series.options, chart = series.chart, dataSorting = options.dataSorting, firstPoint = null, xAxis = series.xAxis, i, turboThreshold = options.turboThreshold, pt, xData = this.xData, yData = this.yData, pointArrayMap = series.pointArrayMap, valueCount = pointArrayMap && pointArrayMap.length, keys = options.keys, indexOfX = 0, indexOfY = 1, updatedData;
data = data || [];
dataLength = data.length;
redraw = pick(redraw, true);
if (dataSorting && dataSorting.enabled) {
data = this.sortData(data);
}
// First try to run Point.update which is cheaper, allows animation,
// and keeps references to points.
if (updatePoints !== false &&
dataLength &&
oldDataLength &&
!series.cropped &&
!series.hasGroupedData &&
series.visible &&
// Soft updating has no benefit in boost, and causes JS error
// (#8355)
!series.isSeriesBoosting) {
updatedData = this.updateData(data, animation);
}
if (!updatedData) {
// Reset properties
series.xIncrement = null;
series.colorCounter = 0; // for series with colorByPoint (#1547)
// Update parallel arrays
this.parallelArrays.forEach(function (key) {
series[key + 'Data'].length = 0;
});
// In turbo mode, only one- or twodimensional arrays of numbers
// are allowed. The first value is tested, and we assume that
// all the rest are defined the same way. Although the 'for'
// loops are similar, they are repeated inside each if-else
// conditional for max performance.
if (turboThreshold && dataLength > turboThreshold) {
firstPoint = series.getFirstValidPoint(data);
if (isNumber(firstPoint)) { // assume all points are numbers
for (i = 0; i < dataLength; i++) {
xData[i] = this.autoIncrement();
yData[i] = data[i];
}
// Assume all points are arrays when first point is
}
else if (isArray(firstPoint)) {
if (valueCount) { // [x, low, high] or [x, o, h, l, c]
for (i = 0; i < dataLength; i++) {
pt = data[i];
xData[i] = pt[0];
yData[i] =
pt.slice(1, valueCount + 1);
}
}
else { // [x, y]
if (keys) {
indexOfX = keys.indexOf('x');
indexOfY = keys.indexOf('y');
indexOfX = indexOfX >= 0 ? indexOfX : 0;
indexOfY = indexOfY >= 0 ? indexOfY : 1;
}
for (i = 0; i < dataLength; i++) {
pt = data[i];
xData[i] = pt[indexOfX];
yData[i] = pt[indexOfY];
}
}
}
else {
// Highcharts expects configs to be numbers or arrays in
// turbo mode
error(12, false, chart);
}
}
else {
for (i = 0; i < dataLength; i++) {
// stray commas in oldIE:
if (typeof data[i] !== 'undefined') {
pt = { series: series };
series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
series.updateParallelArrays(pt, i);
}
}
}
// Forgetting to cast strings to numbers is a common caveat when
// handling CSV or JSON
if (yData && isString(yData[0])) {
error(14, true, chart);
}
series.data = [];
series.options.data = series.userOptions.data = data;
// destroy old points
i = oldDataLength;
while (i--) {
if (oldData[i] && oldData[i].destroy) {
oldData[i].destroy();
}
}
// reset minRange (#878)
if (xAxis) {
xAxis.minRange = xAxis.userMinRange;
}
// redraw
series.isDirty = chart.isDirtyBox = true;
series.isDirtyData = !!oldData;
animation = false;
}
// Typically for pie series, points need to be processed and
// generated prior to rendering the legend
if (options.legendType === 'point') {
this.processData();
this.generatePoints();
}
if (redraw) {
chart.redraw(animation);
}
},
/**
* Internal function to sort series data
*
* @private
* @function Highcharts.Series#sortData
* @param {Array<Highcharts.PointOptionsType>} data
* Force data grouping.
* @return {Array<Highcharts.PointOptionsObject>}
*/
sortData: function (data) {
var series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', sortedData, getPointOptionsObject = function (series, pointOptions) {
return (defined(pointOptions) &&
series.pointClass.prototype.optionsToObject.call({
series: series
}, pointOptions)) || {};
};
data.forEach(function (pointOptions, i) {
data[i] = getPointOptionsObject(series, pointOptions);
data[i].index = i;
}, this);
// Sorting
sortedData = data.concat().sort(function (a, b) {
var aValue = getNestedProperty(sortKey, a);
var bValue = getNestedProperty(sortKey, b);
return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
});
// Set x value depending on the position in the array
sortedData.forEach(function (point, i) {
point.x = i;
}, this);
// Set the same x for linked series points if they don't have their
// own sorting
if (series.linkedSeries) {
series.linkedSeries.forEach(function (linkedSeries) {
var options = linkedSeries.options, seriesData = options.data;
if ((!options.dataSorting ||
!options.dataSorting.enabled) &&
seriesData) {
seriesData.forEach(function (pointOptions, i) {
seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
if (data[i]) {
seriesData[i].x = data[i].x;
seriesData[i].index = i;
}
});
linkedSeries.setData(seriesData, false);
}
});
}
return data;
},
/**
* Internal function to process the data by cropping away unused data
* points if the series is longer than the crop threshold. This saves
* computing time for large series.
*
* @private
* @function Highcharts.Series#getProcessedData
* @param {boolean} [forceExtremesFromAll]
* Force getting extremes of a total series data range.
* @return {Highcharts.SeriesProcessedDataObject}
*/
getProcessedData: function (forceExtremesFromAll) {
var series = this,
// copied during slice operation:
processedXData = series.xData, processedYData = series.yData, dataLength = processedXData.length, croppedData, cropStart = 0, cropped, distance, closestPointRange, xAxis = series.xAxis, i, // loop variable
options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
series.getExtremesFromAll ||
options.getExtremesFromAll, // #4599
isCartesian = series.isCartesian, xExtremes, val2lin = xAxis && xAxis.val2lin, isLog = !!(xAxis && xAxis.logarithmic), throwOnUnsorted = series.requireSorting, min, max;
if (xAxis) {
// corrected for log axis (#3053)
xExtremes = xAxis.getExtremes();
min = xExtremes.min;
max = xExtremes.max;
}
// optionally filter out points outside the plot area
if (isCartesian &&
series.sorted &&
!getExtremesFromAll &&
(!cropThreshold ||
dataLength > cropThreshold ||
series.forceCrop)) {
// it's outside current extremes
if (processedXData[dataLength - 1] < min ||
processedXData[0] > max) {
processedXData = [];
processedYData = [];
// only crop if it's actually spilling out
}
else if (series.yData && (processedXData[0] < min ||
processedXData[dataLength - 1] > max)) {
croppedData = this.cropData(series.xData, series.yData, min, max);
processedXData = croppedData.xData;
processedYData = croppedData.yData;
cropStart = croppedData.start;
cropped = true;
}
}
// Find the closest distance between processed points
i = processedXData.length || 1;
while (--i) {
distance = (isLog ?
(val2lin(processedXData[i]) -
val2lin(processedXData[i - 1])) :
(processedXData[i] -
processedXData[i - 1]));
if (distance > 0 &&
(typeof closestPointRange === 'undefined' ||
distance < closestPointRange)) {
closestPointRange = distance;
// Unsorted data is not supported by the line tooltip, as well
// as data grouping and navigation in Stock charts (#725) and
// width calculation of columns (#1900)
}
else if (distance < 0 && throwOnUnsorted) {
error(15, false, series.chart);
throwOnUnsorted = false; // Only once
}
}
return {
xData: processedXData,
yData: processedYData,
cropped: cropped,
cropStart: cropStart,
closestPointRange: closestPointRange
};
},
/**
* Internal function to apply processed data.
* In Highstock, this function is extended to provide data grouping.
*
* @private
* @function Highcharts.Series#processData
* @param {boolean} [force]
* Force data grouping.
* @return {boolean|undefined}
*/
processData: function (force) {
var series = this, xAxis = series.xAxis, processedData;
// If the series data or axes haven't changed, don't go through
// this. Return false to pass the message on to override methods
// like in data grouping.
if (series.isCartesian &&
!series.isDirty &&
!xAxis.isDirty &&
!series.yAxis.isDirty &&
!force) {
return false;
}
processedData = series.getProcessedData();
// Record the properties
series.cropped = processedData.cropped; // undefined or true
series.cropStart = processedData.cropStart;
series.processedXData = processedData.xData;
series.processedYData = processedData.yData;
series.closestPointRange =
series.basePointRange = processedData.closestPointRange;
},
/**
* Iterate over xData and crop values between min and max. Returns
* object containing crop start/end cropped xData with corresponding
* part of yData, dataMin and dataMax within the cropped range.
*
* @private
* @function Highcharts.Series#cropData
* @param {Array<number>} xData
* @param {Array<number>} yData
* @param {number} min
* @param {number} max
* @param {number} [cropShoulder]
* @return {Highcharts.SeriesCropDataObject}
*/
cropData: function (xData, yData, min, max, cropShoulder) {
var dataLength = xData.length, cropStart = 0, cropEnd = dataLength, i, j;
// line-type series need one point outside
cropShoulder = pick(cropShoulder, this.cropShoulder);
// iterate up to find slice start
for (i = 0; i < dataLength; i++) {
if (xData[i] >= min) {
cropStart = Math.max(0, i - cropShoulder);
break;
}
}
// proceed to find slice end
for (j = i; j < dataLength; j++) {
if (xData[j] > max) {
cropEnd = j + cropShoulder;
break;
}
}
return {
xData: xData.slice(cropStart, cropEnd),
yData: yData.slice(cropStart, cropEnd),
start: cropStart,
end: cropEnd
};
},
/**
* Generate the data point after the data has been processed by cropping
* away unused points and optionally grouped in Highcharts Stock.
*
* @private
* @function Highcharts.Series#generatePoints
* @return {void}
*/
generatePoints: function () {
var series = this, options = series.options, dataOptions = options.data, data = series.data, dataLength, processedXData = series.processedXData, processedYData = series.processedYData, PointClass = series.pointClass, processedDataLength = processedXData.length, cropStart = series.cropStart || 0, cursor, hasGroupedData = series.hasGroupedData, keys = options.keys, point, points = [], i;
if (!data && !hasGroupedData) {
var arr = [];
arr.length = dataOptions.length;
data = series.data = arr;
}
if (keys && hasGroupedData) {
// grouped data has already applied keys (#6590)
series.options.keys = false;
}
for (i = 0; i < processedDataLength; i++) {
cursor = cropStart + i;
if (!hasGroupedData) {
point = data[cursor];
// #970:
if (!point &&
typeof dataOptions[cursor] !== 'undefined') {
data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
}
}
else {
// splat the y data in case of ohlc data array
point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
/**
* Highstock only. If a point object is created by data
* grouping, it doesn't reflect actual points in the raw
* data. In this case, the `dataGroup` property holds
* information that points back to the raw data.
*
* - `dataGroup.start` is the index of the first raw data
* point in the group.
*
* - `dataGroup.length` is the amount of points in the
* group.
*
* @product highstock
*
* @name Highcharts.Point#dataGroup
* @type {Highcharts.DataGroupingInfoObject|undefined}
*/
point.dataGroup = series.groupMap[i];
if (point.dataGroup.options) {
point.options = point.dataGroup.options;
extend(point, point.dataGroup.options);
// Collision of props and options (#9770)
delete point.dataLabels;
}
}
if (point) { // #6279
/**
* Contains the point's index in the `Series.points` array.
*
* @name Highcharts.Point#index
* @type {number}
* @readonly
*/
point.index = cursor; // For faster access in Point.update
points[i] = point;
}
}
// restore keys options (#6590)
series.options.keys = keys;
// Hide cropped-away points - this only runs when the number of
// points is above cropThreshold, or when swithching view from
// non-grouped data to grouped data (#637)
if (data &&
(processedDataLength !== (dataLength = data.length) ||
hasGroupedData)) {
for (i = 0; i < dataLength; i++) {
// when has grouped data, clear all points
if (i === cropStart && !hasGroupedData) {
i += processedDataLength;
}
if (data[i]) {
data[i].destroyElements();
data[i].plotX = void 0; // #1003
}
}
}
/**
* Read only. An array containing those values converted to points.
* In case the series data length exceeds the `cropThreshold`, or if
* the data is grouped, `series.data` doesn't contain all the
* points. Also, in case a series is hidden, the `data` array may be
* empty. To access raw values, `series.options.data` will always be
* up to date. `Series.data` only contains the points that have been
* created on demand. To modify the data, use
* {@link Highcharts.Series#setData} or
* {@link Highcharts.Point#update}.
*
* @see Series.points
*
* @name Highcharts.Series#data
* @type {Array<Highcharts.Point>}
*/
series.data = data;
/**
* An array containing all currently visible point objects. In case
* of cropping, the cropped-away points are not part of this array.
* The `series.points` array starts at `series.cropStart` compared
* to `series.data` and `series.options.data`. If however the series
* data is grouped, these can't be correlated one to one. To modify
* the data, use {@link Highcharts.Series#setData} or
* {@link Highcharts.Point#update}.
*
* @name Highcharts.Series#points
* @type {Array<Highcharts.Point>}
*/
series.points = points;
fireEvent(this, 'afterGeneratePoints');
},
/**
* Get current X extremes for the visible data.
*
* @private
* @function Highcharts.Series#getXExtremes
*
* @param {Array<number>} xData
* The data to inspect. Defaults to the current data within the
* visible range.
* @return {Highcharts.RangeObject}
*/
getXExtremes: function (xData) {
return {
min: arrayMin(xData),
max: arrayMax(xData)
};
},
/**
* Calculate Y extremes for the visible data. The result is returned
* as an object with `dataMin` and `dataMax` properties.
*
* @private
* @function Highcharts.Series#getExtremes
* @param {Array<number>} [yData]
* The data to inspect. Defaults to the current data within the
* visible range.
* @param {boolean} [forceExtremesFromAll]
* Force getting extremes of a total series data range.
* @return {Highcharts.DataExtremesObject}
*/
getExtremes: function (yData, forceExtremesFromAll) {
var xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, yDataLength, activeYData = [], activeCounter = 0,
// #2117, need to compensate for log X axis
xExtremes, xMin = 0, xMax = 0, validValue, withinRange,
// Handle X outside the viewed area. This does not work with
// non-sorted data like scatter (#7639).
shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false, x, y, i, j;
yData = yData || this.stackedYData || this.processedYData || [];
yDataLength = yData.length;
if (xAxis) {
xExtremes = xAxis.getExtremes();
xMin = xExtremes.min;
xMax = xExtremes.max;
}
for (i = 0; i < yDataLength; i++) {
x = xData[i];
y = yData[i];
// For points within the visible range, including the first
// point outside the visible range (#7061), consider y extremes.
validValue = ((isNumber(y) || isArray(y)) &&
((y.length || y > 0) || !positiveValuesOnly));
withinRange = (forceExtremesFromAll ||
this.getExtremesFromAll ||
this.options.getExtremesFromAll ||
this.cropped ||
!xAxis || // for colorAxis support
((xData[i + shoulder] || x) >= xMin &&
(xData[i - shoulder] || x) <= xMax));
if (validValue && withinRange) {
j = y.length;
if (j) { // array, like ohlc or range data
while (j--) {
if (isNumber(y[j])) { // #7380, #11513
activeYData[activeCounter++] = y[j];
}
}
}
else {
activeYData[activeCounter++] = y;
}
}
}
var dataExtremes = {
dataMin: arrayMin(activeYData),
dataMax: arrayMax(activeYData)
};
fireEvent(this, 'afterGetExtremes', { dataExtremes: dataExtremes });
return dataExtremes;
},
/**
* Set the current data extremes as `dataMin` and `dataMax` on the
* Series item. Use this only when the series properties should be
* updated.
*
* @private
* @function Highcharts.Series#applyExtremes
* @return {void}
*/
applyExtremes: function () {
var dataExtremes = this.getExtremes();
/**
* Contains the minimum value of the series' data point.
* @name Highcharts.Series#dataMin
* @type {number}
* @readonly
*/
this.dataMin = dataExtremes.dataMin;
/* *
* Contains the maximum value of the series' data point.
* @name Highcharts.Series#dataMax
* @type {number}
* @readonly
*/
this.dataMax = dataExtremes.dataMax;
return dataExtremes;
},
/**
* Find and return the first non null point in the data
*
* @private
* @function Highcharts.Series.getFirstValidPoint
* @param {Array<Highcharts.PointOptionsType>} data
* Array of options for points
*
* @return {Highcharts.PointOptionsType}
*/
getFirstValidPoint: function (data) {
var firstPoint = null, dataLength = data.length, i = 0;
while (firstPoint === null && i < dataLength) {
firstPoint = data[i];
i++;
}
return firstPoint;
},
/**
* Translate data points from raw data values to chart specific
* positioning data needed later in the `drawPoints` and `drawGraph`
* functions. This function can be overridden in plugins and custom
* series type implementations.
*
* @function Highcharts.Series#translate
* @return {void}
* @fires Highcharts.Series#events:translate
*/
translate: function () {
if (!this.processedXData) { // hidden series
this.processData();
}
this.generatePoints();
var series = this, options = series.options, stacking = options.stacking, xAxis = series.xAxis, categories = xAxis.categories, enabledDataSorting = series.enabledDataSorting, yAxis = series.yAxis, points = series.points, dataLength = points.length, hasModifyValue = !!series.modifyValue, i, pointPlacement = series.pointPlacementToXValue(), // #7860
dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0, plotX, lastPlotX, stackIndicator, zoneAxis = this.zoneAxis || 'y', closestPointRangePx = Number.MAX_VALUE;
/**
* Plotted coordinates need to be within a limited range. Drawing
* too far outside the viewport causes various rendering issues
* (#3201, #3923, #7555).
* @private
*/
function limitedRange(val) {
return clamp(val, -1e5, 1e5);
}
// Translate each point
for (i = 0; i < dataLength; i++) {
var point = points[i], xValue = point.x, yValue = point.y, yBottom = point.low, stack = stacking && yAxis.stacking && yAxis.stacking.stacks[(series.negStacks &&
yValue <
(stackThreshold ? 0 : threshold) ?
'-' :
'') + series.stackKey], pointStack, stackValues;
// Discard disallowed y values for log axes (#3434)
if (yAxis.positiveValuesOnly &&
yValue !== null &&
yValue <= 0) {
point.isNull = true;
}
// Get the plotX translation
point.plotX = plotX = correctFloat(// #5236
limitedRange(xAxis.translate(// #3923
xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')) // #3923
);
// Calculate the bottom y value for stacked series
if (stacking &&
series.visible &&
stack &&
stack[xValue]) {
stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
if (!point.isNull) {
pointStack = stack[xValue];
stackValues =
pointStack.points[stackIndicator.key];
}
}
if (isArray(stackValues)) {
yBottom = stackValues[0];
yValue = stackValues[1];
if (yBottom === stackThreshold &&
stackIndicator.key ===
stack[xValue].base) {
yBottom = pick((isNumber(threshold) && threshold), yAxis.min);
}
// #1200, #1232
if (yAxis.positiveValuesOnly && yBottom <= 0) {
yBottom = null;
}
point.total = point.stackTotal = pointStack.total;
point.percentage =
pointStack.total &&
(point.y / pointStack.total * 100);
point.stackY = yValue;
// Place the stack label
// in case of variwide series (where widths of points are
// different in most cases), stack labels are positioned
// wrongly, so the call of the setOffset is omited here and
// labels are correctly positioned later, at the end of the
// variwide's translate function (#10962)
if (!series.irregularWidths) {
pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
}
}
// Set translated yBottom or remove it
point.yBottom = defined(yBottom) ?
limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
null;
// general hook, used for Highstock compare mode
if (hasModifyValue) {
yValue = series.modifyValue(yValue, point);
}
// Set the the plotY value, reset it for redraws
// #3201
point.plotY = ((typeof yValue === 'number' && yValue !== Infinity) ?
limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
void 0);
point.isInside = this.isPointInside(point);
// Set client related positions for mouse tracking
point.clientX = dynamicallyPlaced ?
correctFloat(xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)) :
plotX; // #1514, #5383, #5518
// Negative points. For bubble charts, this means negative z
// values (#9728)
point.negative = point[zoneAxis] < (options[zoneAxis + 'Threshold'] ||
threshold ||
0);
// some API data
point.category = (categories &&
typeof categories[point.x] !== 'undefined' ?
categories[point.x] :
point.x);
// Determine auto enabling of markers (#3635, #5099)
if (!point.isNull && point.visible !== false) {
if (typeof lastPlotX !== 'undefined') {
closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
}
lastPlotX = plotX;
}
// Find point zone
point.zone = (this.zones.length && point.getZone());
// Animate new points with data sorting
if (!point.graphic && series.group && enabledDataSorting) {
point.isNew = true;
}
}
series.closestPointRangePx = closestPointRangePx;
fireEvent(this, 'afterTranslate');
},
/**
* Return the series points with null points filtered out.
*
* @function Highcharts.Series#getValidPoints
*
* @param {Array<Highcharts.Point>} [points]
* The points to inspect, defaults to {@link Series.points}.
*
* @param {boolean} [insideOnly=false]
* Whether to inspect only the points that are inside the visible
* view.
*
* @param {boolean} [allowNull=false]
* Whether to allow null points to pass as valid points.
*
* @return {Array<Highcharts.Point>}
* The valid points.
*/
getValidPoints: function (points, insideOnly, allowNull) {
var chart = this.chart;
// #3916, #5029, #5085
return (points || this.points || []).filter(function isValidPoint(point) {
if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) {
return false;
}
return point.visible !== false &&
(allowNull || !point.isNull);
});
},
/**
* Get the clipping for the series. Could be called for a series to
* initiate animating the clip or to set the final clip (only width
* and x).
*
* @private
* @function Highcharts.Series#getClip
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
* Initialize the animation.
* @param {boolean} [finalBox]
* Final size for the clip - end state for the animation.
* @return {Highcharts.Dictionary<number>}
*/
getClipBox: function (animation, finalBox) {
var series = this, options = series.options, chart = series.chart, inverted = chart.inverted, xAxis = series.xAxis, yAxis = xAxis && series.yAxis, clipBox;
if (animation && options.clip === false && yAxis) {
// support for not clipped series animation (#10450)
clipBox = inverted ? {
y: -chart.chartWidth + yAxis.len + yAxis.pos,
height: chart.chartWidth,
width: chart.chartHeight,
x: -chart.chartHeight + xAxis.len + xAxis.pos
} : {
y: -yAxis.pos,
height: chart.chartHeight,
width: chart.chartWidth,
x: -xAxis.pos
};
// x and width will be changed later when setting for animation
// initial state in Series.setClip
}
else {
clipBox = series.clipBox || chart.clipBox;
if (finalBox) {
clipBox.width = chart.plotSizeX;
clipBox.x = 0;
}
}
return !finalBox ? clipBox : {
width: clipBox.width,
x: clipBox.x
};
},
/**
* Set the clipping for the series. For animated series it is called
* twice, first to initiate animating the clip then the second time
* without the animation to set the final clip.
*
* @private
* @function Highcharts.Series#setClip
* @param {boolean|Highcharts.AnimationOptionsObject} [animation]
* @return {void}
*/
setClip: function (animation) {
var chart = this.chart, options = this.options, renderer = chart.renderer, inverted = chart.inverted, seriesClipBox = this.clipBox, clipBox = this.getClipBox(animation), sharedClipKey = this.sharedClipKey ||
[
'_sharedClip',
animation && animation.duration,
animation && animation.easing,
clipBox.height,
options.xAxis,
options.yAxis
].join(','), // #4526
clipRect = chart[sharedClipKey], markerClipRect = chart[sharedClipKey + 'm'];
if (animation) {
clipBox.width = 0;
if (inverted) {
clipBox.x = chart.plotHeight +
(options.clip !== false ? 0 : chart.plotTop);
}
}
// If a clipping rectangle with the same properties is currently
// present in the chart, use that.
if (!clipRect) {
// When animation is set, prepare the initial positions
if (animation) {
chart[sharedClipKey + 'm'] = markerClipRect =
renderer.clipRect(
// include the width of the first marker
inverted ? chart.plotSizeX + 99 : -99, inverted ? -chart.plotLeft : -chart.plotTop, 99, inverted ? chart.chartWidth : chart.chartHeight);
}
chart[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
// Create hashmap for series indexes
clipRect.count = { length: 0 };
// When the series is rendered again before starting animating, in
// compliance to a responsive rule
}
else if (!chart.hasLoaded) {
clipRect.attr(clipBox);
}
if (animation) {
if (!clipRect.count[this.index]) {
clipRect.count[this.index] = true;
clipRect.count.length += 1;
}
}
if (options.clip !== false || animation) {
this.group.clip(animation || seriesClipBox ? clipRect : chart.clipRect);
this.markerGroup.clip(markerClipRect);
this.sharedClipKey = sharedClipKey;
}
// Remove the shared clipping rectangle when all series are shown
if (!animation) {
if (clipRect.count[this.index]) {
delete clipRect.count[this.index];
clipRect.count.length -= 1;
}
if (clipRect.count.length === 0 &&
sharedClipKey &&
chart[sharedClipKey]) {
if (!seriesClipBox) {
chart[sharedClipKey] =
chart[sharedClipKey].destroy();
}
if (chart[sharedClipKey + 'm']) {
chart[sharedClipKey + 'm'] =
chart[sharedClipKey + 'm'].destroy();
}
}
}
},
/**
* Animate in the series. Called internally twice. First with the `init`
* parameter set to true, which sets up the initial state of the
* animation. Then when ready, it is called with the `init` parameter
* undefined, in order to perform the actual animation. After the
* second run, the function is removed.
*
* @function Highcharts.Series#animate
*
* @param {boolean} [init]
* Initialize the animation.
*
* @return {void}
*/
animate: function (init) {
var series = this, chart = series.chart, animation = animObject(series.options.animation), clipRect, sharedClipKey, finalBox;
// Initialize the animation. Set up the clipping rectangle.
if (!chart.hasRendered) {
if (init) {
series.setClip(animation);
// Run the animation
}
else {
sharedClipKey = this.sharedClipKey;
clipRect = chart[sharedClipKey];
finalBox = series.getClipBox(animation, true);
if (clipRect) {
clipRect.animate(finalBox, animation);
}
if (chart[sharedClipKey + 'm']) {
chart[sharedClipKey + 'm'].animate({
width: finalBox.width + 99,
x: finalBox.x - (chart.inverted ? 0 : 99)
}, animation);
}
}
}
},
/**
* This runs after animation to land on the final plot clipping.
*
* @private
* @function Highcharts.Series#afterAnimate
* @return {void}
* @fires Highcharts.Series#event:afterAnimate
*/
afterAnimate: function () {
this.setClip();
fireEvent(this, 'afterAnimate');
this.finishedAnimating = true;
},
/**
* Draw the markers for line-like series types, and columns or other
* graphical representation for {@link Point} objects for other series
* types. The resulting element is typically stored as
* {@link Point.graphic}, and is created on the first call and updated
* and moved on subsequent calls.
*
* @function Highcharts.Series#drawPoints
*/
drawPoints: function () {
var series = this, points = series.points, chart = series.chart, i, point, graphic, verb, options = series.options, seriesMarkerOptions = options.marker, pointMarkerOptions, hasPointMarker, markerGroup = (series[series.specialGroup] ||
series.markerGroup), xAxis = series.xAxis, markerAttribs, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
// Use larger or equal as radius is null in bubbles (#6321)
series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
seriesMarkerOptions.radius));
if (seriesMarkerOptions.enabled !== false ||
series._hasPointMarkers) {
for (i = 0; i < points.length; i++) {
point = points[i];
graphic = point.graphic;
verb = graphic ? 'animate' : 'attr';
pointMarkerOptions = point.marker || {};
hasPointMarker = !!point.marker;
var shouldDrawMarker = ((globallyEnabled &&
typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
// only draw the point if y is defined
if (shouldDrawMarker) {
// Shortcuts
var symbol = pick(pointMarkerOptions.symbol, series.symbol);
markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
// Set starting position for point sliding animation.
if (series.enabledDataSorting) {
point.startXPos = xAxis.reversed ?
-markerAttribs.width :
xAxis.width;
}
var isInside = point.isInside !== false;
if (graphic) { // update
// Since the marker group isn't clipped, each
// individual marker must be toggled
graphic[isInside ? 'show' : 'hide'](isInside)
.animate(markerAttribs);
}
else if (isInside &&
(markerAttribs.width > 0 || point.hasImage)) {
/**
* The graphic representation of the point.
* Typically this is a simple shape, like a `rect`
* for column charts or `path` for line markers, but
* for some complex series types like boxplot or 3D
* charts, the graphic may be a `g` element
* containing other shapes. The graphic is generated
* the first time {@link Series#drawPoints} runs,
* and updated and moved on subsequent runs.
*
* @name Point#graphic
* @type {SVGElement}
*/
point.graphic = graphic = chart.renderer
.symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
pointMarkerOptions :
seriesMarkerOptions)
.add(markerGroup);
// Sliding animation for new points
if (series.enabledDataSorting &&
chart.hasRendered) {
graphic.attr({
x: point.startXPos
});
verb = 'animate';
}
}
if (graphic && verb === 'animate') { // update
// Since the marker group isn't clipped, each
// individual marker must be toggled
graphic[isInside ? 'show' : 'hide'](isInside)
.animate(markerAttribs);
}
// Presentational attributes
if (graphic && !chart.styledMode) {
graphic[verb](series.pointAttribs(point, (point.selected && 'select')));
}
if (graphic) {
graphic.addClass(point.getClassName(), true);
}
}
else if (graphic) {
point.graphic = graphic.destroy(); // #1269
}
}
}
},
/**
* Get non-presentational attributes for a point. Used internally for
* both styled mode and classic. Can be overridden for different series
* types.
*
* @see Series#pointAttribs
*
* @function Highcharts.Series#markerAttribs
*
* @param {Highcharts.Point} point
* The Point to inspect.
*
* @param {string} [state]
* The state, can be either `hover`, `select` or undefined.
*
* @return {Highcharts.SVGAttributes}
* A hash containing those attributes that are not settable from
* CSS.
*/
markerAttribs: function (point, state) {
var seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, seriesStateOptions, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
seriesMarkerOptions.symbol), pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions.radius), attribs;
// Handle hover and select states
if (state) {
seriesStateOptions = seriesMarkerOptions.states[state];
pointStateOptions = pointMarkerOptions.states &&
pointMarkerOptions.states[state];
radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
0));
}
point.hasImage = symbol && symbol.indexOf('url') === 0;
if (point.hasImage) {
radius = 0; // and subsequently width and height is not set
}
attribs = {
// Math.floor for #1843:
x: seriesOptions.crisp ?
Math.floor(point.plotX) - radius :
point.plotX - radius,
y: point.plotY - radius
};
if (radius) {
attribs.width = attribs.height = 2 * radius;
}
return attribs;
},
/**
* Internal function to get presentational attributes for each point.
* Unlike {@link Series#markerAttribs}, this function should return
* those attributes that can also be set in CSS. In styled mode,
* `pointAttribs` won't be called.
*
* @private
* @function Highcharts.Series#pointAttribs
*
* @param {Highcharts.Point} [point]
* The point instance to inspect.
*
* @param {string} [state]
* The point state, can be either `hover`, `select` or 'normal'.
* If undefined, normal state is assumed.
*
* @return {Highcharts.SVGAttributes}
* The presentational attributes to be set on the point.
*/
pointAttribs: function (point, state) {
var seriesMarkerOptions = this.options.marker, seriesStateOptions, pointOptions = point && point.options, pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}), pointStateOptions, color = this.color, pointColorOption = pointOptions && pointOptions.color, pointColor = point && point.color, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), zoneColor = point && point.zone && point.zone.color, fill, stroke, opacity = 1;
color = (pointColorOption ||
zoneColor ||
pointColor ||
color);
fill = (pointMarkerOptions.fillColor ||
seriesMarkerOptions.fillColor ||
color);
stroke = (pointMarkerOptions.lineColor ||
seriesMarkerOptions.lineColor ||
color);
// Handle hover and select states
state = state || 'normal';
if (state) {
seriesStateOptions = seriesMarkerOptions.states[state];
pointStateOptions = (pointMarkerOptions.states &&
pointMarkerOptions.states[state]) || {};
strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
fill = (pointStateOptions.fillColor ||
seriesStateOptions.fillColor ||
fill);
stroke = (pointStateOptions.lineColor ||
seriesStateOptions.lineColor ||
stroke);
opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
}
return {
'stroke': stroke,
'stroke-width': strokeWidth,
'fill': fill,
'opacity': opacity
};
},
/**
* Clear DOM objects and free up memory.
*
* @private
* @function Highcharts.Series#destroy
* @param {boolean} [keepEventsForUpdate]
* @return {void}
* @fires Highcharts.Series#event:destroy
*/
destroy: function (keepEventsForUpdate) {
var series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), destroy, i, data = series.data || [], point, axis;
// add event hook
fireEvent(series, 'destroy');
// remove events
this.removeEvents(keepEventsForUpdate);
// erase from axes
(series.axisTypes || []).forEach(function (AXIS) {
axis = series[AXIS];
if (axis && axis.series) {
erase(axis.series, series);
axis.isDirty = axis.forceRedraw = true;
}
});
// remove legend items
if (series.legendItem) {
series.chart.legend.destroyItem(series);
}
// destroy all points with their elements
i = data.length;
while (i--) {
point = data[i];
if (point && point.destroy) {
point.destroy();
}
}
series.points = null;
// Clear the animation timeout if we are destroying the series
// during initial animation
U.clearTimeout(series.animationTimeout);
// Destroy all SVGElements associated to the series
objectEach(series, function (val, prop) {
// Survive provides a hook for not destroying
if (val instanceof SVGElement && !val.survive) {
// issue 134 workaround
destroy = issue134 && prop === 'group' ?
'hide' :
'destroy';
val[destroy]();
}
});
// remove from hoverSeries
if (chart.hoverSeries === series) {
chart.hoverSeries = null;
}
erase(chart.series, series);
chart.orderSeries();
// clear all members
objectEach(series, function (val, prop) {
if (!keepEventsForUpdate || prop !== 'hcEvents') {
delete series[prop];
}
});
},
/**
* Get the graph path.
*
* @private
* @function Highcharts.Series#getGraphPath
* @param {Array<Highcharts.Point>} points
* @param {boolean} [nullsAsZeroes]
* @param {boolean} [connectCliffs]
* @return {Highcharts.SVGPathArray}
*/
getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
var series = this, options = series.options, step = options.step, reversed, graphPath = [], xMap = [], gap;
points = points || series.points;
// Bottom of a stack is reversed
reversed = points.reversed;
if (reversed) {
points.reverse();
}
// Reverse the steps (#5004)
step = {
right: 1,
center: 2
}[step] || (step && 3);
if (step && reversed) {
step = 4 - step;
}
// Remove invalid points, especially in spline (#5015)
points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
// Build the line
points.forEach(function (point, i) {
var plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1],
// the path to this point from the previous
pathToPoint;
if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
!connectCliffs) {
gap = true; // ... and continue
}
// Line series, nullsAsZeroes is not handled
if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
gap = !options.connectNulls;
// Area series, nullsAsZeroes is set
}
else if (point.isNull && !nullsAsZeroes) {
gap = true;
}
else {
if (i === 0 || gap) {
pathToPoint = [[
'M',
point.plotX,
point.plotY
]];
// Generate the spline as defined in the SplineSeries object
}
else if (series.getPointSpline) {
pathToPoint = [series.getPointSpline(points, point, i)];
}
else if (step) {
if (step === 1) { // right
pathToPoint = [[
'L',
lastPoint.plotX,
plotY
]];
}
else if (step === 2) { // center
pathToPoint = [[
'L',
(lastPoint.plotX + plotX) / 2,
lastPoint.plotY
], [
'L',
(lastPoint.plotX + plotX) / 2,
plotY
]];
}
else {
pathToPoint = [[
'L',
plotX,
lastPoint.plotY
]];
}
pathToPoint.push([
'L',
plotX,
plotY
]);
}
else {
// normal line to next point
pathToPoint = [[
'L',
plotX,
plotY
]];
}
// Prepare for animation. When step is enabled, there are
// two path nodes for each x value.
xMap.push(point.x);
if (step) {
xMap.push(point.x);
if (step === 2) { // step = center (#8073)
xMap.push(point.x);
}
}
graphPath.push.apply(graphPath, pathToPoint);
gap = false;
}
});
graphPath.xMap = xMap;
series.graphPath = graphPath;
return graphPath;
},
/**
* Draw the graph. Called internally when rendering line-like series
* types. The first time it generates the `series.graph` item and
* optionally other series-wide items like `series.area` for area
* charts. On subsequent calls these items are updated with new
* positions and attributes.
*
* @function Highcharts.Series#drawGraph
*
* @return {void}
*/
drawGraph: function () {
var series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode, props = [[
'graph',
'highcharts-graph'
]];
// Presentational properties
if (!styledMode) {
props[0].push((options.lineColor ||
this.color ||
'#cccccc' // when colorByPoint = true
), options.dashStyle);
}
props = series.getZonesGraphs(props);
// Draw the graph
props.forEach(function (prop, i) {
var graphKey = prop[0], graph = series[graphKey], verb = graph ? 'animate' : 'attr', attribs;
if (graph) {
graph.endX = series.preventGraphAnimation ?
null :
graphPath.xMap;
graph.animate({ d: graphPath });
}
else if (graphPath.length) { // #1487
/**
* SVG element of area-based charts. Can be used for styling
* purposes. If zones are configured, this element will be
* hidden and replaced by multiple zone areas, accessible
* via `series['zone-area-x']` (where x is a number,
* starting with 0).
*
* @name Highcharts.Series#area
* @type {Highcharts.SVGElement|undefined}
*/
/**
* SVG element of line-based charts. Can be used for styling
* purposes. If zones are configured, this element will be
* hidden and replaced by multiple zone lines, accessible
* via `series['zone-graph-x']` (where x is a number,
* starting with 0).
*
* @name Highcharts.Series#graph
* @type {Highcharts.SVGElement|undefined}
*/
series[graphKey] = graph = series.chart.renderer
.path(graphPath)
.addClass(prop[1])
.attr({ zIndex: 1 }) // #1069
.add(series.group);
}
if (graph && !styledMode) {
attribs = {
'stroke': prop[2],
'stroke-width': options.lineWidth,
// Polygon series use filled graph
'fill': (series.fillGraph && series.color) || 'none'
};
if (prop[3]) {
attribs.dashstyle = prop[3];
}
else if (options.linecap !== 'square') {
attribs['stroke-linecap'] =
attribs['stroke-linejoin'] = 'round';
}
graph[verb](attribs)
// Add shadow to normal series (0) or to first
// zone (1) #3932
.shadow((i < 2) && options.shadow);
}
// Helpers for animation
if (graph) {
graph.startX = graphPath.xMap;
graph.isArea = graphPath.isArea; // For arearange animation
}
});
},
/**
* Get zones properties for building graphs. Extendable by series with
* multiple lines within one series.
*
* @private
* @function Highcharts.Series#getZonesGraphs
*
* @param {Array<Array<string>>} props
*
* @return {Array<Array<string>>}
*/
getZonesGraphs: function (props) {
// Add the zone properties if any
this.zones.forEach(function (zone, i) {
var propset = [
'zone-graph-' + i,
'highcharts-graph highcharts-zone-graph-' + i + ' ' +
(zone.className || '')
];
if (!this.chart.styledMode) {
propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
}
props.push(propset);
}, this);
return props;
},
/**
* Clip the graphs into zones for colors and styling.
*
* @private
* @function Highcharts.Series#applyZones
* @return {void}
*/
applyZones: function () {
var series = this, chart = this.chart, renderer = chart.renderer, zones = this.zones, translatedFrom, translatedTo, clips = (this.clips || []), clipAttr, graph = this.graph, area = this.area, chartSizeMax = Math.max(chart.chartWidth, chart.chartHeight), axis = this[(this.zoneAxis || 'y') + 'Axis'], extremes, reversed, inverted = chart.inverted, horiz, pxRange, pxPosMin, pxPosMax, ignoreZones = false, zoneArea, zoneGraph;
if (zones.length &&
(graph || area) &&
axis &&
typeof axis.min !== 'undefined') {
reversed = axis.reversed;
horiz = axis.horiz;
// The use of the Color Threshold assumes there are no gaps
// so it is safe to hide the original graph and area
// unless it is not waterfall series, then use showLine property
// to set lines between columns to be visible (#7862)
if (graph && !this.showLine) {
graph.hide();
}
if (area) {
area.hide();
}
// Create the clips
extremes = axis.getExtremes();
zones.forEach(function (threshold, i) {
translatedFrom = reversed ?
(horiz ? chart.plotWidth : 0) :
(horiz ? 0 : (axis.toPixels(extremes.min) || 0));
translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, chartSizeMax);
translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, chartSizeMax);
if (ignoreZones) {
translatedFrom = translatedTo =
axis.toPixels(extremes.max);
}
pxRange = Math.abs(translatedFrom - translatedTo);
pxPosMin = Math.min(translatedFrom, translatedTo);
pxPosMax = Math.max(translatedFrom, translatedTo);
if (axis.isXAxis) {
clipAttr = {
x: inverted ? pxPosMax : pxPosMin,
y: 0,
width: pxRange,
height: chartSizeMax
};
if (!horiz) {
clipAttr.x = chart.plotHeight - clipAttr.x;
}
}
else {
clipAttr = {
x: 0,
y: inverted ? pxPosMax : pxPosMin,
width: chartSizeMax,
height: pxRange
};
if (horiz) {
clipAttr.y = chart.plotWidth - clipAttr.y;
}
}
// VML SUPPPORT
if (inverted && renderer.isVML) {
if (axis.isXAxis) {
clipAttr = {
x: 0,
y: reversed ? pxPosMin : pxPosMax,
height: clipAttr.width,
width: chart.chartWidth
};
}
else {
clipAttr = {
x: (clipAttr.y -
chart.plotLeft -
chart.spacingBox.x),
y: 0,
width: clipAttr.height,
height: chart.chartHeight
};
}
}
// END OF VML SUPPORT
if (clips[i]) {
clips[i].animate(clipAttr);
}
else {
clips[i] = renderer.clipRect(clipAttr);
}
// when no data, graph zone is not applied and after setData
// clip was ignored. As a result, it should be applied each
// time.
zoneArea = series['zone-area-' + i];
zoneGraph = series['zone-graph-' + i];
if (graph && zoneGraph) {
zoneGraph.clip(clips[i]);
}
if (area && zoneArea) {
zoneArea.clip(clips[i]);
}
// if this zone extends out of the axis, ignore the others
ignoreZones = threshold.value > extremes.max;
// Clear translatedTo for indicators
if (series.resetZones && translatedTo === 0) {
translatedTo = void 0;
}
});
this.clips = clips;
}
else if (series.visible) {
// If zones were removed, restore graph and area
if (graph) {
graph.show(true);
}
if (area) {
area.show(true);
}
}
},
/**
* Initialize and perform group inversion on series.group and
* series.markerGroup.
*
* @private
* @function Highcharts.Series#invertGroups
* @param {boolean} [inverted]
* @return {void}
*/
invertGroups: function (inverted) {
var series = this, chart = series.chart;
/**
* @private
*/
function setInvert() {
['group', 'markerGroup'].forEach(function (groupName) {
if (series[groupName]) {
// VML/HTML needs explicit attributes for flipping
if (chart.renderer.isVML) {
series[groupName].attr({
width: series.yAxis.len,
height: series.xAxis.len
});
}
series[groupName].width = series.yAxis.len;
series[groupName].height = series.xAxis.len;
// If inverted polar, don't invert series group
series[groupName].invert(series.isRadialSeries ? false : inverted);
}
});
}
// Pie, go away (#1736)
if (!series.xAxis) {
return;
}
// A fixed size is needed for inversion to work
series.eventsToUnbind.push(addEvent(chart, 'resize', setInvert));
// Do it now
setInvert();
// On subsequent render and redraw, just do setInvert without
// setting up events again
series.invertGroups = setInvert;
},
/**
* General abstraction for creating plot groups like series.group,
* series.dataLabelsGroup and series.markerGroup. On subsequent calls,
* the group will only be adjusted to the updated plot size.
*
* @private
* @function Highcharts.Series#plotGroup
* @param {string} prop
* @param {string} name
* @param {string} visibility
* @param {number} [zIndex]
* @param {Highcharts.SVGElement} [parent]
* @return {Highcharts.SVGElement}
*/
plotGroup: function (prop, name, visibility, zIndex, parent) {
var group = this[prop], isNew = !group;
// Generate it on first call
if (isNew) {
this[prop] = group = this.chart.renderer
.g()
.attr({
zIndex: zIndex || 0.1 // IE8 and pointer logic use this
})
.add(parent);
}
// Add the class names, and replace existing ones as response to
// Series.update (#6660)
group.addClass(('highcharts-' + name +
' highcharts-series-' + this.index +
' highcharts-' + this.type + '-series ' +
(defined(this.colorIndex) ?
'highcharts-color-' + this.colorIndex + ' ' :
'') +
(this.options.className || '') +
(group.hasClass('highcharts-tracker') ?
' highcharts-tracker' :
'')), true);
// Place it on first and subsequent (redraw) calls
group.attr({ visibility: visibility })[isNew ? 'attr' : 'animate'](this.getPlotBox());
return group;
},
/**
* Get the translation and scale for the plot area of this series.
*
* @function Highcharts.Series#getPlotBox
*
* @return {Highcharts.SeriesPlotBoxObject}
*/
getPlotBox: function () {
var chart = this.chart, xAxis = this.xAxis, yAxis = this.yAxis;
// Swap axes for inverted (#2339)
if (chart.inverted) {
xAxis = yAxis;
yAxis = this.xAxis;
}
return {
translateX: xAxis ? xAxis.left : chart.plotLeft,
translateY: yAxis ? yAxis.top : chart.plotTop,
scaleX: 1,
scaleY: 1
};
},
/**
* Removes the event handlers attached previously with addEvents.
*
* @private
* @function Highcharts.Series#removeEvents
* @param {boolean} [keepEventsForUpdate]
* @return {void}
*/
removeEvents: function (keepEventsForUpdate) {
var series = this;
if (!keepEventsForUpdate) {
// remove all events
removeEvent(series);
}
else if (series.eventsToUnbind.length) {
// remove only internal events for proper update
// #12355 - solves problem with multiple destroy events
series.eventsToUnbind.forEach(function (unbind) {
unbind();
});
series.eventsToUnbind.length = 0;
}
},
/**
* Render the graph and markers. Called internally when first rendering
* and later when redrawing the chart. This function can be extended in
* plugins, but normally shouldn't be called directly.
*
* @function Highcharts.Series#render
*
* @return {void}
*
* @fires Highcharts.Series#event:afterRender
*/
render: function () {
var series = this, chart = series.chart, group, options = series.options,
// Animation doesn't work in IE8 quirks when the group div is
// hidden, and looks bad in other oldIE
animDuration = (!series.finishedAnimating &&
chart.renderer.isSVG &&
animObject(options.animation).duration), visibility = series.visible ? 'inherit' : 'hidden', // #2597
zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
fireEvent(this, 'render');
// the group
group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
// initiate the animation
if (animDuration && series.animate) {
series.animate(true);
}
// SVGRenderer needs to know this before drawing elements (#1089,
// #1795)
group.inverted = series.isCartesian || series.invertable ?
inverted : false;
// Draw the graph if any
if (series.drawGraph) {
series.drawGraph();
series.applyZones();
}
// Draw the points
if (series.visible) {
series.drawPoints();
}
/* series.points.forEach(function (point) {
if (point.redraw) {
point.redraw();
}
}); */
// Draw the data labels
if (series.drawDataLabels) {
series.drawDataLabels();
}
// In pie charts, slices are added to the DOM, but actual rendering
// is postponed until labels reserved their space
if (series.redrawPoints) {
series.redrawPoints();
}
// draw the mouse tracking area
if (series.drawTracker &&
series.options.enableMouseTracking !== false) {
series.drawTracker();
}
// Handle inverted series and tracker groups
series.invertGroups(inverted);
// Initial clipping, must be defined after inverting groups for VML.
// Applies to columns etc. (#3839).
if (options.clip !== false &&
!series.sharedClipKey &&
!hasRendered) {
group.clip(chart.clipRect);
}
// Run the animation
if (animDuration && series.animate) {
series.animate();
}
// Call the afterAnimate function on animation complete (but don't
// overwrite the animation.complete option which should be available
// to the user).
if (!hasRendered) {
series.animationTimeout = syncTimeout(function () {
series.afterAnimate();
}, animDuration || 0);
}
// Means data is in accordance with what you see
series.isDirty = false;
// (See #322) series.isDirty = series.isDirtyData = false; // means
// data is in accordance with what you see
series.hasRendered = true;
fireEvent(series, 'afterRender');
},
/**
* Redraw the series. This function is called internally from
* `chart.redraw` and normally shouldn't be called directly.
*
* @private
* @function Highcharts.Series#redraw
* @return {void}
*/
redraw: function () {
var series = this, chart = series.chart,
// cache it here as it is set to false in render, but used after
wasDirty = series.isDirty || series.isDirtyData, group = series.group, xAxis = series.xAxis, yAxis = series.yAxis;
// reposition on resize
if (group) {
if (chart.inverted) {
group.attr({
width: chart.plotWidth,
height: chart.plotHeight
});
}
group.animate({
translateX: pick(xAxis && xAxis.left, chart.plotLeft),
translateY: pick(yAxis && yAxis.top, chart.plotTop)
});
}
series.translate();
series.render();
if (wasDirty) { // #3868, #3945
delete this.kdTree;
}
},
kdAxisArray: ['clientX', 'plotY'],
/**
* @private
* @function Highcharts.Series#searchPoint
* @param {Highcharts.PointerEventObject} e
* @param {boolean} [compareX]
* @return {Highcharts.Point}
*/
searchPoint: function (e, compareX) {
var series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
return this.searchKDTree({
clientX: inverted ?
xAxis.len - e.chartY + xAxis.pos :
e.chartX - xAxis.pos,
plotY: inverted ?
yAxis.len - e.chartX + yAxis.pos :
e.chartY - yAxis.pos
}, compareX, e);
},
/**
* Build the k-d-tree that is used by mouse and touch interaction to get
* the closest point. Line-like series typically have a one-dimensional
* tree where points are searched along the X axis, while scatter-like
* series typically search in two dimensions, X and Y.
*
* @private
* @function Highcharts.Series#buildKDTree
* @param {Highcharts.PointerEventObject} [e]
* @return {void}
*/
buildKDTree: function (e) {
// Prevent multiple k-d-trees from being built simultaneously
// (#6235)
this.buildingKdTree = true;
var series = this, dimensions = series.options.findNearestPointBy
.indexOf('y') > -1 ? 2 : 1;
/**
* Internal function
* @private
*/
function _kdtree(points, depth, dimensions) {
var axis, median, length = points && points.length;
if (length) {
// alternate between the axis
axis = series.kdAxisArray[depth % dimensions];
// sort point array
points.sort(function (a, b) {
return a[axis] - b[axis];
});
median = Math.floor(length / 2);
// build and return nod
return {
point: points[median],
left: _kdtree(points.slice(0, median), depth + 1, dimensions),
right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
};
}
}
/**
* Start the recursive build process with a clone of the points
* array and null points filtered out. (#3873)
* @private
*/
function startRecursive() {
series.kdTree = _kdtree(series.getValidPoints(null,
// For line-type series restrict to plot area, but
// column-type series not (#3916, #4511)
!series.directTouch), dimensions, dimensions);
series.buildingKdTree = false;
}
delete series.kdTree;
// For testing tooltips, don't build async. Also if touchstart, we
// may be dealing with click events on mobile, so don't delay
// (#6817).
syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
},
/**
* @private
* @function Highcharts.Series#searchKDTree
* @param {Highcharts.KDPointSearchObject} point
* @param {boolean} [compareX]
* @param {Highcharts.PointerEventObject} [e]
* @return {Highcharts.Point|undefined}
*/
searchKDTree: function (point, compareX, e) {
var series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
.indexOf('y') > -1 ? 2 : 1;
/**
* Set the one and two dimensional distance on the point object.
* @private
*/
function setDistance(p1, p2) {
var x = (defined(p1[kdX]) &&
defined(p2[kdX])) ?
Math.pow(p1[kdX] - p2[kdX], 2) :
null, y = (defined(p1[kdY]) &&
defined(p2[kdY])) ?
Math.pow(p1[kdY] - p2[kdY], 2) :
null, r = (x || 0) + (y || 0);
p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
}
/**
* @private
*/
function _search(search, tree, depth, dimensions) {
var point = tree.point, axis = series.kdAxisArray[depth % dimensions], tdist, sideA, sideB, ret = point, nPoint1, nPoint2;
setDistance(search, point);
// Pick side based on distance to splitting point
tdist = search[axis] - point[axis];
sideA = tdist < 0 ? 'left' : 'right';
sideB = tdist < 0 ? 'right' : 'left';
// End of tree
if (tree[sideA]) {
nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
ret = (nPoint1[kdComparer] <
ret[kdComparer] ?
nPoint1 :
point);
}
if (tree[sideB]) {
// compare distance to current best to splitting point to
// decide wether to check side B or not
if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
ret = (nPoint2[kdComparer] <
ret[kdComparer] ?
nPoint2 :
ret);
}
}
return ret;
}
if (!this.kdTree && !this.buildingKdTree) {
this.buildKDTree(e);
}
if (this.kdTree) {
return _search(point, this.kdTree, kdDimensions, kdDimensions);
}
},
/**
* @private
* @function Highcharts.Series#pointPlacementToXValue
* @return {number}
*/
pointPlacementToXValue: function () {
var _a = this, _b = _a.options, pointPlacement = _b.pointPlacement, pointRange = _b.pointRange, axis = _a.xAxis;
var factor = pointPlacement;
// Point placement is relative to each series pointRange (#5889)
if (factor === 'between') {
factor = axis.reversed ? -0.5 : 0.5; // #11955
}
return isNumber(factor) ?
factor * pick(pointRange, axis.pointRange) :
0;
},
/**
* @private
* @function Highcharts.Series#isPointInside
* @param {Highcharts.Point} point
* @return {boolean}
*/
isPointInside: function (point) {
var isInside = typeof point.plotY !== 'undefined' &&
typeof point.plotX !== 'undefined' &&
point.plotY >= 0 &&
point.plotY <= this.yAxis.len && // #3519
point.plotX >= 0 &&
point.plotX <= this.xAxis.len;
return isInside;
}
}); // end Series prototype
/**
* A line series displays information as a series of data points connected by
* straight line segments.
*
* @sample {highcharts} highcharts/demo/line-basic/
* Line chart
* @sample {highstock} stock/demo/basic-line/
* Line chart
*
* @extends plotOptions.series
* @product highcharts highstock
* @apioption plotOptions.line
*/
/**
* The SVG value used for the `stroke-linecap` and `stroke-linejoin`
* of a line graph. Round means that lines are rounded in the ends and
* bends.
*
* @type {Highcharts.SeriesLinecapValue}
* @default round
* @since 3.0.7
* @apioption plotOptions.line.linecap
*/
/**
* A `line` series. If the [type](#series.line.type) option is not
* specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.line
* @excluding dataParser,dataURL
* @product highcharts highstock
* @apioption series.line
*/
/**
* An array of data points for the series. For the `line` series type,
* points can be given in the following ways:
*
* 1. An array of numerical values. In this case, the numerical values will be
* interpreted as `y` options. The `x` values will be automatically
* calculated, either starting at 0 and incremented by 1, or from
* `pointStart` and `pointInterval` given in the series options. If the axis
* has categories, these will be used. Example:
* ```js
* data: [0, 5, 3, 5]
* ```
*
* 2. An array of arrays with 2 values. In this case, the values correspond to
* `x,y`. If the first value is a string, it is applied as the name of the
* point, and the `x` value is inferred.
* ```js
* data: [
* [0, 1],
* [1, 2],
* [2, 8]
* ]
* ```
*
* 3. 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.line.turboThreshold),
* this option is not available.
* ```js
* data: [{
* x: 1,
* y: 9,
* name: "Point2",
* color: "#00FF00"
* }, {
* x: 1,
* y: 6,
* name: "Point1",
* color: "#FF00FF"
* }]
* ```
*
* **Note:** In TypeScript you have to extend `PointOptionsObject` with an
* additional declaration to allow custom data types:
* ```ts
* declare module `highcharts` {
* interface PointOptionsObject {
* custom: Record<string, (boolean|number|string)>;
* }
* }
* ```
*
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @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
*
* @declare Highcharts.PointOptionsObject
* @type {Array<number|Array<(number|string),(number|null)>|null|*>}
* @apioption series.line.data
*/
/**
* An additional, individual class name for the data point's graphic
* representation.
*
* @type {string}
* @since 5.0.0
* @product highcharts gantt
* @apioption series.line.data.className
*/
/**
* Individual color for the point. By default the color is pulled from
* the global `colors` array.
*
* In styled mode, the `color` option doesn't take effect. Instead, use
* `colorIndex`.
*
* @sample {highcharts} highcharts/point/color/
* Mark the highest point
*
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
* @product highcharts highstock gantt
* @apioption series.line.data.color
*/
/**
* A specific color index to use for the point, so its graphic representations
* are given the class name `highcharts-color-{n}`. In styled mode this will
* change the color of the graphic. In non-styled mode, the color by is set by
* the `fill` attribute, so the change in class name won't have a visual effect
* by default.
*
* @type {number}
* @since 5.0.0
* @product highcharts gantt
* @apioption series.line.data.colorIndex
*/
/**
* A reserved subspace to store options and values for customized functionality.
* Here you can add additional data for your own event callbacks and formatter
* callbacks.
*
* @sample {highcharts} highcharts/point/custom/
* Point and series with custom data
*
* @type {Highcharts.Dictionary<*>}
* @apioption series.line.data.custom
*/
/**
* Individual data label for each point. The options are the same as
* the ones for [plotOptions.series.dataLabels](
* #plotOptions.series.dataLabels).
*
* @sample highcharts/point/datalabels/
* Show a label for the last value
*
* @declare Highcharts.DataLabelsOptions
* @extends plotOptions.line.dataLabels
* @product highcharts highstock gantt
* @apioption series.line.data.dataLabels
*/
/**
* A description of the point to add to the screen reader information
* about the point.
*
* @type {string}
* @since 5.0.0
* @requires modules/accessibility
* @apioption series.line.data.description
*/
/**
* An id for the point. This can be used after render time to get a
* pointer to the point object through `chart.get()`.
*
* @sample {highcharts} highcharts/point/id/
* Remove an id'd point
*
* @type {string}
* @since 1.2.0
* @product highcharts highstock gantt
* @apioption series.line.data.id
*/
/**
* The rank for this point's data label in case of collision. If two
* data labels are about to overlap, only the one with the highest `labelrank`
* will be drawn.
*
* @type {number}
* @apioption series.line.data.labelrank
*/
/**
* The name of the point as shown in the legend, tooltip, dataLabels, etc.
*
* @see [xAxis.uniqueNames](#xAxis.uniqueNames)
*
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Point names
*
* @type {string}
* @apioption series.line.data.name
*/
/**
* Whether the data point is selected initially.
*
* @type {boolean}
* @default false
* @product highcharts highstock gantt
* @apioption series.line.data.selected
*/
/**
* The x value of the point. For datetime axes, the X value is the timestamp
* in milliseconds since 1970.
*
* @type {number}
* @product highcharts highstock
* @apioption series.line.data.x
*/
/**
* The y value of the point.
*
* @type {number|null}
* @product highcharts highstock
* @apioption series.line.data.y
*/
/**
* The individual point events.
*
* @extends plotOptions.series.point.events
* @product highcharts highstock gantt
* @apioption series.line.data.events
*/
/**
* Options for the point markers of line-like series.
*
* @declare Highcharts.PointMarkerOptionsObject
* @extends plotOptions.series.marker
* @product highcharts highstock
* @apioption series.line.data.marker
*/
''; // include precedent doclets in transpilat