yujian
2020-06-09 554de444b87aab5f93cb1593a8095612cf9479a7
fanli/src/main/webapp/admin/new/js/third-party/highcharts/highcharts-more.src.js
@@ -1,2430 +1,2430 @@
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
 * @license Highcharts JS v3.0.6 (2013-10-04)
 *
 * (c) 2009-2013 Torstein Hønsi
 *
 * License: www.highcharts.com/license
 */
// JSLint options:
/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
(function (Highcharts, UNDEFINED) {
var arrayMin = Highcharts.arrayMin,
   arrayMax = Highcharts.arrayMax,
   each = Highcharts.each,
   extend = Highcharts.extend,
   merge = Highcharts.merge,
   map = Highcharts.map,
   pick = Highcharts.pick,
   pInt = Highcharts.pInt,
   defaultPlotOptions = Highcharts.getOptions().plotOptions,
   seriesTypes = Highcharts.seriesTypes,
   extendClass = Highcharts.extendClass,
   splat = Highcharts.splat,
   wrap = Highcharts.wrap,
   Axis = Highcharts.Axis,
   Tick = Highcharts.Tick,
   Series = Highcharts.Series,
   colProto = seriesTypes.column.prototype,
   math = Math,
   mathRound = math.round,
   mathFloor = math.floor,
   mathMax = math.max,
   noop = function () {};/**
 * The Pane object allows options that are common to a set of X and Y axes.
 *
 * In the future, this can be extended to basic Highcharts and Highstock.
 */
function Pane(options, chart, firstAxis) {
   this.init.call(this, options, chart, firstAxis);
}
// Extend the Pane prototype
extend(Pane.prototype, {
   /**
    * Initiate the Pane object
    */
   init: function (options, chart, firstAxis) {
      var pane = this,
         backgroundOption,
         defaultOptions = pane.defaultOptions;
      pane.chart = chart;
      // Set options
      if (chart.angular) { // gauges
         defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
      }
      pane.options = options = merge(defaultOptions, options);
      backgroundOption = options.background;
      // To avoid having weighty logic to place, update and remove the backgrounds,
      // push them to the first axis' plot bands and borrow the existing logic there.
      if (backgroundOption) {
         each([].concat(splat(backgroundOption)).reverse(), function (config) {
            var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
            config = merge(pane.defaultBackgroundOptions, config);
            if (backgroundColor) {
               config.backgroundColor = backgroundColor;
            }
            config.color = config.backgroundColor; // due to naming in plotBands
            firstAxis.options.plotBands.unshift(config);
         });
      }
   },
   /**
    * The default options object
    */
   defaultOptions: {
      // background: {conditional},
      center: ['50%', '50%'],
      size: '85%',
      startAngle: 0
      //endAngle: startAngle + 360
   },
   /**
    * The default background options
    */
   defaultBackgroundOptions: {
      shape: 'circle',
      borderWidth: 1,
      borderColor: 'silver',
      backgroundColor: {
         linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
         stops: [
            [0, '#FFF'],
            [1, '#DDD']
         ]
      },
      from: Number.MIN_VALUE, // corrected to axis min
      innerRadius: 0,
      to: Number.MAX_VALUE, // corrected to axis max
      outerRadius: '105%'
   }
});
var axisProto = Axis.prototype,
   tickProto = Tick.prototype;
/**
 * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
 */
var hiddenAxisMixin = {
   getOffset: noop,
   redraw: function () {
      this.isDirty = false; // prevent setting Y axis dirty
   },
   render: function () {
      this.isDirty = false; // prevent setting Y axis dirty
   },
   setScale: noop,
   setCategories: noop,
   setTitle: noop
};
/**
 * Augmented methods for the value axis
 */
/*jslint unparam: true*/
var radialAxisMixin = {
   isRadial: true,
   /**
    * The default options extend defaultYAxisOptions
    */
   defaultRadialGaugeOptions: {
      labels: {
         align: 'center',
         x: 0,
         y: null // auto
      },
      minorGridLineWidth: 0,
      minorTickInterval: 'auto',
      minorTickLength: 10,
      minorTickPosition: 'inside',
      minorTickWidth: 1,
      plotBands: [],
      tickLength: 10,
      tickPosition: 'inside',
      tickWidth: 2,
      title: {
         rotation: 0
      },
      zIndex: 2 // behind dials, points in the series group
   },
   // Circular axis around the perimeter of a polar chart
   defaultRadialXOptions: {
      gridLineWidth: 1, // spokes
      labels: {
         align: null, // auto
         distance: 15,
         x: 0,
         y: null // auto
      },
      maxPadding: 0,
      minPadding: 0,
      plotBands: [],
      showLastLabel: false,
      tickLength: 0
   },
   // Radial axis, like a spoke in a polar chart
   defaultRadialYOptions: {
      gridLineInterpolation: 'circle',
      labels: {
         align: 'right',
         x: -3,
         y: -2
      },
      plotBands: [],
      showLastLabel: false,
      title: {
         x: 4,
         text: null,
         rotation: 90
      }
   },
   /**
    * Merge and set options
    */
   setOptions: function (userOptions) {
      this.options = merge(
         this.defaultOptions,
         this.defaultRadialOptions,
         userOptions
      );
   },
   /**
    * Wrap the getOffset method to return zero offset for title or labels in a radial
    * axis
    */
   getOffset: function () {
      // Call the Axis prototype method (the method we're in now is on the instance)
      axisProto.getOffset.call(this);
      // Title or label offsets are not counted
      this.chart.axisOffset[this.side] = 0;
   },
   /**
    * Get the path for the axis line. This method is also referenced in the getPlotLinePath
    * method.
    */
   getLinePath: function (lineWidth, radius) {
      var center = this.center;
      radius = pick(radius, center[2] / 2 - this.offset);
      return this.chart.renderer.symbols.arc(
         this.left + center[0],
         this.top + center[1],
         radius,
         radius,
         {
            start: this.startAngleRad,
            end: this.endAngleRad,
            open: true,
            innerR: 0
         }
      );
   },
   /**
    * Override setAxisTranslation by setting the translation to the difference
    * in rotation. This allows the translate method to return angle for
    * any given value.
    */
   setAxisTranslation: function () {
      // Call uber method
      axisProto.setAxisTranslation.call(this);
      // Set transA and minPixelPadding
      if (this.center) { // it's not defined the first time
         if (this.isCircular) {
            this.transA = (this.endAngleRad - this.startAngleRad) /
               ((this.max - this.min) || 1);
         } else {
            this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
         }
         if (this.isXAxis) {
            this.minPixelPadding = this.transA * this.minPointOffset +
               (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ???
         }
      }
   },
   /**
    * In case of auto connect, add one closestPointRange to the max value right before
    * tickPositions are computed, so that ticks will extend passed the real max.
    */
   beforeSetTickPositions: function () {
      if (this.autoConnect) {
         this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260
      }
   },
   /**
    * Override the setAxisSize method to use the arc's circumference as length. This
    * allows tickPixelInterval to apply to pixel lengths along the perimeter
    */
   setAxisSize: function () {
      axisProto.setAxisSize.call(this);
      if (this.isRadial) {
         // Set the center array
         this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane);
         this.len = this.width = this.height = this.isCircular ?
            this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 :
            this.center[2] / 2;
      }
   },
   /**
    * Returns the x, y coordinate of a point given by a value and a pixel distance
    * from center
    */
   getPosition: function (value, length) {
      if (!this.isCircular) {
         length = this.translate(value);
         value = this.min;
      }
      return this.postTranslate(
         this.translate(value),
         pick(length, this.center[2] / 2) - this.offset
      );
   },
   /**
    * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
    */
   postTranslate: function (angle, radius) {
      var chart = this.chart,
         center = this.center;
      angle = this.startAngleRad + angle;
      return {
         x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
         y: chart.plotTop + center[1] + Math.sin(angle) * radius
      };
   },
   /**
    * Find the path for plot bands along the radial axis
    */
   getPlotBandPath: function (from, to, options) {
      var center = this.center,
         startAngleRad = this.startAngleRad,
         fullRadius = center[2] / 2,
         radii = [
            pick(options.outerRadius, '100%'),
            options.innerRadius,
            pick(options.thickness, 10)
         ],
         percentRegex = /%$/,
         start,
         end,
         open,
         isCircular = this.isCircular, // X axis in a polar chart
         ret;
      // Polygonal plot bands
      if (this.options.gridLineInterpolation === 'polygon') {
         ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
      // Circular grid bands
      } else {
         // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
         if (!isCircular) {
            radii[0] = this.translate(from);
            radii[1] = this.translate(to);
         }
         // Convert percentages to pixel values
         radii = map(radii, function (radius) {
            if (percentRegex.test(radius)) {
               radius = (pInt(radius, 10) * fullRadius) / 100;
            }
            return radius;
         });
         // Handle full circle
         if (options.shape === 'circle' || !isCircular) {
            start = -Math.PI / 2;
            end = Math.PI * 1.5;
            open = true;
         } else {
            start = startAngleRad + this.translate(from);
            end = startAngleRad + this.translate(to);
         }
         ret = this.chart.renderer.symbols.arc(
            this.left + center[0],
            this.top + center[1],
            radii[0],
            radii[0],
            {
               start: start,
               end: end,
               innerR: pick(radii[1], radii[0] - radii[2]),
               open: open
            }
         );
      }
      return ret;
   },
   /**
    * Find the path for plot lines perpendicular to the radial axis.
    */
   getPlotLinePath: function (value, reverse) {
      var axis = this,
         center = axis.center,
         chart = axis.chart,
         end = axis.getPosition(value),
         xAxis,
         xy,
         tickPositions,
         ret;
      // Spokes
      if (axis.isCircular) {
         ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
      // Concentric circles
      } else if (axis.options.gridLineInterpolation === 'circle') {
         value = axis.translate(value);
         if (value) { // a value of 0 is in the center
            ret = axis.getLinePath(0, value);
         }
      // Concentric polygons
      } else {
         xAxis = chart.xAxis[0];
         ret = [];
         value = axis.translate(value);
         tickPositions = xAxis.tickPositions;
         if (xAxis.autoConnect) {
            tickPositions = tickPositions.concat([tickPositions[0]]);
         }
         // Reverse the positions for concatenation of polygonal plot bands
         if (reverse) {
            tickPositions = [].concat(tickPositions).reverse();
         }
         each(tickPositions, function (pos, i) {
            xy = xAxis.getPosition(pos, value);
            ret.push(i ? 'L' : 'M', xy.x, xy.y);
         });
      }
      return ret;
   },
   /**
    * Find the position for the axis title, by default inside the gauge
    */
   getTitlePosition: function () {
      var center = this.center,
         chart = this.chart,
         titleOptions = this.options.title;
      return {
         x: chart.plotLeft + center[0] + (titleOptions.x || 0),
         y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *
            center[2]) + (titleOptions.y || 0)
      };
   }
};
/*jslint unparam: false*/
/**
 * Override axisProto.init to mix in special axis instance functions and function overrides
 */
wrap(axisProto, 'init', function (proceed, chart, userOptions) {
   var axis = this,
      angular = chart.angular,
      polar = chart.polar,
      isX = userOptions.isX,
      isHidden = angular && isX,
      isCircular,
      startAngleRad,
      endAngleRad,
      options,
      chartOptions = chart.options,
      paneIndex = userOptions.pane || 0,
      pane,
      paneOptions;
   // Before prototype.init
   if (angular) {
      extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
      isCircular =  !isX;
      if (isCircular) {
         this.defaultRadialOptions = this.defaultRadialGaugeOptions;
      }
   } else if (polar) {
      //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
      extend(this, radialAxisMixin);
      isCircular = isX;
      this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
   }
   // Run prototype.init
   proceed.call(this, chart, userOptions);
   if (!isHidden && (angular || polar)) {
      options = this.options;
      // Create the pane and set the pane options.
      if (!chart.panes) {
         chart.panes = [];
      }
      this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane(
         splat(chartOptions.pane)[paneIndex],
         chart,
         axis
      );
      paneOptions = pane.options;
      // Disable certain features on angular and polar axes
      chart.inverted = false;
      chartOptions.chart.zoomType = null;
      // Start and end angle options are
      // given in degrees relative to top, while internal computations are
      // in radians relative to right (like SVG).
      this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
      this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360)  - 90) * Math.PI / 180;
      this.offset = options.offset || 0;
      this.isCircular = isCircular;
      // Automatically connect grid lines?
      if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
         this.autoConnect = true;
      }
   }
});
/**
 * Add special cases within the Tick class' methods for radial axes.
 */
wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
   var axis = this.axis;
   return axis.getPosition ?
      axis.getPosition(pos) :
      proceed.call(this, horiz, pos, tickmarkOffset, old);
});
/**
 * Wrap the getLabelPosition function to find the center position of the label
 * based on the distance option
 */
wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
   var axis = this.axis,
      optionsY = labelOptions.y,
      ret,
      align = labelOptions.align,
      angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360;
   if (axis.isRadial) {
      ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
      // Automatically rotated
      if (labelOptions.rotation === 'auto') {
         label.attr({
            rotation: angle
         });
      // Vertically centered
      } else if (optionsY === null) {
         optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
      }
      // Automatic alignment
      if (align === null) {
         if (axis.isCircular) {
            if (angle > 20 && angle < 160) {
               align = 'left'; // right hemisphere
            } else if (angle > 200 && angle < 340) {
               align = 'right'; // left hemisphere
            } else {
               align = 'center'; // top or bottom
            }
         } else {
            align = 'center';
         }
         label.attr({
            align: align
         });
      }
      ret.x += labelOptions.x;
      ret.y += optionsY;
   } else {
      ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
   }
   return ret;
});
/**
 * Wrap the getMarkPath function to return the path of the radial marker
 */
wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
   var axis = this.axis,
      endPoint,
      ret;
   if (axis.isRadial) {
      endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
      ret = [
         'M',
         x,
         y,
         'L',
         endPoint.x,
         endPoint.y
      ];
   } else {
      ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
   }
   return ret;
});/*
 * The AreaRangeSeries class
 *
 */
/**
 * Extend the default options with map options
 */
defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
   lineWidth: 1,
   marker: null,
   threshold: null,
   tooltip: {
      pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'
   },
   trackByArea: true,
   dataLabels: {
      verticalAlign: null,
      xLow: 0,
      xHigh: 0,
      yLow: 0,
      yHigh: 0
   }
});
/**
 * Add the series type
 */
seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
   type: 'arearange',
   pointArrayMap: ['low', 'high'],
   toYData: function (point) {
      return [point.low, point.high];
   },
   pointValKey: 'low',
   /**
    * Extend getSegments to force null points if the higher value is null. #1703.
    */
   getSegments: function () {
      var series = this;
      each(series.points, function (point) {
         if (!series.options.connectNulls && (point.low === null || point.high === null)) {
            point.y = null;
         } else if (point.low === null && point.high !== null) {
            point.y = point.high;
         }
      });
      Series.prototype.getSegments.call(this);
   },
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis;
      seriesTypes.area.prototype.translate.apply(series);
      // Set plotLow and plotHigh
      each(series.points, function (point) {
         var low = point.low,
            high = point.high,
            plotY = point.plotY;
         if (high === null && low === null) {
            point.y = null;
         } else if (low === null) {
            point.plotLow = point.plotY = null;
            point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
         } else if (high === null) {
            point.plotLow = plotY;
            point.plotHigh = null;
         } else {
            point.plotLow = plotY;
            point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
         }
      });
   },
   /**
    * Extend the line series' getSegmentPath method by applying the segment
    * path to both lower and higher values of the range
    */
   getSegmentPath: function (segment) {
      var lowSegment,
         highSegment = [],
         i = segment.length,
         baseGetSegmentPath = Series.prototype.getSegmentPath,
         point,
         linePath,
         lowerPath,
         options = this.options,
         step = options.step,
         higherPath;
      // Remove nulls from low segment
      lowSegment = HighchartsAdapter.grep(segment, function (point) {
         return point.plotLow !== null;
      });
      // Make a segment with plotX and plotY for the top values
      while (i--) {
         point = segment[i];
         if (point.plotHigh !== null) {
            highSegment.push({
               plotX: point.plotX,
               plotY: point.plotHigh
            });
         }
      }
      // Get the paths
      lowerPath = baseGetSegmentPath.call(this, lowSegment);
      if (step) {
         if (step === true) {
            step = 'left';
         }
         options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
      }
      higherPath = baseGetSegmentPath.call(this, highSegment);
      options.step = step;
      // Create a line on both top and bottom of the range
      linePath = [].concat(lowerPath, higherPath);
      // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
      higherPath[0] = 'L'; // this probably doesn't work for spline
      this.areaPath = this.areaPath.concat(lowerPath, higherPath);
      return linePath;
   },
   /**
    * Extend the basic drawDataLabels method by running it for both lower and higher
    * values.
    */
   drawDataLabels: function () {
      var data = this.data,
         length = data.length,
         i,
         originalDataLabels = [],
         seriesProto = Series.prototype,
         dataLabelOptions = this.options.dataLabels,
         point,
         inverted = this.chart.inverted;
      if (dataLabelOptions.enabled || this._hasPointLabels) {
         // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
         i = length;
         while (i--) {
            point = data[i];
            // Set preliminary values
            point.y = point.high;
            point.plotY = point.plotHigh;
            // Store original data labels and set preliminary label objects to be picked up
            // in the uber method
            originalDataLabels[i] = point.dataLabel;
            point.dataLabel = point.dataLabelUpper;
            // Set the default offset
            point.below = false;
            if (inverted) {
               dataLabelOptions.align = 'left';
               dataLabelOptions.x = dataLabelOptions.xHigh;
            } else {
               dataLabelOptions.y = dataLabelOptions.yHigh;
            }
         }
         seriesProto.drawDataLabels.apply(this, arguments); // #1209
         // Step 2: reorganize and handle data labels for the lower values
         i = length;
         while (i--) {
            point = data[i];
            // Move the generated labels from step 1, and reassign the original data labels
            point.dataLabelUpper = point.dataLabel;
            point.dataLabel = originalDataLabels[i];
            // Reset values
            point.y = point.low;
            point.plotY = point.plotLow;
            // Set the default offset
            point.below = true;
            if (inverted) {
               dataLabelOptions.align = 'right';
               dataLabelOptions.x = dataLabelOptions.xLow;
            } else {
               dataLabelOptions.y = dataLabelOptions.yLow;
            }
         }
         seriesProto.drawDataLabels.apply(this, arguments);
      }
   },
   alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
   getSymbol: seriesTypes.column.prototype.getSymbol,
   drawPoints: noop
});/**
 * The AreaSplineRangeSeries class
 */
defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
/**
 * AreaSplineRangeSeries object
 */
seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
   type: 'areasplinerange',
   getPointSpline: seriesTypes.spline.prototype.getPointSpline
});/**
 * The ColumnRangeSeries class
 */
defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
   lineWidth: 1,
   pointRange: null
});
/**
 * ColumnRangeSeries object
 */
seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
   type: 'columnrange',
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         plotHigh;
      colProto.translate.apply(series);
      // Set plotLow and plotHigh
      each(series.points, function (point) {
         var shapeArgs = point.shapeArgs,
            minPointLength = series.options.minPointLength,
            heightDifference,
            height,
            y;
         point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
         point.plotLow = point.plotY;
         // adjust shape
         y = plotHigh;
         height = point.plotY - plotHigh;
         if (height < minPointLength) {
            heightDifference = (minPointLength - height);
            height += heightDifference;
            y -= heightDifference / 2;
         }
         shapeArgs.height = height;
         shapeArgs.y = y;
      });
   },
   trackerGroups: ['group', 'dataLabels'],
   drawGraph: noop,
   pointAttrToOptions: colProto.pointAttrToOptions,
   drawPoints: colProto.drawPoints,
   drawTracker: colProto.drawTracker,
   animate: colProto.animate,
   getColumnMetrics: colProto.getColumnMetrics
});
/*
 * The GaugeSeries class
 */
/**
 * Extend the default options
 */
defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
   dataLabels: {
      enabled: true,
      y: 15,
      borderWidth: 1,
      borderColor: 'silver',
      borderRadius: 3,
      style: {
         fontWeight: 'bold'
      },
      verticalAlign: 'top',
      zIndex: 2
   },
   dial: {
      // radius: '80%',
      // backgroundColor: 'black',
      // borderColor: 'silver',
      // borderWidth: 0,
      // baseWidth: 3,
      // topWidth: 1,
      // baseLength: '70%' // of radius
      // rearLength: '10%'
   },
   pivot: {
      //radius: 5,
      //borderWidth: 0
      //borderColor: 'silver',
      //backgroundColor: 'black'
   },
   tooltip: {
      headerFormat: ''
   },
   showInLegend: false
});
/**
 * Extend the point object
 */
var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
   /**
    * Don't do any hover colors or anything
    */
   setState: function (state) {
      this.state = state;
   }
});
/**
 * Add the series type
 */
var GaugeSeries = {
   type: 'gauge',
   pointClass: GaugePoint,
   // chart.angular will be set to true when a gauge series is present, and this will
   // be used on the axes
   angular: true,
   drawGraph: noop,
   fixedBox: true,
   trackerGroups: ['group', 'dataLabels'],
   /**
    * Calculate paths etc
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         options = series.options,
         center = yAxis.center;
      series.generatePoints();
      each(series.points, function (point) {
         var dialOptions = merge(options.dial, point.dial),
            radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
            baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
            rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
            baseWidth = dialOptions.baseWidth || 3,
            topWidth = dialOptions.topWidth || 1,
            rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
         // Handle the wrap option
         if (options.wrap === false) {
            rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
         }
         rotation = rotation * 180 / Math.PI;
         point.shapeType = 'path';
         point.shapeArgs = {
            d: dialOptions.path || [
               'M',
               -rearLength, -baseWidth / 2,
               'L',
               baseLength, -baseWidth / 2,
               radius, -topWidth / 2,
               radius, topWidth / 2,
               baseLength, baseWidth / 2,
               -rearLength, baseWidth / 2,
               'z'
            ],
            translateX: center[0],
            translateY: center[1],
            rotation: rotation
         };
         // Positions for data label
         point.plotX = center[0];
         point.plotY = center[1];
      });
   },
   /**
    * Draw the points where each point is one needle
    */
   drawPoints: function () {
      var series = this,
         center = series.yAxis.center,
         pivot = series.pivot,
         options = series.options,
         pivotOptions = options.pivot,
         renderer = series.chart.renderer;
      each(series.points, function (point) {
         var graphic = point.graphic,
            shapeArgs = point.shapeArgs,
            d = shapeArgs.d,
            dialOptions = merge(options.dial, point.dial); // #1233
         if (graphic) {
            graphic.animate(shapeArgs);
            shapeArgs.d = d; // animate alters it
         } else {
            point.graphic = renderer[point.shapeType](shapeArgs)
               .attr({
                  stroke: dialOptions.borderColor || 'none',
                  'stroke-width': dialOptions.borderWidth || 0,
                  fill: dialOptions.backgroundColor || 'black',
                  rotation: shapeArgs.rotation // required by VML when animation is false
               })
               .add(series.group);
         }
      });
      // Add or move the pivot
      if (pivot) {
         pivot.animate({ // #1235
            translateX: center[0],
            translateY: center[1]
         });
      } else {
         series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
            .attr({
               'stroke-width': pivotOptions.borderWidth || 0,
               stroke: pivotOptions.borderColor || 'silver',
               fill: pivotOptions.backgroundColor || 'black'
            })
            .translate(center[0], center[1])
            .add(series.group);
      }
   },
   /**
    * Animate the arrow up from startAngle
    */
   animate: function (init) {
      var series = this;
      if (!init) {
         each(series.points, function (point) {
            var graphic = point.graphic;
            if (graphic) {
               // start value
               graphic.attr({
                  rotation: series.yAxis.startAngleRad * 180 / Math.PI
               });
               // animate
               graphic.animate({
                  rotation: point.shapeArgs.rotation
               }, series.options.animation);
            }
         });
         // delete this function to allow it only once
         series.animate = null;
      }
   },
   render: function () {
      this.group = this.plotGroup(
         'group',
         'series',
         this.visible ? 'visible' : 'hidden',
         this.options.zIndex,
         this.chart.seriesGroup
      );
      seriesTypes.pie.prototype.render.call(this);
      this.group.clip(this.chart.clipRect);
   },
   setData: seriesTypes.pie.prototype.setData,
   drawTracker: seriesTypes.column.prototype.drawTracker
};
seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* ****************************************************************************
 * Start Box plot series code                                       *
 *****************************************************************************/
// Set default options
defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
   fillColor: '#FFFFFF',
   lineWidth: 1,
   //medianColor: null,
   medianWidth: 2,
   states: {
      hover: {
         brightness: -0.3
      }
   },
   //stemColor: null,
   //stemDashStyle: 'solid'
   //stemWidth: null,
   threshold: null,
   tooltip: {
      pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' +
         'Maximum: {point.high}<br/>' +
         'Upper quartile: {point.q3}<br/>' +
         'Median: {point.median}<br/>' +
         'Lower quartile: {point.q1}<br/>' +
         'Minimum: {point.low}<br/>'
   },
   //whiskerColor: null,
   whiskerLength: '50%',
   whiskerWidth: 2
});
// Create the series object
seriesTypes.boxplot = extendClass(seriesTypes.column, {
   type: 'boxplot',
   pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
   toYData: function (point) { // return a plain array for speedy calculation
      return [point.low, point.q1, point.median, point.q3, point.high];
   },
   pointValKey: 'high', // defines the top of the tracker
   /**
    * One-to-one mapping from options to SVG attributes
    */
   pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
      fill: 'fillColor',
      stroke: 'color',
      'stroke-width': 'lineWidth'
   },
   /**
    * Disable data labels for box plot
    */
   drawDataLabels: noop,
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         pointArrayMap = series.pointArrayMap;
      seriesTypes.column.prototype.translate.apply(series);
      // do the translation on each point dimension
      each(series.points, function (point) {
         each(pointArrayMap, function (key) {
            if (point[key] !== null) {
               point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
            }
         });
      });
   },
   /**
    * Draw the data points
    */
   drawPoints: function () {
      var series = this,  //state = series.state,
         points = series.points,
         options = series.options,
         chart = series.chart,
         renderer = chart.renderer,
         pointAttr,
         q1Plot,
         q3Plot,
         highPlot,
         lowPlot,
         medianPlot,
         crispCorr,
         crispX,
         graphic,
         stemPath,
         stemAttr,
         boxPath,
         whiskersPath,
         whiskersAttr,
         medianPath,
         medianAttr,
         width,
         left,
         right,
         halfWidth,
         shapeArgs,
         color,
         doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
         whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
      each(points, function (point) {
         graphic = point.graphic;
         shapeArgs = point.shapeArgs; // the box
         stemAttr = {};
         whiskersAttr = {};
         medianAttr = {};
         color = point.color || series.color;
         if (point.plotY !== UNDEFINED) {
            pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
            // crisp vector coordinates
            width = shapeArgs.width;
            left = mathFloor(shapeArgs.x);
            right = left + width;
            halfWidth = mathRound(width / 2);
            //crispX = mathRound(left + halfWidth) + crispCorr;
            q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
            q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
            highPlot = mathFloor(point.highPlot);// + crispCorr;
            lowPlot = mathFloor(point.lowPlot);// + crispCorr;
            // Stem attributes
            stemAttr.stroke = point.stemColor || options.stemColor || color;
            stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
            stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
            // Whiskers attributes
            whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
            whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
            // Median attributes
            medianAttr.stroke = point.medianColor || options.medianColor || color;
            medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
            // The stem
            crispCorr = (stemAttr['stroke-width'] % 2) / 2;
            crispX = left + halfWidth + crispCorr;
            stemPath = [
               // stem up
               'M',
               crispX, q3Plot,
               'L',
               crispX, highPlot,
               // stem down
               'M',
               crispX, q1Plot,
               'L',
               crispX, lowPlot,
               'z'
            ];
            // The box
            if (doQuartiles) {
               crispCorr = (pointAttr['stroke-width'] % 2) / 2;
               crispX = mathFloor(crispX) + crispCorr;
               q1Plot = mathFloor(q1Plot) + crispCorr;
               q3Plot = mathFloor(q3Plot) + crispCorr;
               left += crispCorr;
               right += crispCorr;
               boxPath = [
                  'M',
                  left, q3Plot,
                  'L',
                  left, q1Plot,
                  'L',
                  right, q1Plot,
                  'L',
                  right, q3Plot,
                  'L',
                  left, q3Plot,
                  'z'
               ];
            }
            // The whiskers
            if (whiskerLength) {
               crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
               highPlot = highPlot + crispCorr;
               lowPlot = lowPlot + crispCorr;
               whiskersPath = [
                  // High whisker
                  'M',
                  crispX - halfWidth * whiskerLength,
                  highPlot,
                  'L',
                  crispX + halfWidth * whiskerLength,
                  highPlot,
                  // Low whisker
                  'M',
                  crispX - halfWidth * whiskerLength,
                  lowPlot,
                  'L',
                  crispX + halfWidth * whiskerLength,
                  lowPlot
               ];
            }
            // The median
            crispCorr = (medianAttr['stroke-width'] % 2) / 2;
            medianPlot = mathRound(point.medianPlot) + crispCorr;
            medianPath = [
               'M',
               left,
               medianPlot,
               'L',
               right,
               medianPlot,
               'z'
            ];
            // Create or update the graphics
            if (graphic) { // update
               point.stem.animate({ d: stemPath });
               if (whiskerLength) {
                  point.whiskers.animate({ d: whiskersPath });
               }
               if (doQuartiles) {
                  point.box.animate({ d: boxPath });
               }
               point.medianShape.animate({ d: medianPath });
            } else { // create new
               point.graphic = graphic = renderer.g()
                  .add(series.group);
               point.stem = renderer.path(stemPath)
                  .attr(stemAttr)
                  .add(graphic);
               if (whiskerLength) {
                  point.whiskers = renderer.path(whiskersPath)
                     .attr(whiskersAttr)
                     .add(graphic);
               }
               if (doQuartiles) {
                  point.box = renderer.path(boxPath)
                     .attr(pointAttr)
                     .add(graphic);
               }
               point.medianShape = renderer.path(medianPath)
                  .attr(medianAttr)
                  .add(graphic);
            }
         }
      });
   }
});
/* ****************************************************************************
 * End Box plot series code                                    *
 *****************************************************************************/
/* ****************************************************************************
 * Start error bar series code                                                *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
   color: '#000000',
   grouping: false,
   linkedTo: ':previous',
   tooltip: {
      pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat
   },
   whiskerWidth: null
});
// 2 - Create the series object
seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
   type: 'errorbar',
   pointArrayMap: ['low', 'high'], // array point configs are mapped to this
   toYData: function (point) { // return a plain array for speedy calculation
      return [point.low, point.high];
   },
   pointValKey: 'high', // defines the top of the tracker
   doQuartiles: false,
   /**
    * Get the width and X offset, either on top of the linked series column
    * or standalone
    */
   getColumnMetrics: function () {
      return (this.linkedParent && this.linkedParent.columnMetrics) ||
         seriesTypes.column.prototype.getColumnMetrics.call(this);
   }
});
/* ****************************************************************************
 * End error bar series code                                                  *
 *****************************************************************************/
/* ****************************************************************************
 * Start Waterfall series code                                                *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
   lineWidth: 1,
   lineColor: '#333',
   dashStyle: 'dot',
   borderColor: '#333'
});
// 2 - Create the series object
seriesTypes.waterfall = extendClass(seriesTypes.column, {
   type: 'waterfall',
   upColorProp: 'fill',
   pointArrayMap: ['low', 'y'],
   pointValKey: 'y',
   /**
    * Init waterfall series, force stacking
    */
   init: function (chart, options) {
      // force stacking
      options.stacking = true;
      seriesTypes.column.prototype.init.call(this, chart, options);
   },
   /**
    * Translate data points from raw values
    */
   translate: function () {
      var series = this,
         options = series.options,
         axis = series.yAxis,
         len,
         i,
         points,
         point,
         shapeArgs,
         stack,
         y,
         previousY,
         stackPoint,
         threshold = options.threshold,
         crispCorr = (options.borderWidth % 2) / 2;
      // run column series translate
      seriesTypes.column.prototype.translate.apply(this);
      previousY = threshold;
      points = series.points;
      for (i = 0, len = points.length; i < len; i++) {
         // cache current point object
         point = points[i];
         shapeArgs = point.shapeArgs;
         // get current stack
         stack = series.getStack(i);
         stackPoint = stack.points[series.index];
         // override point value for sums
         if (isNaN(point.y)) {
            point.y = series.yData[i];
         }
         // up points
         y = mathMax(previousY, previousY + point.y) + stackPoint[0];
         shapeArgs.y = axis.translate(y, 0, 1);
         // sum points
         if (point.isSum || point.isIntermediateSum) {
            shapeArgs.y = axis.translate(stackPoint[1], 0, 1);
            shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
         // if it's not the sum point, update previous stack end position
         } else {
            previousY += stack.total;
         }
         // negative points
         if (shapeArgs.height < 0) {
            shapeArgs.y += shapeArgs.height;
            shapeArgs.height *= -1;
         }
         point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - crispCorr;
         shapeArgs.height = mathRound(shapeArgs.height);
         point.yBottom = shapeArgs.y + shapeArgs.height;
      }
   },
   /**
    * Call default processData then override yData to reflect waterfall's extremes on yAxis
    */
   processData: function (force) {
      var series = this,
         options = series.options,
         yData = series.yData,
         points = series.points,
         point,
         dataLength = yData.length,
         threshold = options.threshold || 0,
         subSum,
         sum,
         dataMin,
         dataMax,
         y,
         i;
      sum = subSum = dataMin = dataMax = threshold;
      for (i = 0; i < dataLength; i++) {
         y = yData[i];
         point = points && points[i] ? points[i] : {};
         if (y === "sum" || point.isSum) {
            yData[i] = sum;
         } else if (y === "intermediateSum" || point.isIntermediateSum) {
            yData[i] = subSum;
            subSum = threshold;
         } else {
            sum += y;
            subSum += y;
         }
         dataMin = Math.min(sum, dataMin);
         dataMax = Math.max(sum, dataMax);
      }
      Series.prototype.processData.call(this, force);
      // Record extremes
      series.dataMin = dataMin;
      series.dataMax = dataMax;
   },
   /**
    * Return y value or string if point is sum
    */
   toYData: function (pt) {
      if (pt.isSum) {
         return "sum";
      } else if (pt.isIntermediateSum) {
         return "intermediateSum";
      }
      return pt.y;
   },
   /**
    * Postprocess mapping between options and SVG attributes
    */
   getAttribs: function () {
      seriesTypes.column.prototype.getAttribs.apply(this, arguments);
      var series = this,
         options = series.options,
         stateOptions = options.states,
         upColor = options.upColor || series.color,
         hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
         seriesDownPointAttr = merge(series.pointAttr),
         upColorProp = series.upColorProp;
      seriesDownPointAttr[''][upColorProp] = upColor;
      seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
      seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
      each(series.points, function (point) {
         if (point.y > 0 && !point.color) {
            point.pointAttr = seriesDownPointAttr;
            point.color = upColor;
         }
      });
   },
   /**
    * Draw columns' connector lines
    */
   getGraphPath: function () {
      var data = this.data,
         length = data.length,
         lineWidth = this.options.lineWidth + this.options.borderWidth,
         normalizer = mathRound(lineWidth) % 2 / 2,
         path = [],
         M = 'M',
         L = 'L',
         prevArgs,
         pointArgs,
         i,
         d;
      for (i = 1; i < length; i++) {
         pointArgs = data[i].shapeArgs;
         prevArgs = data[i - 1].shapeArgs;
         d = [
            M,
            prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
            L,
            pointArgs.x, prevArgs.y + normalizer
         ];
         if (data[i - 1].y < 0) {
            d[2] += prevArgs.height;
            d[5] += prevArgs.height;
         }
         path = path.concat(d);
      }
      return path;
   },
   /**
    * Extremes are recorded in processData
    */
   getExtremes: noop,
   /**
    * Return stack for given index
    */
   getStack: function (i) {
      var axis = this.yAxis,
         stacks = axis.stacks,
         key = this.stackKey;
      if (this.processedYData[i] < this.options.threshold) {
         key = '-' + key;
      }
      return stacks[key][i];
   },
   drawGraph: Series.prototype.drawGraph
});
/* ****************************************************************************
 * End Waterfall series code                                                  *
 *****************************************************************************/
/* ****************************************************************************
 * Start Bubble series code                                           *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
   dataLabels: {
      inside: true,
      style: {
         color: 'white',
         textShadow: '0px 0px 3px black'
      },
      verticalAlign: 'middle'
   },
   // displayNegative: true,
   marker: {
      // fillOpacity: 0.5,
      lineColor: null, // inherit from series.color
      lineWidth: 1
   },
   minSize: 8,
   maxSize: '20%',
   // negativeColor: null,
   tooltip: {
      pointFormat: '({point.x}, {point.y}), Size: {point.z}'
   },
   turboThreshold: 0,
   zThreshold: 0
});
// 2 - Create the series object
seriesTypes.bubble = extendClass(seriesTypes.scatter, {
   type: 'bubble',
   pointArrayMap: ['y', 'z'],
   trackerGroups: ['group', 'dataLabelsGroup'],
   /**
    * Mapping between SVG attributes and the corresponding options
    */
   pointAttrToOptions: {
      stroke: 'lineColor',
      'stroke-width': 'lineWidth',
      fill: 'fillColor'
   },
   /**
    * Apply the fillOpacity to all fill positions
    */
   applyOpacity: function (fill) {
      var markerOptions = this.options.marker,
         fillOpacity = pick(markerOptions.fillOpacity, 0.5);
      // When called from Legend.colorizeItem, the fill isn't predefined
      fill = fill || markerOptions.fillColor || this.color;
      if (fillOpacity !== 1) {
         fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba');
      }
      return fill;
   },
   /**
    * Extend the convertAttribs method by applying opacity to the fill
    */
   convertAttribs: function () {
      var obj = Series.prototype.convertAttribs.apply(this, arguments);
      obj.fill = this.applyOpacity(obj.fill);
      return obj;
   },
   /**
    * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
    * must be done prior to Series.translate because the axis needs to add padding in
    * accordance with the point sizes.
    */
   getRadii: function (zMin, zMax, minSize, maxSize) {
      var len,
         i,
         pos,
         zData = this.zData,
         radii = [],
         zRange;
      // Set the shape type and arguments to be picked up in drawPoints
      for (i = 0, len = zData.length; i < len; i++) {
         zRange = zMax - zMin;
         pos = zRange > 0 ? // relative size, a number between 0 and 1
            (zData[i] - zMin) / (zMax - zMin) :
            0.5;
         radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
      }
      this.radii = radii;
   },
   /**
    * Perform animation on the bubbles
    */
   animate: function (init) {
      var animation = this.options.animation;
      if (!init) { // run the animation
         each(this.points, function (point) {
            var graphic = point.graphic,
               shapeArgs = point.shapeArgs;
            if (graphic && shapeArgs) {
               // start values
               graphic.attr('r', 1);
               // animate
               graphic.animate({
                  r: shapeArgs.r
               }, animation);
            }
         });
         // delete this function to allow it only once
         this.animate = null;
      }
   },
   /**
    * Extend the base translate method to handle bubble size
    */
   translate: function () {
      var i,
         data = this.data,
         point,
         radius,
         radii = this.radii;
      // Run the parent method
      seriesTypes.scatter.prototype.translate.call(this);
      // Set the shape type and arguments to be picked up in drawPoints
      i = data.length;
      while (i--) {
         point = data[i];
         radius = radii ? radii[i] : 0; // #1737
         // Flag for negativeColor to be applied in Series.js
         point.negative = point.z < (this.options.zThreshold || 0);
         if (radius >= this.minPxSize / 2) {
            // Shape arguments
            point.shapeType = 'circle';
            point.shapeArgs = {
               x: point.plotX,
               y: point.plotY,
               r: radius
            };
            // Alignment box for the data label
            point.dlBox = {
               x: point.plotX - radius,
               y: point.plotY - radius,
               width: 2 * radius,
               height: 2 * radius
            };
         } else { // below zThreshold
            point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
         }
      }
   },
   /**
    * Get the series' symbol in the legend
    *
    * @param {Object} legend The legend object
    * @param {Object} item The series (this) or point
    */
   drawLegendSymbol: function (legend, item) {
      var radius = pInt(legend.itemStyle.fontSize) / 2;
      item.legendSymbol = this.chart.renderer.circle(
         radius,
         legend.baseline - radius,
         radius
      ).attr({
         zIndex: 3
      }).add(item.legendGroup);
      item.legendSymbol.isMarker = true;
   },
   drawPoints: seriesTypes.column.prototype.drawPoints,
   alignDataLabel: seriesTypes.column.prototype.alignDataLabel
});
/**
 * Add logic to pad each axis with the amount of pixels
 * necessary to avoid the bubbles to overflow.
 */
Axis.prototype.beforePadding = function () {
   var axis = this,
      axisLength = this.len,
      chart = this.chart,
      pxMin = 0,
      pxMax = axisLength,
      isXAxis = this.isXAxis,
      dataKey = isXAxis ? 'xData' : 'yData',
      min = this.min,
      extremes = {},
      smallestSize = math.min(chart.plotWidth, chart.plotHeight),
      zMin = Number.MAX_VALUE,
      zMax = -Number.MAX_VALUE,
      range = this.max - min,
      transA = axisLength / range,
      activeSeries = [];
   // Handle padding on the second pass, or on redraw
   if (this.tickPositions) {
      each(this.series, function (series) {
         var seriesOptions = series.options,
            zData;
         if (series.type === 'bubble' && series.visible) {
            // Correction for #1673
            axis.allowZoomOutside = true;
            // Cache it
            activeSeries.push(series);
            if (isXAxis) { // because X axis is evaluated first
               // For each series, translate the size extremes to pixel values
               each(['minSize', 'maxSize'], function (prop) {
                  var length = seriesOptions[prop],
                     isPercent = /%$/.test(length);
                  length = pInt(length);
                  extremes[prop] = isPercent ?
                     smallestSize * length / 100 :
                     length;
               });
               series.minPxSize = extremes.minSize;
               // Find the min and max Z
               zData = series.zData;
               if (zData.length) { // #1735
                  zMin = math.min(
                     zMin,
                     math.max(
                        arrayMin(zData),
                        seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
                     )
                  );
                  zMax = math.max(zMax, arrayMax(zData));
               }
            }
         }
      });
      each(activeSeries, function (series) {
         var data = series[dataKey],
            i = data.length,
            radius;
         if (isXAxis) {
            series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
         }
         if (range > 0) {
            while (i--) {
               radius = series.radii[i];
               pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
               pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
            }
         }
      });
      if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
         pxMax -= axisLength;
         transA *= (axisLength + pxMin - pxMax) / axisLength;
         this.min += pxMin / transA;
         this.max += pxMax / transA;
      }
   }
};
/* ****************************************************************************
 * End Bubble series code                                                     *
 *****************************************************************************/
/**
 * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
 * gathered in RadialAxes.js.
 *
 */
var seriesProto = Series.prototype,
   pointerProto = Highcharts.Pointer.prototype;
/**
 * Translate a point's plotX and plotY from the internal angle and radius measures to
 * true plotX, plotY coordinates
 */
seriesProto.toXY = function (point) {
   var xy,
      chart = this.chart,
      plotX = point.plotX,
      plotY = point.plotY;
   // Save rectangular plotX, plotY for later computation
   point.rectPlotX = plotX;
   point.rectPlotY = plotY;
   // Record the angle in degrees for use in tooltip
   point.clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360;
   // Find the polar plotX and plotY
   xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
   point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
   point.plotY = point.polarPlotY = xy.y - chart.plotTop;
};
/**
 * Order the tooltip points to get the mouse capture ranges correct. #1915.
 */
seriesProto.orderTooltipPoints = function (points) {
   if (this.chart.polar) {
      points.sort(function (a, b) {
         return a.clientX - b.clientX;
      });
      // Wrap mouse tracking around to capture movement on the segment to the left
      // of the north point (#1469, #2093).
      if (points[0]) {
         points[0].wrappedClientX = points[0].clientX + 360;
         points.push(points[0]);
      }
   }
};
/**
 * Add some special init logic to areas and areasplines
 */
function initArea(proceed, chart, options) {
   proceed.call(this, chart, options);
   if (this.chart.polar) {
      /**
       * Overridden method to close a segment path. While in a cartesian plane the area
       * goes down to the threshold, in the polar chart it goes to the center.
       */
      this.closeSegment = function (path) {
         var center = this.xAxis.center;
         path.push(
            'L',
            center[0],
            center[1]
         );
      };
      // Instead of complicated logic to draw an area around the inner area in a stack,
      // just draw it behind
      this.closedStacks = true;
   }
}
wrap(seriesTypes.area.prototype, 'init', initArea);
wrap(seriesTypes.areaspline.prototype, 'init', initArea);
/**
 * Overridden method for calculating a spline from one point to the next
 */
wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
   var ret,
      smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
      denom = smoothing + 1,
      plotX,
      plotY,
      lastPoint,
      nextPoint,
      lastX,
      lastY,
      nextX,
      nextY,
      leftContX,
      leftContY,
      rightContX,
      rightContY,
      distanceLeftControlPoint,
      distanceRightControlPoint,
      leftContAngle,
      rightContAngle,
      jointAngle;
   if (this.chart.polar) {
      plotX = point.plotX;
      plotY = point.plotY;
      lastPoint = segment[i - 1];
      nextPoint = segment[i + 1];
      // Connect ends
      if (this.connectEnds) {
         if (!lastPoint) {
            lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
         }
         if (!nextPoint) {
            nextPoint = segment[1];
         }
      }
      // find control points
      if (lastPoint && nextPoint) {
         lastX = lastPoint.plotX;
         lastY = lastPoint.plotY;
         nextX = nextPoint.plotX;
         nextY = nextPoint.plotY;
         leftContX = (smoothing * plotX + lastX) / denom;
         leftContY = (smoothing * plotY + lastY) / denom;
         rightContX = (smoothing * plotX + nextX) / denom;
         rightContY = (smoothing * plotY + nextY) / denom;
         distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
         distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
         leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
         rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
         jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
         // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
         if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
            jointAngle -= Math.PI;
         }
         // Find the corrected control points for a spline straight through the point
         leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
         leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
         rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
         rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
         // Record for drawing in next point
         point.rightContX = rightContX;
         point.rightContY = rightContY;
      }
      // moveTo or lineTo
      if (!i) {
         ret = ['M', plotX, plotY];
      } else { // curve from last point to this
         ret = [
            'C',
            lastPoint.rightContX || lastPoint.plotX,
            lastPoint.rightContY || lastPoint.plotY,
            leftContX || plotX,
            leftContY || plotY,
            plotX,
            plotY
         ];
         lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
      }
   } else {
      ret = proceed.call(this, segment, point, i);
   }
   return ret;
});
/**
 * Extend translate. The plotX and plotY values are computed as if the polar chart were a
 * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
 * center.
 */
wrap(seriesProto, 'translate', function (proceed) {
   // Run uber method
   proceed.call(this);
   // Postprocess plot coordinates
   if (this.chart.polar && !this.preventPostTranslate) {
      var points = this.points,
         i = points.length;
      while (i--) {
         // Translate plotX, plotY from angle and radius to true plot coordinates
         this.toXY(points[i]);
      }
   }
});
/**
 * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
 * line-like series.
 */
wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
   var points = this.points;
   // Connect the path
   if (this.chart.polar && this.options.connectEnds !== false &&
         segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
      this.connectEnds = true; // re-used in splines
      segment = [].concat(segment, [points[0]]);
   }
   // Run uber method
   return proceed.call(this, segment);
});
function polarAnimate(proceed, init) {
   var chart = this.chart,
      animation = this.options.animation,
      group = this.group,
      markerGroup = this.markerGroup,
      center = this.xAxis.center,
      plotLeft = chart.plotLeft,
      plotTop = chart.plotTop,
      attribs;
   // Specific animation for polar charts
   if (chart.polar) {
      // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
      // would be so slow it would't matter.
      if (chart.renderer.isSVG) {
         if (animation === true) {
            animation = {};
         }
         // Initialize the animation
         if (init) {
            // Scale down the group and place it in the center
            attribs = {
               translateX: center[0] + plotLeft,
               translateY: center[1] + plotTop,
               scaleX: 0.001, // #1499
               scaleY: 0.001
            };
            group.attr(attribs);
            if (markerGroup) {
               markerGroup.attrSetters = group.attrSetters;
               markerGroup.attr(attribs);
            }
         // Run the animation
         } else {
            attribs = {
               translateX: plotLeft,
               translateY: plotTop,
               scaleX: 1,
               scaleY: 1
            };
            group.animate(attribs, animation);
            if (markerGroup) {
               markerGroup.animate(attribs, animation);
            }
            // Delete this function to allow it only once
            this.animate = null;
         }
      }
   // For non-polar charts, revert to the basic animation
   } else {
      proceed.call(this, init);
   }
}
// Define the animate method for both regular series and column series and their derivatives
wrap(seriesProto, 'animate', polarAnimate);
wrap(colProto, 'animate', polarAnimate);
/**
 * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
 * in degrees (0-360), not plot pixel width.
 */
wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
   if (this.chart.polar) {
      extend(this.xAxis, {
         tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
      });
   }
   // Run uber method
   return proceed.call(this, renew);
});
/**
 * Extend the column prototype's translate method
 */
wrap(colProto, 'translate', function (proceed) {
   var xAxis = this.xAxis,
      len = this.yAxis.len,
      center = xAxis.center,
      startAngleRad = xAxis.startAngleRad,
      renderer = this.chart.renderer,
      start,
      points,
      point,
      i;
   this.preventPostTranslate = true;
   // Run uber method
   proceed.call(this);
   // Postprocess plot coordinates
   if (xAxis.isRadial) {
      points = this.points;
      i = points.length;
      while (i--) {
         point = points[i];
         start = point.barX + startAngleRad;
         point.shapeType = 'path';
         point.shapeArgs = {
            d: renderer.symbols.arc(
               center[0],
               center[1],
               len - point.plotY,
               null,
               {
                  start: start,
                  end: start + point.pointWidth,
                  innerR: len - pick(point.yBottom, len)
               }
            )
         };
         this.toXY(point); // provide correct plotX, plotY for tooltip
      }
   }
});
/**
 * Align column data labels outside the columns. #1199.
 */
wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
   if (this.chart.polar) {
      var angle = point.rectPlotX / Math.PI * 180,
         align,
         verticalAlign;
      // Align nicely outside the perimeter of the columns
      if (options.align === null) {
         if (angle > 20 && angle < 160) {
            align = 'left'; // right hemisphere
         } else if (angle > 200 && angle < 340) {
            align = 'right'; // left hemisphere
         } else {
            align = 'center'; // top or bottom
         }
         options.align = align;
      }
      if (options.verticalAlign === null) {
         if (angle < 45 || angle > 315) {
            verticalAlign = 'bottom'; // top part
         } else if (angle > 135 && angle < 225) {
            verticalAlign = 'top'; // bottom part
         } else {
            verticalAlign = 'middle'; // left or right
         }
         options.verticalAlign = verticalAlign;
      }
      seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
   } else {
      proceed.call(this, point, dataLabel, options, alignTo, isNew);
   }
});
/**
 * Extend the mouse tracker to return the tooltip position index in terms of
 * degrees rather than pixels
 */
wrap(pointerProto, 'getIndex', function (proceed, e) {
   var ret,
      chart = this.chart,
      center,
      x,
      y;
   if (chart.polar) {
      center = chart.xAxis[0].center;
      x = e.chartX - center[0] - chart.plotLeft;
      y = e.chartY - center[1] - chart.plotTop;
      ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
   } else {
      // Run uber method
      ret = proceed.call(this, e);
   }
   return ret;
});
/**
 * Extend getCoordinates to prepare for polar axis values
 */
wrap(pointerProto, 'getCoordinates', function (proceed, e) {
   var chart = this.chart,
      ret = {
         xAxis: [],
         yAxis: []
      };
   if (chart.polar) {
      each(chart.axes, function (axis) {
         var isXAxis = axis.isXAxis,
            center = axis.center,
            x = e.chartX - center[0] - chart.plotLeft,
            y = e.chartY - center[1] - chart.plotTop;
         ret[isXAxis ? 'xAxis' : 'yAxis'].push({
            axis: axis,
            value: axis.translate(
               isXAxis ?
                  Math.PI - Math.atan2(x, y) : // angle
                  Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
               true
            )
         });
      });
   } else {
      ret = proceed.call(this, e);
   }
   return ret;
});
}(Highcharts));
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
 * @license Highcharts JS v3.0.6 (2013-10-04)
 *
 * (c) 2009-2013 Torstein Hønsi
 *
 * License: www.highcharts.com/license
 */
// JSLint options:
/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
(function (Highcharts, UNDEFINED) {
var arrayMin = Highcharts.arrayMin,
   arrayMax = Highcharts.arrayMax,
   each = Highcharts.each,
   extend = Highcharts.extend,
   merge = Highcharts.merge,
   map = Highcharts.map,
   pick = Highcharts.pick,
   pInt = Highcharts.pInt,
   defaultPlotOptions = Highcharts.getOptions().plotOptions,
   seriesTypes = Highcharts.seriesTypes,
   extendClass = Highcharts.extendClass,
   splat = Highcharts.splat,
   wrap = Highcharts.wrap,
   Axis = Highcharts.Axis,
   Tick = Highcharts.Tick,
   Series = Highcharts.Series,
   colProto = seriesTypes.column.prototype,
   math = Math,
   mathRound = math.round,
   mathFloor = math.floor,
   mathMax = math.max,
   noop = function () {};/**
 * The Pane object allows options that are common to a set of X and Y axes.
 *
 * In the future, this can be extended to basic Highcharts and Highstock.
 */
function Pane(options, chart, firstAxis) {
   this.init.call(this, options, chart, firstAxis);
}
// Extend the Pane prototype
extend(Pane.prototype, {
   /**
    * Initiate the Pane object
    */
   init: function (options, chart, firstAxis) {
      var pane = this,
         backgroundOption,
         defaultOptions = pane.defaultOptions;
      pane.chart = chart;
      // Set options
      if (chart.angular) { // gauges
         defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions
      }
      pane.options = options = merge(defaultOptions, options);
      backgroundOption = options.background;
      // To avoid having weighty logic to place, update and remove the backgrounds,
      // push them to the first axis' plot bands and borrow the existing logic there.
      if (backgroundOption) {
         each([].concat(splat(backgroundOption)).reverse(), function (config) {
            var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients)
            config = merge(pane.defaultBackgroundOptions, config);
            if (backgroundColor) {
               config.backgroundColor = backgroundColor;
            }
            config.color = config.backgroundColor; // due to naming in plotBands
            firstAxis.options.plotBands.unshift(config);
         });
      }
   },
   /**
    * The default options object
    */
   defaultOptions: {
      // background: {conditional},
      center: ['50%', '50%'],
      size: '85%',
      startAngle: 0
      //endAngle: startAngle + 360
   },
   /**
    * The default background options
    */
   defaultBackgroundOptions: {
      shape: 'circle',
      borderWidth: 1,
      borderColor: 'silver',
      backgroundColor: {
         linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
         stops: [
            [0, '#FFF'],
            [1, '#DDD']
         ]
      },
      from: Number.MIN_VALUE, // corrected to axis min
      innerRadius: 0,
      to: Number.MAX_VALUE, // corrected to axis max
      outerRadius: '105%'
   }
});
var axisProto = Axis.prototype,
   tickProto = Tick.prototype;
/**
 * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges
 */
var hiddenAxisMixin = {
   getOffset: noop,
   redraw: function () {
      this.isDirty = false; // prevent setting Y axis dirty
   },
   render: function () {
      this.isDirty = false; // prevent setting Y axis dirty
   },
   setScale: noop,
   setCategories: noop,
   setTitle: noop
};
/**
 * Augmented methods for the value axis
 */
/*jslint unparam: true*/
var radialAxisMixin = {
   isRadial: true,
   /**
    * The default options extend defaultYAxisOptions
    */
   defaultRadialGaugeOptions: {
      labels: {
         align: 'center',
         x: 0,
         y: null // auto
      },
      minorGridLineWidth: 0,
      minorTickInterval: 'auto',
      minorTickLength: 10,
      minorTickPosition: 'inside',
      minorTickWidth: 1,
      plotBands: [],
      tickLength: 10,
      tickPosition: 'inside',
      tickWidth: 2,
      title: {
         rotation: 0
      },
      zIndex: 2 // behind dials, points in the series group
   },
   // Circular axis around the perimeter of a polar chart
   defaultRadialXOptions: {
      gridLineWidth: 1, // spokes
      labels: {
         align: null, // auto
         distance: 15,
         x: 0,
         y: null // auto
      },
      maxPadding: 0,
      minPadding: 0,
      plotBands: [],
      showLastLabel: false,
      tickLength: 0
   },
   // Radial axis, like a spoke in a polar chart
   defaultRadialYOptions: {
      gridLineInterpolation: 'circle',
      labels: {
         align: 'right',
         x: -3,
         y: -2
      },
      plotBands: [],
      showLastLabel: false,
      title: {
         x: 4,
         text: null,
         rotation: 90
      }
   },
   /**
    * Merge and set options
    */
   setOptions: function (userOptions) {
      this.options = merge(
         this.defaultOptions,
         this.defaultRadialOptions,
         userOptions
      );
   },
   /**
    * Wrap the getOffset method to return zero offset for title or labels in a radial
    * axis
    */
   getOffset: function () {
      // Call the Axis prototype method (the method we're in now is on the instance)
      axisProto.getOffset.call(this);
      // Title or label offsets are not counted
      this.chart.axisOffset[this.side] = 0;
   },
   /**
    * Get the path for the axis line. This method is also referenced in the getPlotLinePath
    * method.
    */
   getLinePath: function (lineWidth, radius) {
      var center = this.center;
      radius = pick(radius, center[2] / 2 - this.offset);
      return this.chart.renderer.symbols.arc(
         this.left + center[0],
         this.top + center[1],
         radius,
         radius,
         {
            start: this.startAngleRad,
            end: this.endAngleRad,
            open: true,
            innerR: 0
         }
      );
   },
   /**
    * Override setAxisTranslation by setting the translation to the difference
    * in rotation. This allows the translate method to return angle for
    * any given value.
    */
   setAxisTranslation: function () {
      // Call uber method
      axisProto.setAxisTranslation.call(this);
      // Set transA and minPixelPadding
      if (this.center) { // it's not defined the first time
         if (this.isCircular) {
            this.transA = (this.endAngleRad - this.startAngleRad) /
               ((this.max - this.min) || 1);
         } else {
            this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1);
         }
         if (this.isXAxis) {
            this.minPixelPadding = this.transA * this.minPointOffset +
               (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ???
         }
      }
   },
   /**
    * In case of auto connect, add one closestPointRange to the max value right before
    * tickPositions are computed, so that ticks will extend passed the real max.
    */
   beforeSetTickPositions: function () {
      if (this.autoConnect) {
         this.max += (this.categories && 1) || this.pointRange || this.closestPointRange || 0; // #1197, #2260
      }
   },
   /**
    * Override the setAxisSize method to use the arc's circumference as length. This
    * allows tickPixelInterval to apply to pixel lengths along the perimeter
    */
   setAxisSize: function () {
      axisProto.setAxisSize.call(this);
      if (this.isRadial) {
         // Set the center array
         this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane);
         this.len = this.width = this.height = this.isCircular ?
            this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 :
            this.center[2] / 2;
      }
   },
   /**
    * Returns the x, y coordinate of a point given by a value and a pixel distance
    * from center
    */
   getPosition: function (value, length) {
      if (!this.isCircular) {
         length = this.translate(value);
         value = this.min;
      }
      return this.postTranslate(
         this.translate(value),
         pick(length, this.center[2] / 2) - this.offset
      );
   },
   /**
    * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates.
    */
   postTranslate: function (angle, radius) {
      var chart = this.chart,
         center = this.center;
      angle = this.startAngleRad + angle;
      return {
         x: chart.plotLeft + center[0] + Math.cos(angle) * radius,
         y: chart.plotTop + center[1] + Math.sin(angle) * radius
      };
   },
   /**
    * Find the path for plot bands along the radial axis
    */
   getPlotBandPath: function (from, to, options) {
      var center = this.center,
         startAngleRad = this.startAngleRad,
         fullRadius = center[2] / 2,
         radii = [
            pick(options.outerRadius, '100%'),
            options.innerRadius,
            pick(options.thickness, 10)
         ],
         percentRegex = /%$/,
         start,
         end,
         open,
         isCircular = this.isCircular, // X axis in a polar chart
         ret;
      // Polygonal plot bands
      if (this.options.gridLineInterpolation === 'polygon') {
         ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true));
      // Circular grid bands
      } else {
         // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from
         if (!isCircular) {
            radii[0] = this.translate(from);
            radii[1] = this.translate(to);
         }
         // Convert percentages to pixel values
         radii = map(radii, function (radius) {
            if (percentRegex.test(radius)) {
               radius = (pInt(radius, 10) * fullRadius) / 100;
            }
            return radius;
         });
         // Handle full circle
         if (options.shape === 'circle' || !isCircular) {
            start = -Math.PI / 2;
            end = Math.PI * 1.5;
            open = true;
         } else {
            start = startAngleRad + this.translate(from);
            end = startAngleRad + this.translate(to);
         }
         ret = this.chart.renderer.symbols.arc(
            this.left + center[0],
            this.top + center[1],
            radii[0],
            radii[0],
            {
               start: start,
               end: end,
               innerR: pick(radii[1], radii[0] - radii[2]),
               open: open
            }
         );
      }
      return ret;
   },
   /**
    * Find the path for plot lines perpendicular to the radial axis.
    */
   getPlotLinePath: function (value, reverse) {
      var axis = this,
         center = axis.center,
         chart = axis.chart,
         end = axis.getPosition(value),
         xAxis,
         xy,
         tickPositions,
         ret;
      // Spokes
      if (axis.isCircular) {
         ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y];
      // Concentric circles
      } else if (axis.options.gridLineInterpolation === 'circle') {
         value = axis.translate(value);
         if (value) { // a value of 0 is in the center
            ret = axis.getLinePath(0, value);
         }
      // Concentric polygons
      } else {
         xAxis = chart.xAxis[0];
         ret = [];
         value = axis.translate(value);
         tickPositions = xAxis.tickPositions;
         if (xAxis.autoConnect) {
            tickPositions = tickPositions.concat([tickPositions[0]]);
         }
         // Reverse the positions for concatenation of polygonal plot bands
         if (reverse) {
            tickPositions = [].concat(tickPositions).reverse();
         }
         each(tickPositions, function (pos, i) {
            xy = xAxis.getPosition(pos, value);
            ret.push(i ? 'L' : 'M', xy.x, xy.y);
         });
      }
      return ret;
   },
   /**
    * Find the position for the axis title, by default inside the gauge
    */
   getTitlePosition: function () {
      var center = this.center,
         chart = this.chart,
         titleOptions = this.options.title;
      return {
         x: chart.plotLeft + center[0] + (titleOptions.x || 0),
         y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] *
            center[2]) + (titleOptions.y || 0)
      };
   }
};
/*jslint unparam: false*/
/**
 * Override axisProto.init to mix in special axis instance functions and function overrides
 */
wrap(axisProto, 'init', function (proceed, chart, userOptions) {
   var axis = this,
      angular = chart.angular,
      polar = chart.polar,
      isX = userOptions.isX,
      isHidden = angular && isX,
      isCircular,
      startAngleRad,
      endAngleRad,
      options,
      chartOptions = chart.options,
      paneIndex = userOptions.pane || 0,
      pane,
      paneOptions;
   // Before prototype.init
   if (angular) {
      extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin);
      isCircular =  !isX;
      if (isCircular) {
         this.defaultRadialOptions = this.defaultRadialGaugeOptions;
      }
   } else if (polar) {
      //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin);
      extend(this, radialAxisMixin);
      isCircular = isX;
      this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions);
   }
   // Run prototype.init
   proceed.call(this, chart, userOptions);
   if (!isHidden && (angular || polar)) {
      options = this.options;
      // Create the pane and set the pane options.
      if (!chart.panes) {
         chart.panes = [];
      }
      this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane(
         splat(chartOptions.pane)[paneIndex],
         chart,
         axis
      );
      paneOptions = pane.options;
      // Disable certain features on angular and polar axes
      chart.inverted = false;
      chartOptions.chart.zoomType = null;
      // Start and end angle options are
      // given in degrees relative to top, while internal computations are
      // in radians relative to right (like SVG).
      this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180;
      this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360)  - 90) * Math.PI / 180;
      this.offset = options.offset || 0;
      this.isCircular = isCircular;
      // Automatically connect grid lines?
      if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) {
         this.autoConnect = true;
      }
   }
});
/**
 * Add special cases within the Tick class' methods for radial axes.
 */
wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) {
   var axis = this.axis;
   return axis.getPosition ?
      axis.getPosition(pos) :
      proceed.call(this, horiz, pos, tickmarkOffset, old);
});
/**
 * Wrap the getLabelPosition function to find the center position of the label
 * based on the distance option
 */
wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
   var axis = this.axis,
      optionsY = labelOptions.y,
      ret,
      align = labelOptions.align,
      angle = ((axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180) % 360;
   if (axis.isRadial) {
      ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25));
      // Automatically rotated
      if (labelOptions.rotation === 'auto') {
         label.attr({
            rotation: angle
         });
      // Vertically centered
      } else if (optionsY === null) {
         optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2;
      }
      // Automatic alignment
      if (align === null) {
         if (axis.isCircular) {
            if (angle > 20 && angle < 160) {
               align = 'left'; // right hemisphere
            } else if (angle > 200 && angle < 340) {
               align = 'right'; // left hemisphere
            } else {
               align = 'center'; // top or bottom
            }
         } else {
            align = 'center';
         }
         label.attr({
            align: align
         });
      }
      ret.x += labelOptions.x;
      ret.y += optionsY;
   } else {
      ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
   }
   return ret;
});
/**
 * Wrap the getMarkPath function to return the path of the radial marker
 */
wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) {
   var axis = this.axis,
      endPoint,
      ret;
   if (axis.isRadial) {
      endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength);
      ret = [
         'M',
         x,
         y,
         'L',
         endPoint.x,
         endPoint.y
      ];
   } else {
      ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer);
   }
   return ret;
});/*
 * The AreaRangeSeries class
 *
 */
/**
 * Extend the default options with map options
 */
defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
   lineWidth: 1,
   marker: null,
   threshold: null,
   tooltip: {
      pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.low}</b> - <b>{point.high}</b><br/>'
   },
   trackByArea: true,
   dataLabels: {
      verticalAlign: null,
      xLow: 0,
      xHigh: 0,
      yLow: 0,
      yHigh: 0
   }
});
/**
 * Add the series type
 */
seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, {
   type: 'arearange',
   pointArrayMap: ['low', 'high'],
   toYData: function (point) {
      return [point.low, point.high];
   },
   pointValKey: 'low',
   /**
    * Extend getSegments to force null points if the higher value is null. #1703.
    */
   getSegments: function () {
      var series = this;
      each(series.points, function (point) {
         if (!series.options.connectNulls && (point.low === null || point.high === null)) {
            point.y = null;
         } else if (point.low === null && point.high !== null) {
            point.y = point.high;
         }
      });
      Series.prototype.getSegments.call(this);
   },
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis;
      seriesTypes.area.prototype.translate.apply(series);
      // Set plotLow and plotHigh
      each(series.points, function (point) {
         var low = point.low,
            high = point.high,
            plotY = point.plotY;
         if (high === null && low === null) {
            point.y = null;
         } else if (low === null) {
            point.plotLow = point.plotY = null;
            point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
         } else if (high === null) {
            point.plotLow = plotY;
            point.plotHigh = null;
         } else {
            point.plotLow = plotY;
            point.plotHigh = yAxis.translate(high, 0, 1, 0, 1);
         }
      });
   },
   /**
    * Extend the line series' getSegmentPath method by applying the segment
    * path to both lower and higher values of the range
    */
   getSegmentPath: function (segment) {
      var lowSegment,
         highSegment = [],
         i = segment.length,
         baseGetSegmentPath = Series.prototype.getSegmentPath,
         point,
         linePath,
         lowerPath,
         options = this.options,
         step = options.step,
         higherPath;
      // Remove nulls from low segment
      lowSegment = HighchartsAdapter.grep(segment, function (point) {
         return point.plotLow !== null;
      });
      // Make a segment with plotX and plotY for the top values
      while (i--) {
         point = segment[i];
         if (point.plotHigh !== null) {
            highSegment.push({
               plotX: point.plotX,
               plotY: point.plotHigh
            });
         }
      }
      // Get the paths
      lowerPath = baseGetSegmentPath.call(this, lowSegment);
      if (step) {
         if (step === true) {
            step = 'left';
         }
         options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath
      }
      higherPath = baseGetSegmentPath.call(this, highSegment);
      options.step = step;
      // Create a line on both top and bottom of the range
      linePath = [].concat(lowerPath, higherPath);
      // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo'
      higherPath[0] = 'L'; // this probably doesn't work for spline
      this.areaPath = this.areaPath.concat(lowerPath, higherPath);
      return linePath;
   },
   /**
    * Extend the basic drawDataLabels method by running it for both lower and higher
    * values.
    */
   drawDataLabels: function () {
      var data = this.data,
         length = data.length,
         i,
         originalDataLabels = [],
         seriesProto = Series.prototype,
         dataLabelOptions = this.options.dataLabels,
         point,
         inverted = this.chart.inverted;
      if (dataLabelOptions.enabled || this._hasPointLabels) {
         // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels
         i = length;
         while (i--) {
            point = data[i];
            // Set preliminary values
            point.y = point.high;
            point.plotY = point.plotHigh;
            // Store original data labels and set preliminary label objects to be picked up
            // in the uber method
            originalDataLabels[i] = point.dataLabel;
            point.dataLabel = point.dataLabelUpper;
            // Set the default offset
            point.below = false;
            if (inverted) {
               dataLabelOptions.align = 'left';
               dataLabelOptions.x = dataLabelOptions.xHigh;
            } else {
               dataLabelOptions.y = dataLabelOptions.yHigh;
            }
         }
         seriesProto.drawDataLabels.apply(this, arguments); // #1209
         // Step 2: reorganize and handle data labels for the lower values
         i = length;
         while (i--) {
            point = data[i];
            // Move the generated labels from step 1, and reassign the original data labels
            point.dataLabelUpper = point.dataLabel;
            point.dataLabel = originalDataLabels[i];
            // Reset values
            point.y = point.low;
            point.plotY = point.plotLow;
            // Set the default offset
            point.below = true;
            if (inverted) {
               dataLabelOptions.align = 'right';
               dataLabelOptions.x = dataLabelOptions.xLow;
            } else {
               dataLabelOptions.y = dataLabelOptions.yLow;
            }
         }
         seriesProto.drawDataLabels.apply(this, arguments);
      }
   },
   alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
   getSymbol: seriesTypes.column.prototype.getSymbol,
   drawPoints: noop
});/**
 * The AreaSplineRangeSeries class
 */
defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange);
/**
 * AreaSplineRangeSeries object
 */
seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
   type: 'areasplinerange',
   getPointSpline: seriesTypes.spline.prototype.getPointSpline
});/**
 * The ColumnRangeSeries class
 */
defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, {
   lineWidth: 1,
   pointRange: null
});
/**
 * ColumnRangeSeries object
 */
seriesTypes.columnrange = extendClass(seriesTypes.arearange, {
   type: 'columnrange',
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         plotHigh;
      colProto.translate.apply(series);
      // Set plotLow and plotHigh
      each(series.points, function (point) {
         var shapeArgs = point.shapeArgs,
            minPointLength = series.options.minPointLength,
            heightDifference,
            height,
            y;
         point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1);
         point.plotLow = point.plotY;
         // adjust shape
         y = plotHigh;
         height = point.plotY - plotHigh;
         if (height < minPointLength) {
            heightDifference = (minPointLength - height);
            height += heightDifference;
            y -= heightDifference / 2;
         }
         shapeArgs.height = height;
         shapeArgs.y = y;
      });
   },
   trackerGroups: ['group', 'dataLabels'],
   drawGraph: noop,
   pointAttrToOptions: colProto.pointAttrToOptions,
   drawPoints: colProto.drawPoints,
   drawTracker: colProto.drawTracker,
   animate: colProto.animate,
   getColumnMetrics: colProto.getColumnMetrics
});
/*
 * The GaugeSeries class
 */
/**
 * Extend the default options
 */
defaultPlotOptions.gauge = merge(defaultPlotOptions.line, {
   dataLabels: {
      enabled: true,
      y: 15,
      borderWidth: 1,
      borderColor: 'silver',
      borderRadius: 3,
      style: {
         fontWeight: 'bold'
      },
      verticalAlign: 'top',
      zIndex: 2
   },
   dial: {
      // radius: '80%',
      // backgroundColor: 'black',
      // borderColor: 'silver',
      // borderWidth: 0,
      // baseWidth: 3,
      // topWidth: 1,
      // baseLength: '70%' // of radius
      // rearLength: '10%'
   },
   pivot: {
      //radius: 5,
      //borderWidth: 0
      //borderColor: 'silver',
      //backgroundColor: 'black'
   },
   tooltip: {
      headerFormat: ''
   },
   showInLegend: false
});
/**
 * Extend the point object
 */
var GaugePoint = Highcharts.extendClass(Highcharts.Point, {
   /**
    * Don't do any hover colors or anything
    */
   setState: function (state) {
      this.state = state;
   }
});
/**
 * Add the series type
 */
var GaugeSeries = {
   type: 'gauge',
   pointClass: GaugePoint,
   // chart.angular will be set to true when a gauge series is present, and this will
   // be used on the axes
   angular: true,
   drawGraph: noop,
   fixedBox: true,
   trackerGroups: ['group', 'dataLabels'],
   /**
    * Calculate paths etc
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         options = series.options,
         center = yAxis.center;
      series.generatePoints();
      each(series.points, function (point) {
         var dialOptions = merge(options.dial, point.dial),
            radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200,
            baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100,
            rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100,
            baseWidth = dialOptions.baseWidth || 3,
            topWidth = dialOptions.topWidth || 1,
            rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
         // Handle the wrap option
         if (options.wrap === false) {
            rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation));
         }
         rotation = rotation * 180 / Math.PI;
         point.shapeType = 'path';
         point.shapeArgs = {
            d: dialOptions.path || [
               'M',
               -rearLength, -baseWidth / 2,
               'L',
               baseLength, -baseWidth / 2,
               radius, -topWidth / 2,
               radius, topWidth / 2,
               baseLength, baseWidth / 2,
               -rearLength, baseWidth / 2,
               'z'
            ],
            translateX: center[0],
            translateY: center[1],
            rotation: rotation
         };
         // Positions for data label
         point.plotX = center[0];
         point.plotY = center[1];
      });
   },
   /**
    * Draw the points where each point is one needle
    */
   drawPoints: function () {
      var series = this,
         center = series.yAxis.center,
         pivot = series.pivot,
         options = series.options,
         pivotOptions = options.pivot,
         renderer = series.chart.renderer;
      each(series.points, function (point) {
         var graphic = point.graphic,
            shapeArgs = point.shapeArgs,
            d = shapeArgs.d,
            dialOptions = merge(options.dial, point.dial); // #1233
         if (graphic) {
            graphic.animate(shapeArgs);
            shapeArgs.d = d; // animate alters it
         } else {
            point.graphic = renderer[point.shapeType](shapeArgs)
               .attr({
                  stroke: dialOptions.borderColor || 'none',
                  'stroke-width': dialOptions.borderWidth || 0,
                  fill: dialOptions.backgroundColor || 'black',
                  rotation: shapeArgs.rotation // required by VML when animation is false
               })
               .add(series.group);
         }
      });
      // Add or move the pivot
      if (pivot) {
         pivot.animate({ // #1235
            translateX: center[0],
            translateY: center[1]
         });
      } else {
         series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5))
            .attr({
               'stroke-width': pivotOptions.borderWidth || 0,
               stroke: pivotOptions.borderColor || 'silver',
               fill: pivotOptions.backgroundColor || 'black'
            })
            .translate(center[0], center[1])
            .add(series.group);
      }
   },
   /**
    * Animate the arrow up from startAngle
    */
   animate: function (init) {
      var series = this;
      if (!init) {
         each(series.points, function (point) {
            var graphic = point.graphic;
            if (graphic) {
               // start value
               graphic.attr({
                  rotation: series.yAxis.startAngleRad * 180 / Math.PI
               });
               // animate
               graphic.animate({
                  rotation: point.shapeArgs.rotation
               }, series.options.animation);
            }
         });
         // delete this function to allow it only once
         series.animate = null;
      }
   },
   render: function () {
      this.group = this.plotGroup(
         'group',
         'series',
         this.visible ? 'visible' : 'hidden',
         this.options.zIndex,
         this.chart.seriesGroup
      );
      seriesTypes.pie.prototype.render.call(this);
      this.group.clip(this.chart.clipRect);
   },
   setData: seriesTypes.pie.prototype.setData,
   drawTracker: seriesTypes.column.prototype.drawTracker
};
seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* ****************************************************************************
 * Start Box plot series code                                       *
 *****************************************************************************/
// Set default options
defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, {
   fillColor: '#FFFFFF',
   lineWidth: 1,
   //medianColor: null,
   medianWidth: 2,
   states: {
      hover: {
         brightness: -0.3
      }
   },
   //stemColor: null,
   //stemDashStyle: 'solid'
   //stemWidth: null,
   threshold: null,
   tooltip: {
      pointFormat: '<span style="color:{series.color};font-weight:bold">{series.name}</span><br/>' +
         'Maximum: {point.high}<br/>' +
         'Upper quartile: {point.q3}<br/>' +
         'Median: {point.median}<br/>' +
         'Lower quartile: {point.q1}<br/>' +
         'Minimum: {point.low}<br/>'
   },
   //whiskerColor: null,
   whiskerLength: '50%',
   whiskerWidth: 2
});
// Create the series object
seriesTypes.boxplot = extendClass(seriesTypes.column, {
   type: 'boxplot',
   pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this
   toYData: function (point) { // return a plain array for speedy calculation
      return [point.low, point.q1, point.median, point.q3, point.high];
   },
   pointValKey: 'high', // defines the top of the tracker
   /**
    * One-to-one mapping from options to SVG attributes
    */
   pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
      fill: 'fillColor',
      stroke: 'color',
      'stroke-width': 'lineWidth'
   },
   /**
    * Disable data labels for box plot
    */
   drawDataLabels: noop,
   /**
    * Translate data points from raw values x and y to plotX and plotY
    */
   translate: function () {
      var series = this,
         yAxis = series.yAxis,
         pointArrayMap = series.pointArrayMap;
      seriesTypes.column.prototype.translate.apply(series);
      // do the translation on each point dimension
      each(series.points, function (point) {
         each(pointArrayMap, function (key) {
            if (point[key] !== null) {
               point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
            }
         });
      });
   },
   /**
    * Draw the data points
    */
   drawPoints: function () {
      var series = this,  //state = series.state,
         points = series.points,
         options = series.options,
         chart = series.chart,
         renderer = chart.renderer,
         pointAttr,
         q1Plot,
         q3Plot,
         highPlot,
         lowPlot,
         medianPlot,
         crispCorr,
         crispX,
         graphic,
         stemPath,
         stemAttr,
         boxPath,
         whiskersPath,
         whiskersAttr,
         medianPath,
         medianAttr,
         width,
         left,
         right,
         halfWidth,
         shapeArgs,
         color,
         doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles
         whiskerLength = parseInt(series.options.whiskerLength, 10) / 100;
      each(points, function (point) {
         graphic = point.graphic;
         shapeArgs = point.shapeArgs; // the box
         stemAttr = {};
         whiskersAttr = {};
         medianAttr = {};
         color = point.color || series.color;
         if (point.plotY !== UNDEFINED) {
            pointAttr = point.pointAttr[point.selected ? 'selected' : ''];
            // crisp vector coordinates
            width = shapeArgs.width;
            left = mathFloor(shapeArgs.x);
            right = left + width;
            halfWidth = mathRound(width / 2);
            //crispX = mathRound(left + halfWidth) + crispCorr;
            q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr;
            q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr;
            highPlot = mathFloor(point.highPlot);// + crispCorr;
            lowPlot = mathFloor(point.lowPlot);// + crispCorr;
            // Stem attributes
            stemAttr.stroke = point.stemColor || options.stemColor || color;
            stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
            stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle;
            // Whiskers attributes
            whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color;
            whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
            // Median attributes
            medianAttr.stroke = point.medianColor || options.medianColor || color;
            medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
            // The stem
            crispCorr = (stemAttr['stroke-width'] % 2) / 2;
            crispX = left + halfWidth + crispCorr;
            stemPath = [
               // stem up
               'M',
               crispX, q3Plot,
               'L',
               crispX, highPlot,
               // stem down
               'M',
               crispX, q1Plot,
               'L',
               crispX, lowPlot,
               'z'
            ];
            // The box
            if (doQuartiles) {
               crispCorr = (pointAttr['stroke-width'] % 2) / 2;
               crispX = mathFloor(crispX) + crispCorr;
               q1Plot = mathFloor(q1Plot) + crispCorr;
               q3Plot = mathFloor(q3Plot) + crispCorr;
               left += crispCorr;
               right += crispCorr;
               boxPath = [
                  'M',
                  left, q3Plot,
                  'L',
                  left, q1Plot,
                  'L',
                  right, q1Plot,
                  'L',
                  right, q3Plot,
                  'L',
                  left, q3Plot,
                  'z'
               ];
            }
            // The whiskers
            if (whiskerLength) {
               crispCorr = (whiskersAttr['stroke-width'] % 2) / 2;
               highPlot = highPlot + crispCorr;
               lowPlot = lowPlot + crispCorr;
               whiskersPath = [
                  // High whisker
                  'M',
                  crispX - halfWidth * whiskerLength,
                  highPlot,
                  'L',
                  crispX + halfWidth * whiskerLength,
                  highPlot,
                  // Low whisker
                  'M',
                  crispX - halfWidth * whiskerLength,
                  lowPlot,
                  'L',
                  crispX + halfWidth * whiskerLength,
                  lowPlot
               ];
            }
            // The median
            crispCorr = (medianAttr['stroke-width'] % 2) / 2;
            medianPlot = mathRound(point.medianPlot) + crispCorr;
            medianPath = [
               'M',
               left,
               medianPlot,
               'L',
               right,
               medianPlot,
               'z'
            ];
            // Create or update the graphics
            if (graphic) { // update
               point.stem.animate({ d: stemPath });
               if (whiskerLength) {
                  point.whiskers.animate({ d: whiskersPath });
               }
               if (doQuartiles) {
                  point.box.animate({ d: boxPath });
               }
               point.medianShape.animate({ d: medianPath });
            } else { // create new
               point.graphic = graphic = renderer.g()
                  .add(series.group);
               point.stem = renderer.path(stemPath)
                  .attr(stemAttr)
                  .add(graphic);
               if (whiskerLength) {
                  point.whiskers = renderer.path(whiskersPath)
                     .attr(whiskersAttr)
                     .add(graphic);
               }
               if (doQuartiles) {
                  point.box = renderer.path(boxPath)
                     .attr(pointAttr)
                     .add(graphic);
               }
               point.medianShape = renderer.path(medianPath)
                  .attr(medianAttr)
                  .add(graphic);
            }
         }
      });
   }
});
/* ****************************************************************************
 * End Box plot series code                                    *
 *****************************************************************************/
/* ****************************************************************************
 * Start error bar series code                                                *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, {
   color: '#000000',
   grouping: false,
   linkedTo: ':previous',
   tooltip: {
      pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat
   },
   whiskerWidth: null
});
// 2 - Create the series object
seriesTypes.errorbar = extendClass(seriesTypes.boxplot, {
   type: 'errorbar',
   pointArrayMap: ['low', 'high'], // array point configs are mapped to this
   toYData: function (point) { // return a plain array for speedy calculation
      return [point.low, point.high];
   },
   pointValKey: 'high', // defines the top of the tracker
   doQuartiles: false,
   /**
    * Get the width and X offset, either on top of the linked series column
    * or standalone
    */
   getColumnMetrics: function () {
      return (this.linkedParent && this.linkedParent.columnMetrics) ||
         seriesTypes.column.prototype.getColumnMetrics.call(this);
   }
});
/* ****************************************************************************
 * End error bar series code                                                  *
 *****************************************************************************/
/* ****************************************************************************
 * Start Waterfall series code                                                *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
   lineWidth: 1,
   lineColor: '#333',
   dashStyle: 'dot',
   borderColor: '#333'
});
// 2 - Create the series object
seriesTypes.waterfall = extendClass(seriesTypes.column, {
   type: 'waterfall',
   upColorProp: 'fill',
   pointArrayMap: ['low', 'y'],
   pointValKey: 'y',
   /**
    * Init waterfall series, force stacking
    */
   init: function (chart, options) {
      // force stacking
      options.stacking = true;
      seriesTypes.column.prototype.init.call(this, chart, options);
   },
   /**
    * Translate data points from raw values
    */
   translate: function () {
      var series = this,
         options = series.options,
         axis = series.yAxis,
         len,
         i,
         points,
         point,
         shapeArgs,
         stack,
         y,
         previousY,
         stackPoint,
         threshold = options.threshold,
         crispCorr = (options.borderWidth % 2) / 2;
      // run column series translate
      seriesTypes.column.prototype.translate.apply(this);
      previousY = threshold;
      points = series.points;
      for (i = 0, len = points.length; i < len; i++) {
         // cache current point object
         point = points[i];
         shapeArgs = point.shapeArgs;
         // get current stack
         stack = series.getStack(i);
         stackPoint = stack.points[series.index];
         // override point value for sums
         if (isNaN(point.y)) {
            point.y = series.yData[i];
         }
         // up points
         y = mathMax(previousY, previousY + point.y) + stackPoint[0];
         shapeArgs.y = axis.translate(y, 0, 1);
         // sum points
         if (point.isSum || point.isIntermediateSum) {
            shapeArgs.y = axis.translate(stackPoint[1], 0, 1);
            shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
         // if it's not the sum point, update previous stack end position
         } else {
            previousY += stack.total;
         }
         // negative points
         if (shapeArgs.height < 0) {
            shapeArgs.y += shapeArgs.height;
            shapeArgs.height *= -1;
         }
         point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - crispCorr;
         shapeArgs.height = mathRound(shapeArgs.height);
         point.yBottom = shapeArgs.y + shapeArgs.height;
      }
   },
   /**
    * Call default processData then override yData to reflect waterfall's extremes on yAxis
    */
   processData: function (force) {
      var series = this,
         options = series.options,
         yData = series.yData,
         points = series.points,
         point,
         dataLength = yData.length,
         threshold = options.threshold || 0,
         subSum,
         sum,
         dataMin,
         dataMax,
         y,
         i;
      sum = subSum = dataMin = dataMax = threshold;
      for (i = 0; i < dataLength; i++) {
         y = yData[i];
         point = points && points[i] ? points[i] : {};
         if (y === "sum" || point.isSum) {
            yData[i] = sum;
         } else if (y === "intermediateSum" || point.isIntermediateSum) {
            yData[i] = subSum;
            subSum = threshold;
         } else {
            sum += y;
            subSum += y;
         }
         dataMin = Math.min(sum, dataMin);
         dataMax = Math.max(sum, dataMax);
      }
      Series.prototype.processData.call(this, force);
      // Record extremes
      series.dataMin = dataMin;
      series.dataMax = dataMax;
   },
   /**
    * Return y value or string if point is sum
    */
   toYData: function (pt) {
      if (pt.isSum) {
         return "sum";
      } else if (pt.isIntermediateSum) {
         return "intermediateSum";
      }
      return pt.y;
   },
   /**
    * Postprocess mapping between options and SVG attributes
    */
   getAttribs: function () {
      seriesTypes.column.prototype.getAttribs.apply(this, arguments);
      var series = this,
         options = series.options,
         stateOptions = options.states,
         upColor = options.upColor || series.color,
         hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
         seriesDownPointAttr = merge(series.pointAttr),
         upColorProp = series.upColorProp;
      seriesDownPointAttr[''][upColorProp] = upColor;
      seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor;
      seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor;
      each(series.points, function (point) {
         if (point.y > 0 && !point.color) {
            point.pointAttr = seriesDownPointAttr;
            point.color = upColor;
         }
      });
   },
   /**
    * Draw columns' connector lines
    */
   getGraphPath: function () {
      var data = this.data,
         length = data.length,
         lineWidth = this.options.lineWidth + this.options.borderWidth,
         normalizer = mathRound(lineWidth) % 2 / 2,
         path = [],
         M = 'M',
         L = 'L',
         prevArgs,
         pointArgs,
         i,
         d;
      for (i = 1; i < length; i++) {
         pointArgs = data[i].shapeArgs;
         prevArgs = data[i - 1].shapeArgs;
         d = [
            M,
            prevArgs.x + prevArgs.width, prevArgs.y + normalizer,
            L,
            pointArgs.x, prevArgs.y + normalizer
         ];
         if (data[i - 1].y < 0) {
            d[2] += prevArgs.height;
            d[5] += prevArgs.height;
         }
         path = path.concat(d);
      }
      return path;
   },
   /**
    * Extremes are recorded in processData
    */
   getExtremes: noop,
   /**
    * Return stack for given index
    */
   getStack: function (i) {
      var axis = this.yAxis,
         stacks = axis.stacks,
         key = this.stackKey;
      if (this.processedYData[i] < this.options.threshold) {
         key = '-' + key;
      }
      return stacks[key][i];
   },
   drawGraph: Series.prototype.drawGraph
});
/* ****************************************************************************
 * End Waterfall series code                                                  *
 *****************************************************************************/
/* ****************************************************************************
 * Start Bubble series code                                           *
 *****************************************************************************/
// 1 - set default options
defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
   dataLabels: {
      inside: true,
      style: {
         color: 'white',
         textShadow: '0px 0px 3px black'
      },
      verticalAlign: 'middle'
   },
   // displayNegative: true,
   marker: {
      // fillOpacity: 0.5,
      lineColor: null, // inherit from series.color
      lineWidth: 1
   },
   minSize: 8,
   maxSize: '20%',
   // negativeColor: null,
   tooltip: {
      pointFormat: '({point.x}, {point.y}), Size: {point.z}'
   },
   turboThreshold: 0,
   zThreshold: 0
});
// 2 - Create the series object
seriesTypes.bubble = extendClass(seriesTypes.scatter, {
   type: 'bubble',
   pointArrayMap: ['y', 'z'],
   trackerGroups: ['group', 'dataLabelsGroup'],
   /**
    * Mapping between SVG attributes and the corresponding options
    */
   pointAttrToOptions: {
      stroke: 'lineColor',
      'stroke-width': 'lineWidth',
      fill: 'fillColor'
   },
   /**
    * Apply the fillOpacity to all fill positions
    */
   applyOpacity: function (fill) {
      var markerOptions = this.options.marker,
         fillOpacity = pick(markerOptions.fillOpacity, 0.5);
      // When called from Legend.colorizeItem, the fill isn't predefined
      fill = fill || markerOptions.fillColor || this.color;
      if (fillOpacity !== 1) {
         fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba');
      }
      return fill;
   },
   /**
    * Extend the convertAttribs method by applying opacity to the fill
    */
   convertAttribs: function () {
      var obj = Series.prototype.convertAttribs.apply(this, arguments);
      obj.fill = this.applyOpacity(obj.fill);
      return obj;
   },
   /**
    * Get the radius for each point based on the minSize, maxSize and each point's Z value. This
    * must be done prior to Series.translate because the axis needs to add padding in
    * accordance with the point sizes.
    */
   getRadii: function (zMin, zMax, minSize, maxSize) {
      var len,
         i,
         pos,
         zData = this.zData,
         radii = [],
         zRange;
      // Set the shape type and arguments to be picked up in drawPoints
      for (i = 0, len = zData.length; i < len; i++) {
         zRange = zMax - zMin;
         pos = zRange > 0 ? // relative size, a number between 0 and 1
            (zData[i] - zMin) / (zMax - zMin) :
            0.5;
         radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2);
      }
      this.radii = radii;
   },
   /**
    * Perform animation on the bubbles
    */
   animate: function (init) {
      var animation = this.options.animation;
      if (!init) { // run the animation
         each(this.points, function (point) {
            var graphic = point.graphic,
               shapeArgs = point.shapeArgs;
            if (graphic && shapeArgs) {
               // start values
               graphic.attr('r', 1);
               // animate
               graphic.animate({
                  r: shapeArgs.r
               }, animation);
            }
         });
         // delete this function to allow it only once
         this.animate = null;
      }
   },
   /**
    * Extend the base translate method to handle bubble size
    */
   translate: function () {
      var i,
         data = this.data,
         point,
         radius,
         radii = this.radii;
      // Run the parent method
      seriesTypes.scatter.prototype.translate.call(this);
      // Set the shape type and arguments to be picked up in drawPoints
      i = data.length;
      while (i--) {
         point = data[i];
         radius = radii ? radii[i] : 0; // #1737
         // Flag for negativeColor to be applied in Series.js
         point.negative = point.z < (this.options.zThreshold || 0);
         if (radius >= this.minPxSize / 2) {
            // Shape arguments
            point.shapeType = 'circle';
            point.shapeArgs = {
               x: point.plotX,
               y: point.plotY,
               r: radius
            };
            // Alignment box for the data label
            point.dlBox = {
               x: point.plotX - radius,
               y: point.plotY - radius,
               width: 2 * radius,
               height: 2 * radius
            };
         } else { // below zThreshold
            point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691
         }
      }
   },
   /**
    * Get the series' symbol in the legend
    *
    * @param {Object} legend The legend object
    * @param {Object} item The series (this) or point
    */
   drawLegendSymbol: function (legend, item) {
      var radius = pInt(legend.itemStyle.fontSize) / 2;
      item.legendSymbol = this.chart.renderer.circle(
         radius,
         legend.baseline - radius,
         radius
      ).attr({
         zIndex: 3
      }).add(item.legendGroup);
      item.legendSymbol.isMarker = true;
   },
   drawPoints: seriesTypes.column.prototype.drawPoints,
   alignDataLabel: seriesTypes.column.prototype.alignDataLabel
});
/**
 * Add logic to pad each axis with the amount of pixels
 * necessary to avoid the bubbles to overflow.
 */
Axis.prototype.beforePadding = function () {
   var axis = this,
      axisLength = this.len,
      chart = this.chart,
      pxMin = 0,
      pxMax = axisLength,
      isXAxis = this.isXAxis,
      dataKey = isXAxis ? 'xData' : 'yData',
      min = this.min,
      extremes = {},
      smallestSize = math.min(chart.plotWidth, chart.plotHeight),
      zMin = Number.MAX_VALUE,
      zMax = -Number.MAX_VALUE,
      range = this.max - min,
      transA = axisLength / range,
      activeSeries = [];
   // Handle padding on the second pass, or on redraw
   if (this.tickPositions) {
      each(this.series, function (series) {
         var seriesOptions = series.options,
            zData;
         if (series.type === 'bubble' && series.visible) {
            // Correction for #1673
            axis.allowZoomOutside = true;
            // Cache it
            activeSeries.push(series);
            if (isXAxis) { // because X axis is evaluated first
               // For each series, translate the size extremes to pixel values
               each(['minSize', 'maxSize'], function (prop) {
                  var length = seriesOptions[prop],
                     isPercent = /%$/.test(length);
                  length = pInt(length);
                  extremes[prop] = isPercent ?
                     smallestSize * length / 100 :
                     length;
               });
               series.minPxSize = extremes.minSize;
               // Find the min and max Z
               zData = series.zData;
               if (zData.length) { // #1735
                  zMin = math.min(
                     zMin,
                     math.max(
                        arrayMin(zData),
                        seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
                     )
                  );
                  zMax = math.max(zMax, arrayMax(zData));
               }
            }
         }
      });
      each(activeSeries, function (series) {
         var data = series[dataKey],
            i = data.length,
            radius;
         if (isXAxis) {
            series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize);
         }
         if (range > 0) {
            while (i--) {
               radius = series.radii[i];
               pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
               pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
            }
         }
      });
      if (activeSeries.length && range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) {
         pxMax -= axisLength;
         transA *= (axisLength + pxMin - pxMax) / axisLength;
         this.min += pxMin / transA;
         this.max += pxMax / transA;
      }
   }
};
/* ****************************************************************************
 * End Bubble series code                                                     *
 *****************************************************************************/
/**
 * Extensions for polar charts. Additionally, much of the geometry required for polar charts is
 * gathered in RadialAxes.js.
 *
 */
var seriesProto = Series.prototype,
   pointerProto = Highcharts.Pointer.prototype;
/**
 * Translate a point's plotX and plotY from the internal angle and radius measures to
 * true plotX, plotY coordinates
 */
seriesProto.toXY = function (point) {
   var xy,
      chart = this.chart,
      plotX = point.plotX,
      plotY = point.plotY;
   // Save rectangular plotX, plotY for later computation
   point.rectPlotX = plotX;
   point.rectPlotY = plotY;
   // Record the angle in degrees for use in tooltip
   point.clientX = ((plotX / Math.PI * 180) + this.xAxis.pane.options.startAngle) % 360;
   // Find the polar plotX and plotY
   xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY);
   point.plotX = point.polarPlotX = xy.x - chart.plotLeft;
   point.plotY = point.polarPlotY = xy.y - chart.plotTop;
};
/**
 * Order the tooltip points to get the mouse capture ranges correct. #1915.
 */
seriesProto.orderTooltipPoints = function (points) {
   if (this.chart.polar) {
      points.sort(function (a, b) {
         return a.clientX - b.clientX;
      });
      // Wrap mouse tracking around to capture movement on the segment to the left
      // of the north point (#1469, #2093).
      if (points[0]) {
         points[0].wrappedClientX = points[0].clientX + 360;
         points.push(points[0]);
      }
   }
};
/**
 * Add some special init logic to areas and areasplines
 */
function initArea(proceed, chart, options) {
   proceed.call(this, chart, options);
   if (this.chart.polar) {
      /**
       * Overridden method to close a segment path. While in a cartesian plane the area
       * goes down to the threshold, in the polar chart it goes to the center.
       */
      this.closeSegment = function (path) {
         var center = this.xAxis.center;
         path.push(
            'L',
            center[0],
            center[1]
         );
      };
      // Instead of complicated logic to draw an area around the inner area in a stack,
      // just draw it behind
      this.closedStacks = true;
   }
}
wrap(seriesTypes.area.prototype, 'init', initArea);
wrap(seriesTypes.areaspline.prototype, 'init', initArea);
/**
 * Overridden method for calculating a spline from one point to the next
 */
wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) {
   var ret,
      smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc;
      denom = smoothing + 1,
      plotX,
      plotY,
      lastPoint,
      nextPoint,
      lastX,
      lastY,
      nextX,
      nextY,
      leftContX,
      leftContY,
      rightContX,
      rightContY,
      distanceLeftControlPoint,
      distanceRightControlPoint,
      leftContAngle,
      rightContAngle,
      jointAngle;
   if (this.chart.polar) {
      plotX = point.plotX;
      plotY = point.plotY;
      lastPoint = segment[i - 1];
      nextPoint = segment[i + 1];
      // Connect ends
      if (this.connectEnds) {
         if (!lastPoint) {
            lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected
         }
         if (!nextPoint) {
            nextPoint = segment[1];
         }
      }
      // find control points
      if (lastPoint && nextPoint) {
         lastX = lastPoint.plotX;
         lastY = lastPoint.plotY;
         nextX = nextPoint.plotX;
         nextY = nextPoint.plotY;
         leftContX = (smoothing * plotX + lastX) / denom;
         leftContY = (smoothing * plotY + lastY) / denom;
         rightContX = (smoothing * plotX + nextX) / denom;
         rightContY = (smoothing * plotY + nextY) / denom;
         distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2));
         distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2));
         leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX);
         rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX);
         jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2);
         // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle
         if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) {
            jointAngle -= Math.PI;
         }
         // Find the corrected control points for a spline straight through the point
         leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint;
         leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint;
         rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint;
         rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint;
         // Record for drawing in next point
         point.rightContX = rightContX;
         point.rightContY = rightContY;
      }
      // moveTo or lineTo
      if (!i) {
         ret = ['M', plotX, plotY];
      } else { // curve from last point to this
         ret = [
            'C',
            lastPoint.rightContX || lastPoint.plotX,
            lastPoint.rightContY || lastPoint.plotY,
            leftContX || plotX,
            leftContY || plotY,
            plotX,
            plotY
         ];
         lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later
      }
   } else {
      ret = proceed.call(this, segment, point, i);
   }
   return ret;
});
/**
 * Extend translate. The plotX and plotY values are computed as if the polar chart were a
 * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from
 * center.
 */
wrap(seriesProto, 'translate', function (proceed) {
   // Run uber method
   proceed.call(this);
   // Postprocess plot coordinates
   if (this.chart.polar && !this.preventPostTranslate) {
      var points = this.points,
         i = points.length;
      while (i--) {
         // Translate plotX, plotY from angle and radius to true plot coordinates
         this.toXY(points[i]);
      }
   }
});
/**
 * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in
 * line-like series.
 */
wrap(seriesProto, 'getSegmentPath', function (proceed, segment) {
   var points = this.points;
   // Connect the path
   if (this.chart.polar && this.options.connectEnds !== false &&
         segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) {
      this.connectEnds = true; // re-used in splines
      segment = [].concat(segment, [points[0]]);
   }
   // Run uber method
   return proceed.call(this, segment);
});
function polarAnimate(proceed, init) {
   var chart = this.chart,
      animation = this.options.animation,
      group = this.group,
      markerGroup = this.markerGroup,
      center = this.xAxis.center,
      plotLeft = chart.plotLeft,
      plotTop = chart.plotTop,
      attribs;
   // Specific animation for polar charts
   if (chart.polar) {
      // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation
      // would be so slow it would't matter.
      if (chart.renderer.isSVG) {
         if (animation === true) {
            animation = {};
         }
         // Initialize the animation
         if (init) {
            // Scale down the group and place it in the center
            attribs = {
               translateX: center[0] + plotLeft,
               translateY: center[1] + plotTop,
               scaleX: 0.001, // #1499
               scaleY: 0.001
            };
            group.attr(attribs);
            if (markerGroup) {
               markerGroup.attrSetters = group.attrSetters;
               markerGroup.attr(attribs);
            }
         // Run the animation
         } else {
            attribs = {
               translateX: plotLeft,
               translateY: plotTop,
               scaleX: 1,
               scaleY: 1
            };
            group.animate(attribs, animation);
            if (markerGroup) {
               markerGroup.animate(attribs, animation);
            }
            // Delete this function to allow it only once
            this.animate = null;
         }
      }
   // For non-polar charts, revert to the basic animation
   } else {
      proceed.call(this, init);
   }
}
// Define the animate method for both regular series and column series and their derivatives
wrap(seriesProto, 'animate', polarAnimate);
wrap(colProto, 'animate', polarAnimate);
/**
 * Throw in a couple of properties to let setTooltipPoints know we're indexing the points
 * in degrees (0-360), not plot pixel width.
 */
wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) {
   if (this.chart.polar) {
      extend(this.xAxis, {
         tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array
      });
   }
   // Run uber method
   return proceed.call(this, renew);
});
/**
 * Extend the column prototype's translate method
 */
wrap(colProto, 'translate', function (proceed) {
   var xAxis = this.xAxis,
      len = this.yAxis.len,
      center = xAxis.center,
      startAngleRad = xAxis.startAngleRad,
      renderer = this.chart.renderer,
      start,
      points,
      point,
      i;
   this.preventPostTranslate = true;
   // Run uber method
   proceed.call(this);
   // Postprocess plot coordinates
   if (xAxis.isRadial) {
      points = this.points;
      i = points.length;
      while (i--) {
         point = points[i];
         start = point.barX + startAngleRad;
         point.shapeType = 'path';
         point.shapeArgs = {
            d: renderer.symbols.arc(
               center[0],
               center[1],
               len - point.plotY,
               null,
               {
                  start: start,
                  end: start + point.pointWidth,
                  innerR: len - pick(point.yBottom, len)
               }
            )
         };
         this.toXY(point); // provide correct plotX, plotY for tooltip
      }
   }
});
/**
 * Align column data labels outside the columns. #1199.
 */
wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) {
   if (this.chart.polar) {
      var angle = point.rectPlotX / Math.PI * 180,
         align,
         verticalAlign;
      // Align nicely outside the perimeter of the columns
      if (options.align === null) {
         if (angle > 20 && angle < 160) {
            align = 'left'; // right hemisphere
         } else if (angle > 200 && angle < 340) {
            align = 'right'; // left hemisphere
         } else {
            align = 'center'; // top or bottom
         }
         options.align = align;
      }
      if (options.verticalAlign === null) {
         if (angle < 45 || angle > 315) {
            verticalAlign = 'bottom'; // top part
         } else if (angle > 135 && angle < 225) {
            verticalAlign = 'top'; // bottom part
         } else {
            verticalAlign = 'middle'; // left or right
         }
         options.verticalAlign = verticalAlign;
      }
      seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
   } else {
      proceed.call(this, point, dataLabel, options, alignTo, isNew);
   }
});
/**
 * Extend the mouse tracker to return the tooltip position index in terms of
 * degrees rather than pixels
 */
wrap(pointerProto, 'getIndex', function (proceed, e) {
   var ret,
      chart = this.chart,
      center,
      x,
      y;
   if (chart.polar) {
      center = chart.xAxis[0].center;
      x = e.chartX - center[0] - chart.plotLeft;
      y = e.chartY - center[1] - chart.plotTop;
      ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180);
   } else {
      // Run uber method
      ret = proceed.call(this, e);
   }
   return ret;
});
/**
 * Extend getCoordinates to prepare for polar axis values
 */
wrap(pointerProto, 'getCoordinates', function (proceed, e) {
   var chart = this.chart,
      ret = {
         xAxis: [],
         yAxis: []
      };
   if (chart.polar) {
      each(chart.axes, function (axis) {
         var isXAxis = axis.isXAxis,
            center = axis.center,
            x = e.chartX - center[0] - chart.plotLeft,
            y = e.chartY - center[1] - chart.plotTop;
         ret[isXAxis ? 'xAxis' : 'yAxis'].push({
            axis: axis,
            value: axis.translate(
               isXAxis ?
                  Math.PI - Math.atan2(x, y) : // angle
                  Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center
               true
            )
         });
      });
   } else {
      ret = proceed.call(this, e);
   }
   return ret;
});
}(Highcharts));