/* *
 *
 *  Timeline Series.
 *
 *  (c) 2010-2020 Highsoft AS
 *
 *  Author: Daniel Studencki
 *
 *  License: www.highcharts.com/license
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */

import type ColorType from '../Core/Color/ColorType';
import type SVGAttributes from '../Core/Renderer/SVG/SVGAttributes';
import type SVGPath from '../Core/Renderer/SVG/SVGPath';
import BaseSeries from '../Core/Series/Series.js';
const {
    seriesTypes
} = BaseSeries;
import H from '../Core/Globals.js';
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
import Point from '../Core/Series/Point.js';
import SVGElement from '../Core/Renderer/SVG/SVGElement.js';
import U from '../Core/Utilities.js';
const {
    addEvent,
    arrayMax,
    arrayMin,
    defined,
    isNumber,
    merge,
    objectEach,
    pick
} = U;

/**
 * Internal types
 * @private
 */
declare global {
    namespace Highcharts {
        class TimelinePoint extends LinePoint {
            public isValid: () => boolean;
            public label?: string;
            public options: TimelinePointOptions;
            public series: TimelineSeries;
            public userDLOptions?: TimelineDataLabelsOptionsObject;
            public alignConnector(): void;
            public drawConnector(): void;
            public getConnectorPath(): SVGPath;
            public init(): this;
            public setState(): void;
            public setVisible(vis: boolean, redraw?: boolean): void;
        }
        class TimelineSeries extends LineSeries {
            public data: Array<TimelinePoint>;
            public drawLegendSymbol: LegendSymbolMixin['drawRectangle'];
            public drawTracker: TrackerMixin['drawTrackerPoint'];
            public options: TimelineSeriesOptions;
            public pointClass: typeof TimelinePoint;
            public points: Array<TimelinePoint>;
            public trackerGroups: Array<string>;
            public userOptions: TimelineSeriesOptions;
            public visibilityMap: Array<(
                boolean|TimelinePoint|TimelinePointOptions
            )>;
            public visiblePointsCount?: number;
            public yData?: Array<number>;
            public alignDataLabel(
                point: TimelinePoint,
                dataLabel: SVGElement,
                options: TimelineDataLabelsOptionsObject,
                alignTo: BBoxObject
            ): void;
            public bindAxes(): void;
            public distributeDL(): void;
            public generatePoints(): void;
            public getVisibilityMap(): Array<(
                boolean|TimelinePoint|TimelinePointOptions
            )>;
            public getXExtremes(xData: Array<number>): RangeObject;
            public init(): void;
            public markerAttribs(
                point: TimelinePoint,
                state?: string
            ): SVGAttributes;
            public processData(): undefined;
        }
        interface TimelineDataLabelsFormatterCallbackFunction
            extends DataLabelsFormatterCallbackFunction
        {
            (
                this: (
                    PointLabelObject|
                    TimelineDataLabelsFormatterContextObject
                )
            ): string;
        }
        interface TimelineDataLabelsFormatterContextObject
            extends PointLabelObject
        {
            key?: string;
            point: TimelinePoint;
            series: TimelineSeries;
        }
        interface TimelineDataLabelsOptionsObject
            extends DataLabelsOptions
        {
            alternate?: boolean;
            connectorColor?: ColorType;
            connectorWidth?: number;
            distance?: number;
            formatter?: TimelineDataLabelsFormatterCallbackFunction;
            width?: number;
        }
        interface TimelinePointOptions extends LinePointOptions {
            dataLabels?: TimelineDataLabelsOptionsObject;
            isNull?: boolean;
            radius?: number;
            visible?: boolean;
        }
        interface TimelineSeriesOptions extends LineSeriesOptions {
            data?: Array<TimelinePointOptions>;
            dataLabels?: TimelineDataLabelsOptionsObject;
            ignoreHiddenPoint?: boolean;
            radius?: number;
            radiusPlus?: number;
            states?: SeriesStatesOptionsObject<TimelineSeries>;
        }
    }
}

/**
 * @private
 */
declare module '../Core/Series/Types' {
    interface SeriesTypeRegistry {
        timeline: typeof Highcharts.TimelineSeries;
    }
}

import '../Series/LineSeries.js';

var TrackerMixin = H.TrackerMixin,
    Series = H.Series;

/**
 * Callback JavaScript function to format the data label as a string. Note that
 * if a `format` is defined, the format takes precedence and the formatter is
 * ignored.
 *
 * @callback Highcharts.TimelineDataLabelsFormatterCallbackFunction
 *
 * @param {Highcharts.PointLabelObject|Highcharts.TimelineDataLabelsFormatterContextObject} this
 *        Data label context to format
 *
 * @return {number|string|null|undefined}
 *         Formatted data label text
 */

/**
 * @interface Highcharts.TimelineDataLabelsFormatterContextObject
 * @extends Highcharts.PointLabelObject
 *//**
 * @name Highcharts.TimelineDataLabelsFormatterContextObject#key
 * @type {string|undefined}
 *//**
 * @name Highcharts.TimelineDataLabelsFormatterContextObject#point
 * @type {Highcharts.Point}
 *//**
 * @name Highcharts.TimelineDataLabelsFormatterContextObject#series
 * @type {Highcharts.Series}
 */

''; // dettach doclets above

/**
 * The timeline series type.
 *
 * @private
 * @class
 * @name Highcharts.seriesTypes.timeline
 *
 * @augments Highcharts.Series
 */
BaseSeries.seriesType<typeof Highcharts.TimelineSeries>('timeline', 'line',

    /**
     * The timeline series presents given events along a drawn line.
     *
     * @sample highcharts/series-timeline/alternate-labels
     *         Timeline series
     * @sample highcharts/series-timeline/inverted
     *         Inverted timeline
     * @sample highcharts/series-timeline/datetime-axis
     *         With true datetime axis
     *
     * @extends      plotOptions.line
     * @since        7.0.0
     * @product      highcharts
     * @excluding    animationLimit, boostThreshold, connectEnds, connectNulls,
     *               cropThreshold, dashStyle, findNearestPointBy,
     *               getExtremesFromAll, lineWidth, negativeColor,
     *               pointInterval, pointIntervalUnit, pointPlacement,
     *               pointStart, softThreshold, stacking, step, threshold,
     *               turboThreshold, zoneAxis, zones, dataSorting,
     *               boostBlending
     * @requires     modules/timeline
     * @optionparent plotOptions.timeline
     */
    {
        colorByPoint: true,
        stickyTracking: false,
        ignoreHiddenPoint: true,
        legendType: 'point',
        lineWidth: 4,
        tooltip: {
            headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
                '<span style="font-size: 10px"> {point.key}</span><br/>',
            pointFormat: '{point.description}'
        },
        states: {
            hover: {
                lineWidthPlus: 0
            }
        },
        /**
         * @declare Highcharts.TimelineDataLabelsOptionsObject
         *
         * @private
         */
        dataLabels: {

            enabled: true,

            allowOverlap: true,

            /**
             * Whether to position data labels alternately. For example, if
             * [distance](#plotOptions.timeline.dataLabels.distance)
             * is set equal to `100`, then data labels will be positioned
             * alternately (on both sides of the point) at a distance of 100px.
             *
             * @sample {highcharts} highcharts/series-timeline/alternate-disabled
             *         Alternate disabled
             */
            alternate: true,

            backgroundColor: '${palette.backgroundColor}',

            borderWidth: 1,

            borderColor: '${palette.neutralColor40}',

            borderRadius: 3,

            color: '${palette.neutralColor80}',

            /**
             * The color of the line connecting the data label to the point.
             * The default color is the same as the point's color.
             *
             * In styled mode, the connector stroke is given in the
             * `.highcharts-data-label-connector` class.
             *
             * @sample {highcharts} highcharts/series-timeline/connector-styles
             *         Custom connector width and color
             *
             * @type      {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
             * @apioption plotOptions.timeline.dataLabels.connectorColor
             */

            /**
             * The width of the line connecting the data label to the point.
             *
             * In styled mode, the connector stroke width is given in the
             * `.highcharts-data-label-connector` class.
             *
             * @sample {highcharts} highcharts/series-timeline/connector-styles
             *         Custom connector width and color
             */
            connectorWidth: 1,

            /**
             * A pixel value defining the distance between the data label and
             * the point. Negative numbers puts the label on top of the point.
             */
            distance: 100,

            // eslint-disable-next-line valid-jsdoc
            /**
             * @type    {Highcharts.TimelineDataLabelsFormatterCallbackFunction}
             * @default function () {
             *   var format;
             *
             *   if (!this.series.chart.styledMode) {
             *       format = '<span style="color:' + this.point.color +
             *           '">● </span>';
             *   } else {
             *       format = '<span>● </span>';
             *   }
             *   format += '<span>' + (this.key || '') + '</span><br/>' +
             *       (this.point.label || '');
             *   return format;
             * }
             */
            formatter: function (
                this: (
                    Highcharts.PointLabelObject|
                    Highcharts.TimelineDataLabelsFormatterContextObject
                )
            ): string {
                var format;

                if (!this.series.chart.styledMode) {
                    format = '<span style="color:' + this.point.color +
                        '">● </span>';
                } else {
                    format = '<span>● </span>';
                }
                format += '<span class="highcharts-strong">' +
                    ((this as any).key || '') + '</span><br/>' +
                    ((this.point as any).label || '');
                return format;
            },

            style: {
                /** @internal */
                textOutline: 'none',
                /** @internal */
                fontWeight: 'normal',
                /** @internal */
                fontSize: '12px'
            },

            /**
             * Shadow options for the data label.
             *
             * @type {boolean|Highcharts.CSSObject}
             */
            shadow: false,

            /**
             * @type      {number}
             * @apioption plotOptions.timeline.dataLabels.width
             */

            verticalAlign: 'middle'

        },
        marker: {
            enabledThreshold: 0,
            symbol: 'square',
            radius: 6,
            lineWidth: 2,
            height: 15
        },
        showInLegend: false,
        colorKey: 'x'
    },
    /**
     * @lends Highcharts.Series#
     */
    {
        trackerGroups: ['markerGroup', 'dataLabelsGroup'],
        // Use a simple symbol from LegendSymbolMixin
        drawLegendSymbol: LegendSymbolMixin.drawRectangle,
        // Use a group of trackers from TrackerMixin
        drawTracker: TrackerMixin.drawTrackerPoint,
        init: function (this: Highcharts.TimelineSeries): void {
            var series = this;

            Series.prototype.init.apply(series, arguments);

            addEvent(series, 'afterTranslate', function (): void {
                var lastPlotX: (number|undefined),
                    closestPointRangePx = Number.MAX_VALUE;

                series.points.forEach(function (
                    point: Highcharts.TimelinePoint
                ): void {
                    // Set the isInside parameter basing also on the real point
                    // visibility, in order to avoid showing hidden points
                    // in drawPoints method.
                    point.isInside = point.isInside && point.visible;

                    // New way of calculating closestPointRangePx value, which
                    // respects the real point visibility is needed.
                    if (point.visible && !point.isNull) {
                        if (defined(lastPlotX)) {
                            closestPointRangePx = Math.min(
                                closestPointRangePx,
                                Math.abs((point.plotX as any) - lastPlotX)
                            );
                        }
                        lastPlotX = point.plotX;
                    }
                });
                series.closestPointRangePx = closestPointRangePx;
            });

            // Distribute data labels before rendering them. Distribution is
            // based on the 'dataLabels.distance' and 'dataLabels.alternate'
            // property.
            addEvent(series, 'drawDataLabels', function (): void {
                // Distribute data labels basing on defined algorithm.
                series.distributeDL(); // @todo use this scope for series
            });

            addEvent(series, 'afterDrawDataLabels', function (): void {
                var dataLabel; // @todo use this scope for series

                // Draw or align connector for each point.
                series.points.forEach(function (
                    point: Highcharts.TimelinePoint
                ): void {
                    dataLabel = point.dataLabel;

                    if (dataLabel) {
                        // Within this wrap method is necessary to save the
                        // current animation params, because the data label
                        // target position (after animation) is needed to align
                        // connectors.
                        dataLabel.animate = function (
                            this: SVGElement,
                            params: SVGAttributes
                        ): SVGElement {
                            if (this.targetPosition) {
                                this.targetPosition = params;
                            }
                            return SVGElement.prototype.animate.apply(
                                this,
                                arguments
                            );
                        };

                        // Initialize the targetPosition field within data label
                        // object. It's necessary because there is need to know
                        // expected position of specific data label, when
                        // aligning connectors. This field is overrided inside
                        // of SVGElement.animate() wrapped  method.
                        if (!dataLabel.targetPosition) {
                            dataLabel.targetPosition = {};
                        }

                        return point.drawConnector();
                    }
                });
            });
            addEvent(
                series.chart,
                'afterHideOverlappingLabel',
                function (): void {
                    series.points.forEach(function (
                        p: Highcharts.TimelinePoint
                    ): void {
                        if (
                            p.connector &&
                            p.dataLabel &&
                            p.dataLabel.oldOpacity !== p.dataLabel.newOpacity
                        ) {
                            p.alignConnector();
                        }
                    });
                }
            );
        },
        alignDataLabel: function (
            this: Highcharts.TimelineSeries,
            point: Highcharts.TimelinePoint,
            dataLabel: SVGElement,
            options: Highcharts.TimelineDataLabelsOptionsObject,
            alignTo: Highcharts.BBoxObject
        ): void {
            var series = this,
                isInverted = series.chart.inverted,
                visiblePoints = series.visibilityMap.filter(function (
                    point: (
                        boolean|Highcharts.TimelinePoint|
                        Highcharts.TimelinePointOptions
                    )
                ): boolean {
                    return point as any;
                }),
                visiblePointsCount: number = series.visiblePointsCount as any,
                pointIndex = visiblePoints.indexOf(point),
                isFirstOrLast = (
                    !pointIndex || pointIndex === visiblePointsCount - 1
                ),
                dataLabelsOptions: Highcharts.TimelineDataLabelsOptionsObject =
                    series.options.dataLabels as any,
                userDLOptions = point.userDLOptions || {},
                // Define multiplier which is used to calculate data label
                // width. If data labels are alternate, they have two times more
                // space to adapt (excepting first and last ones, which has only
                // one and half), than in case of placing all data labels side
                // by side.
                multiplier = dataLabelsOptions.alternate ?
                    (isFirstOrLast ? 1.5 : 2) :
                    1,
                distance,
                availableSpace = Math.floor(
                    series.xAxis.len / visiblePointsCount
                ),
                pad = dataLabel.padding,
                targetDLWidth,
                styles;

            // Adjust data label width to the currently available space.
            if (point.visible) {
                distance = Math.abs(
                    userDLOptions.x || (point.options.dataLabels as any).x
                );
                if (isInverted) {
                    targetDLWidth = (
                        (distance - pad) * 2 - ((point.itemHeight as any) / 2)
                    );
                    styles = {
                        width: targetDLWidth + 'px',
                        // Apply ellipsis when data label height is exceeded.
                        textOverflow: dataLabel.width / targetDLWidth *
                            dataLabel.height / 2 > availableSpace * multiplier ?
                            'ellipsis' : 'none'
                    };
                } else {
                    styles = {
                        width: (
                            userDLOptions.width ||
                            dataLabelsOptions.width ||
                            availableSpace * multiplier - (pad * 2)
                        ) + 'px'
                    };
                }
                dataLabel.css(styles);

                if (!series.chart.styledMode) {
                    dataLabel.shadow(dataLabelsOptions.shadow);
                }
            }
            Series.prototype.alignDataLabel.apply(series, arguments);
        },
        processData: function (this: Highcharts.TimelineSeries): undefined {
            var series = this,
                visiblePoints = 0,
                i: (number|undefined);

            series.visibilityMap = series.getVisibilityMap();

            // Calculate currently visible points.
            series.visibilityMap.forEach(function (
                point: (
                    boolean|Highcharts.TimelinePoint|
                    Highcharts.TimelinePointOptions
                )
            ): void {
                if (point) {
                    visiblePoints++;
                }
            });

            series.visiblePointsCount = visiblePoints;

            for (i = 0; i < (series.xData as any).length; i++) {
                (series.yData as any)[i] = 1;
            }

            Series.prototype.processData.call(this, arguments as any);

            return;
        },
        getXExtremes: function (
            this: Highcharts.TimelineSeries,
            xData: Array<number>
        ): Highcharts.RangeObject {
            var series = this,
                filteredData = xData.filter(function (
                    x: number,
                    i: number
                ): boolean {
                    return series.points[i].isValid() &&
                        series.points[i].visible;
                });

            return {
                min: arrayMin(filteredData),
                max: arrayMax(filteredData)
            };
        },
        generatePoints: function (this: Highcharts.TimelineSeries): void {
            var series = this;

            Series.prototype.generatePoints.apply(series);
            series.points.forEach(function (
                point: Highcharts.TimelinePoint,
                i: number
            ): void {
                point.applyOptions({
                    x: (series.xData as any)[i]
                }, (series.xData as any)[i]);
            });
        },
        getVisibilityMap: function (
            this: Highcharts.TimelineSeries
        ): Array<(boolean|Highcharts.TimelinePoint|
            Highcharts.TimelinePointOptions)> {
            var series = this,
                map = (series.data.length ?
                    series.data : (series.userOptions.data as any)
                ).map(function (
                    point: (
                        Highcharts.TimelinePoint|
                        Highcharts.TimelinePointOptions
                    )
                ): (boolean|Highcharts.TimelinePoint|
                    Highcharts.TimelinePointOptions
                    ) {
                    return (
                        point &&
                        point.visible !== false &&
                        !point.isNull
                    ) ? point : false;
                });
            return map;
        },
        distributeDL: function (this: Highcharts.TimelineSeries): void {
            var series = this,
                dataLabelsOptions: Highcharts.TimelineDataLabelsOptionsObject =
                    series.options.dataLabels as any,
                options,
                pointDLOptions,
                newOptions: Highcharts.TimelineDataLabelsOptionsObject = {},
                visibilityIndex = 1,
                distance: number = dataLabelsOptions.distance as any;

            series.points.forEach(function (
                point: Highcharts.TimelinePoint
            ): void {
                if (point.visible && !point.isNull) {
                    options = point.options;
                    pointDLOptions = point.options.dataLabels;

                    if (!series.hasRendered) {
                        point.userDLOptions =
                            merge<Highcharts.TimelineDataLabelsOptionsObject>(
                                {},
                                pointDLOptions
                            );
                    }

                    newOptions[series.chart.inverted ? 'x' : 'y'] =
                        dataLabelsOptions.alternate && visibilityIndex % 2 ?
                            -distance : distance;

                    options.dataLabels = merge(newOptions, point.userDLOptions);
                    visibilityIndex++;
                }
            });
        },
        markerAttribs: function (
            this: Highcharts.TimelineSeries,
            point: Highcharts.TimelinePoint,
            state?: string
        ): SVGAttributes {
            var series = this,
                seriesMarkerOptions: Highcharts.PointMarkerOptionsObject =
                    series.options.marker as any,
                seriesStateOptions: Highcharts.SeriesStateOptionsObject<Highcharts.TimelineSeries>,
                pointMarkerOptions = point.marker || {},
                symbol = (
                    pointMarkerOptions.symbol || seriesMarkerOptions.symbol
                ),
                pointStateOptions,
                width = pick<number|undefined, number|undefined, number>(
                    pointMarkerOptions.width,
                    seriesMarkerOptions.width,
                    series.closestPointRangePx as any
                ),
                height = pick<number|undefined, number>(
                    pointMarkerOptions.height,
                    seriesMarkerOptions.height as any
                ),
                radius = 0,
                attribs: (SVGAttributes|undefined);

            // Call default markerAttribs method, when the xAxis type
            // is set to datetime.
            if (series.xAxis.dateTime) {
                return seriesTypes.line.prototype.markerAttribs
                    .call(this, point, state);
            }

            // Handle hover and select states
            if (state) {
                seriesStateOptions =
                    (seriesMarkerOptions.states as any)[state] || {};
                pointStateOptions = pointMarkerOptions.states &&
                    (pointMarkerOptions.states as any)[state] || {};

                radius = pick(
                    pointStateOptions.radius,
                    seriesStateOptions.radius,
                    radius + (
                        seriesStateOptions.radiusPlus as any || 0
                    )
                );
            }

            point.hasImage = (symbol && symbol.indexOf('url') === 0) as any;

            attribs = {
                x: Math.floor(point.plotX as any) - (width / 2) - (radius / 2),
                y: (point.plotY as any) - (height / 2) - (radius / 2),
                width: width + radius,
                height: height + radius
            };

            return attribs;

        },
        bindAxes: function (this: Highcharts.TimelineSeries): void {
            var series = this;

            Series.prototype.bindAxes.call(series);

            ['xAxis', 'yAxis'].forEach(function (axis: string): void {
                // Initially set the linked xAxis type to category.
                if (axis === 'xAxis' && !series[axis].userOptions.type) {
                    series[axis].categories = series[axis].hasNames = true as any;
                }
            });
        }
    },
    /**
     * @lends Highcharts.Point#
     */
    {
        init: function (
            this: Highcharts.TimelinePoint
        ): Highcharts.TimelinePoint {
            var point: Highcharts.TimelinePoint =
                Point.prototype.init.apply(this, arguments) as any;

            point.name = pick(point.name, 'Event');
            point.y = 1;

            return point;
        },
        isValid: function (this: Highcharts.TimelinePoint): boolean {
            return this.options.y !== null;
        },
        setVisible: function (
            this: Highcharts.TimelinePoint,
            vis: boolean,
            redraw?: boolean
        ): void {
            var point = this,
                series = point.series;

            redraw = pick(redraw, series.options.ignoreHiddenPoint);

            seriesTypes.pie.prototype.pointClass.prototype
                .setVisible.call(point, vis, false);
            // Process new data
            series.processData();

            if (redraw) {
                series.chart.redraw();
            }
        },
        setState: function (this: Highcharts.TimelinePoint): void {
            var proceed = Series.prototype.pointClass.prototype.setState;

            // Prevent triggering the setState method on null points.
            if (!this.isNull) {
                proceed.apply(this, arguments);
            }
        },
        getConnectorPath: function (
            this: Highcharts.TimelinePoint
        ): SVGPath {
            var point = this,
                chart = point.series.chart,
                xAxisLen = point.series.xAxis.len,
                inverted = chart.inverted,
                direction = inverted ? 'x2' : 'y2',
                dl: SVGElement = point.dataLabel as any,
                targetDLPos = dl.targetPosition,
                coords: Highcharts.Dictionary<(number|string)> = {
                    x1: point.plotX as any,
                    y1: point.plotY as any,
                    x2: point.plotX as any,
                    y2: isNumber(targetDLPos.y) ? targetDLPos.y : dl.y
                },
                negativeDistance = (
                    (dl.alignAttr || dl)[direction[0]] <
                        point.series.yAxis.len / 2
                ),
                path: SVGPath;

            // Recalculate coords when the chart is inverted.
            if (inverted) {
                coords = {
                    x1: point.plotY as any,
                    y1: xAxisLen - (point.plotX as any),
                    x2: targetDLPos.x || dl.x,
                    y2: xAxisLen - (point.plotX as any)
                };
            }

            // Subtract data label width or height from expected coordinate so
            // that the connector would start from the appropriate edge.
            if (negativeDistance) {
                coords[direction] += dl[inverted ? 'width' : 'height'];
            }

            // Change coordinates so that they will be relative to data label.
            objectEach(coords, function (
                _coord: (number|string),
                i: string
            ): void {
                (coords[i] as any) -= (dl.alignAttr || dl)[i[0]];
            });

            path = chart.renderer.crispLine(
                [
                    ['M', coords.x1, coords.y1],
                    ['L', coords.x2, coords.y2]
                ] as SVGPath,
                dl.options.connectorWidth
            );

            return path;
        },
        drawConnector: function (this: Highcharts.TimelinePoint): void {
            var point = this,
                series = point.series;

            if (!point.connector) {
                point.connector = series.chart.renderer
                    .path(point.getConnectorPath())
                    .attr({
                        zIndex: -1
                    })
                    .add(point.dataLabel);
            }

            if (point.series.chart.isInsidePlot( // #10507
                (point.dataLabel as any).x, (point.dataLabel as any).y
            )) {
                point.alignConnector();
            }
        },
        alignConnector: function (this: Highcharts.TimelinePoint): void {
            var point = this,
                series = point.series,
                connector: SVGElement = point.connector as any,
                dl: SVGElement = point.dataLabel as any,
                dlOptions = (point.dataLabel as any).options = merge(
                    series.options.dataLabels,
                    point.options.dataLabels
                ),
                chart = point.series.chart,
                bBox = connector.getBBox(),
                plotPos = {
                    x: bBox.x + dl.translateX,
                    y: bBox.y + dl.translateY
                },
                isVisible;

            // Include a half of connector width in order to run animation,
            // when connectors are aligned to the plot area edge.
            if (chart.inverted) {
                plotPos.y -= dl.options.connectorWidth / 2;
            } else {
                plotPos.x += dl.options.connectorWidth / 2;
            }

            isVisible = chart.isInsidePlot(
                plotPos.x, plotPos.y
            );

            connector[isVisible ? 'animate' : 'attr']({
                d: point.getConnectorPath()
            });

            if (!series.chart.styledMode) {
                connector.attr({
                    stroke: dlOptions.connectorColor || point.color,
                    'stroke-width': dlOptions.connectorWidth,
                    opacity: dl[
                        defined(dl.newOpacity) ? 'newOpacity' : 'opacity'
                    ]
                });
            }
        }
    }
);

/**
 * The `timeline` series. If the [type](#series.timeline.type) option is
 * not specified, it is inherited from [chart.type](#chart.type).
 *
 * @extends   series,plotOptions.timeline
 * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
 *            cropThreshold, dashStyle, dataParser, dataURL, findNearestPointBy,
 *            getExtremesFromAll, lineWidth, negativeColor,
 *            pointInterval, pointIntervalUnit, pointPlacement, pointStart,
 *            softThreshold, stacking, stack, step, threshold, turboThreshold,
 *            zoneAxis, zones, dataSorting, boostBlending
 * @product   highcharts
 * @requires  modules/timeline
 * @apioption series.timeline
 */

/**
 * An array of data points for the series. For the `timeline` series type,
 * points can be given with three general parameters, `name`, `label`,
 * and `description`:
 *
 * Example:
 *
 * ```js
 * series: [{
 *    type: 'timeline',
 *    data: [{
 *        name: 'Jan 2018',
 *        label: 'Some event label',
 *        description: 'Description to show in tooltip'
 *    }]
 * }]
 * ```
 * If all points additionally have the `x` values, and xAxis type is set to
 * `datetime`, then events are laid out on a true time axis, where their
 * placement reflects the actual time between them.
 *
 * @sample {highcharts} highcharts/series-timeline/alternate-labels
 *         Alternate labels
 * @sample {highcharts} highcharts/series-timeline/datetime-axis
 *         Real time intervals
 *
 * @type      {Array<*>}
 * @extends   series.line.data
 * @excluding marker, y
 * @product   highcharts
 * @apioption series.timeline.data
 */

/**
 * The name of event.
 *
 * @type      {string}
 * @product   highcharts
 * @apioption series.timeline.data.name
 */

/**
 * The label of event.
 *
 * @type      {string}
 * @product   highcharts
 * @apioption series.timeline.data.label
 */

/**
 * The description of event. This description will be shown in tooltip.
 *
 * @type      {string}
 * @product   highcharts
 * @apioption series.timeline.data.description
 */

''; // adds doclets above to transpiled file
