/* *
 * (c) 2010-2019 Torstein Honsi
 *
 * License: www.highcharts.com/license
 */

/**
 * 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 {Highcharts.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.Event} 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.Event} event
 *        Event that occured.
 */

/**
 * Translation and scale for the plot area of a series.
 *
 * @interface Highcharts.SeriesPlotBoxObject
 *//**
 * @name Highcharts.SeriesPlotBoxObject#translateX
 * @type {number}
 *//**
 * @name Highcharts.SeriesPlotBoxObject#translateY
 * @type {number}
 *//**
 * @name Highcharts.SeriesPlotBoxObject#scaleX
 * @type {number}
 *//**
 * @name Highcharts.SeriesPlotBoxObject#scaleY
 * @type {number}
 */

/**
 * Function callback when a series point is clicked. Return false to cancel the
 * action.
 *
 * @callback Highcharts.SeriesPointClickCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        The point where the event occured.
 *
 * @param {Highcharts.SeriesPointClickEventObject} event
 *        Event arguments.
 */

/**
 * Common information for a click event on a series point.
 *
 * @interface Highcharts.SeriesPointClickEventObject
 * @extends Highcharts.PointerEventObject
 *//**
 * Clicked point.
 * @name Highcharts.SeriesPointClickEventObject#point
 * @type {Highcharts.Point}
 */

/**
 * Gets fired when the mouse leaves the area close to the point.
 *
 * @callback Highcharts.SeriesPointMouseOutCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {global.Event} event
 *        Event that occured.
 */

/**
 * Gets fired when the mouse enters the area close to the point.
 *
 * @callback Highcharts.SeriesPointMouseOverCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {global.Event} event
 *        Event that occured.
 */

/**
 * Gets fired when the point is removed using the `.remove()` method.
 *
 * @callback Highcharts.SeriesPointRemoveCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {global.Event} event
 *        Event that occured.
 */

/**
 * Gets fired when the point is selected either programmatically or following a
 * click on the point.
 *
 * @callback Highcharts.SeriesPointSelectCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {Highcharts.SeriesPointSelectEventObject} event
 *        Event that occured.
 */

/**
 * Information about the select event.
 *
 * @interface Highcharts.SeriesPointSelectEventObject
 * @extends global.Event
 *//**
 * @name Highcharts.SeriesPointSelectEventObject#accumulate
 * @type {boolean}
 */

/**
 * Fires when the point is unselected either programmatically or following a
 * click on the point.
 *
 * @callback Highcharts.SeriesPointUnselectCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {Highcharts.SeriesPointUnselectEventObject} event
 *        Event that occured.
 */

/**
 * Information about the unselect event.
 *
 * @interface Highcharts.SeriesPointUnselectEventObject
 * @extends global.Event
 *//**
 * @name Highcharts.SeriesPointUnselectEventObject#accumulate
 * @type {boolean}
 */

/**
 * Gets fired when the point is updated programmatically through the `.update()`
 * method.
 *
 * @callback Highcharts.SeriesPointUpdateCallbackFunction
 *
 * @param {Highcharts.Point} this
 *        Point where the event occured.
 *
 * @param {Highcharts.SeriesPointUpdateEventObject} event
 *        Event that occured.
 */

/**
 * Information about the update event.
 *
 * @interface Highcharts.SeriesPointUpdateEventObject
 * @extends global.Event
 *//**
 * Options data of the update event.
 * @name Highcharts.SeriesPointUpdateEventObject#options
 * @type {number|object|Array<(number|string)>|null}
 */

/**
 * 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.
 */

'use strict';

import H from './Globals.js';
import './Utilities.js';
import './Options.js';
import './Legend.js';
import './Point.js';
import './SvgRenderer.js';

var addEvent = H.addEvent,
    animObject = H.animObject,
    arrayMax = H.arrayMax,
    arrayMin = H.arrayMin,
    correctFloat = H.correctFloat,
    defaultOptions = H.defaultOptions,
    defaultPlotOptions = H.defaultPlotOptions,
    defined = H.defined,
    erase = H.erase,
    extend = H.extend,
    fireEvent = H.fireEvent,
    isArray = H.isArray,
    isNumber = H.isNumber,
    isString = H.isString,
    LegendSymbolMixin = H.LegendSymbolMixin, // @todo add as a requirement
    merge = H.merge,
    objectEach = H.objectEach,
    pick = H.pick,
    Point = H.Point, // @todo  add as a requirement
    removeEvent = H.removeEvent,
    splat = H.splat,
    SVGElement = H.SVGElement,
    syncTimeout = H.syncTimeout,
    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 = H.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      {string|object}
     * @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.
     *
     * In TypeScript instead the `type` option must always be set.
     *
     * @sample {highcharts} highcharts/series/type/
     *         Line and column in the same chart
     * @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
     */
    { // base series options

        /**
         * 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](
         * 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,

        /**
         * 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: {

            /**
             * @type      {number}
             * @default   1000
             * @apioption plotOptions.series.animation.duration
             */
            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 name for 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
         */

        /**
         * Requires the Accessibility module.
         *
         * A description of the series to add to the screen reader information
         * about the series.
         *
         * @type      {string}
         * @since     5.0.0
         * @apioption plotOptions.series.description
         */

        /**
         * 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
         */

        /**
         * By default, series are exposed to screen readers as regions. By
         * enabling this option, the series element itself will be exposed in
         * the same way as the data points. This is useful if the series is not
         * used as a grouping entity in the chart, but you still want to attach
         * a description to the series.
         *
         * Requires the Accessibility module.
         *
         * @sample highcharts/accessibility/art-grants/
         *         Accessible data visualization
         *
         * @type      {boolean}
         * @since     5.0.12
         * @apioption plotOptions.series.exposeElementToA11y
         */

        /**
         * 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.
         *
         * @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).
         *
         * @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. The default value is `true` for standalone series, `false`
         * for linked series.
         *
         * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
         *         One series in the legend, one hidden
         *
         * @type      {boolean}
         * @default   true
         * @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", "percent"]
         * @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.
         *
         * @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.
         *
         * @private
         */
        marker: {

            /**
             * The width of the point marker's outline.
             *
             * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
             *         2px blue marker
             */
            lineWidth: 0,

            /**
             * 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}
             */
            lineColor: '#ffffff',

            /**
             * 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
             */

            /**
             * 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
             */

            /**
             * 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
             */

            /**
             * 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
             */

            /**
             * 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 radius of the point marker.
             *
             * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
             *         Bigger markers
             *
             * @default  {highstock} 2
             */
            radius: 4,

            /**
             * 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.
             */
            states: {

                /**
                 * The normal state of a single point marker. Currently only
                 * used for setting animation when returning to normal state
                 * from hover.
                 */
                normal: {
                    /**
                     * Animation when returning to normal state after hovering.
                     *
                     * @type {boolean|Highcharts.AnimationOptionsObject}
                     */
                    animation: true
                },

                /**
                 * The hover state for a single point marker.
                 */
                hover: {

                    /**
                     * Animation when hovering over the marker.
                     *
                     * @type {boolean|Highcharts.AnimationOptionsObject}
                     */
                    animation: {

                        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}
                     * @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.
                 */
                select: {

                    /**
                     * 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
                     */

                    /**
                     * 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 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}
                     */
                    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.
         *
         * @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.SeriesPointClickCallbackFunction}
             * @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.SeriesPointMouseOutCallbackFunction}
             * @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.SeriesPointMouseOverCallbackFunction}
             * @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.SeriesPointRemoveCallbackFunction}
             * @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.SeriesPointSelectCallbackFunction}
             * @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.SeriesPointUnselectCallbackFunction}
             * @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.SeriesPointUpdateCallbackFunction}
             * @since     1.2.0
             * @context   Highcharts.Point
             * @apioption plotOptions.series.point.events.update
             */

            /**
             * Events for each single point.
             */
            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/plotoptions/series-datalabels-enabled
         *         Data labels enabled
         * @sample highcharts/plotoptions/series-datalabels-multiple
         *         Multiple data labels on a bar series
         *
         * @type {Highcharts.DataLabelsOptionsObject}
         *
         * @private
         */
        dataLabels: {
            /** @ignore-option */
            align: 'center',
            /** @ignore-option */
            formatter: function () {
                return this.y === null ? '' : H.numberFormat(this.y, -1);
            },
            /** @ignore-option */
            padding: 5,
            /** @ignore-option */
            style: {
                /** @ignore-option */
                fontSize: '11px',
                /** @ignore-option */
                fontWeight: 'bold',
                /** @ignore-option */
                color: 'contrast',
                /** @ignore-option */
                textOutline: '1px contrast'
            },
            /**
             * @ignore-option
             * above singular point
             */
            verticalAlign: 'bottom',
            /** @ignore-option */
            x: 0,
            /** @ignore-option */
            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,

        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.
             */
            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.
             */
            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.
                     */
                    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
                 * @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
                 *
                 * @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
             *
             * @extends   plotOptions.series.states.hover
             * @excluding brightness
             * @product   highmaps
             */
            select: {
                animation: {
                    duration: 0
                }
            },

            /**
             * The opposite state of a hover for series.
             *
             * @sample {highcharts} highcharts/demo/windbarb-series/
             *         Disabled inactive state
             */
            inactive: {
                animation: {
                    duration: 50
                },
                /**
                 * Opacity of series elements (dataLabels, line, area).
                 *
                 * @apioption plotOptions.series.states.inactive.opacity
                 * @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.
         *
         * @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.
         *
         * @since     2.3
         * @extends   tooltip
         * @excluding animation, backgroundColor, borderColor, borderRadius,
         *            borderWidth, crosshairs, enabled, formatter, positioner,
         *            shadow, shape, shared, snap, 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.
         *
         * @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
         *
         * @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 [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
         */

        /**
         * 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'

    },
    /** @lends Highcharts.Series.prototype */
    {
        isCartesian: true,
        pointClass: Point,
        sorted: true, // requires the data to be sorted
        requireSorting: true,
        directTouch: false,
        axisTypes: ['xAxis', 'yAxis'],
        colorCounter: 0,
        // each point's x and y values are stored in this.xData and this.yData
        parallelArrays: ['x', 'y'],
        coll: 'series',
        cropShoulder: 1,
        init: function (chart, options) {

            fireEvent(this, 'init', { options: options });

            var series = this,
                events,
                chartSeries = chart.series,
                lastSeries;

            /**
             * 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, // true by default
                /**
                 * 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 (
                    // In case we're doing Series.update(), first check if the
                    // event already exists.
                    !series.hcEvents ||
                    !series.hcEvents[eventType] ||
                    series.hcEvents[eventType].indexOf(event) === -1
                ) {
                    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'] = [];
                }
            });
            if (!series.points) {
                series.setData(options.data, false);
            }

            // 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));

            fireEvent(this, 'afterInit');
        },

        /**
         * 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
         *
         * @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 ||
                            (
                                seriesOptions[AXIS] !== undefined &&
                                seriesOptions[AXIS] === axisOptions.id
                            ) ||
                            (
                                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) {
                        H.error(18, true, chart);
                    }

                });
            });
        },

        /**
         * 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
         */
        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 &&
                    this.dataMax !== undefined &&
                    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;
        },

        /**
         * 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}.
         *
         * @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 || {},
                userPlotOptions = userOptions.plotOptions || {},
                typeOptions = plotOptions[this.type],
                seriesUserOptions = merge(itemOptions),
                options,
                zones,
                zone,
                styledMode = chart.styledMode;

            fireEvent(this, 'setOptions', { userOptions: seriesUserOptions });

            // use copy to prevent undetected changes (#9762)
            this.userOptions = seriesUserOptions;

            // General series options take precedence over type options because
            // otherwise, default type options like column.animation would be
            // overwritten by the general option. But issues have been raised
            // here (#3881), and the solution may be to distinguish between
            // default option and userOptions like in the tooltip below.
            options = merge(
                typeOptions,
                plotOptions.series,
                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 {*} [defaults]
         */
        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 (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
         */
        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 the series' symbol based on either the options or pulled from
         * global options.
         *
         * @private
         * @function Highcharts.Series#getSymbol
         */
        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    {object} 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;

            if (id) {
                matchingPoint = this.chart.get(id);
                pointIndex = matchingPoint && matchingPoint.index;
                if (pointIndex !== undefined) {
                    matchedById = true;
                }
            }

            // Search for the same X in the existing data set
            if (pointIndex === undefined && isNumber(x)) {
                pointIndex = this.xData.indexOf(x, fromIndex);
            }

            // Reduce pointIndex if data is cropped
            if (pointIndex !== -1 &&
                pointIndex !== undefined &&
                this.cropped
            ) {
                pointIndex = (pointIndex >= this.cropStart) ?
                    pointIndex - this.cropStart : pointIndex;
            }

            if (
                !matchedById &&
                oldData[pointIndex] && oldData[pointIndex].touched
            ) {
                pointIndex = undefined;
            }
            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<*>} data
         *
         * @return {boolean}
         */
        updateData: function (data) {
            var options = this.options,
                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 = (
                        H.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 ||
                        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 ||
                        this.hasDerivedData
                    ) {
                        hasUpdatedByKey = true;
                    }
                }
            }, 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(false);
                    }
                }

            // If we did not find keys (ids or x-values), and the length is the
            // same, update one-to-one
            } else if (equalLength) {
                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);
                    }
                });

            // 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);

            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<*>} 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 {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.
         */
        setData: function (data, redraw, animation, updatePoints) {
            var series = this,
                oldData = series.points,
                oldDataLength = (oldData && oldData.length) || 0,
                dataLength,
                options = series.options,
                chart = series.chart,
                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);

            // 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);
            }

            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) {

                    // find the first non-null point
                    i = 0;
                    while (firstPoint === null && i < dataLength) {
                        firstPoint = data[i];
                        i++;
                    }


                    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
                        H.error(12, false, chart);
                    }
                } else {
                    for (i = 0; i < dataLength; i++) {
                        if (data[i] !== undefined) { // stray commas in oldIE
                            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])) {
                    H.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 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. 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,
                processedXData = series.xData, // copied during slice operation
                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 =
                    series.getExtremesFromAll ||
                    options.getExtremesFromAll, // #4599
                isCartesian = series.isCartesian,
                xExtremes,
                val2lin = xAxis && xAxis.val2lin,
                isLog = xAxis && xAxis.isLog,
                throwOnUnsorted = series.requireSorting,
                min,
                max;

            // 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 (
                isCartesian &&
                !series.isDirty &&
                !xAxis.isDirty &&
                !series.yAxis.isDirty &&
                !force
            ) {
                return false;
            }

            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 &&
                    (
                        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) {
                    H.error(15, false, series.chart);
                    throwOnUnsorted = false; // Only once
                }
            }

            // Record the properties
            series.cropped = cropped; // undefined or true
            series.cropStart = cropStart;
            series.processedXData = processedXData;
            series.processedYData = processedYData;

            series.closestPointRange = 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 {*}
         */
        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
         */
        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];
                    if (!point && dataOptions[cursor] !== undefined) { // #970
                        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.SVGElement|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
                    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 = undefined; // #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#getExtremes
         *
         * @param {Array<number>} [xData]
         *        The data to inspect. Defaults to the current data within the
         *        visible range.
         * @return {object}
         */
        getXExtremes: function (xData) {
            return {
                min: arrayMin(xData),
                max: arrayMax(xData)
            };
        },

        /**
         * Calculate Y extremes for the visible data. The result is set as
         * `dataMin` and `dataMax` on the Series item.
         *
         * @private
         * @function Highcharts.Series#getExtremes
         *
         * @param {Array<number>} [yData]
         *        The data to inspect. Defaults to the current data within the
         *        visible range.
         */
        getExtremes: function (yData) {
            var xAxis = this.xAxis,
                yAxis = this.yAxis,
                xData = this.processedXData,
                yDataLength,
                activeYData = [],
                activeCounter = 0,
                // #2117, need to compensate for log X axis
                xExtremes = xAxis.getExtremes(),
                xMin = xExtremes.min,
                xMax = xExtremes.max,
                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,
                x,
                y,
                i,
                j;

            yData = yData || this.stackedYData || this.processedYData || [];
            yDataLength = yData.length;

            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, true) || isArray(y)) &&
                    (!yAxis.positiveValuesOnly || (y.length || y > 0))
                );
                withinRange = (
                    this.getExtremesFromAll ||
                    this.options.getExtremesFromAll ||
                    this.cropped ||
                    (
                        (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 (typeof y[j] === 'number') { // #7380
                                activeYData[activeCounter++] = y[j];
                            }
                        }
                    } else {
                        activeYData[activeCounter++] = y;
                    }
                }
            }

            this.dataMin = arrayMin(activeYData);
            this.dataMax = arrayMax(activeYData);

            fireEvent(this, 'afterGetExtremes');
        },

        /**
         * 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
         *
         * @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,
                yAxis = series.yAxis,
                points = series.points,
                dataLength = points.length,
                hasModifyValue = !!series.modifyValue,
                i,
                pointPlacement = series.pointPlacementToXValue(), // #7860
                dynamicallyPlaced = isNumber(pointPlacement),
                threshold = options.threshold,
                stackThreshold = options.startFromThreshold ? threshold : 0,
                plotX,
                plotY,
                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).
            function limitedRange(val) {
                return Math.min(Math.max(-1e5, val), 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.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 &&
                    !point.isNull &&
                    stack &&
                    stack[xValue]
                ) {
                    stackIndicator = series.getStackIndicator(
                        stackIndicator,
                        xValue,
                        series.index
                    );
                    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
                    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 = plotY = (
                    (typeof yValue === 'number' && yValue !== Infinity) ?
                        limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
                        undefined
                );

                point.isInside =
                    plotY !== undefined &&
                    plotY >= 0 &&
                    plotY <= yAxis.len && // #3519
                    plotX >= 0 &&
                    plotX <= xAxis.len;


                // 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 &&
                    categories[point.x] !== undefined ?
                        categories[point.x] :
                        point.x
                );

                // Determine auto enabling of markers (#3635, #5099)
                if (!point.isNull) {
                    if (lastPlotX !== undefined) {
                        closestPointRangePx = Math.min(
                            closestPointRangePx,
                            Math.abs(plotX - lastPlotX)
                        );
                    }
                    lastPlotX = plotX;
                }

                // Find point zone
                point.zone = this.zones.length && point.getZone();
            }
            series.closestPointRangePx = closestPointRangePx;

            fireEvent(this, 'afterTranslate');
        },

        /**
         * Return the series points with null points filtered out.
         *
         * @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 allowNull || !point.isNull;
                }
            );
        },

        /**
         * 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} [animation]
         */
        setClip: function (animation) {
            var chart = this.chart,
                options = this.options,
                renderer = chart.renderer,
                inverted = chart.inverted,
                seriesClipBox = this.clipBox,
                clipBox = seriesClipBox || chart.clipBox,
                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 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) {
                    clipBox.width = 0;
                    if (inverted) {
                        clipBox.x = chart.plotSizeX;
                    }

                    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 };

            }
            if (animation) {
                if (!clipRect.count[this.index]) {
                    clipRect.count[this.index] = true;
                    clipRect.count.length += 1;
                }
            }

            if (options.clip !== false) {
                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.
         */
        animate: function (init) {
            var series = this,
                chart = series.chart,
                clipRect,
                animation = animObject(series.options.animation),
                sharedClipKey;

            // Initialize the animation. Set up the clipping rectangle.
            if (init) {

                series.setClip(animation);

            // Run the animation
            } else {
                sharedClipKey = this.sharedClipKey;
                clipRect = chart[sharedClipKey];
                if (clipRect) {
                    clipRect.animate({
                        width: chart.plotSizeX,
                        x: 0
                    }, animation);
                }
                if (chart[sharedClipKey + 'm']) {
                    chart[sharedClipKey + 'm'].animate({
                        width: chart.plotSizeX + 99,
                        x: chart.inverted ? 0 : -99
                    }, animation);
                }

                // Delete this function to allow it only once
                series.animate = null;

            }
        },

        /**
         * This runs after animation to land on the final plot clipping.
         *
         * @private
         * @function Highcharts.Series#afterAnimate
         *
         * @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,
                symbol,
                graphic,
                verb,
                options = series.options,
                seriesMarkerOptions = options.marker,
                pointMarkerOptions,
                hasPointMarker,
                enabled,
                isInside,
                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;
                    enabled = (
                        globallyEnabled &&
                        pointMarkerOptions.enabled === undefined
                    ) || pointMarkerOptions.enabled;
                    isInside = point.isInside !== false;

                    // only draw the point if y is defined
                    if (enabled && !point.isNull) {

                        // Shortcuts
                        symbol = pick(pointMarkerOptions.symbol, series.symbol);

                        markerAttribs = series.markerAttribs(
                            point,
                            point.selected && 'select'
                        );

                        if (graphic) { // update
                            // Since the marker group isn't clipped, each
                            // individual marker must be toggled
                            graphic[isInside ? 'show' : 'hide'](true)
                                .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);
                        }

                        // 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 seriesMarkerOptions = this.options.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 = {
                x: Math.floor(point.plotX) - radius, // Math.floor for #1843
                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 undefined
         *        for normal state.
         *
         * @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
            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
         *
         * @fires Highcharts.Series#event:destroy
         */
        destroy: function (keepEvents) {
            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 all events
            if (!keepEvents) {
                removeEvent(series);
            }

            // 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
            H.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 (!keepEvents || prop !== 'hcEvents') {
                    delete series[prop];
                }
            });
        },

        /**
         * Get the graph path.
         *
         * @private
         * @function Highcharts.Series#getGraphPath
         *
         * @param {Array<*>} 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)
            if (options.connectNulls && !nullsAsZeroes && !connectCliffs) {
                points = this.getValidPoints(points);
            }

            // Build the line
            points.forEach(function (point, i) {

                var plotX = point.plotX,
                    plotY = point.plotY,
                    lastPoint = points[i - 1],
                    pathToPoint; // the path to this point from the previous

                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
         */
        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

                    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
         */
        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;

            if (zones.length &&
                (graph || area) &&
                axis &&
                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 = Math.min(
                        Math.max(
                            pick(translatedTo, translatedFrom), 0
                        ),
                        chartSizeMax
                    );
                    translatedTo = Math.min(
                        Math.max(
                            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);

                        if (graph) {
                            series['zone-graph-' + i].clip(clips[i]);
                        }

                        if (area) {
                            series['zone-area-' + i].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 = undefined;
                    }
                });
                this.clips = clips;
            }
        },

        /**
         * Initialize and perform group inversion on series.group and
         * series.markerGroup.
         *
         * @private
         * @function Highcharts.Series#invertGroups
         *
         * @param {boolean} inverted
         */
        invertGroups: function (inverted) {
            var series = this,
                chart = series.chart,
                remover;

            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;
                        series[groupName].invert(inverted);
                    }
                });
            }

            // Pie, go away (#1736)
            if (!series.xAxis) {
                return;
            }

            // A fixed size is needed for inversion to work
            remover = addEvent(chart, 'resize', setInvert);
            addEvent(series, 'destroy', remover);

            // Do it now
            setInvert(inverted); // do it now

            // 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, // #1623
                scaleY: 1
            };
        },

        /**
         * 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
         *
         * @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.animate &&
                    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(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();
            }

            // 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);
            }

            // 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
         */
        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 {object} 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
         */
        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
            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)
            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 {object} point
         *
         * @param {boolean} [compareX]
         *
         * @return {Highcharts.Point}
         */
        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
            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;
            }
            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 series = this,
                pointPlacement = series.options.pointPlacement;

            // Point placement is relative to each series pointRange (#5889)
            if (pointPlacement === 'between') {
                pointPlacement = 0.5;
            }
            if (isNumber(pointPlacement)) {
                pointPlacement *=
                    pick(series.options.pointRange || series.xAxis.pointRange);
            }

            return pointPlacement;
        }
    }
); // 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).
 *
 * In TypeScript instead the `type` option must always be set.
 *
 * @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"
 *    }]
 *    ```
 *
 * @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
 *
 * @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
 */

/**
 * 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
 *
 * @type      {Highcharts.DataLabelsOptionsObject}
 * @product   highcharts highstock gantt
 * @apioption series.line.data.dataLabels
 */

/**
 * A description of the point to add to the screen reader information
 * about the point. Requires the Accessibility module.
 *
 * @type      {string}
 * @since     5.0.0
 * @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
 */

/**
 * Individual point events
 *
 * @extends   plotOptions.series.point.events
 * @product   highcharts highstock gantt
 * @apioption series.line.data.events
 */

/**
 * @extends   plotOptions.series.marker
 * @product   highcharts highstock
 * @apioption series.line.data.marker
 */
