text: /**
   * @license Highcharts Gantt JS v7.2.1 (2019-10-31)
   *
   * Pathfinder
   *
   * (c) 2016-2019 Øystein Moseng
   *
   * License: www.highcharts.com/license
   */
  'use strict';
  (function (factory) {
      if (typeof module === 'object' && module.exports) {
          factory['default'] = factory;
          module.exports = factory;
      } else if (typeof define === 'function' && define.amd) {
          define('highcharts/modules/pathfinder', ['highcharts'], function (Highcharts) {
              factory(Highcharts);
              factory.Highcharts = Highcharts;
              return factory;
          });
      } else {
          factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
      }
  }(function (Highcharts) {
      var _modules = Highcharts ? Highcharts._modules : {};
      function _registerModule(obj, path, args, fn) {
          if (!obj.hasOwnProperty(path)) {
              obj[path] = fn.apply(null, args);
          }
      }
      _registerModule(_modules, 'parts-gantt/PathfinderAlgorithms.js', [_modules['parts/Utilities.js']], function (U) {
          /* *
           *
           *  (c) 2016 Highsoft AS
           *  Author: Øystein Moseng
           *
           *  License: www.highcharts.com/license
           *
           *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
           *
           * */
          var extend = U.extend, pick = U.pick;
          var min = Math.min, max = Math.max, abs = Math.abs;
          /**
           * Get index of last obstacle before xMin. Employs a type of binary search, and
           * thus requires that obstacles are sorted by xMin value.
           *
           * @private
           * @function findLastObstacleBefore
           *
           * @param {Array<object>} obstacles
           *        Array of obstacles to search in.
           *
           * @param {number} xMin
           *        The xMin threshold.
           *
           * @param {number} [startIx]
           *        Starting index to search from. Must be within array range.
           *
           * @return {number}
           *         The index of the last obstacle element before xMin.
           */
          function findLastObstacleBefore(obstacles, xMin, startIx) {
              var left = startIx || 0, // left limit
              right = obstacles.length - 1, // right limit
              min = xMin - 0.0000001, // Make sure we include all obstacles at xMin
              cursor, cmp;
              while (left <= right) {
                  cursor = (right + left) >> 1;
                  cmp = min - obstacles[cursor].xMin;
                  if (cmp > 0) {
                      left = cursor + 1;
                  }
                  else if (cmp < 0) {
                      right = cursor - 1;
                  }
                  else {
                      return cursor;
                  }
              }
              return left > 0 ? left - 1 : 0;
          }
          /**
           * Test if a point lays within an obstacle.
           *
           * @private
           * @function pointWithinObstacle
           *
           * @param {object} obstacle
           *        Obstacle to test.
           *
           * @param {Highcharts.Point} point
           *        Point with x/y props.
           *
           * @return {boolean}
           *         Whether point is within the obstacle or not.
           */
          function pointWithinObstacle(obstacle, point) {
              return (point.x <= obstacle.xMax &&
                  point.x >= obstacle.xMin &&
                  point.y <= obstacle.yMax &&
                  point.y >= obstacle.yMin);
          }
          /**
           * Find the index of an obstacle that wraps around a point.
           * Returns -1 if not found.
           *
           * @private
           * @function findObstacleFromPoint
           *
           * @param {Array<object>} obstacles
           *        Obstacles to test.
           *
           * @param {Highcharts.Point} point
           *        Point with x/y props.
           *
           * @return {number}
           *         Ix of the obstacle in the array, or -1 if not found.
           */
          function findObstacleFromPoint(obstacles, point) {
              var i = findLastObstacleBefore(obstacles, point.x + 1) + 1;
              while (i--) {
                  if (obstacles[i].xMax >= point.x &&
                      // optimization using lazy evaluation
                      pointWithinObstacle(obstacles[i], point)) {
                      return i;
                  }
              }
              return -1;
          }
          /**
           * Get SVG path array from array of line segments.
           *
           * @private
           * @function pathFromSegments
           *
           * @param {Array<object>} segments
           *        The segments to build the path from.
           *
           * @return {Highcharts.SVGPathArray}
           *         SVG path array as accepted by the SVG Renderer.
           */
          function pathFromSegments(segments) {
              var path = [];
              if (segments.length) {
                  path.push('M', segments[0].start.x, segments[0].start.y);
                  for (var i = 0; i < segments.length; ++i) {
                      path.push('L', segments[i].end.x, segments[i].end.y);
                  }
              }
              return path;
          }
          /**
           * Limits obstacle max/mins in all directions to bounds. Modifies input
           * obstacle.
           *
           * @private
           * @function limitObstacleToBounds
           *
           * @param {object} obstacle
           *        Obstacle to limit.
           *
           * @param {object} bounds
           *        Bounds to use as limit.
           *
           * @return {void}
           */
          function limitObstacleToBounds(obstacle, bounds) {
              obstacle.yMin = max(obstacle.yMin, bounds.yMin);
              obstacle.yMax = min(obstacle.yMax, bounds.yMax);
              obstacle.xMin = max(obstacle.xMin, bounds.xMin);
              obstacle.xMax = min(obstacle.xMax, bounds.xMax);
          }
          // Define the available pathfinding algorithms.
          // Algorithms take up to 3 arguments: starting point, ending point, and an
          // options object.
          var algorithms = {
              /**
               * Get an SVG path from a starting coordinate to an ending coordinate.
               * Draws a straight line.
               *
               * @function Highcharts.Pathfinder.algorithms.straight
               *
               * @param {Highcharts.PositionObject} start
               *        Starting coordinate, object with x/y props.
               *
               * @param {Highcharts.PositionObject} end
               *        Ending coordinate, object with x/y props.
               *
               * @return {object}
               *         An object with the SVG path in Array form as accepted by the SVG
               *         renderer, as well as an array of new obstacles making up this
               *         path.
               */
              straight: function (start, end) {
                  return {
                      path: ['M', start.x, start.y, 'L', end.x, end.y],
                      obstacles: [{ start: start, end: end }]
                  };
              },
              /**
               * Find a path from a starting coordinate to an ending coordinate, using
               * right angles only, and taking only starting/ending obstacle into
               * consideration.
               *
               * @function Highcharts.Pathfinder.algorithms.simpleConnect
               *
               * @param {Highcharts.PositionObject} start
               *        Starting coordinate, object with x/y props.
               *
               * @param {Highcharts.PositionObject} end
               *        Ending coordinate, object with x/y props.
               *
               * @param {object} options
               *        Options for the algorithm:
               *        - chartObstacles: Array of chart obstacles to avoid
               *        - startDirectionX: Optional. True if starting in the X direction.
               *          If not provided, the algorithm starts in the direction that is
               *          the furthest between start/end.
               *
               * @return {object}
               *         An object with the SVG path in Array form as accepted by the SVG
               *         renderer, as well as an array of new obstacles making up this
               *         path.
               */
              simpleConnect: extend(function (start, end, options) {
                  var segments = [], endSegment, dir = pick(options.startDirectionX, abs(end.x - start.x) > abs(end.y - start.y)) ? 'x' : 'y', chartObstacles = options.chartObstacles, startObstacleIx = findObstacleFromPoint(chartObstacles, start), endObstacleIx = findObstacleFromPoint(chartObstacles, end), startObstacle, endObstacle, prevWaypoint, waypoint, waypoint2, useMax, endPoint;
                  // eslint-disable-next-line valid-jsdoc
                  /**
                   * Return a clone of a point with a property set from a target object,
                   * optionally with an offset
                   * @private
                   */
                  function copyFromPoint(from, fromKey, to, toKey, offset) {
                      var point = {
                          x: from.x,
                          y: from.y
                      };
                      point[fromKey] = to[toKey || fromKey] + (offset || 0);
                      return point;
                  }
                  // eslint-disable-next-line valid-jsdoc
                  /**
                   * Return waypoint outside obstacle.
                   * @private
                   */
                  function getMeOut(obstacle, point, direction) {
                      var useMax = abs(point[direction] - obstacle[direction + 'Min']) >
                          abs(point[direction] - obstacle[direction + 'Max']);
                      return copyFromPoint(point, direction, obstacle, direction + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1);
                  }
                  // Pull out end point
                  if (endObstacleIx > -1) {
                      endObstacle = chartObstacles[endObstacleIx];
                      waypoint = getMeOut(endObstacle, end, dir);
                      endSegment = {
                          start: waypoint,
                          end: end
                      };
                      endPoint = waypoint;
                  }
                  else {
                      endPoint = end;
                  }
                  // If an obstacle envelops the start point, add a segment to get out,
                  // and around it.
                  if (startObstacleIx > -1) {
                      startObstacle = chartObstacles[startObstacleIx];
                      waypoint = getMeOut(startObstacle, start, dir);
                      segments.push({
                          start: start,
                          end: waypoint
                      });
                      // If we are going back again, switch direction to get around start
                      // obstacle.
                      if (
                      // Going towards max from start:
                      waypoint[dir] >= start[dir] ===
                          // Going towards min to end:
                          waypoint[dir] >= endPoint[dir]) {
                          dir = dir === 'y' ? 'x' : 'y';
                          useMax = start[dir] < end[dir];
                          segments.push({
                              start: waypoint,
                              end: copyFromPoint(waypoint, dir, startObstacle, dir + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1)
                          });
                          // Switch direction again
                          dir = dir === 'y' ? 'x' : 'y';
                      }
                  }
                  // We are around the start obstacle. Go towards the end in one
                  // direction.
                  prevWaypoint = segments.length ?
                      segments[segments.length - 1].end :
                      start;
                  waypoint = copyFromPoint(prevWaypoint, dir, endPoint);
                  segments.push({
                      start: prevWaypoint,
                      end: waypoint
                  });
                  // Final run to end point in the other direction
                  dir = dir === 'y' ? 'x' : 'y';
                  waypoint2 = copyFromPoint(waypoint, dir, endPoint);
                  segments.push({
                      start: waypoint,
                      end: waypoint2
                  });
                  // Finally add the endSegment
                  segments.push(endSegment);
                  return {
                      path: pathFromSegments(segments),
                      obstacles: segments
                  };
              }, {
                  requiresObstacles: true
              }),
              /**
               * Find a path from a starting coordinate to an ending coordinate, taking
               * obstacles into consideration. Might not always find the optimal path,
               * but is fast, and usually good enough.
               *
               * @function Highcharts.Pathfinder.algorithms.fastAvoid
               *
               * @param {Highcharts.PositionObject} start
               *        Starting coordinate, object with x/y props.
               *
               * @param {Highcharts.PositionObject} end
               *        Ending coordinate, object with x/y props.
               *
               * @param {object} options
               *        Options for the algorithm.
               *        - chartObstacles:  Array of chart obstacles to avoid
               *        - lineObstacles:   Array of line obstacles to jump over
               *        - obstacleMetrics: Object with metrics of chartObstacles cached
               *        - hardBounds:      Hard boundaries to not cross
               *        - obstacleOptions: Options for the obstacles, including margin
               *        - startDirectionX: Optional. True if starting in the X direction.
               *                           If not provided, the algorithm starts in the
               *                           direction that is the furthest between
               *                           start/end.
               *
               * @return {object}
               *         An object with the SVG path in Array form as accepted by the SVG
               *         renderer, as well as an array of new obstacles making up this
               *         path.
               */
              fastAvoid: extend(function (start, end, options) {
                  /*
                      Algorithm rules/description
                      - Find initial direction
                      - Determine soft/hard max for each direction.
                      - Move along initial direction until obstacle.
                      - Change direction.
                      - If hitting obstacle, first try to change length of previous line
                        before changing direction again.
  
                      Soft min/max x = start/destination x +/- widest obstacle + margin
                      Soft min/max y = start/destination y +/- tallest obstacle + margin
  
                      @todo:
                          - Make retrospective, try changing prev segment to reduce
                            corners
                          - Fix logic for breaking out of end-points - not always picking
                            the best direction currently
                          - When going around the end obstacle we should not always go the
                            shortest route, rather pick the one closer to the end point
                  */
                  var dirIsX = pick(options.startDirectionX, abs(end.x - start.x) > abs(end.y - start.y)), dir = dirIsX ? 'x' : 'y', segments, useMax, extractedEndPoint, endSegments = [], forceObstacleBreak = false, // Used in clearPathTo to keep track of
                  // when to force break through an obstacle.
                  // Boundaries to stay within. If beyond soft boundary, prefer to
                  // change direction ASAP. If at hard max, always change immediately.
                  metrics = options.obstacleMetrics, softMinX = min(start.x, end.x) - metrics.maxWidth - 10, softMaxX = max(start.x, end.x) + metrics.maxWidth + 10, softMinY = min(start.y, end.y) - metrics.maxHeight - 10, softMaxY = max(start.y, end.y) + metrics.maxHeight + 10, 
                  // Obstacles
                  chartObstacles = options.chartObstacles, startObstacleIx = findLastObstacleBefore(chartObstacles, softMinX), endObstacleIx = findLastObstacleBefore(chartObstacles, softMaxX);
                  // eslint-disable-next-line valid-jsdoc
                  /**
                   * How far can you go between two points before hitting an obstacle?
                   * Does not work for diagonal lines (because it doesn't have to).
                   * @private
                   */
                  function pivotPoint(fromPoint, toPoint, directionIsX) {
                      var firstPoint, lastPoint, highestPoint, lowestPoint, i, searchDirection = fromPoint.x < toPoint.x ? 1 : -1;
                      if (fromPoint.x < toPoint.x) {
                          firstPoint = fromPoint;
                          lastPoint = toPoint;
                      }
                      else {
                          firstPoint = toPoint;
                          lastPoint = fromPoint;
                      }
                      if (fromPoint.y < toPoint.y) {
                          lowestPoint = fromPoint;
                          highestPoint = toPoint;
                      }
                      else {
                          lowestPoint = toPoint;
                          highestPoint = fromPoint;
                      }
                      // Go through obstacle range in reverse if toPoint is before
                      // fromPoint in the X-dimension.
                      i = searchDirection < 0 ?
                          // Searching backwards, start at last obstacle before last point
                          min(findLastObstacleBefore(chartObstacles, lastPoint.x), chartObstacles.length - 1) :
                          // Forwards. Since we're not sorted by xMax, we have to look
                          // at all obstacles.
                          0;
                      // Go through obstacles in this X range
                      while (chartObstacles[i] && (searchDirection > 0 && chartObstacles[i].xMin <= lastPoint.x ||
                          searchDirection < 0 && chartObstacles[i].xMax >= firstPoint.x)) {
                          // If this obstacle is between from and to points in a straight
                          // line, pivot at the intersection.
                          if (chartObstacles[i].xMin <= lastPoint.x &&
                              chartObstacles[i].xMax >= firstPoint.x &&
                              chartObstacles[i].yMin <= highestPoint.y &&
                              chartObstacles[i].yMax >= lowestPoint.y) {
                              if (directionIsX) {
                                  return {
                                      y: fromPoint.y,
                                      x: fromPoint.x < toPoint.x ?
                                          chartObstacles[i].xMin - 1 :
                                          chartObstacles[i].xMax + 1,
                                      obstacle: chartObstacles[i]
                                  };
                              }
                              // else ...
                              return {
                                  x: fromPoint.x,
                                  y: fromPoint.y < toPoint.y ?
                                      chartObstacles[i].yMin - 1 :
                                      chartObstacles[i].yMax + 1,
                                  obstacle: chartObstacles[i]
                              };
                          }
                          i += searchDirection;
                      }
                      return toPoint;
                  }
                  /**
                   * Decide in which direction to dodge or get out of an obstacle.
                   * Considers desired direction, which way is shortest, soft and hard
                   * bounds.
                   *
                   * (? Returns a string, either xMin, xMax, yMin or yMax.)
                   *
                   * @private
                   * @function
                   *
                   * @param {object} obstacle
                   *        Obstacle to dodge/escape.
                   *
                   * @param {object} fromPoint
                   *        Point with x/y props that's dodging/escaping.
                   *
                   * @param {object} toPoint
                   *        Goal point.
                   *
                   * @param {boolean} dirIsX
                   *        Dodge in X dimension.
                   *
                   * @param {object} bounds
                   *        Hard and soft boundaries.
                   *
                   * @return {boolean}
                   *         Use max or not.
                   */
                  function getDodgeDirection(obstacle, fromPoint, toPoint, dirIsX, bounds) {
                      var softBounds = bounds.soft, hardBounds = bounds.hard, dir = dirIsX ? 'x' : 'y', toPointMax = { x: fromPoint.x, y: fromPoint.y }, toPointMin = { x: fromPoint.x, y: fromPoint.y }, minPivot, maxPivot, maxOutOfSoftBounds = obstacle[dir + 'Max'] >=
                          softBounds[dir + 'Max'], minOutOfSoftBounds = obstacle[dir + 'Min'] <=
                          softBounds[dir + 'Min'], maxOutOfHardBounds = obstacle[dir + 'Max'] >=
                          hardBounds[dir + 'Max'], minOutOfHardBounds = obstacle[dir + 'Min'] <=
                          hardBounds[dir + 'Min'], 
                      // Find out if we should prefer one direction over the other if
                      // we can choose freely
                      minDistance = abs(obstacle[dir + 'Min'] - fromPoint[dir]), maxDistance = abs(obstacle[dir + 'Max'] - fromPoint[dir]), 
                      // If it's a small difference, pick the one leading towards dest
                      // point. Otherwise pick the shortest distance
                      useMax = abs(minDistance - maxDistance) < 10 ?
                          fromPoint[dir] < toPoint[dir] :
                          maxDistance < minDistance;
                      // Check if we hit any obstacles trying to go around in either
                      // direction.
                      toPointMin[dir] = obstacle[dir + 'Min'];
                      toPointMax[dir] = obstacle[dir + 'Max'];
                      minPivot = pivotPoint(fromPoint, toPointMin, dirIsX)[dir] !==
                          toPointMin[dir];
                      maxPivot = pivotPoint(fromPoint, toPointMax, dirIsX)[dir] !==
                          toPointMax[dir];
                      useMax = minPivot ?
                          (maxPivot ? useMax : true) :
                          (maxPivot ? false : useMax);
                      // useMax now contains our preferred choice, bounds not taken into
                      // account. If both or neither direction is out of bounds we want to
                      // use this.
                      // Deal with soft bounds
                      useMax = minOutOfSoftBounds ?
                          (maxOutOfSoftBounds ? useMax : true) : // Out on min
                          (maxOutOfSoftBounds ? false : useMax); // Not out on min
                      // Deal with hard bounds
                      useMax = minOutOfHardBounds ?
                          (maxOutOfHardBounds ? useMax : true) : // Out on min
                          (maxOutOfHardBounds ? false : useMax); // Not out on min
                      return useMax;
                  }
                  // eslint-disable-next-line valid-jsdoc
                  /**
                   * Find a clear path between point.
                   * @private
                   */
                  function clearPathTo(fromPoint, toPoint, dirIsX) {
                      // Don't waste time if we've hit goal
                      if (fromPoint.x === toPoint.x && fromPoint.y === toPoint.y) {
                          return [];
                      }
                      var dir = dirIsX ? 'x' : 'y', pivot, segments, waypoint, waypointUseMax, envelopingObstacle, secondEnvelopingObstacle, envelopWaypoint, obstacleMargin = options.obstacleOptions.margin, bounds = {
                          soft: {
                              xMin: softMinX,
                              xMax: softMaxX,
                              yMin: softMinY,
                              yMax: softMaxY
                          },
                          hard: options.hardBounds
                      };
                      // If fromPoint is inside an obstacle we have a problem. Break out
                      // by just going to the outside of this obstacle. We prefer to go to
                      // the nearest edge in the chosen direction.
                      envelopingObstacle =
                          findObstacleFromPoint(chartObstacles, fromPoint);
                      if (envelopingObstacle > -1) {
                          envelopingObstacle = chartObstacles[envelopingObstacle];
                          waypointUseMax = getDodgeDirection(envelopingObstacle, fromPoint, toPoint, dirIsX, bounds);
                          // Cut obstacle to hard bounds to make sure we stay within
                          limitObstacleToBounds(envelopingObstacle, options.hardBounds);
                          envelopWaypoint = dirIsX ? {
                              y: fromPoint.y,
                              x: envelopingObstacle[waypointUseMax ? 'xMax' : 'xMin'] +
                                  (waypointUseMax ? 1 : -1)
                          } : {
                              x: fromPoint.x,
                              y: envelopingObstacle[waypointUseMax ? 'yMax' : 'yMin'] +
                                  (waypointUseMax ? 1 : -1)
                          };
                          // If we crashed into another obstacle doing this, we put the
                          // waypoint between them instead
                          secondEnvelopingObstacle = findObstacleFromPoint(chartObstacles, envelopWaypoint);
                          if (secondEnvelopingObstacle > -1) {
                              secondEnvelopingObstacle = chartObstacles[secondEnvelopingObstacle];
                              // Cut obstacle to hard bounds
                              limitObstacleToBounds(secondEnvelopingObstacle, options.hardBounds);
                              // Modify waypoint to lay between obstacles
                              envelopWaypoint[dir] = waypointUseMax ? max(envelopingObstacle[dir + 'Max'] - obstacleMargin + 1, (secondEnvelopingObstacle[dir + 'Min'] +
                                  envelopingObstacle[dir + 'Max']) / 2) :
                                  min((envelopingObstacle[dir + 'Min'] + obstacleMargin - 1), ((secondEnvelopingObstacle[dir + 'Max'] +
                                      envelopingObstacle[dir + 'Min']) / 2));
                              // We are not going anywhere. If this happens for the first
                              // time, do nothing. Otherwise, try to go to the extreme of
                              // the obstacle pair in the current direction.
                              if (fromPoint.x === envelopWaypoint.x &&
                                  fromPoint.y === envelopWaypoint.y) {
                                  if (forceObstacleBreak) {
                                      envelopWaypoint[dir] = waypointUseMax ?
                                          max(envelopingObstacle[dir + 'Max'], secondEnvelopingObstacle[dir + 'Max']) + 1 :
                                          min(envelopingObstacle[dir + 'Min'], secondEnvelopingObstacle[dir + 'Min']) - 1;
                                  }
                                  // Toggle on if off, and the opposite
                                  forceObstacleBreak = !forceObstacleBreak;
                              }
                              else {
                                  // This point is not identical to previous.
                                  // Clear break trigger.
                                  forceObstacleBreak = false;
                              }
                          }
                          segments = [{
                                  start: fromPoint,
                                  end: envelopWaypoint
                              }];
                      }
                      else { // If not enveloping, use standard pivot calculation
                          pivot = pivotPoint(fromPoint, {
                              x: dirIsX ? toPoint.x : fromPoint.x,
                              y: dirIsX ? fromPoint.y : toPoint.y
                          }, dirIsX);
                          segments = [{
                                  start: fromPoint,
                                  end: {
                                      x: pivot.x,
                                      y: pivot.y
                                  }
                              }];
                          // Pivot before goal, use a waypoint to dodge obstacle
                          if (pivot[dirIsX ? 'x' : 'y'] !== toPoint[dirIsX ? 'x' : 'y']) {
                              // Find direction of waypoint
                              waypointUseMax = getDodgeDirection(pivot.obstacle, pivot, toPoint, !dirIsX, bounds);
                              // Cut waypoint to hard bounds
                              limitObstacleToBounds(pivot.obstacle, options.hardBounds);
                              waypoint = {
                                  x: dirIsX ?
                                      pivot.x :
                                      pivot.obstacle[waypointUseMax ? 'xMax' : 'xMin'] +
                                          (waypointUseMax ? 1 : -1),
                                  y: dirIsX ?
                                      pivot.obstacle[waypointUseMax ? 'yMax' : 'yMin'] +
                                          (waypointUseMax ? 1 : -1) :
                                      pivot.y
                              };
                              // We're changing direction here, store that to make sure we
                              // also change direction when adding the last segment array
                              // after handling waypoint.
                              dirIsX = !dirIsX;
                              segments = segments.concat(clearPathTo({
                                  x: pivot.x,
                                  y: pivot.y
                              }, waypoint, dirIsX));
                          }
                      }
                      // Get segments for the other direction too
                      // Recursion is our friend
                      segments = segments.concat(clearPathTo(segments[segments.length - 1].end, toPoint, !dirIsX));
                      return segments;
                  }
                  // eslint-disable-next-line valid-jsdoc
                  /**
                   * Extract point to outside of obstacle in whichever direction is
                   * closest. Returns new point outside obstacle.
                   * @private
                   */
                  function extractFromObstacle(obstacle, point, goalPoint) {
                      var dirIsX = min(obstacle.xMax - point.x, point.x - obstacle.xMin) <
                          min(obstacle.yMax - point.y, point.y - obstacle.yMin), bounds = {
                          soft: options.hardBounds,
                          hard: options.hardBounds
                      }, useMax = getDodgeDirection(obstacle, point, goalPoint, dirIsX, bounds);
                      return dirIsX ? {
                          y: point.y,
                          x: obstacle[useMax ? 'xMax' : 'xMin'] + (useMax ? 1 : -1)
                      } : {
                          x: point.x,
                          y: obstacle[useMax ? 'yMax' : 'yMin'] + (useMax ? 1 : -1)
                      };
                  }
                  // Cut the obstacle array to soft bounds for optimization in large
                  // datasets.
                  chartObstacles =
                      chartObstacles.slice(startObstacleIx, endObstacleIx + 1);
                  // If an obstacle envelops the end point, move it out of there and add
                  // a little segment to where it was.
                  if ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
                      extractedEndPoint = extractFromObstacle(chartObstacles[endObstacleIx], end, start);
                      endSegments.push({
                          end: end,
                          start: extractedEndPoint
                      });
                      end = extractedEndPoint;
                  }
                  // If it's still inside one or more obstacles, get out of there by
                  // force-moving towards the start point.
                  while ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
                      useMax = end[dir] - start[dir] < 0;
                      extractedEndPoint = {
                          x: end.x,
                          y: end.y
                      };
                      extractedEndPoint[dir] = chartObstacles[endObstacleIx][useMax ? dir + 'Max' : dir + 'Min'] + (useMax ? 1 : -1);
                      endSegments.push({
                          end: end,
                          start: extractedEndPoint
                      });
                      end = extractedEndPoint;
                  }
                  // Find the path
                  segments = clearPathTo(start, end, dirIsX);
                  // Add the end-point segments
                  segments = segments.concat(endSegments.reverse());
                  return {
                      path: pathFromSegments(segments),
                      obstacles: segments
                  };
              }, {
                  requiresObstacles: true
              })
          };
  
          return algorithms;
      });
      _registerModule(_modules, 'parts-gantt/ArrowSymbols.js', [_modules['parts/Globals.js']], function (H) {
          /* *
           *
           *  (c) 2017 Highsoft AS
           *  Authors: Lars A. V. Cabrera
           *
           *  License: www.highcharts.com/license
           *
           *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
           *
           * */
          /**
           * Creates an arrow symbol. Like a triangle, except not filled.
           * ```
           *                   o
           *             o
           *       o
           * o
           *       o
           *             o
           *                   o
           * ```
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the arrow
           *
           * @param {number} y
           *        y position of the arrow
           *
           * @param {number} w
           *        width of the arrow
           *
           * @param {number} h
           *        height of the arrow
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols.arrow = function (x, y, w, h) {
              return [
                  'M', x, y + h / 2,
                  'L', x + w, y,
                  'L', x, y + h / 2,
                  'L', x + w, y + h
              ];
          };
          /**
           * Creates a half-width arrow symbol. Like a triangle, except not filled.
           * ```
           *       o
           *    o
           * o
           *    o
           *       o
           * ```
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the arrow
           *
           * @param {number} y
           *        y position of the arrow
           *
           * @param {number} w
           *        width of the arrow
           *
           * @param {number} h
           *        height of the arrow
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols['arrow-half'] = function (x, y, w, h) {
              return H.SVGRenderer.prototype.symbols.arrow(x, y, w / 2, h);
          };
          /**
           * Creates a left-oriented triangle.
           * ```
           *             o
           *       ooooooo
           * ooooooooooooo
           *       ooooooo
           *             o
           * ```
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the triangle
           *
           * @param {number} y
           *        y position of the triangle
           *
           * @param {number} w
           *        width of the triangle
           *
           * @param {number} h
           *        height of the triangle
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols['triangle-left'] = function (x, y, w, h) {
              return [
                  'M', x + w, y,
                  'L', x, y + h / 2,
                  'L', x + w, y + h,
                  'Z'
              ];
          };
          /**
           * Alias function for triangle-left.
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the arrow
           *
           * @param {number} y
           *        y position of the arrow
           *
           * @param {number} w
           *        width of the arrow
           *
           * @param {number} h
           *        height of the arrow
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols['arrow-filled'] =
              H.SVGRenderer.prototype.symbols['triangle-left'];
          /**
           * Creates a half-width, left-oriented triangle.
           * ```
           *       o
           *    oooo
           * ooooooo
           *    oooo
           *       o
           * ```
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the triangle
           *
           * @param {number} y
           *        y position of the triangle
           *
           * @param {number} w
           *        width of the triangle
           *
           * @param {number} h
           *        height of the triangle
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols['triangle-left-half'] = function (x, y, w, h) {
              return H.SVGRenderer.prototype.symbols['triangle-left'](x, y, w / 2, h);
          };
          /**
           * Alias function for triangle-left-half.
           *
           * @private
           * @function
           *
           * @param {number} x
           *        x position of the arrow
           *
           * @param {number} y
           *        y position of the arrow
           *
           * @param {number} w
           *        width of the arrow
           *
           * @param {number} h
           *        height of the arrow
           *
           * @return {Highcharts.SVGPathArray}
           *         Path array
           */
          H.SVGRenderer.prototype.symbols['arrow-filled-half'] =
              H.SVGRenderer.prototype.symbols['triangle-left-half'];
  
      });
      _registerModule(_modules, 'parts-gantt/Pathfinder.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['parts-gantt/PathfinderAlgorithms.js']], function (H, U, pathfinderAlgorithms) {
          /* *
           *
           *  (c) 2016 Highsoft AS
           *  Authors: Øystein Moseng, Lars A. V. Cabrera
           *
           *  License: www.highcharts.com/license
           *
           *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
           *
           * */
          /**
           * The default pathfinder algorithm to use for a chart. It is possible to define
           * your own algorithms by adding them to the
           * `Highcharts.Pathfinder.prototype.algorithms`
           * object before the chart has been created.
           *
           * The default algorithms are as follows:
           *
           * `straight`:      Draws a straight line between the connecting
           *                  points. Does not avoid other points when drawing.
           *
           * `simpleConnect`: Finds a path between the points using right angles
           *                  only. Takes only starting/ending points into
           *                  account, and will not avoid other points.
           *
           * `fastAvoid`:     Finds a path between the points using right angles
           *                  only. Will attempt to avoid other points, but its
           *                  focus is performance over accuracy. Works well with
           *                  less dense datasets.
           *
           * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
           */
          var defined = U.defined, extend = U.extend, objectEach = U.objectEach, pick = U.pick, splat = U.splat;
          var deg2rad = H.deg2rad, addEvent = H.addEvent, merge = H.merge, max = Math.max, min = Math.min;
          /*
           @todo:
               - Document how to write your own algorithms
               - Consider adding a Point.pathTo method that wraps creating a connection
                 and rendering it
          */
          // Set default Pathfinder options
          extend(H.defaultOptions, {
              /**
               * The Pathfinder module allows you to define connections between any two
               * points, represented as lines - optionally with markers for the start
               * and/or end points. Multiple algorithms are available for calculating how
               * the connecting lines are drawn.
               *
               * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
               * charts, the connectors are used to draw dependencies between tasks.
               *
               * @see [dependency](series.gantt.data.dependency)
               *
               * @sample gantt/pathfinder/demo
               *         Pathfinder connections
               *
               * @product      gantt
               * @optionparent connectors
               */
              connectors: {
                  /**
                   * Enable connectors for this chart. Requires Highcharts Gantt.
                   *
                   * @type      {boolean}
                   * @default   true
                   * @since     6.2.0
                   * @apioption connectors.enabled
                   */
                  /**
                   * Set the default dash style for this chart's connecting lines.
                   *
                   * @type      {string}
                   * @default   solid
                   * @since     6.2.0
                   * @apioption connectors.dashStyle
                   */
                  /**
                   * Set the default color for this chart's Pathfinder connecting lines.
                   * Defaults to the color of the point being connected.
                   *
                   * @type      {Highcharts.ColorString}
                   * @since     6.2.0
                   * @apioption connectors.lineColor
                   */
                  /**
                   * Set the default pathfinder margin to use, in pixels. Some Pathfinder
                   * algorithms attempt to avoid obstacles, such as other points in the
                   * chart. These algorithms use this margin to determine how close lines
                   * can be to an obstacle. The default is to compute this automatically
                   * from the size of the obstacles in the chart.
                   *
                   * To draw connecting lines close to existing points, set this to a low
                   * number. For more space around existing points, set this number
                   * higher.
                   *
                   * @sample gantt/pathfinder/algorithm-margin
                   *         Small algorithmMargin
                   *
                   * @type      {number}
                   * @since     6.2.0
                   * @apioption connectors.algorithmMargin
                   */
                  /**
                   * Set the default pathfinder algorithm to use for this chart. It is
                   * possible to define your own algorithms by adding them to the
                   * Highcharts.Pathfinder.prototype.algorithms object before the chart
                   * has been created.
                   *
                   * The default algorithms are as follows:
                   *
                   * `straight`:      Draws a straight line between the connecting
                   *                  points. Does not avoid other points when drawing.
                   *
                   * `simpleConnect`: Finds a path between the points using right angles
                   *                  only. Takes only starting/ending points into
                   *                  account, and will not avoid other points.
                   *
                   * `fastAvoid`:     Finds a path between the points using right angles
                   *                  only. Will attempt to avoid other points, but its
                   *                  focus is performance over accuracy. Works well with
                   *                  less dense datasets.
                   *
                   * Default value: `straight` is used as default for most series types,
                   * while `simpleConnect` is used as default for Gantt series, to show
                   * dependencies between points.
                   *
                   * @sample gantt/pathfinder/demo
                   *         Different types used
                   *
                   * @type    {Highcharts.PathfinderTypeValue}
                   * @default undefined
                   * @since   6.2.0
                   */
                  type: 'straight',
                  /**
                   * Set the default pixel width for this chart's Pathfinder connecting
                   * lines.
                   *
                   * @since 6.2.0
                   */
                  lineWidth: 1,
                  /**
                   * Marker options for this chart's Pathfinder connectors. Note that
                   * this option is overridden by the `startMarker` and `endMarker`
                   * options.
                   *
                   * @since 6.2.0
                   */
                  marker: {
                      /**
                       * Set the radius of the connector markers. The default is
                       * automatically computed based on the algorithmMargin setting.
                       *
                       * Setting marker.width and marker.height will override this
                       * setting.
                       *
                       * @type      {number}
                       * @since     6.2.0
                       * @apioption connectors.marker.radius
                       */
                      /**
                       * Set the width of the connector markers. If not supplied, this
                       * is inferred from the marker radius.
                       *
                       * @type      {number}
                       * @since     6.2.0
                       * @apioption connectors.marker.width
                       */
                      /**
                       * Set the height of the connector markers. If not supplied, this
                       * is inferred from the marker radius.
                       *
                       * @type      {number}
                       * @since     6.2.0
                       * @apioption connectors.marker.height
                       */
                      /**
                       * Set the color of the connector markers. By default this is the
                       * same as the connector color.
                       *
                       * @type      {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
                       * @since     6.2.0
                       * @apioption connectors.marker.color
                       */
                      /**
                       * Set the line/border color of the connector markers. By default
                       * this is the same as the marker color.
                       *
                       * @type      {Highcharts.ColorString}
                       * @since     6.2.0
                       * @apioption connectors.marker.lineColor
                       */
                      /**
                       * Enable markers for the connectors.
                       */
                      enabled: false,
                      /**
                       * Horizontal alignment of the markers relative to the points.
                       *
                       * @type {Highcharts.AlignValue}
                       */
                      align: 'center',
                      /**
                       * Vertical alignment of the markers relative to the points.
                       *
                       * @type {Highcharts.VerticalAlignValue}
                       */
                      verticalAlign: 'middle',
                      /**
                       * Whether or not to draw the markers inside the points.
                       */
                      inside: false,
                      /**
                       * Set the line/border width of the pathfinder markers.
                       */
                      lineWidth: 1
                  },
                  /**
                   * Marker options specific to the start markers for this chart's
                   * Pathfinder connectors. Overrides the generic marker options.
                   *
                   * @extends connectors.marker
                   * @since   6.2.0
                   */
                  startMarker: {
                      /**
                       * Set the symbol of the connector start markers.
                       */
                      symbol: 'diamond'
                  },
                  /**
                   * Marker options specific to the end markers for this chart's
                   * Pathfinder connectors. Overrides the generic marker options.
                   *
                   * @extends connectors.marker
                   * @since   6.2.0
                   */
                  endMarker: {
                      /**
                       * Set the symbol of the connector end markers.
                       */
                      symbol: 'arrow-filled'
                  }
              }
          });
          /**
           * Override Pathfinder connector options for a series. Requires Highcharts Gantt
           * to be loaded.
           *
           * @extends   connectors
           * @since     6.2.0
           * @excluding enabled, algorithmMargin
           * @product   gantt
           * @apioption plotOptions.series.connectors
           */
          /**
           * Connect to a point. This option can be either a string, referring to the ID
           * of another point, or an object, or an array of either. If the option is an
           * array, each element defines a connection.
           *
           * @sample gantt/pathfinder/demo
           *         Different connection types
           *
           * @type      {string|Array<string|*>|*}
           * @extends   plotOptions.series.connectors
           * @since     6.2.0
           * @excluding enabled
           * @product   gantt
           * @requires  highcharts-gantt
           * @apioption series.xrange.data.connect
           */
          /**
           * The ID of the point to connect to.
           *
           * @type      {string}
           * @since     6.2.0
           * @product   gantt
           * @apioption series.xrange.data.connect.to
           */
          /**
           * Get point bounding box using plotX/plotY and shapeArgs. If using
           * graphic.getBBox() directly, the bbox will be affected by animation.
           *
           * @private
           * @function
           *
           * @param {Highcharts.Point} point
           *        The point to get BB of.
           *
           * @return {Highcharts.Dictionary<number>|null}
           *         Result xMax, xMin, yMax, yMin.
           */
          function getPointBB(point) {
              var shapeArgs = point.shapeArgs, bb;
              // Prefer using shapeArgs (columns)
              if (shapeArgs) {
                  return {
                      xMin: shapeArgs.x,
                      xMax: shapeArgs.x + shapeArgs.width,
                      yMin: shapeArgs.y,
                      yMax: shapeArgs.y + shapeArgs.height
                  };
              }
              // Otherwise use plotX/plotY and bb
              bb = point.graphic && point.graphic.getBBox();
              return bb ? {
                  xMin: point.plotX - bb.width / 2,
                  xMax: point.plotX + bb.width / 2,
                  yMin: point.plotY - bb.height / 2,
                  yMax: point.plotY + bb.height / 2
              } : null;
          }
          /**
           * Calculate margin to place around obstacles for the pathfinder in pixels.
           * Returns a minimum of 1 pixel margin.
           *
           * @private
           * @function
           *
           * @param {Array<object>} obstacles
           *        Obstacles to calculate margin from.
           *
           * @return {number}
           *         The calculated margin in pixels. At least 1.
           */
          function calculateObstacleMargin(obstacles) {
              var len = obstacles.length, i = 0, j, obstacleDistance, distances = [], 
              // Compute smallest distance between two rectangles
              distance = function (a, b, bbMargin) {
                  // Count the distance even if we are slightly off
                  var margin = pick(bbMargin, 10), yOverlap = a.yMax + margin > b.yMin - margin &&
                      a.yMin - margin < b.yMax + margin, xOverlap = a.xMax + margin > b.xMin - margin &&
                      a.xMin - margin < b.xMax + margin, xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity, yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
                  // If the rectangles collide, try recomputing with smaller margin.
                  // If they collide anyway, discard the obstacle.
                  if (xOverlap && yOverlap) {
                      return (margin ?
                          distance(a, b, Math.floor(margin / 2)) :
                          Infinity);
                  }
                  return min(xDistance, yDistance);
              };
              // Go over all obstacles and compare them to the others.
              for (; i < len; ++i) {
                  // Compare to all obstacles ahead. We will already have compared this
                  // obstacle to the ones before.
                  for (j = i + 1; j < len; ++j) {
                      obstacleDistance = distance(obstacles[i], obstacles[j]);
                      // TODO: Magic number 80
                      if (obstacleDistance < 80) { // Ignore large distances
                          distances.push(obstacleDistance);
                      }
                  }
              }
              // Ensure we always have at least one value, even in very spaceous charts
              distances.push(80);
              return max(Math.floor(distances.sort(function (a, b) {
                  return (a - b);
              })[
              // Discard first 10% of the relevant distances, and then grab
              // the smallest one.
              Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
              ), 1 // 1 is the minimum margin
              );
          }
          /* eslint-disable no-invalid-this, valid-jsdoc */
          /**
           * The Connection class. Used internally to represent a connection between two
           * points.
           *
           * @private
           * @class
           * @name Highcharts.Connection
           *
           * @param {Highcharts.Point} from
           *        Connection runs from this Point.
           *
           * @param {Highcharts.Point} to
           *        Connection runs to this Point.
           *
           * @param {Highcharts.ConnectorsOptions} [options]
           *        Connection options.
           */
          function Connection(from, to, options) {
              this.init(from, to, options);
          }
          Connection.prototype = {
              /**
               * Initialize the Connection object. Used as constructor only.
               *
               * @function Highcharts.Connection#init
               *
               * @param {Highcharts.Point} from
               *        Connection runs from this Point.
               *
               * @param {Highcharts.Point} to
               *        Connection runs to this Point.
               *
               * @param {Highcharts.ConnectorsOptions} [options]
               *        Connection options.
               */
              init: function (from, to, options) {
                  this.fromPoint = from;
                  this.toPoint = to;
                  this.options = options;
                  this.chart = from.series.chart;
                  this.pathfinder = this.chart.pathfinder;
              },
              /**
               * Add (or update) this connection's path on chart. Stores reference to the
               * created element on this.graphics.path.
               *
               * @function Highcharts.Connection#renderPath
               *
               * @param {Highcharts.SVGPathArray} path
               *        Path to render, in array format. E.g. ['M', 0, 0, 'L', 10, 10]
               *
               * @param {Highcharts.SVGAttributes} [attribs]
               *        SVG attributes for the path.
               *
               * @param {Highcharts.AnimationOptionsObject} [animation]
               *        Animation options for the rendering.
               */
              renderPath: function (path, attribs, animation) {
                  var connection = this, chart = this.chart, styledMode = chart.styledMode, pathfinder = chart.pathfinder, animate = !chart.options.chart.forExport && animation !== false, pathGraphic = connection.graphics && connection.graphics.path, anim;
                  // Add the SVG element of the pathfinder group if it doesn't exist
                  if (!pathfinder.group) {
                      pathfinder.group = chart.renderer.g()
                          .addClass('highcharts-pathfinder-group')
                          .attr({ zIndex: -1 })
                          .add(chart.seriesGroup);
                  }
                  // Shift the group to compensate for plot area.
                  // Note: Do this always (even when redrawing a path) to avoid issues
                  // when updating chart in a way that changes plot metrics.
                  pathfinder.group.translate(chart.plotLeft, chart.plotTop);
                  // Create path if does not exist
                  if (!(pathGraphic && pathGraphic.renderer)) {
                      pathGraphic = chart.renderer.path()
                          .add(pathfinder.group);
                      if (!styledMode) {
                          pathGraphic.attr({
                              opacity: 0
                          });
                      }
                  }
                  // Set path attribs and animate to the new path
                  pathGraphic.attr(attribs);
                  anim = { d: path };
                  if (!styledMode) {
                      anim.opacity = 1;
                  }
                  pathGraphic[animate ? 'animate' : 'attr'](anim, animation);
                  // Store reference on connection
                  this.graphics = this.graphics || {};
                  this.graphics.path = pathGraphic;
              },
              /**
               * Calculate and add marker graphics for connection to the chart. The
               * created/updated elements are stored on this.graphics.start and
               * this.graphics.end.
               *
               * @function Highcharts.Connection#addMarker
               *
               * @param {string} type
               *        Marker type, either 'start' or 'end'.
               *
               * @param {Highcharts.ConnectorsMarkerOptions} options
               *        All options for this marker. Not calculated or merged with other
               *        options.
               *
               * @param {Highcharts.SVGPathArray} path
               *        Connection path in array format. This is used to calculate the
               *        rotation angle of the markers.
               */
              addMarker: function (type, options, path) {
                  var connection = this, chart = connection.fromPoint.series.chart, pathfinder = chart.pathfinder, renderer = chart.renderer, point = (type === 'start' ?
                      connection.fromPoint :
                      connection.toPoint), anchor = point.getPathfinderAnchorPoint(options), markerVector, radians, rotation, box, width, height, pathVector;
                  if (!options.enabled) {
                      return;
                  }
                  // Last vector before start/end of path, used to get angle
                  if (type === 'start') {
                      pathVector = {
                          x: path[4],
                          y: path[5]
                      };
                  }
                  else { // 'end'
                      pathVector = {
                          x: path[path.length - 5],
                          y: path[path.length - 4]
                      };
                  }
                  // Get angle between pathVector and anchor point and use it to create
                  // marker position.
                  radians = point.getRadiansToVector(pathVector, anchor);
                  markerVector = point.getMarkerVector(radians, options.radius, anchor);
                  // Rotation of marker is calculated from angle between pathVector and
                  // markerVector.
                  // (Note:
                  //  Used to recalculate radians between markerVector and pathVector,
                  //  but this should be the same as between pathVector and anchor.)
                  rotation = -radians / deg2rad;
                  if (options.width && options.height) {
                      width = options.width;
                      height = options.height;
                  }
                  else {
                      width = height = options.radius * 2;
                  }
                  // Add graphics object if it does not exist
                  connection.graphics = connection.graphics || {};
                  box = {
                      x: markerVector.x - (width / 2),
                      y: markerVector.y - (height / 2),
                      width: width,
                      height: height,
                      rotation: rotation,
                      rotationOriginX: markerVector.x,
                      rotationOriginY: markerVector.y
                  };
                  if (!connection.graphics[type]) {
                      // Create new marker element
                      connection.graphics[type] = renderer
                          .symbol(options.symbol)
                          .addClass('highcharts-point-connecting-path-' + type + '-marker')
                          .attr(box)
                          .add(pathfinder.group);
                      if (!renderer.styledMode) {
                          connection.graphics[type].attr({
                              fill: options.color || connection.fromPoint.color,
                              stroke: options.lineColor,
                              'stroke-width': options.lineWidth,
                              opacity: 0
                          })
                              .animate({
                              opacity: 1
                          }, point.series.options.animation);
                      }
                  }
                  else {
                      connection.graphics[type].animate(box);
                  }
              },
              /**
               * Calculate and return connection path.
               * Note: Recalculates chart obstacles on demand if they aren't calculated.
               *
               * @function Highcharts.Connection#getPath
               *
               * @param {Highcharts.ConnectorsOptions} options
               *        Connector options. Not calculated or merged with other options.
               *
               * @return {object|undefined}
               *         Calculated SVG path data in array format.
               */
              getPath: function (options) {
                  var pathfinder = this.pathfinder, chart = this.chart, algorithm = pathfinder.algorithms[options.type], chartObstacles = pathfinder.chartObstacles;
                  if (typeof algorithm !== 'function') {
                      H.error('"' + options.type + '" is not a Pathfinder algorithm.');
                      return;
                  }
                  // This function calculates obstacles on demand if they don't exist
                  if (algorithm.requiresObstacles && !chartObstacles) {
                      chartObstacles =
                          pathfinder.chartObstacles =
                              pathfinder.getChartObstacles(options);
                      // If the algorithmMargin was computed, store the result in default
                      // options.
                      chart.options.connectors.algorithmMargin =
                          options.algorithmMargin;
                      // Cache some metrics too
                      pathfinder.chartObstacleMetrics =
                          pathfinder.getObstacleMetrics(chartObstacles);
                  }
                  // Get the SVG path
                  return algorithm(
                  // From
                  this.fromPoint.getPathfinderAnchorPoint(options.startMarker), 
                  // To
                  this.toPoint.getPathfinderAnchorPoint(options.endMarker), merge({
                      chartObstacles: chartObstacles,
                      lineObstacles: pathfinder.lineObstacles || [],
                      obstacleMetrics: pathfinder.chartObstacleMetrics,
                      hardBounds: {
                          xMin: 0,
                          xMax: chart.plotWidth,
                          yMin: 0,
                          yMax: chart.plotHeight
                      },
                      obstacleOptions: {
                          margin: options.algorithmMargin
                      },
                      startDirectionX: pathfinder.getAlgorithmStartDirection(options.startMarker)
                  }, options));
              },
              /**
               * (re)Calculate and (re)draw the connection.
               *
               * @function Highcharts.Connection#render
               */
              render: function () {
                  var connection = this, fromPoint = connection.fromPoint, series = fromPoint.series, chart = series.chart, pathfinder = chart.pathfinder, pathResult, path, options = merge(chart.options.connectors, series.options.connectors, fromPoint.options.connectors, connection.options), attribs = {};
                  // Set path attribs
                  if (!chart.styledMode) {
                      attribs.stroke = options.lineColor || fromPoint.color;
                      attribs['stroke-width'] = options.lineWidth;
                      if (options.dashStyle) {
                          attribs.dashstyle = options.dashStyle;
                      }
                  }
                  attribs['class'] = // eslint-disable-line dot-notation
                      'highcharts-point-connecting-path ' +
                          'highcharts-color-' + fromPoint.colorIndex;
                  options = merge(attribs, options);
                  // Set common marker options
                  if (!defined(options.marker.radius)) {
                      options.marker.radius = min(max(Math.ceil((options.algorithmMargin || 8) / 2) - 1, 1), 5);
                  }
                  // Get the path
                  pathResult = connection.getPath(options);
                  path = pathResult.path;
                  // Always update obstacle storage with obstacles from this path.
                  // We don't know if future calls will need this for their algorithm.
                  if (pathResult.obstacles) {
                      pathfinder.lineObstacles =
                          pathfinder.lineObstacles || [];
                      pathfinder.lineObstacles =
                          pathfinder.lineObstacles.concat(pathResult.obstacles);
                  }
                  // Add the calculated path to the pathfinder group
                  connection.renderPath(path, attribs, series.options.animation);
                  // Render the markers
                  connection.addMarker('start', merge(options.marker, options.startMarker), path);
                  connection.addMarker('end', merge(options.marker, options.endMarker), path);
              },
              /**
               * Destroy connection by destroying the added graphics elements.
               *
               * @function Highcharts.Connection#destroy
               */
              destroy: function () {
                  if (this.graphics) {
                      objectEach(this.graphics, function (val) {
                          val.destroy();
                      });
                      delete this.graphics;
                  }
              }
          };
          /**
           * The Pathfinder class.
           *
           * @private
           * @class
           * @name Highcharts.Pathfinder
           *
           * @param {Highcharts.Chart} chart
           *        The chart to operate on.
           */
          function Pathfinder(chart) {
              this.init(chart);
          }
          Pathfinder.prototype = {
              /**
               * @name Highcharts.Pathfinder#algorithms
               * @type {Highcharts.Dictionary<Function>}
               */
              algorithms: pathfinderAlgorithms,
              /**
               * Initialize the Pathfinder object.
               *
               * @function Highcharts.Pathfinder#init
               *
               * @param {Highcharts.Chart} chart
               *        The chart context.
               */
              init: function (chart) {
                  // Initialize pathfinder with chart context
                  this.chart = chart;
                  // Init connection reference list
                  this.connections = [];
                  // Recalculate paths/obstacles on chart redraw
                  addEvent(chart, 'redraw', function () {
                      this.pathfinder.update();
                  });
              },
              /**
               * Update Pathfinder connections from scratch.
               *
               * @function Highcharts.Pathfinder#update
               *
               * @param {boolean} [deferRender]
               *        Whether or not to defer rendering of connections until
               *        series.afterAnimate event has fired. Used on first render.
               */
              update: function (deferRender) {
                  var chart = this.chart, pathfinder = this, oldConnections = pathfinder.connections;
                  // Rebuild pathfinder connections from options
                  pathfinder.connections = [];
                  chart.series.forEach(function (series) {
                      if (series.visible && !series.options.isInternal) {
                          series.points.forEach(function (point) {
                              var to, connects = (point.options &&
                                  point.options.connect &&
                                  splat(point.options.connect));
                              if (point.visible && point.isInside !== false && connects) {
                                  connects.forEach(function (connect) {
                                      to = chart.get(typeof connect === 'string' ?
                                          connect : connect.to);
                                      if (to instanceof H.Point &&
                                          to.series.visible &&
                                          to.visible &&
                                          to.isInside !== false) {
                                          // Add new connection
                                          pathfinder.connections.push(new Connection(point, // from
                                          to, typeof connect === 'string' ?
                                              {} :
                                              connect));
                                      }
                                  });
                              }
                          });
                      }
                  });
                  // Clear connections that should not be updated, and move old info over
                  // to new connections.
                  for (var j = 0, k, found, lenOld = oldConnections.length, lenNew = pathfinder.connections.length; j < lenOld; ++j) {
                      found = false;
                      for (k = 0; k < lenNew; ++k) {
                          if (oldConnections[j].fromPoint ===
                              pathfinder.connections[k].fromPoint &&
                              oldConnections[j].toPoint ===
                                  pathfinder.connections[k].toPoint) {
                              pathfinder.connections[k].graphics =
                                  oldConnections[j].graphics;
                              found = true;
                              break;
                          }
                      }
                      if (!found) {
                          oldConnections[j].destroy();
                      }
                  }
                  // Clear obstacles to force recalculation. This must be done on every
                  // redraw in case positions have changed. Recalculation is handled in
                  // Connection.getPath on demand.
                  delete this.chartObstacles;
                  delete this.lineObstacles;
                  // Draw the pending connections
                  pathfinder.renderConnections(deferRender);
              },
              /**
               * Draw the chart's connecting paths.
               *
               * @function Highcharts.Pathfinder#renderConnections
               *
               * @param {boolean} [deferRender]
               *        Whether or not to defer render until series animation is finished.
               *        Used on first render.
               */
              renderConnections: function (deferRender) {
                  if (deferRender) {
                      // Render after series are done animating
                      this.chart.series.forEach(function (series) {
                          var render = function () {
                              // Find pathfinder connections belonging to this series
                              // that haven't rendered, and render them now.
                              var pathfinder = series.chart.pathfinder, conns = pathfinder && pathfinder.connections || [];
                              conns.forEach(function (connection) {
                                  if (connection.fromPoint &&
                                      connection.fromPoint.series === series) {
                                      connection.render();
                                  }
                              });
                              if (series.pathfinderRemoveRenderEvent) {
                                  series.pathfinderRemoveRenderEvent();
                                  delete series.pathfinderRemoveRenderEvent;
                              }
                          };
                          if (series.options.animation === false) {
                              render();
                          }
                          else {
                              series.pathfinderRemoveRenderEvent = addEvent(series, 'afterAnimate', render);
                          }
                      });
                  }
                  else {
                      // Go through connections and render them
                      this.connections.forEach(function (connection) {
                          connection.render();
                      });
                  }
              },
              /**
               * Get obstacles for the points in the chart. Does not include connecting
               * lines from Pathfinder. Applies algorithmMargin to the obstacles.
               *
               * @function Highcharts.Pathfinder#getChartObstacles
               *
               * @param {object} options
               *        Options for the calculation. Currenlty only
               *        options.algorithmMargin.
               *
               * @return {Array<object>}
               *         An array of calculated obstacles. Each obstacle is defined as an
               *         object with xMin, xMax, yMin and yMax properties.
               */
              getChartObstacles: function (options) {
                  var obstacles = [], series = this.chart.series, margin = pick(options.algorithmMargin, 0), calculatedMargin;
                  for (var i = 0, sLen = series.length; i < sLen; ++i) {
                      if (series[i].visible && !series[i].options.isInternal) {
                          for (var j = 0, pLen = series[i].points.length, bb, point; j < pLen; ++j) {
                              point = series[i].points[j];
                              if (point.visible) {
                                  bb = getPointBB(point);
                                  if (bb) {
                                      obstacles.push({
                                          xMin: bb.xMin - margin,
                                          xMax: bb.xMax + margin,
                                          yMin: bb.yMin - margin,
                                          yMax: bb.yMax + margin
                                      });
                                  }
                              }
                          }
                      }
                  }
                  // Sort obstacles by xMin for optimization
                  obstacles = obstacles.sort(function (a, b) {
                      return a.xMin - b.xMin;
                  });
                  // Add auto-calculated margin if the option is not defined
                  if (!defined(options.algorithmMargin)) {
                      calculatedMargin =
                          options.algorithmMargin =
                              calculateObstacleMargin(obstacles);
                      obstacles.forEach(function (obstacle) {
                          obstacle.xMin -= calculatedMargin;
                          obstacle.xMax += calculatedMargin;
                          obstacle.yMin -= calculatedMargin;
                          obstacle.yMax += calculatedMargin;
                      });
                  }
                  return obstacles;
              },
              /**
               * Utility function to get metrics for obstacles:
               * - Widest obstacle width
               * - Tallest obstacle height
               *
               * @function Highcharts.Pathfinder#getObstacleMetrics
               *
               * @param {Array<object>} obstacles
               *        An array of obstacles to inspect.
               *
               * @return {object}
               *         The calculated metrics, as an object with maxHeight and maxWidth
               *         properties.
               */
              getObstacleMetrics: function (obstacles) {
                  var maxWidth = 0, maxHeight = 0, width, height, i = obstacles.length;
                  while (i--) {
                      width = obstacles[i].xMax - obstacles[i].xMin;
                      height = obstacles[i].yMax - obstacles[i].yMin;
                      if (maxWidth < width) {
                          maxWidth = width;
                      }
                      if (maxHeight < height) {
                          maxHeight = height;
                      }
                  }
                  return {
                      maxHeight: maxHeight,
                      maxWidth: maxWidth
                  };
              },
              /**
               * Utility to get which direction to start the pathfinding algorithm
               * (X vs Y), calculated from a set of marker options.
               *
               * @function Highcharts.Pathfinder#getAlgorithmStartDirection
               *
               * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
               *        Marker options to calculate from.
               *
               * @return {boolean}
               *         Returns true for X, false for Y, and undefined for autocalculate.
               */
              getAlgorithmStartDirection: function (markerOptions) {
                  var xCenter = markerOptions.align !== 'left' &&
                      markerOptions.align !== 'right', yCenter = markerOptions.verticalAlign !== 'top' &&
                      markerOptions.verticalAlign !== 'bottom', undef;
                  return xCenter ?
                      (yCenter ? undef : false) : // x is centered
                      (yCenter ? true : undef); // x is off-center
              }
          };
          // Add to Highcharts namespace
          H.Connection = Connection;
          H.Pathfinder = Pathfinder;
          // Add pathfinding capabilities to Points
          extend(H.Point.prototype, /** @lends Point.prototype */ {
              /**
               * Get coordinates of anchor point for pathfinder connection.
               *
               * @private
               * @function Highcharts.Point#getPathfinderAnchorPoint
               *
               * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
               *        Connection options for position on point.
               *
               * @return {Highcharts.PositionObject}
               *         An object with x/y properties for the position. Coordinates are
               *         in plot values, not relative to point.
               */
              getPathfinderAnchorPoint: function (markerOptions) {
                  var bb = getPointBB(this), x, y;
                  switch (markerOptions.align) { // eslint-disable-line default-case
                      case 'right':
                          x = 'xMax';
                          break;
                      case 'left':
                          x = 'xMin';
                  }
                  switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
                      case 'top':
                          y = 'yMin';
                          break;
                      case 'bottom':
                          y = 'yMax';
                  }
                  return {
                      x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
                      y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
                  };
              },
              /**
               * Utility to get the angle from one point to another.
               *
               * @private
               * @function Highcharts.Point#getRadiansToVector
               *
               * @param {Highcharts.PositionObject} v1
               *        The first vector, as an object with x/y properties.
               *
               * @param {Highcharts.PositionObject} v2
               *        The second vector, as an object with x/y properties.
               *
               * @return {number}
               *         The angle in degrees
               */
              getRadiansToVector: function (v1, v2) {
                  var box;
                  if (!defined(v2)) {
                      box = getPointBB(this);
                      v2 = {
                          x: (box.xMin + box.xMax) / 2,
                          y: (box.yMin + box.yMax) / 2
                      };
                  }
                  return Math.atan2(v2.y - v1.y, v1.x - v2.x);
              },
              /**
               * Utility to get the position of the marker, based on the path angle and
               * the marker's radius.
               *
               * @private
               * @function Highcharts.Point#getMarkerVector
               *
               * @param {number} radians
               *        The angle in radians from the point center to another vector.
               *
               * @param {number} markerRadius
               *        The radius of the marker, to calculate the additional distance to
               *        the center of the marker.
               *
               * @param {object} anchor
               *        The anchor point of the path and marker as an object with x/y
               *        properties.
               *
               * @return {object}
               *         The marker vector as an object with x/y properties.
               */
              getMarkerVector: function (radians, markerRadius, anchor) {
                  var twoPI = Math.PI * 2.0, theta = radians, bb = getPointBB(this), rectWidth = bb.xMax - bb.xMin, rectHeight = bb.yMax - bb.yMin, rAtan = Math.atan2(rectHeight, rectWidth), tanTheta = 1, leftOrRightRegion = false, rectHalfWidth = rectWidth / 2.0, rectHalfHeight = rectHeight / 2.0, rectHorizontalCenter = bb.xMin + rectHalfWidth, rectVerticalCenter = bb.yMin + rectHalfHeight, edgePoint = {
                      x: rectHorizontalCenter,
                      y: rectVerticalCenter
                  }, markerPoint = {}, xFactor = 1, yFactor = 1;
                  while (theta < -Math.PI) {
                      theta += twoPI;
                  }
                  while (theta > Math.PI) {
                      theta -= twoPI;
                  }
                  tanTheta = Math.tan(theta);
                  if ((theta > -rAtan) && (theta <= rAtan)) {
                      // Right side
                      yFactor = -1;
                      leftOrRightRegion = true;
                  }
                  else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
                      // Top side
                      yFactor = -1;
                  }
                  else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
                      // Left side
                      xFactor = -1;
                      leftOrRightRegion = true;
                  }
                  else {
                      // Bottom side
                      xFactor = -1;
                  }
                  // Correct the edgePoint according to the placement of the marker
                  if (leftOrRightRegion) {
                      edgePoint.x += xFactor * (rectHalfWidth);
                      edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
                  }
                  else {
                      edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
                      edgePoint.y += yFactor * (rectHalfHeight);
                  }
                  if (anchor.x !== rectHorizontalCenter) {
                      edgePoint.x = anchor.x;
                  }
                  if (anchor.y !== rectVerticalCenter) {
                      edgePoint.y = anchor.y;
                  }
                  markerPoint.x = edgePoint.x + (markerRadius * Math.cos(theta));
                  markerPoint.y = edgePoint.y - (markerRadius * Math.sin(theta));
                  return markerPoint;
              }
          });
          /**
           * Warn if using legacy options. Copy the options over. Note that this will
           * still break if using the legacy options in chart.update, addSeries etc.
           * @private
           */
          function warnLegacy(chart) {
              if (chart.options.pathfinder ||
                  chart.series.reduce(function (acc, series) {
                      if (series.options) {
                          merge(true, (series.options.connectors = series.options.connectors ||
                              {}), series.options.pathfinder);
                      }
                      return acc || series.options && series.options.pathfinder;
                  }, false)) {
                  merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
                  H.error('WARNING: Pathfinder options have been renamed. ' +
                      'Use "chart.connectors" or "series.connectors" instead.');
              }
          }
          // Initialize Pathfinder for charts
          H.Chart.prototype.callbacks.push(function (chart) {
              var options = chart.options;
              if (options.connectors.enabled !== false) {
                  warnLegacy(chart);
                  this.pathfinder = new Pathfinder(this);
                  this.pathfinder.update(true); // First draw, defer render
              }
          });
  
      });
      _registerModule(_modules, 'masters/modules/pathfinder.src.js', [], function () {
  
  
      });
  }));