/**
 * @license  Highcharts JS v7.0.1 (2018-12-19)
 * Timeline series
 *
 * (c) 2010-2018 Highsoft AS
 * Author: Daniel Studencki
 *
 * License: www.highcharts.com/license
 */
'use strict';
(function (factory) {
	if (typeof module === 'object' && module.exports) {
		module.exports = factory;
	} else if (typeof define === 'function' && define.amd) {
		define(function () {
			return factory;
		});
	} else {
		factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
	}
}(function (Highcharts) {
	(function (H) {
		/* *
		 *
		 *  Experimental Timeline Series.
		 *  Note: This API is in alpha stage and will be changed before final release.
		 *
		 *  (c) 2010-2018 Highsoft AS
		 *
		 *  Author: Daniel Studencki
		 *
		 *  License: www.highcharts.com/license
		 *
		 * */



		var addEvent = H.addEvent,
		    extend = H.extend,
		    defined = H.defined,
		    LegendSymbolMixin = H.LegendSymbolMixin,
		    TrackerMixin = H.TrackerMixin,
		    merge = H.merge,
		    pick = H.pick,
		    Point = H.Point,
		    Series = H.Series,
		    undocumentedSeriesType = H.seriesType,
		    wrap = H.wrap;

		/* *
		 * The timeline series type.
		 *
		 * @private
		 * @class
		 * @name Highcharts.seriesTypes.timeline
		 *
		 * @augments Highcharts.Series
		 */
		undocumentedSeriesType('timeline', 'line'

		/* *
		 * The timeline series presents given events along a drawn line.
		 *
		 * @sample highcharts/series-timeline/alternate-labels Timeline series
		 *
		 * @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
		 * @optionparent plotOptions.timeline
		 */
		, {
		    colorByPoint: true,
		    stickyTracking: false,
		    ignoreHiddenPoint: true,
		    legendType: 'point',
		    lineWidth: 0,
		    tooltip: {
		        headerFormat: '<span style="color:{point.color}">● </span>' +
		            '<span style="font-weight: bold;">{point.point.date}</span><br/>',
		        pointFormat: '{point.description}'
		    },
		    states: {
		        hover: {
		            lineWidthPlus: 5,
		            halo: {
		                size: 0
		            }
		        }
		    },
		    dataLabels: {
		        enabled: true,
		        allowOverlap: true,
		        /* *
		         * 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.
		         *
		         * @type {Number}
		         * @default 1
		         * @sample {highcharts} highcharts/series-timeline/connector-styles
		         *         Custom connector width and color
		         */
		        connectorWidth: 1,
		        /* *
		         * The color of the line connecting the data label to the point.
		         *
		         * In styled mode, the connector stroke is given in the
		         * `.highcharts-data-label-connector` class.
		         *
		         * @type {String}
		         * @sample {highcharts} highcharts/series-timeline/connector-styles
		         *         Custom connector width and color
		         */
		        connectorColor: '#000000',
		        backgroundColor: '#ffffff',
		        /* *
		         * @type      {Highcharts.FormatterCallbackFunction<object>}
		         * @default function () {
		         *   var format;
		         *
		         *   if (!this.series.chart.styledMode) {
		         *       format = '<span style="color:' + this.point.color +
		         *           '">● </span><span style="font-weight: bold;" > ' +
		         *           (this.point.date || '') + '</span><br/>' +
		         *           (this.point.label || '');
		         *   } else {
		         *       format = '<span>● </span>' +
		         *           '<span>' + (this.point.date || '') +
		         *           '</span><br/>' + (this.point.label || '');
		         *   }
		         *   return format;
		         * }
		         * @apioption plotOptions.timeline.dataLabels.formatter
		         */
		        formatter: function () {
		            var format;

		            if (!this.series.chart.styledMode) {
		                format = '<span style="color:' + this.point.color +
		                    '">● </span><span style="font-weight: bold;" > ' +
		                    (this.point.date || '') + '</span><br/>' +
		                    (this.point.label || '');
		            } else {
		                format = '<span>● </span>' +
		                    '<span>' + (this.point.date || '') +
		                    '</span><br/>' + (this.point.label || '');
		            }
		            return format;
		        },
		        borderWidth: 1,
		        borderColor: '#666666',
		        /* *
		         * A pixel value defining the distance between the data label
		         * and the point. Negative numbers puts the label on top
		         * of the point.
		         *
		         * @type {Number}
		         * @default 100
		         */
		        distance: 100,
		        /* *
		         * Whether to position data labels alternately. For example, if
		         * [distance](#plotOptions.timeline.dataLabels.distance) is set
		         * equal to `100`, then the first data label 's distance will be
		         * set equal to `100`, the second one equal to `-100`, and so on.
		         *
		         * @type {Boolean}
		         * @default true
		         * @sample {highcharts} highcharts/series-timeline/alternate-disabled
		         *         Alternate disabled
		         */
		        alternate: true,
		        verticalAlign: 'middle',
		        color: '#333333'
		    },
		    marker: {
		        enabledThreshold: 0,
		        symbol: 'square',
		        height: 15
		    }
		}
		/* *
		 * @lends Highcharts.Series#
		 */
		, {
		    requireSorting: false,
		    trackerGroups: ['markerGroup', 'dataLabelsGroup'],
		    // Use a simple symbol from LegendSymbolMixin
		    drawLegendSymbol: LegendSymbolMixin.drawRectangle,
		    // Use a group of trackers from TrackerMixin
		    drawTracker: TrackerMixin.drawTrackerPoint,
		    init: function () {
		        var series = this;

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

		        // Distribute data labels before rendering them. Distribution is based
		        // on the 'dataLabels.distance' and 'dataLabels.alternate' property.
		        addEvent(series, 'drawDataLabels', function () {
		            // Delete the oldTextWidth parameter, in order to force adjusting
		            // data label wrapper box width. It's needed only when useHTML
		            // is enabled. This prevents the data label text getting out
		            // of the box range.
		            if (series.options.dataLabels.useHTML) {
		                series.points.forEach(function (p) {
		                    if (p.visible && p.dataLabel) {
		                        delete p.dataLabel.text.oldTextWidth;
		                    }
		                });
		            }

		            // Distribute data labels basing on defined algorithm.
		            series.distributeDL();
		        });

		        addEvent(series, 'afterDrawDataLabels', function () {
		            var seriesOptions = series.options,
		                options = seriesOptions.dataLabels,
		                hasRendered = series.hasRendered || 0,
		                defer = pick(options.defer, !!seriesOptions.animation),
		                connectorsGroup = series.connectorsGroup,
		                dataLabel;

		            // Create (or redraw) the group for all connectors.
		            connectorsGroup = series.plotGroup(
		                'connectorsGroup',
		                'data-labels-connectors',
		                defer && !hasRendered ? 'hidden' : 'visible',
		                options.zIndex || 5
		            );

		            // Draw or align connector for each point.
		            series.points.forEach(function (point) {
		                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.
		                    wrap(dataLabel, 'animate', function (proceed, params) {
		                        if (this.targetPosition) {
		                            this.targetPosition = params;
		                        }
		                        return proceed.apply(
		                            this,
		                            Array.prototype.slice.call(arguments, 1)
		                        );
		                    });

		                    // Initiate 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.connector ?
		                        point.drawConnector() :
		                        point.alignConnector();
		                }
		            });
		            // Animate connectors group. It's animated in the same way like
		            // dataLabels, and also depends on dataLabels.defer parameter.
		            if (defer) {
		                connectorsGroup.attr({
		                    opacity: +hasRendered
		                });
		                if (!hasRendered) {
		                    addEvent(series, 'afterAnimate', function () {
		                        if (series.visible) {
		                            connectorsGroup.show(true);
		                        }
		                        connectorsGroup[
		                            seriesOptions.animation ? 'animate' : 'attr'
		                        ]({
		                            opacity: 1
		                        }, {
		                            duration: 200
		                        });
		                    });
		                }
		            }
		        });
		    },
		    alignDataLabel: function (point, dataLabel) {
		        var series = this,
		            isInverted = series.chart.inverted,
		            visiblePoints = series.visibilityMap.filter(function (point) {
		                return point;
		            }),
		            visiblePointsCount = series.visiblePointsCount,
		            pointIndex = visiblePoints.indexOf(point),
		            isFirstOrLast = !pointIndex ||
		                pointIndex === visiblePointsCount - 1,
		            dataLabelsOptions = series.options.dataLabels,
		            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.x);
		            if (isInverted) {
		                targetDLWidth = (distance - pad) * 2 - (point.itemHeight / 2);
		                styles = {
		                    width: targetDLWidth,
		                    // 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)
		                };
		            }
		            dataLabel.css(styles);

		            if (!series.chart.styledMode) {
		                dataLabel.shadow({});
		            }
		        }
		        Series.prototype.alignDataLabel.apply(series, arguments);
		    },
		    processData: function () {
		        var series = this,
		            xMap = [],
		            base,
		            visiblePoints = 0,
		            i;

		        series.visibilityMap = series.getVisibilityMap();

		        // Calculate currently visible points.
		        series.visibilityMap.forEach(function (point) {
		            if (point) {
		                visiblePoints++;
		            }
		        });

		        series.visiblePointsCount = visiblePoints;
		        base = series.xAxis.options.max / visiblePoints;

		        // Generate xData map.
		        for (i = 1; i <= visiblePoints; i++) {
		            xMap.push(
		                (base * i) - (base / 2)
		            );
		        }

		        // Set all hidden points y values as negatives, in order to move them
		        // away from plot area. It is necessary to avoid hiding data labels,
		        // when dataLabels.allowOverlap is set to false.
		        series.visibilityMap.forEach(function (vis, i) {
		            if (!vis) {
		                xMap.splice(i, 0, series.yData[i] === null ? null : -99);
		            }
		        });

		        series.xData = xMap;
		        series.yData = xMap.map(function (data) {
		            return defined(data) ? 1 : null;
		        });

		        Series.prototype.processData.call(this, arguments);
		    },
		    generatePoints: function () {
		        var series = this;

		        Series.prototype.generatePoints.apply(series);
		        series.points.forEach(function (point, i) {
		            point.applyOptions({
		                x: series.xData[i]
		            });
		        });
		    },
		    getVisibilityMap: function () {
		        var series = this,
		            map = (series.data.length ?
		                    series.data : series.userOptions.data
		                ).map(function (point) {
		                    return (
		                        point &&
		                        point.visible !== false &&
		                        !point.isNull
		                    ) ? point : false;
		                });

		        return map;
		    },
		    distributeDL: function () {
		        var series = this,
		            dataLabelsOptions = series.options.dataLabels,
		            options,
		            pointDLOptions,
		            newOptions = {},
		            visibilityIndex = 1,
		            distance = dataLabelsOptions.distance;

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

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

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

		                options.dataLabels = merge(newOptions, point.userDLOptions);
		                visibilityIndex++;
		            }
		        });
		    },
		    markerAttribs: function (point, state) {
		        var series = this,
		            seriesMarkerOptions = series.options.marker,
		            seriesStateOptions,
		            pointMarkerOptions = point.marker || {},
		            symbol = pointMarkerOptions.symbol || seriesMarkerOptions.symbol,
		            pointStateOptions,
		            width = pick(
		                pointMarkerOptions.width,
		                seriesMarkerOptions.width,
		                series.xAxis.len / series.visiblePointsCount
		            ),
		            height = pick(
		                pointMarkerOptions.height,
		                seriesMarkerOptions.height
		            ),
		            radius = 0,
		            attribs;

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

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

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

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

		        return attribs;

		    },
		    bindAxes: function () {
		        var series = this,
		            timelineXAxis = {
		                gridLineWidth: 0,
		                lineWidth: 0,
		                min: 0,
		                dataMin: 0,
		                minPadding: 0,
		                max: 100,
		                dataMax: 100,
		                maxPadding: 0,
		                title: null,
		                tickPositions: []
		            },
		            timelineYAxis = {
		                gridLineWidth: 0,
		                min: 0.5,
		                dataMin: 0.5,
		                minPadding: 0,
		                max: 1.5,
		                dataMax: 1.5,
		                maxPadding: 0,
		                title: null,
		                labels: {
		                    enabled: false
		                }
		            };
		        Series.prototype.bindAxes.call(series);
		        extend(series.xAxis.options, timelineXAxis);
		        extend(series.yAxis.options, timelineYAxis);
		    }
		}
		/* *
		 * @lends Highcharts.Point#
		 */
		, {
		    init: function () {
		        var point = Point.prototype.init.apply(this, arguments);
		        point.name = pick(point.name, point.date, 'Event');
		        point.y = 1;

		        return point;
		    },
		    // The setVisible method is taken from Pie series prototype, in order to
		    // prevent importing whole Pie series.
		    setVisible: function (vis, redraw) {
		        var point = this,
		            series = point.series,
		            chart = series.chart,
		            ignoreHiddenPoint = series.options.ignoreHiddenPoint;

		        redraw = pick(redraw, ignoreHiddenPoint);

		        if (vis !== point.visible) {

		            // If called without an argument, toggle visibility
		            point.visible = point.options.visible = vis =
		                vis === undefined ? !point.visible : vis;
		            // update userOptions.data
		            series.options.data[series.data.indexOf(point)] = point.options;

		            // Show and hide associated elements. This is performed regardless
		            // of redraw or not, because chart.redraw only handles full series.
		            ['graphic', 'dataLabel', 'connector'].forEach(
		                function (key) {
		                    if (point[key]) {
		                        point[key][vis ? 'show' : 'hide'](true);
		                    }
		                }
		            );

		            if (point.legendItem) {
		                chart.legend.colorizeItem(point, vis);
		            }

		            // #4170, hide halo after hiding point
		            if (!vis && point.state === 'hover') {
		                point.setState('');
		            }

		            // Handle ignore hidden slices
		            if (ignoreHiddenPoint) {
		                series.isDirty = true;
		            }

		            if (redraw) {
		                chart.redraw();
		            }
		        }
		    },
		    setState: function () {
		        var proceed = Series.prototype.pointClass.prototype.setState;

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

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

		        // 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'];
		        }

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

		        return path;
		    },
		    drawConnector: function () {
		        var point = this,
		            series = point.series,
		            dlOptions = point.dataLabel.options = merge(
		                {}, series.options.dataLabels,
		                point.options.dataLabels
		            );

		        point.connector = series.chart.renderer.path(point.getConnectorPath())
		            .add(series.connectorsGroup);

		        if (!series.chart.styledMode) {
		            point.connector.attr({
		                stroke: dlOptions.connectorColor,
		                'stroke-width': dlOptions.connectorWidth,
		                opacity: point.dataLabel.opacity
		            });
		        }
		    },
		    alignConnector: function () {
		        var point = this,
		            connector = point.connector,
		            bBox = connector.getBBox(),
		            isVisible = bBox.y > 0;

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

		// Hide/show connector related with a specific data label, after overlapping
		// detected.
		addEvent(H.Chart, 'afterHideOverlappingLabels', function () {
		    var series = this.series,
		        dataLabel,
		        connector;

		    series.forEach(function (series) {
		        if (series.points) {
		            series.points.forEach(function (point) {
		                dataLabel = point.dataLabel;
		                connector = point.connector;

		                if (
		                    dataLabel &&
		                    dataLabel.targetPosition &&
		                    connector
		                ) {
		                    connector.attr({
		                        opacity: dataLabel.targetPosition.opacity ||
		                            dataLabel.newOpacity
		                    });
		                }
		            });
		        }
		    });
		});

		/* *
		 * 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
		 * @product   highcharts
		 * @apioption series.timeline
		 */

		/* *
		 * An array of data points for the series. For the `timeline` series type,
		 * points can be given with three general parameters, `date`, `label`,
		 * and `description`:
		 *
		 * Example:
		 *
		 * ```js
		 * series: [{
		 *    type: 'timeline',
		 *    data: [{
		 *        date: 'Jan 2018',
		 *        label: 'Some event label',
		 *        description: 'Description to show in tooltip'
		 *    }]
		 * }]
		 * ```
		 *
		 * @sample {highcharts} highcharts/series-timeline/alternate-labels
		 *         Alternate labels
		 *
		 * @type      {Array<number|*>}
		 * @extends   series.line.data
		 * @excluding marker, x, y
		 * @product   highcharts
		 * @apioption series.timeline.data
		 */

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

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

	}(Highcharts));
	return (function () {


	}());
}));
