/**
 * @license Highcharts JS v7.0.1 (2018-12-19)
 * GridAxis
 *
 * (c) 2016-2018 Lars A. V. Cabrera
 *
 * 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) {
		/* *
		 * (c) 2016 Highsoft AS
		 * Authors: Lars A. V. Cabrera
		 *
		 * License: www.highcharts.com/license
		 */



		var argsToArray = function (args) {
		        return Array.prototype.slice.call(args, 1);
		    },
		    dateFormat = H.dateFormat,
		    defined = H.defined,
		    isArray = H.isArray,
		    isNumber = H.isNumber,
		    isObject = function (x) {
		        // Always use strict mode
		        return H.isObject(x, true);
		    },
		    merge = H.merge,
		    pick = H.pick,
		    wrap = H.wrap,
		    Axis = H.Axis,
		    Tick = H.Tick;

		/**
		 * Set grid options for the axis labels. Requires Highcharts Gantt.
		 *
		 * @since     6.2.0
		 * @product   gantt
		 * @apioption xAxis.grid
		 */

		/**
		 * Enable grid on the axis labels. Defaults to true for Gantt charts.
		 *
		 * @type      {boolean}
		 * @default   true
		 * @since     6.2.0
		 * @product   gantt
		 * @apioption xAxis.grid.enabled
		 */

		/**
		 * Set specific options for each column (or row for horizontal axes) in the
		 * grid. Each extra column/row is its own axis, and the axis options can be set
		 * here.
		 *
		 * @sample gantt/demo/left-axis-table
		 *         Left axis as a table
		 *
		 * @type      {Array<Highcharts.XAxisOptions>}
		 * @apioption xAxis.grid.columns
		 */

		/**
		 * Set border color for the label grid lines.
		 *
		 * @type      {Highcharts.ColorString}
		 * @apioption xAxis.grid.borderColor
		 */

		/**
		 * Set border width of the label grid lines.
		 *
		 * @type      {number}
		 * @default   1
		 * @apioption xAxis.grid.borderWidth
		 */

		/**
		 * Set cell height for grid axis labels. By default this is calculated from font
		 * size.
		 *
		 * @type      {number}
		 * @apioption xAxis.grid.cellHeight
		 */


		// Enum for which side the axis is on.
		// Maps to axis.side
		var axisSide = {
		    top: 0,
		    right: 1,
		    bottom: 2,
		    left: 3,
		    0: 'top',
		    1: 'right',
		    2: 'bottom',
		    3: 'left'
		};

		/**
		 * Checks if an axis is a navigator axis.
		 *
		 * @private
		 * @function Highcharts.Axis#isNavigatorAxis
		 *
		 * @return {boolean}
		 *         true if axis is found in axis.chart.navigator
		 */
		Axis.prototype.isNavigatorAxis = function () {
		    return /highcharts-navigator-[xy]axis/.test(this.options.className);
		};

		/**
		 * Checks if an axis is the outer axis in its dimension. Since
		 * axes are placed outwards in order, the axis with the highest
		 * index is the outermost axis.
		 *
		 * Example: If there are multiple x-axes at the top of the chart,
		 * this function returns true if the axis supplied is the last
		 * of the x-axes.
		 *
		 * @private
		 * @function Highcharts.Axis#isOuterAxis
		 *
		 * @return {boolean}
		 *         true if the axis is the outermost axis in its dimension; false if not
		 */
		Axis.prototype.isOuterAxis = function () {
		    var axis = this,
		        chart = axis.chart,
		        thisIndex = -1,
		        isOuter = true;

		    chart.axes.forEach(function (otherAxis, index) {
		        if (otherAxis.side === axis.side && !otherAxis.isNavigatorAxis()) {
		            if (otherAxis === axis) {
		                // Get the index of the axis in question
		                thisIndex = index;

		                // Check thisIndex >= 0 in case thisIndex has
		                // not been found yet
		            } else if (thisIndex >= 0 && index > thisIndex) {
		                // There was an axis on the same side with a
		                // higher index.
		                isOuter = false;
		            }
		        }
		    });
		    // There were either no other axes on the same side,
		    // or the other axes were not farther from the chart
		    return isOuter;
		};

		/**
		 * Get the largest label width and height.
		 *
		 * @private
		 * @function Highcharts.Axis#getMaxLabelDimensions
		 *
		 * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
		 *        All the ticks on one axis.
		 *
		 * @param {Array<number|string>} tickPositions
		 *        All the tick positions on one axis.
		 *
		 * @return {object}
		 *         object containing the properties height and width.
		 */
		Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
		    var dimensions = {
		        width: 0,
		        height: 0
		    };

		    tickPositions.forEach(function (pos) {
		        var tick = ticks[pos],
		            tickHeight = 0,
		            tickWidth = 0,
		            label;

		        if (isObject(tick)) {
		            label = isObject(tick.label) ? tick.label : {};

		            // Find width and height of tick
		            tickHeight = label.getBBox ? label.getBBox().height : 0;
		            tickWidth = isNumber(label.textPxLength) ? label.textPxLength : 0;

		            // Update the result if width and/or height are larger
		            dimensions.height = Math.max(tickHeight, dimensions.height);
		            dimensions.width = Math.max(tickWidth, dimensions.width);
		        }
		    });

		    return dimensions;
		};

		// Add custom date formats
		H.dateFormats = {
		    // Week number
		    W: function (timestamp) {
		        var d = new Date(timestamp),
		            yearStart,
		            weekNo;
		        d.setHours(0, 0, 0, 0);
		        d.setDate(d.getDate() - (d.getDay() || 7));
		        yearStart = new Date(d.getFullYear(), 0, 1);
		        weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
		        return weekNo;
		    },
		    // First letter of the day of the week, e.g. 'M' for 'Monday'.
		    E: function (timestamp) {
		        return dateFormat('%a', timestamp, true).charAt(0);
		    }
		};

		wrap(Axis.prototype, 'autoLabelAlign',
		    /**
		     * If chart is stockChart, always return 'left' to avoid labels being placed
		     * inside chart. Stock charts place yAxis labels inside by default.
		     *
		     * @private
		     * @function
		     *
		     * @param {Function} proceed
		     *        the original function
		     *
		     * @return {string}
		     *         'left' if stockChart, or auto calculated alignment
		     */
		    function (proceed) {
		        var axis = this,
		            retVal;
		        if (axis.chart.isStock) {
		            retVal = 'left';
		        } else {
		            retVal = proceed.apply(axis, argsToArray(arguments));
		        }
		        return retVal;
		    }
		);

		wrap(Tick.prototype, 'getLabelPosition',
		    /**
		     * Center tick labels in cells.
		     *
		     * @private
		     * @function
		     *
		     * @param {Function} proceed
		     *        the original function
		     *
		     * @return {object}
		     *         an object containing x and y positions for the tick
		     */
		    function (
		        proceed,
		        x,
		        y,
		        label,
		        horiz,
		        labelOpts,
		        tickmarkOffset,
		        index
		    ) {
		        var tick = this,
		            axis = tick.axis,
		            reversed = axis.reversed,
		            chart = axis.chart,
		            options = axis.options,
		            gridOptions = (
		                (options && isObject(options.grid)) ? options.grid : {}
		            ),
		            align = labelOpts.align,
		            // verticalAlign is currently not supported for axis.labels.
		            verticalAlign = 'middle', // labelOpts.verticalAlign,
		            side = axisSide[axis.side],
		            tickPositions = axis.tickPositions,
		            tickPos = tick.pos - tickmarkOffset,
		            nextTickPos = (
		                isNumber(tickPositions[index + 1]) ?
		                tickPositions[index + 1] - tickmarkOffset :
		                axis.max + tickmarkOffset
		            ),
		            tickSize = axis.tickSize('tick', true),
		            tickWidth = isArray(tickSize) ? tickSize[0] : 0,
		            crispCorr = tickSize && tickSize[1] / 2,
		            labelHeight,
		            lblMetrics,
		            lines,
		            result,
		            bottom,
		            top,
		            left,
		            right;

		        // Only center tick labels in grid axes
		        if (gridOptions.enabled === true) {
		            // Calculate top and bottom positions of the cell.
		            if (side === 'top') {
		                bottom = axis.top + axis.offset;
		                top = bottom - tickWidth;
		            } else if (side === 'bottom') {
		                top = chart.chartHeight - axis.bottom + axis.offset;
		                bottom = top + tickWidth;
		            } else {
		                bottom = axis.top + axis.len - axis.translate(
		                    reversed ? nextTickPos : tickPos
		                );
		                top = axis.top + axis.len - axis.translate(
		                    reversed ? tickPos : nextTickPos
		                );
		            }

		            // Calculate left and right positions of the cell.
		            if (side === 'right') {
		                left = chart.chartWidth - axis.right + axis.offset;
		                right = left + tickWidth;
		            } else if (side === 'left') {
		                right = axis.left + axis.offset;
		                left = right - tickWidth;
		            } else {
		                left = Math.round(axis.left + axis.translate(
		                    reversed ? nextTickPos : tickPos
		                )) - crispCorr;
		                right = Math.round(axis.left + axis.translate(
		                    reversed ? tickPos : nextTickPos
		                )) - crispCorr;
		            }

		            tick.slotWidth = right - left;

		            // Calculate the positioning of the label based on alignment.
		            result = {
		                x: (
		                    align === 'left' ?
		                    left :
		                    align === 'right' ?
		                    right :
		                    left + ((right - left) / 2) // default to center
		                ),
		                y: (
		                    verticalAlign === 'top' ?
		                    top :
		                    verticalAlign === 'bottom' ?
		                    bottom :
		                    top + ((bottom - top) / 2) // default to middle
		                )
		            };

		            lblMetrics = chart.renderer.fontMetrics(
		                labelOpts.style.fontSize,
		                label.element
		            );
		            labelHeight = label.getBBox().height;

		            // Adjustment to y position to align the label correctly.
		            // Would be better to have a setter or similar for this.
		            if (!labelOpts.useHTML) {
		                lines = Math.round(labelHeight / lblMetrics.h);
		                result.y += (
		                    // Center the label
		                    // TODO: why does this actually center the label?
		                    ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
		                    // Adjust for height of additional lines.
		                    -(((lines - 1) * lblMetrics.h) / 2)
		                );
		            } else {
		                result.y += (
		                    // Readjust yCorr in htmlUpdateTransform
		                    lblMetrics.b +
		                    // Adjust for height of html label
		                    -(labelHeight / 2)
		                );
		            }

		            result.x += (axis.horiz && labelOpts.x || 0);
		        } else {
		            result = proceed.apply(tick, argsToArray(arguments));
		        }
		        return result;
		    }
		);

		/**
		 * Draw vertical axis ticks extra long to create cell floors and roofs.
		 * Overrides the tickLength for vertical axes.
		 *
		 * @private
		 * @function
		 *
		 * @param {Function} proceed
		 *        the original function
		 *
		 * @returns {Array<number>}
		 */
		wrap(Axis.prototype, 'tickSize', function (proceed) {
		    var axis = this,
		        dimensions = axis.maxLabelDimensions,
		        options = axis.options,
		        gridOptions = (options && isObject(options.grid)) ? options.grid : {},
		        retVal = proceed.apply(axis, argsToArray(arguments)),
		        labelPadding,
		        distance;

		    if (gridOptions.enabled === true) {
		        labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
		        distance = labelPadding +
		            (axis.horiz ? dimensions.height : dimensions.width);

		        if (isArray(retVal)) {
		            retVal[0] = distance;
		        } else {
		            retVal = [distance];
		        }
		    }
		    return retVal;
		});

		wrap(Axis.prototype, 'getTitlePosition', function (proceed) {
		    var axis = this,
		        options = axis.options,
		        gridOptions = (options && isObject(options.grid)) ? options.grid : {};

		    if (gridOptions.enabled === true) {
		        // compute anchor points for each of the title align options
		        var title = axis.axisTitle,
		            titleWidth = title && title.getBBox().width,
		            horiz = axis.horiz,
		            axisLeft = axis.left,
		            axisTop = axis.top,
		            axisWidth = axis.width,
		            axisHeight = axis.height,
		            axisTitleOptions = options.title,
		            opposite = axis.opposite,
		            offset = axis.offset,
		            tickSize = axis.tickSize() || [0],
		            xOption = axisTitleOptions.x || 0,
		            yOption = axisTitleOptions.y || 0,
		            titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10),
		            titleFontSize = axis.chart.renderer.fontMetrics(
		                axisTitleOptions.style && axisTitleOptions.style.fontSize,
		                title
		            ).f,
		            // TODO account for alignment
		            // the position in the perpendicular direction of the axis
		            offAxis = (horiz ? axisTop + axisHeight : axisLeft) +
		                (horiz ? 1 : -1) * // horizontal axis reverses the margin
		                (opposite ? -1 : 1) * // so does opposite axes
		                (tickSize[0] / 2) +
		                (axis.side === axisSide.bottom ? titleFontSize : 0);

		        return {
		            x: horiz ?
		                axisLeft - titleWidth / 2 - titleMargin + xOption :
		                offAxis + (opposite ? axisWidth : 0) + offset + xOption,
		            y: horiz ?
		                (
		                    offAxis -
		                    (opposite ? axisHeight : 0) +
		                    (opposite ? titleFontSize : -titleFontSize) / 2 +
		                    offset +
		                    yOption
		                ) :
		                axisTop - titleMargin + yOption
		        };
		    }

		    return proceed.apply(this, argsToArray(arguments));
		});

		// Avoid altering tickInterval when reserving space.
		wrap(Axis.prototype, 'unsquish', function (proceed) {
		    var axis = this,
		        options = axis.options,
		        gridOptions = (options && isObject(options.grid)) ? options.grid : {};

		    if (gridOptions.enabled === true && this.categories) {
		        return this.tickInterval;
		    }

		    return proceed.apply(this, argsToArray(arguments));
		});

		H.addEvent(Axis, 'afterSetOptions',
		    /**
		     * Creates a left and right wall on horizontal axes:
		     *
		     * - Places leftmost tick at the start of the axis, to create a left wall
		     *
		     * - Ensures that the rightmost tick is at the end of the axis, to create a
		     *   right wall.
		     *
		     * @private
		     * @function
		     */
		    function (e) {
		        var options = this.options,
		            userOptions = e.userOptions,
		            gridAxisOptions,
		            gridOptions = (
		                (options && isObject(options.grid)) ? options.grid : {}
		            );

		        if (gridOptions.enabled === true) {

		            // Merge the user options into default grid axis options so that
		            // when a user option is set, it takes presedence.
		            gridAxisOptions = merge(true, {

		                className: (
		                    'highcharts-grid-axis ' + (userOptions.className || '')
		                ),

		                dateTimeLabelFormats: {
		                    hour: {
		                        list: ['%H:%M', '%H']
		                    },
		                    day: {
		                        list: ['%A, %e. %B', '%a, %e. %b', '%E']
		                    },
		                    week: {
		                        list: ['Week %W', 'W%W']
		                    },
		                    month: {
		                        list: ['%B', '%b', '%o']
		                    }
		                },

		                grid: {
		                    borderWidth: 1
		                },

		                labels: {
		                    padding: 2,
		                    style: {
		                        fontSize: '13px'
		                    }
		                },

		                title: {
		                    text: null,
		                    reserveSpace: false,
		                    rotation: 0
		                },

		                // In a grid axis, only allow one unit of certain types, for
		                // example we shouln't have one grid cell spanning two days.
		                units: [[
		                    'millisecond', // unit name
		                    [1, 10, 100]
		                ], [
		                    'second',
		                    [1, 10]
		                ], [
		                    'minute',
		                    [1, 5, 15]
		                ], [
		                    'hour',
		                    [1, 6]
		                ], [
		                    'day',
		                    [1]
		                ], [
		                    'week',
		                    [1]
		                ], [
		                    'month',
		                    [1]
		                ], [
		                    'year',
		                    null
		                ]]
		            }, userOptions);

		            // X-axis specific options
		            if (this.coll === 'xAxis') {

		                // For linked axes, tickPixelInterval is used only if the
		                // tickPositioner below doesn't run or returns undefined (like
		                // multiple years)
		                if (
		                    defined(userOptions.linkedTo) &&
		                    !defined(userOptions.tickPixelInterval)
		                ) {
		                    gridAxisOptions.tickPixelInterval = 350;
		                }

		                // For the secondary grid axis, use the primary axis' tick
		                // intervals and return ticks one level higher.
		                if (
		                    // Check for tick pixel interval in options
		                    !defined(userOptions.tickPixelInterval) &&

		                    // Only for linked axes
		                    defined(userOptions.linkedTo) &&

		                    !defined(userOptions.tickPositioner) &&
		                    !defined(userOptions.tickInterval)
		                ) {
		                    gridAxisOptions.tickPositioner = function (min, max) {

		                        var parentInfo = (
		                            this.linkedParent &&
		                            this.linkedParent.tickPositions &&
		                            this.linkedParent.tickPositions.info
		                        );

		                        if (parentInfo) {

		                            var unitIdx,
		                                count,
		                                unitName,
		                                i,
		                                units = gridAxisOptions.units,
		                                unitRange;

		                            for (i = 0; i < units.length; i++) {
		                                if (units[i][0] === parentInfo.unitName) {
		                                    unitIdx = i;
		                                    break;
		                                }
		                            }

		                            // Spanning multiple years, go default
		                            if (!units[unitIdx][1]) {
		                                return;
		                            }

		                            // Get the first allowed count on the next unit.
		                            if (units[unitIdx + 1]) {
		                                unitName = units[unitIdx + 1][0];
		                                count = (units[unitIdx + 1][1] || [1])[0];
		                            }

		                            unitRange = H.timeUnits[unitName];
		                            this.tickInterval = unitRange * count;
		                            return this.getTimeTicks(
		                                {
		                                    unitRange: unitRange,
		                                    count: count,
		                                    unitName: unitName
		                                },
		                                min,
		                                max,
		                                this.options.startOfWeek
		                            );
		                        }
		                    };
		                }

		            }

		            // Now merge the combined options into the axis options
		            merge(true, this.options, gridAxisOptions);

		            if (this.horiz) {
		                /*               _________________________
		                   Make this:    ___|_____|_____|_____|__|
		                                 ^                     ^
		                                 _________________________
		                   Into this:    |_____|_____|_____|_____|
		                                    ^                 ^    */
		                options.minPadding = pick(userOptions.minPadding, 0);
		                options.maxPadding = pick(userOptions.maxPadding, 0);
		            }

		            // If borderWidth is set, then use its value for tick and line
		            // width.
		            if (isNumber(options.grid.borderWidth)) {
		                options.tickWidth = options.lineWidth = gridOptions.borderWidth;
		            }

		        }
		    }
		);

		wrap(Axis.prototype, 'setAxisTranslation',
		    /**
		     * Ensures a left wall on horizontal axes with series inheriting from
		     * column. ColumnSeries normally sets pointRange to null, resulting in Axis
		     * to select other values for point ranges. This enforces the above
		     * Axis.setOptions() override.
		     * ```
		     *                  _________________________
		     * Enforce this:    ___|_____|_____|_____|__|
		     *                  ^
		     *                  _________________________
		     * To be this:      |_____|_____|_____|_____|
		     *                  ^
		     * ```
		     *
		     * @private
		     * @function
		     *
		     * @param {Function} proceed
		     *        the original function
		     */
		    function (proceed) {
		        var axis = this,
		            options = axis.options,
		            gridOptions = (
		                (options && isObject(options.grid)) ? options.grid : {}
		            ),
		            tickInfo = this.tickPositions && this.tickPositions.info,
		            userLabels = this.userOptions.labels || {};

		        if (this.horiz) {
		            if (gridOptions.enabled === true) {
		                axis.series.forEach(function (series) {
		                    series.options.pointRange = 0;
		                });
		            }

		            // Lower level time ticks, like hours or minutes, represent points
		            // in time and not ranges. These should be aligned left in the grid
		            // cell by default. The same applies to years of higher order.
		            if (
		                tickInfo &&
		                (
		                    options.dateTimeLabelFormats[tickInfo.unitName]
		                        .range === false ||
		                    tickInfo.count > 1 // years
		                ) &&
		                !defined(userLabels.align)
		            ) {
		                options.labels.align = 'left';

		                if (!defined(userLabels.x)) {
		                    options.labels.x = 3;
		                }
		            }
		        }

		        proceed.apply(axis, argsToArray(arguments));
		    }
		);

		// @todo Does this function do what the drawing says? Seems to affect ticks and
		//       not the labels directly?
		wrap(Axis.prototype, 'trimTicks',
		    /**
		     * Makes tick labels which are usually ignored in a linked axis displayed if
		     * they are within range of linkedParent.min.
		     * ```
		     *                        _____________________________
		     *                        |   |       |       |       |
		     * Make this:             |   |   2   |   3   |   4   |
		     *                        |___|_______|_______|_______|
		     *                          ^
		     *                        _____________________________
		     *                        |   |       |       |       |
		     * Into this:             | 1 |   2   |   3   |   4   |
		     *                        |___|_______|_______|_______|
		     *                          ^
		     * ```
		     *
		     * @private
		     * @function
		     *
		     * @param {Function} proceed
		     *        the original function
		     */
		    function (proceed) {
		        var axis = this,
		            options = axis.options,
		            gridOptions = (
		                (options && isObject(options.grid)) ? options.grid : {}
		            ),
		            categoryAxis = axis.categories,
		            tickPositions = axis.tickPositions,
		            firstPos = tickPositions[0],
		            lastPos = tickPositions[tickPositions.length - 1],
		            linkedMin = axis.linkedParent && axis.linkedParent.min,
		            linkedMax = axis.linkedParent && axis.linkedParent.max,
		            min = linkedMin || axis.min,
		            max = linkedMax || axis.max,
		            tickInterval = axis.tickInterval,
		            moreThanMin = firstPos > min,
		            lessThanMax = lastPos < max,
		            endMoreThanMin = firstPos < min && firstPos + tickInterval > min,
		            startLessThanMax = lastPos > max && lastPos - tickInterval < max;

		        if (
		            gridOptions.enabled === true &&
		            !categoryAxis &&
		            (axis.horiz || axis.isLinked)
		        ) {
		            if ((moreThanMin || endMoreThanMin) && !options.startOnTick) {
		                tickPositions[0] = min;
		            }

		            if ((lessThanMax || startLessThanMax) && !options.endOnTick) {
		                tickPositions[tickPositions.length - 1] = max;
		            }
		        }

		        proceed.apply(axis, argsToArray(arguments));
		    }
		);

		wrap(Axis.prototype, 'render',
		    /**
		     * Draw an extra line on the far side of the outermost axis,
		     * creating floor/roof/wall of a grid. And some padding.
		     * ```
		     * Make this:
		     *             (axis.min) __________________________ (axis.max)
		     *                           |    |    |    |    |
		     * Into this:
		     *             (axis.min) __________________________ (axis.max)
		     *                        ___|____|____|____|____|__
		     * ```
		     *
		     * @private
		     * @function
		     *
		     * @param {Function} proceed
		     *        the original function
		     */
		    function (proceed) {
		        var axis = this,
		            options = axis.options,
		            gridOptions = ((
		                options && isObject(options.grid)) ? options.grid : {}
		            ),
		            labelPadding,
		            distance,
		            lineWidth,
		            linePath,
		            yStartIndex,
		            yEndIndex,
		            xStartIndex,
		            xEndIndex,
		            renderer = axis.chart.renderer,
		            horiz = axis.horiz,
		            axisGroupBox;

		        if (gridOptions.enabled === true) {

		            // @todo acutual label padding (top, bottom, left, right)

		            // Label padding is needed to figure out where to draw the outer
		            // line.
		            labelPadding = (Math.abs(axis.defaultLeftAxisOptions.labels.x) * 2);
		            axis.maxLabelDimensions = axis.getMaxLabelDimensions(
		                axis.ticks,
		                axis.tickPositions
		            );
		            distance = axis.maxLabelDimensions.width + labelPadding;
		            lineWidth = options.lineWidth;

		            // Remove right wall before rendering if updating
		            if (axis.rightWall) {
		                axis.rightWall.destroy();
		            }

		            // Call original Axis.render() to obtain axis.axisLine and
		            // axis.axisGroup
		            proceed.apply(axis);

		            axisGroupBox = axis.axisGroup.getBBox();

		            /*
		               Draw an extra axis line on outer axes
		                           >
		               Make this:    |______|______|______|___

		                           > _________________________
		               Into this:    |______|______|______|__|
		                                                       */
		            if (axis.isOuterAxis() && axis.axisLine) {
		                if (horiz) {
		                    // -1 to avoid adding distance each time the chart updates
		                    distance = axisGroupBox.height - 1;
		                }

		                if (lineWidth) {
		                    linePath = axis.getLinePath(lineWidth);
		                    xStartIndex = linePath.indexOf('M') + 1;
		                    xEndIndex = linePath.indexOf('L') + 1;
		                    yStartIndex = linePath.indexOf('M') + 2;
		                    yEndIndex = linePath.indexOf('L') + 2;

		                    // Negate distance if top or left axis
		                    if (axis.side === axisSide.top ||
		                        axis.side === axisSide.left
		                    ) {
		                        distance = -distance;
		                    }

		                    // If axis is horizontal, reposition line path vertically
		                    if (horiz) {
		                        linePath[yStartIndex] = (
		                            linePath[yStartIndex] + distance
		                        );
		                        linePath[yEndIndex] = linePath[yEndIndex] + distance;
		                    } else {
		                        // If axis is vertical, reposition line path
		                        // horizontally
		                        linePath[xStartIndex] = (
		                            linePath[xStartIndex] + distance
		                        );
		                        linePath[xEndIndex] = linePath[xEndIndex] + distance;
		                    }

		                    if (!axis.axisLineExtra) {
		                        axis.axisLineExtra = renderer.path(linePath)
		                            .attr({
                                
		                                stroke: options.lineColor,
		                                'stroke-width': lineWidth,
                                
		                                zIndex: 7
		                            })
		                            .addClass('highcharts-axis-line')
		                            .add(axis.axisGroup);
		                    } else {
		                        axis.axisLineExtra.animate({
		                            d: linePath
		                        });
		                    }

		                    // show or hide the line depending on options.showEmpty
		                    axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
		                }
		            }

		        } else {
		            proceed.apply(axis);
		        }
		    }
		);

		// Wraps axis init to draw cell walls on vertical axes.
		wrap(Axis.prototype, 'init', function (proceed, chart, userOptions) {
		    var axis = this,
		        gridOptions = (
		            (userOptions && isObject(userOptions.grid)) ?
		            userOptions.grid :
		            {}
		        ),
		        columnOptions,
		        column,
		        columnIndex,
		        i;
		    function applyGridOptions(axis) {
		        var options = axis.options,
		            // TODO: Consider using cell margins defined in % of font size?
		            // 25 is optimal height for default fontSize (11px)
		            // 25 / 11 ≈ 2.28
		            fontSizeToCellHeightRatio = 25 / 11,
		            fontSize = options.labels.style.fontSize,
		            fontMetrics = axis.chart.renderer.fontMetrics(fontSize);

		        // Center-align by default
		        if (!options.labels) {
		            options.labels = {};
		        }
		        options.labels.align = pick(options.labels.align, 'center');

		        // @todo: Check against tickLabelPlacement between/on etc

		        /* Prevents adding the last tick label if the axis is not a category
		           axis.
		           Since numeric labels are normally placed at starts and ends of a
		           range of value, and this module makes the label point at the value,
		           an "extra" label would appear. */
		        if (!axis.categories) {
		            options.showLastLabel = false;
		        }

		        // Make tick marks taller, creating cell walls of a grid. Use cellHeight
		        // axis option if set
		        if (axis.horiz) {
		            options.tickLength = gridOptions.cellHeight ||
		                    fontMetrics.h * fontSizeToCellHeightRatio;
		        }

		        // Prevents rotation of labels when squished, as rotating them would not
		        // help.
		        axis.labelRotation = 0;
		        options.labels.rotation = 0;
		    }

		    if (gridOptions.enabled) {
		        if (defined(gridOptions.borderColor)) {
		            userOptions.tickColor =
		                userOptions.lineColor = gridOptions.borderColor;
		        }

		        // Handle columns, each column is a grid axis
		        if (isArray(gridOptions.columns)) {
		            columnIndex = 0;
		            i = gridOptions.columns.length;
		            while (i--) {
		                columnOptions = merge(
		                    userOptions,
		                    gridOptions.columns[i],
		                    {
		                        // Force to behave like category axis
		                        type: 'category'
		                    }
		                );

		                delete columnOptions.grid.columns; // Prevent recursion

		                column = new Axis(chart, columnOptions);
		                column.isColumn = true;
		                column.columnIndex = columnIndex;

		                wrap(column, 'labelFormatter', function (proceed) {
		                    var axis = this.axis,
		                        tickPos = axis.tickPositions,
		                        value = this.value,
		                        series = axis.series[0],
		                        isFirst = value === tickPos[0],
		                        isLast = value === tickPos[tickPos.length - 1],
		                        point = H.find(series.options.data, function (p) {
		                            return p[axis.isXAxis ? 'x' : 'y'] === value;
		                        });

		                    // Make additional properties available for the formatter
		                    this.isFirst = isFirst;
		                    this.isLast = isLast;
		                    this.point = point;

		                    // Call original labelFormatter
		                    return proceed.call(this);
		                });

		                columnIndex++;
		            }
		        } else {
		            // Call original Axis.init()
		            proceed.apply(axis, argsToArray(arguments));
		            applyGridOptions(axis);
		        }
		    } else {
		        // Call original Axis.init()
		        proceed.apply(axis, argsToArray(arguments));
		    }
		});

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


	}());
}));
