highcharts-rails 2.3.2 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2.3.3 (2012-10-04)
2
+
3
+ * Updated Highcharts to 2.3.3
4
+
1
5
  ## 2.3.2 (2012-08-31)
2
6
 
3
7
  * Updated Highcharts to 2.3.2
@@ -1,3 +1,3 @@
1
1
  module Highcharts
2
- VERSION = "2.3.2"
2
+ VERSION = "2.3.3"
3
3
  end
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v2.3.2 (2012-08-31)
5
+ * @license Highcharts JS v2.3.3 (2012-10-04)
6
6
  *
7
7
  * (c) 2009-2011 Torstein Hønsi
8
8
  *
@@ -1258,8 +1258,10 @@ pathAnim = {
1258
1258
  var ret = e.originalEvent || e;
1259
1259
 
1260
1260
  // computed by jQuery, needed by IE8
1261
- ret.pageX = e.pageX;
1262
- ret.pageY = e.pageY;
1261
+ if (ret.pageX === UNDEFINED) { // #1236
1262
+ ret.pageX = e.pageX;
1263
+ ret.pageY = e.pageY;
1264
+ }
1263
1265
 
1264
1266
  return ret;
1265
1267
  },
@@ -1360,8 +1362,8 @@ defaultOptions = {
1360
1362
  },
1361
1363
  global: {
1362
1364
  useUTC: true,
1363
- canvasToolsURL: 'http://code.highcharts.com/2.3.2/modules/canvas-tools.js',
1364
- VMLRadialGradientURL: 'http://code.highcharts.com/2.3.2/gfx/vml-radial-gradient.png'
1365
+ canvasToolsURL: 'http://code.highcharts.com/2.3.3/modules/canvas-tools.js',
1366
+ VMLRadialGradientURL: 'http://code.highcharts.com/2.3.3/gfx/vml-radial-gradient.png'
1365
1367
  },
1366
1368
  chart: {
1367
1369
  //animation: true,
@@ -1475,10 +1477,11 @@ defaultOptions = {
1475
1477
  },
1476
1478
  dataLabels: merge(defaultLabelOptions, {
1477
1479
  enabled: false,
1478
- y: -6,
1479
1480
  formatter: function () {
1480
1481
  return this.y;
1481
- }
1482
+ },
1483
+ verticalAlign: 'bottom', // above singular point
1484
+ y: 0
1482
1485
  // backgroundColor: undefined,
1483
1486
  // borderColor: undefined,
1484
1487
  // borderRadius: undefined,
@@ -1867,7 +1870,7 @@ SVGElement.prototype = {
1867
1870
  i,
1868
1871
  child,
1869
1872
  element = wrapper.element,
1870
- nodeName = element.nodeName,
1873
+ nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text"
1871
1874
  renderer = wrapper.renderer,
1872
1875
  skipAttr,
1873
1876
  titleNode,
@@ -2154,7 +2157,7 @@ SVGElement.prototype = {
2154
2157
  /*jslint unparam: true*//* allow unused param a in the regexp function below */
2155
2158
  var elemWrapper = this,
2156
2159
  elem = elemWrapper.element,
2157
- textWidth = styles && styles.width && elem.nodeName === 'text',
2160
+ textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text',
2158
2161
  n,
2159
2162
  serializedCss = '',
2160
2163
  hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
@@ -2174,6 +2177,12 @@ SVGElement.prototype = {
2174
2177
  // store object
2175
2178
  elemWrapper.styles = styles;
2176
2179
 
2180
+
2181
+ // Don't handle line wrap on canvas
2182
+ if (useCanVG && textWidth) {
2183
+ delete styles.width;
2184
+ }
2185
+
2177
2186
  // serialize and set style attribute
2178
2187
  if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute
2179
2188
  if (textWidth) {
@@ -2364,15 +2373,15 @@ SVGElement.prototype = {
2364
2373
  yCorr = wrapper.yCorr || 0,
2365
2374
  currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','),
2366
2375
  rotationStyle = {},
2367
- prefix;
2376
+ cssTransformKey;
2368
2377
 
2369
2378
  if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
2370
2379
 
2371
2380
  if (defined(rotation)) {
2372
2381
 
2373
2382
  if (renderer.isSVG) { // #916
2374
- prefix = isIE ? '-ms' : isWebKit ? '-webkit' : isFirefox ? '-moz' : isOpera ? '-o' : '';
2375
- rotationStyle[prefix + '-transform'] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
2383
+ cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
2384
+ rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
2376
2385
 
2377
2386
  } else {
2378
2387
  radians = rotation * deg2rad; // deg to rad
@@ -2388,7 +2397,6 @@ SVGElement.prototype = {
2388
2397
  ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
2389
2398
  ', sizingMethod=\'auto expand\')'].join('') : NONE;
2390
2399
  }
2391
-
2392
2400
  css(elem, rotationStyle);
2393
2401
  }
2394
2402
 
@@ -2519,7 +2527,7 @@ SVGElement.prototype = {
2519
2527
 
2520
2528
 
2521
2529
  // align
2522
- if (/^(right|center)$/.test(align)) {
2530
+ if (align === 'right' || align === 'center') {
2523
2531
  x += (box.width - (alignOptions.width || 0)) /
2524
2532
  { right: 1, center: 2 }[align];
2525
2533
  }
@@ -2527,7 +2535,7 @@ SVGElement.prototype = {
2527
2535
 
2528
2536
 
2529
2537
  // vertical align
2530
- if (/^(bottom|middle)$/.test(vAlign)) {
2538
+ if (vAlign === 'bottom' || vAlign === 'middle') {
2531
2539
  y += (box.height - (alignOptions.height || 0)) /
2532
2540
  ({ bottom: 1, middle: 2 }[vAlign] || 1);
2533
2541
 
@@ -2553,6 +2561,7 @@ SVGElement.prototype = {
2553
2561
  height,
2554
2562
  rotation = wrapper.rotation,
2555
2563
  element = wrapper.element,
2564
+ styles = wrapper.styles,
2556
2565
  rad = rotation * deg2rad;
2557
2566
 
2558
2567
  if (!bBox) {
@@ -2598,6 +2607,11 @@ SVGElement.prototype = {
2598
2607
  }
2599
2608
  }
2600
2609
 
2610
+ // Workaround for wrong bounding box in IE9 and IE10 (#1101)
2611
+ if (isIE && styles && styles.fontSize === '11px' && height === 22.700000762939453) {
2612
+ bBox.height = 14;
2613
+ }
2614
+
2601
2615
  wrapper.bBox = bBox;
2602
2616
  }
2603
2617
  return bBox;
@@ -2704,7 +2718,6 @@ SVGElement.prototype = {
2704
2718
  var wrapper = this,
2705
2719
  element = wrapper.element || {},
2706
2720
  shadows = wrapper.shadows,
2707
- box = wrapper.box,
2708
2721
  key,
2709
2722
  i;
2710
2723
 
@@ -2734,11 +2747,6 @@ SVGElement.prototype = {
2734
2747
  });
2735
2748
  }
2736
2749
 
2737
- // destroy label box
2738
- if (box) {
2739
- box.destroy();
2740
- }
2741
-
2742
2750
  // remove from alignObjects
2743
2751
  erase(wrapper.renderer.alignedObjects, wrapper);
2744
2752
 
@@ -2784,7 +2792,7 @@ SVGElement.prototype = {
2784
2792
  shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
2785
2793
  transform = this.parentInverted ?
2786
2794
  '(-1,-1)' :
2787
- '(' + (shadowOptions.offsetX || 1) + ', ' + (shadowOptions.offsetY || 1) + ')';
2795
+ '(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')';
2788
2796
  for (i = 1; i <= shadowWidth; i++) {
2789
2797
  shadow = element.cloneNode(0);
2790
2798
  strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
@@ -2965,13 +2973,13 @@ SVGRenderer.prototype = {
2965
2973
  hrefRegex = /href="([^"]+)"/,
2966
2974
  parentX = attr(textNode, 'x'),
2967
2975
  textStyles = wrapper.styles,
2968
- width = textStyles && pInt(textStyles.width),
2976
+ width = textStyles && textStyles.width && pInt(textStyles.width),
2969
2977
  textLineHeight = textStyles && textStyles.lineHeight,
2970
2978
  lastLine,
2971
2979
  GET_COMPUTED_STYLE = 'getComputedStyle',
2972
2980
  i = childNodes.length,
2973
2981
  linePositions = [];
2974
-
2982
+
2975
2983
  // Needed in IE9 because it doesn't report tspan's offsetHeight (#893)
2976
2984
  function getLineHeightByBBox(lineNo) {
2977
2985
  linePositions[lineNo] = textNode.getBBox ?
@@ -3004,13 +3012,11 @@ SVGRenderer.prototype = {
3004
3012
  each(spans, function (span) {
3005
3013
  if (span !== '' || spans.length === 1) {
3006
3014
  var attributes = {},
3007
- tspan = doc.createElementNS(SVG_NS, 'tspan');
3015
+ tspan = doc.createElementNS(SVG_NS, 'tspan'),
3016
+ spanStyle; // #390
3008
3017
  if (styleRegex.test(span)) {
3009
- attr(
3010
- tspan,
3011
- 'style',
3012
- span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2')
3013
- );
3018
+ spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2');
3019
+ attr(tspan, 'style', spanStyle);
3014
3020
  }
3015
3021
  if (hrefRegex.test(span)) {
3016
3022
  attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
@@ -3073,7 +3079,7 @@ SVGRenderer.prototype = {
3073
3079
 
3074
3080
  // check width and apply soft breaks
3075
3081
  if (width) {
3076
- var words = span.replace(/-/g, '- ').split(' '),
3082
+ var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
3077
3083
  tooLong,
3078
3084
  actualWidth,
3079
3085
  rest = [];
@@ -3091,6 +3097,9 @@ SVGRenderer.prototype = {
3091
3097
  dy: textLineHeight || 16,
3092
3098
  x: parentX
3093
3099
  });
3100
+ if (spanStyle) { // #390
3101
+ attr(tspan, 'style', spanStyle);
3102
+ }
3094
3103
  textNode.appendChild(tspan);
3095
3104
 
3096
3105
  if (actualWidth > width) { // a single word is pressing it out
@@ -3724,6 +3733,7 @@ SVGRenderer.prototype = {
3724
3733
  // declare variables
3725
3734
  var renderer = this,
3726
3735
  defaultChartStyle = defaultOptions.chart.style,
3736
+ fakeSVG = useCanVG || (!hasSVG && renderer.forExport),
3727
3737
  wrapper;
3728
3738
 
3729
3739
  if (useHTML && !renderer.forExport) {
@@ -3745,7 +3755,7 @@ SVGRenderer.prototype = {
3745
3755
  });
3746
3756
 
3747
3757
  // Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063)
3748
- if (!hasSVG && renderer.forExport) {
3758
+ if (fakeSVG) {
3749
3759
  wrapper.css({
3750
3760
  position: ABSOLUTE
3751
3761
  });
@@ -4152,9 +4162,11 @@ SVGRenderer.prototype = {
4152
4162
  removeEvent(wrapper.element, 'mouseleave');
4153
4163
 
4154
4164
  if (text) {
4155
- // Destroy the text element
4156
4165
  text = text.destroy();
4157
4166
  }
4167
+ if (box) {
4168
+ box = box.destroy();
4169
+ }
4158
4170
  // Call base implementation to destroy the rest
4159
4171
  SVGElement.prototype.destroy.call(wrapper);
4160
4172
  }
@@ -4642,8 +4654,8 @@ var VMLElement = {
4642
4654
 
4643
4655
  shadow = createElement(renderer.prepVML(markup),
4644
4656
  null, {
4645
- left: pInt(elemStyle.left) + (shadowOptions.offsetX || 1),
4646
- top: pInt(elemStyle.top) + (shadowOptions.offsetY || 1)
4657
+ left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1),
4658
+ top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1)
4647
4659
  }
4648
4660
  );
4649
4661
  if (cutOff) {
@@ -5062,9 +5074,9 @@ var VMLRendererExtension = { // inherit SVGRenderer
5062
5074
  .attr({ src: src });
5063
5075
 
5064
5076
  if (arguments.length > 1) {
5065
- obj.css({
5066
- left: x,
5067
- top: y,
5077
+ obj.attr({
5078
+ x: x,
5079
+ y: y,
5068
5080
  width: width,
5069
5081
  height: height
5070
5082
  });
@@ -5289,7 +5301,8 @@ if (useCanVG) {
5289
5301
  * together with the canvg library.
5290
5302
  */
5291
5303
  CanVGRenderer = function () {
5292
- // Empty constructor
5304
+ // Override the global SVG namespace to fake SVG/HTML that accepts CSS
5305
+ SVG_NS = 'http://www.w3.org/1999/xhtml';
5293
5306
  };
5294
5307
 
5295
5308
  /**
@@ -5908,7 +5921,8 @@ PlotLineOrBand.prototype = {
5908
5921
  /**
5909
5922
  * The class for stack items
5910
5923
  */
5911
- function StackItem(axis, options, isNegative, x, stackOption) {
5924
+ function StackItem(axis, options, isNegative, x, stackOption, stacking) {
5925
+
5912
5926
  var inverted = axis.chart.inverted;
5913
5927
 
5914
5928
  this.axis = axis;
@@ -5922,8 +5936,9 @@ function StackItem(axis, options, isNegative, x, stackOption) {
5922
5936
  // Save the x value to be able to position the label later
5923
5937
  this.x = x;
5924
5938
 
5925
- // Save the stack option on the series configuration object
5939
+ // Save the stack option on the series configuration object, and whether to treat it as percent
5926
5940
  this.stack = stackOption;
5941
+ this.percent = stacking === 'percent';
5927
5942
 
5928
5943
  // The align options and text align varies on whether the stack is negative and
5929
5944
  // if the chart is inverted or not.
@@ -5966,9 +5981,11 @@ StackItem.prototype = {
5966
5981
  this.label =
5967
5982
  this.axis.chart.renderer.text(str, 0, 0) // dummy positions, actual position updated with setOffset method in columnseries
5968
5983
  .css(this.options.style) // apply style
5969
- .attr({align: this.textAlign, // fix the text-anchor
5984
+ .attr({
5985
+ align: this.textAlign, // fix the text-anchor
5970
5986
  rotation: this.options.rotation, // rotation
5971
- visibility: HIDDEN }) // hidden until setOffset is called
5987
+ visibility: HIDDEN // hidden until setOffset is called
5988
+ })
5972
5989
  .add(group); // add to the labels-group
5973
5990
  }
5974
5991
  },
@@ -5982,24 +5999,31 @@ StackItem.prototype = {
5982
5999
  chart = axis.chart,
5983
6000
  inverted = chart.inverted,
5984
6001
  neg = this.isNegative, // special treatment is needed for negative stacks
5985
- y = axis.translate(this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates
6002
+ y = axis.translate(this.percent ? 100 : this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates
5986
6003
  yZero = axis.translate(0), // stack origin
5987
6004
  h = mathAbs(y - yZero), // stack height
5988
6005
  x = chart.xAxis[0].translate(this.x) + xOffset, // stack x position
5989
6006
  plotHeight = chart.plotHeight,
5990
6007
  stackBox = { // this is the box for the complete stack
5991
- x: inverted ? (neg ? y : y - h) : x,
5992
- y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y),
5993
- width: inverted ? h : xWidth,
5994
- height: inverted ? xWidth : h
5995
- };
5996
-
5997
- if (this.label) {
5998
- this.label
5999
- .align(this.alignOptions, null, stackBox) // align the label to the box
6000
- .attr({visibility: VISIBLE}); // set visibility
6001
- }
6008
+ x: inverted ? (neg ? y : y - h) : x,
6009
+ y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y),
6010
+ width: inverted ? h : xWidth,
6011
+ height: inverted ? xWidth : h
6012
+ },
6013
+ label = this.label,
6014
+ alignAttr;
6002
6015
 
6016
+ if (label) {
6017
+ label.align(this.alignOptions, null, stackBox); // align the label to the box
6018
+
6019
+ // Set visibility (#678)
6020
+ alignAttr = label.alignAttr;
6021
+ label.attr({
6022
+ visibility: this.options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) ?
6023
+ (hasSVG ? 'inherit' : VISIBLE) :
6024
+ HIDDEN
6025
+ });
6026
+ }
6003
6027
  }
6004
6028
  };
6005
6029
  /**
@@ -6511,44 +6535,41 @@ Axis.prototype = {
6511
6535
  for (i = 0; i < yDataLength; i++) {
6512
6536
  x = xData[i];
6513
6537
  y = yData[i];
6514
- if (y !== null && y !== UNDEFINED) {
6515
-
6516
- // read stacked values into a stack based on the x value,
6517
- // the sign of y and the stack key
6518
- if (stacking) {
6519
- isNegative = y < threshold;
6520
- pointStack = isNegative ? negPointStack : posPointStack;
6521
- key = isNegative ? negKey : stackKey;
6522
-
6523
- y = pointStack[x] =
6524
- defined(pointStack[x]) ?
6525
- pointStack[x] + y : y;
6526
-
6527
-
6528
- // add the series
6529
- if (!stacks[key]) {
6530
- stacks[key] = {};
6531
- }
6532
-
6533
- // If the StackItem is there, just update the values,
6534
- // if not, create one first
6535
- if (!stacks[key][x]) {
6536
- stacks[key][x] = new StackItem(axis, axis.options.stackLabels, isNegative, x, stackOption);
6537
- }
6538
- stacks[key][x].setTotal(y);
6538
+
6539
+ // Read stacked values into a stack based on the x value,
6540
+ // the sign of y and the stack key. Stacking is also handled for null values (#739)
6541
+ if (stacking) {
6542
+ isNegative = y < threshold;
6543
+ pointStack = isNegative ? negPointStack : posPointStack;
6544
+ key = isNegative ? negKey : stackKey;
6545
+
6546
+ y = pointStack[x] =
6547
+ defined(pointStack[x]) ?
6548
+ correctFloat(pointStack[x] + y) :
6549
+ y;
6550
+
6551
+
6552
+ // add the series
6553
+ if (!stacks[key]) {
6554
+ stacks[key] = {};
6555
+ }
6539
6556
 
6557
+ // If the StackItem is there, just update the values,
6558
+ // if not, create one first
6559
+ if (!stacks[key][x]) {
6560
+ stacks[key][x] = new StackItem(axis, axis.options.stackLabels, isNegative, x, stackOption, stacking);
6561
+ }
6562
+ stacks[key][x].setTotal(y);
6563
+ }
6564
+
6565
+ // Handle non null values
6566
+ if (y !== null && y !== UNDEFINED) {
6540
6567
 
6541
6568
  // general hook, used for Highstock compare values feature
6542
- } else if (hasModifyValue) {
6569
+ if (hasModifyValue) {
6543
6570
  y = series.modifyValue(y);
6544
6571
  }
6545
6572
 
6546
- // get the smallest distance between points
6547
- /*if (i) {
6548
- distance = mathAbs(xData[i] - xData[i - 1]);
6549
- pointRange = pointRange === UNDEFINED ? distance : mathMin(distance, pointRange);
6550
- }*/
6551
-
6552
6573
  // for points within the visible range, including the first point outside the
6553
6574
  // visible range, consider y extremes
6554
6575
  if (cropped || ((xData[i + 1] || x) >= xExtremes.min && (xData[i - 1] || x) <= xExtremes.max)) {
@@ -6962,12 +6983,15 @@ Axis.prototype = {
6962
6983
  closestPointRange,
6963
6984
  minPointOffset = 0,
6964
6985
  pointRangePadding = 0,
6986
+ linkedParent = axis.linkedParent,
6965
6987
  transA = axis.transA;
6966
6988
 
6967
6989
  // adjust translation for padding
6968
6990
  if (axis.isXAxis) {
6969
- if (axis.isLinked) {
6970
- minPointOffset = axis.linkedParent.minPointOffset;
6991
+ if (linkedParent) {
6992
+ minPointOffset = linkedParent.minPointOffset;
6993
+ pointRangePadding = linkedParent.pointRangePadding;
6994
+
6971
6995
  } else {
6972
6996
  each(axis.series, function (series) {
6973
6997
  var seriesPointRange = series.pointRange,
@@ -7000,8 +7024,9 @@ Axis.prototype = {
7000
7024
  });
7001
7025
  }
7002
7026
 
7003
- // Record minPointOffse
7027
+ // Record minPointOffset and pointRangePadding
7004
7028
  axis.minPointOffset = minPointOffset;
7029
+ axis.pointRangePadding = pointRangePadding;
7005
7030
 
7006
7031
  // pointRange means the width reserved for each point, like in a column chart
7007
7032
  axis.pointRange = pointRange;
@@ -7941,9 +7966,6 @@ function Tooltip(chart, options) {
7941
7966
  // Current values of x and y when animating
7942
7967
  this.now = { x: 0, y: 0 };
7943
7968
 
7944
- // The tooltipTick function, initialized to nothing
7945
- //this.tooltipTick = UNDEFINED;
7946
-
7947
7969
  // The tooltip is initially hidden
7948
7970
  this.isHidden = true;
7949
7971
 
@@ -8011,13 +8033,21 @@ Tooltip.prototype = {
8011
8033
  // move to the intermediate value
8012
8034
  tooltip.label.attr(now);
8013
8035
 
8036
+
8014
8037
  // run on next tick of the mouse tracker
8015
8038
  if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) {
8016
- tooltip.tooltipTick = function () {
8017
- tooltip.move(x, y, anchorX, anchorY);
8018
- };
8019
- } else {
8020
- tooltip.tooltipTick = null;
8039
+
8040
+ // never allow two timeouts
8041
+ clearTimeout(this.tooltipTimeout);
8042
+
8043
+ // set the fixed interval ticking for the smooth tooltip
8044
+ this.tooltipTimeout = setTimeout(function () {
8045
+ // The interval function may still be running during destroy, so check that the chart is really there before calling.
8046
+ if (tooltip) {
8047
+ tooltip.move(x, y, anchorX, anchorY);
8048
+ }
8049
+ }, 32);
8050
+
8021
8051
  }
8022
8052
  },
8023
8053
 
@@ -8114,7 +8144,7 @@ Tooltip.prototype = {
8114
8144
 
8115
8145
  // It is too far to the left, adjust it
8116
8146
  if (x < 7) {
8117
- x = plotLeft + pointX + distance;
8147
+ x = plotLeft + mathMax(pointX, 0) + distance;
8118
8148
  }
8119
8149
 
8120
8150
  // Test to see if the tooltip is too far to the right,
@@ -8140,7 +8170,6 @@ Tooltip.prototype = {
8140
8170
  if (y + boxHeight > plotTop + plotHeight) {
8141
8171
  y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below
8142
8172
  }
8143
-
8144
8173
 
8145
8174
  return {x: x, y: y};
8146
8175
  },
@@ -8320,15 +8349,6 @@ Tooltip.prototype = {
8320
8349
  y: y + chart.plotTop,
8321
8350
  borderColor: borderColor
8322
8351
  });
8323
- },
8324
-
8325
- /**
8326
- * Runs the tooltip animation one tick.
8327
- */
8328
- tick: function () {
8329
- if (this.tooltipTick) {
8330
- this.tooltipTick();
8331
- }
8332
8352
  }
8333
8353
  };
8334
8354
  /**
@@ -8350,7 +8370,7 @@ function MouseTracker(chart, options) {
8350
8370
  this.chart = chart;
8351
8371
 
8352
8372
  // The interval id
8353
- //this.tooltipInterval = UNDEFINED;
8373
+ //this.tooltipTimeout = UNDEFINED;
8354
8374
 
8355
8375
  // The cached x hover position
8356
8376
  //this.hoverX = UNDEFINED;
@@ -8907,7 +8927,7 @@ MouseTracker.prototype = {
8907
8927
  container.onclick = container.onmousedown = container.onmousemove = container.ontouchstart = container.ontouchend = container.ontouchmove = null;
8908
8928
 
8909
8929
  // memory and CPU leak
8910
- clearInterval(this.tooltipInterval);
8930
+ clearInterval(this.tooltipTimeout);
8911
8931
  },
8912
8932
 
8913
8933
  // Run MouseTracker
@@ -8920,14 +8940,6 @@ MouseTracker.prototype = {
8920
8940
 
8921
8941
  if (options.enabled) {
8922
8942
  chart.tooltip = new Tooltip(chart, options);
8923
-
8924
- // set the fixed interval ticking for the smooth tooltip
8925
- this.tooltipInterval = setInterval(function () {
8926
- // The interval function may still be running during destroy, so check that the chart is really there before calling.
8927
- if (chart && chart.tooltip) {
8928
- chart.tooltip.tick();
8929
- }
8930
- }, 32);
8931
8943
  }
8932
8944
 
8933
8945
  this.setDOMEvents();
@@ -9012,19 +9024,37 @@ Legend.prototype = {
9012
9024
  legendSymbol = item.legendSymbol,
9013
9025
  hiddenColor = legend.itemHiddenStyle.color,
9014
9026
  textColor = visible ? options.itemStyle.color : hiddenColor,
9015
- symbolColor = visible ? item.color : hiddenColor;
9027
+ symbolColor = visible ? item.color : hiddenColor,
9028
+ markerOptions = item.options && item.options.marker,
9029
+ symbolAttr = {
9030
+ stroke: symbolColor,
9031
+ fill: symbolColor
9032
+ },
9033
+ key,
9034
+ val;
9016
9035
 
9036
+
9017
9037
  if (legendItem) {
9018
9038
  legendItem.css({ fill: textColor });
9019
9039
  }
9020
9040
  if (legendLine) {
9021
9041
  legendLine.attr({ stroke: symbolColor });
9022
9042
  }
9043
+
9023
9044
  if (legendSymbol) {
9024
- legendSymbol.attr({
9025
- stroke: symbolColor,
9026
- fill: symbolColor
9027
- });
9045
+
9046
+ // Apply marker options
9047
+ if (markerOptions) {
9048
+ markerOptions = item.convertAttribs(markerOptions);
9049
+ for (key in markerOptions) {
9050
+ val = markerOptions[key];
9051
+ if (val !== UNDEFINED) {
9052
+ symbolAttr[key] = val;
9053
+ }
9054
+ }
9055
+ }
9056
+
9057
+ legendSymbol.attr(symbolAttr);
9028
9058
  }
9029
9059
  },
9030
9060
 
@@ -9714,7 +9744,8 @@ Chart.prototype = {
9714
9744
  i = seriesLength,
9715
9745
  serie,
9716
9746
  renderer = chart.renderer,
9717
- isHiddenChart = renderer.isHidden();
9747
+ isHiddenChart = renderer.isHidden(),
9748
+ afterRedraw = [];
9718
9749
 
9719
9750
  setAnimation(animation, chart);
9720
9751
 
@@ -9778,7 +9809,9 @@ Chart.prototype = {
9778
9809
  // Fire 'afterSetExtremes' only if extremes are set
9779
9810
  if (axis.isDirtyExtremes) { // #821
9780
9811
  axis.isDirtyExtremes = false;
9781
- fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751
9812
+ afterRedraw.push(function () { // prevent a recursive call to chart.redraw() (#1119)
9813
+ fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751
9814
+ });
9782
9815
  }
9783
9816
 
9784
9817
  if (axis.isDirty || isDirtyBox || hasStackedSeries) {
@@ -9819,6 +9852,11 @@ Chart.prototype = {
9819
9852
  if (isHiddenChart) {
9820
9853
  chart.cloneRenderTo(true);
9821
9854
  }
9855
+
9856
+ // Fire callbacks that are put on hold until after the redraw
9857
+ each(afterRedraw, function (callback) {
9858
+ callback.call();
9859
+ });
9822
9860
  },
9823
9861
 
9824
9862
 
@@ -10379,9 +10417,9 @@ Chart.prototype = {
10379
10417
  initReflow: function () {
10380
10418
  var chart = this,
10381
10419
  optionsChart = chart.options.chart,
10382
- renderTo = chart.renderTo;
10383
-
10384
- var reflowTimeout;
10420
+ renderTo = chart.renderTo,
10421
+ reflowTimeout;
10422
+
10385
10423
  function reflow(e) {
10386
10424
  var width = optionsChart.width || adapterRun(renderTo, 'width'),
10387
10425
  height = optionsChart.height || adapterRun(renderTo, 'height'),
@@ -10393,8 +10431,10 @@ Chart.prototype = {
10393
10431
 
10394
10432
  if (width !== chart.containerWidth || height !== chart.containerHeight) {
10395
10433
  clearTimeout(reflowTimeout);
10396
- reflowTimeout = setTimeout(function () {
10397
- chart.resize(width, height, false);
10434
+ chart.reflowTimeout = reflowTimeout = setTimeout(function () {
10435
+ if (chart.container) { // It may have been destroyed in the meantime (#1257)
10436
+ chart.resize(width, height, false);
10437
+ }
10398
10438
  }, 100);
10399
10439
  }
10400
10440
  chart.containerWidth = width;
@@ -10483,7 +10523,7 @@ Chart.prototype = {
10483
10523
  }
10484
10524
 
10485
10525
  // Move resize button (#1115)
10486
- if (resetZoomButton) {
10526
+ if (resetZoomButton && resetZoomButton.align) {
10487
10527
  resetZoomButton.align(null, null, chart[resetZoomButton.alignTo]);
10488
10528
  }
10489
10529
 
@@ -10841,18 +10881,10 @@ Chart.prototype = {
10841
10881
  var chart = this,
10842
10882
  axes = chart.axes,
10843
10883
  series = chart.series,
10844
- container = chart.container;
10845
-
10846
- var i,
10884
+ container = chart.container,
10885
+ i,
10847
10886
  parentNode = container && container.parentNode;
10848
10887
 
10849
- // If the chart is destroyed already, do nothing.
10850
- // This will happen if if a script invokes chart.destroy and
10851
- // then it will be called again on win.unload
10852
- if (chart === null) {
10853
- return;
10854
- }
10855
-
10856
10888
  // fire the chart.destoy event
10857
10889
  fireEvent(chart, 'destroy');
10858
10890
 
@@ -10876,7 +10908,7 @@ Chart.prototype = {
10876
10908
  each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'tracker', 'scroller', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) {
10877
10909
  var prop = chart[name];
10878
10910
 
10879
- if (prop) {
10911
+ if (prop && prop.destroy) {
10880
10912
  chart[name] = prop.destroy();
10881
10913
  }
10882
10914
  });
@@ -10889,8 +10921,6 @@ Chart.prototype = {
10889
10921
  discardElement(container);
10890
10922
  }
10891
10923
 
10892
- // IE6 leak
10893
- container = null;
10894
10924
  }
10895
10925
 
10896
10926
  // clean it all up
@@ -10898,8 +10928,6 @@ Chart.prototype = {
10898
10928
  delete chart[i];
10899
10929
  }
10900
10930
 
10901
- chart.options = null;
10902
- chart = null;
10903
10931
  },
10904
10932
 
10905
10933
  /**
@@ -11297,7 +11325,7 @@ Point.prototype = {
11297
11325
  parts,
11298
11326
  prop,
11299
11327
  i,
11300
- cfg = { // docs: percentageDecimals, percentagePrefix, percentageSuffix, totalDecimals, totalPrefix, totalSuffix
11328
+ cfg = {
11301
11329
  y: 0, // 0: use 'value' for repOptionKey
11302
11330
  open: 0,
11303
11331
  high: 0,
@@ -11875,7 +11903,6 @@ Series.prototype = {
11875
11903
  2 * radius,
11876
11904
  2 * radius
11877
11905
  )
11878
- .attr(this.pointAttr[NORMAL_STATE])
11879
11906
  .add(legendItemGroup);
11880
11907
  }
11881
11908
  },
@@ -12298,7 +12325,11 @@ Series.prototype = {
12298
12325
  yValue = yBottom + yValue;
12299
12326
 
12300
12327
  if (isBottomSeries) {
12301
- yBottom = pick(options.threshold, yAxis.isLog ? null : yAxis.min); // #1200
12328
+ yBottom = pick(options.threshold, yAxis.min);
12329
+ }
12330
+
12331
+ if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
12332
+ yBottom = null;
12302
12333
  }
12303
12334
 
12304
12335
  if (stacking === 'percent') {
@@ -12417,7 +12448,7 @@ Series.prototype = {
12417
12448
  }
12418
12449
 
12419
12450
  return tooltipOptions.headerFormat
12420
- .replace('{point.key}', isDateTime ? dateFormat(xDateFormat, key) : key)
12451
+ .replace('{point.key}', isDateTime && isNumber(key) ? dateFormat(xDateFormat, key) : key)
12421
12452
  .replace('{series.name}', series.name)
12422
12453
  .replace('{series.color}', series.color);
12423
12454
  },
@@ -12636,6 +12667,9 @@ Series.prototype = {
12636
12667
  .attr(pointAttr)
12637
12668
  .add(markerGroup);
12638
12669
  }
12670
+
12671
+ } else if (graphic) {
12672
+ point.graphic = graphic.destroy(); // #1269
12639
12673
  }
12640
12674
  }
12641
12675
  }
@@ -12875,59 +12909,21 @@ Series.prototype = {
12875
12909
 
12876
12910
  var series = this,
12877
12911
  seriesOptions = series.options,
12878
- options = seriesOptions.dataLabels;
12912
+ options = seriesOptions.dataLabels,
12913
+ points = series.points,
12914
+ pointOptions,
12915
+ generalOptions,
12916
+ str,
12917
+ dataLabelsGroup;
12879
12918
 
12880
12919
  if (options.enabled || series._hasPointLabels) {
12881
- var x,
12882
- y,
12883
- points = series.points,
12884
- pointOptions,
12885
- generalOptions,
12886
- str,
12887
- dataLabelsGroup,
12888
- chart = series.chart,
12889
- renderer = chart.renderer,
12890
- inverted = chart.inverted,
12891
- seriesType = series.type,
12892
- stacking = seriesOptions.stacking,
12893
- isBarLike = seriesType === 'column' || seriesType === 'bar',
12894
- vAlignIsNull = options.verticalAlign === null,
12895
- yIsNull = options.y === null,
12896
- fontMetrics = renderer.fontMetrics(options.style.fontSize), // height and baseline
12897
- fontLineHeight = fontMetrics.h,
12898
- fontBaseline = fontMetrics.b;
12899
-
12900
- if (isBarLike) {
12901
- var defaultYs = {
12902
- top: fontBaseline,
12903
- middle: fontBaseline - fontLineHeight / 2,
12904
- bottom: -fontLineHeight + fontBaseline
12905
- };
12906
- if (stacking) {
12907
- // In stacked series the default label placement is inside the bars
12908
- if (vAlignIsNull) {
12909
- options = merge(options, {verticalAlign: 'middle'});
12910
- }
12911
-
12912
- // If no y delta is specified, try to create a good default
12913
- if (yIsNull) {
12914
- options = merge(options, { y: defaultYs[options.verticalAlign]});
12915
- }
12916
- } else {
12917
- // In non stacked series the default label placement is on top of the bars
12918
- if (vAlignIsNull) {
12919
- options = merge(options, {verticalAlign: 'top'});
12920
-
12921
- // If no y delta is specified, try to create a good default (like default bar)
12922
- } else if (yIsNull) {
12923
- options = merge(options, { y: defaultYs[options.verticalAlign]});
12924
- }
12925
-
12926
- }
12920
+
12921
+ // Process default alignment of data labels for columns
12922
+ if (series.dlProcessOptions) {
12923
+ series.dlProcessOptions(options);
12927
12924
  }
12928
12925
 
12929
-
12930
- // create a separate group for the data labels to avoid rotation
12926
+ // Create a separate group for the data labels to avoid rotation
12931
12927
  dataLabelsGroup = series.plotGroup(
12932
12928
  'dataLabelsGroup',
12933
12929
  'data-labels',
@@ -12935,47 +12931,21 @@ Series.prototype = {
12935
12931
  6
12936
12932
  );
12937
12933
 
12938
- // make the labels for each point
12934
+ // Make the labels for each point
12939
12935
  generalOptions = options;
12940
12936
  each(points, function (point) {
12941
12937
 
12942
- var plotX,
12943
- plotY,
12944
- individualYDelta,
12945
- enabled,
12946
- dataLabel = point.dataLabel;
12947
-
12948
- // Merge in individual options from point
12949
- options = generalOptions; // reset changes from previous points
12950
- pointOptions = point.options;
12951
- if (pointOptions && pointOptions.dataLabels) {
12952
- options = merge(options, pointOptions.dataLabels);
12953
- }
12954
- enabled = options.enabled;
12938
+ var enabled,
12939
+ dataLabel = point.dataLabel,
12940
+ attr,
12941
+ name,
12942
+ rotation,
12943
+ isNew = true;
12955
12944
 
12956
- // Get the positions
12957
- if (enabled) {
12958
- plotX = (point.barX && point.barX + point.barW / 2) || pick(point.plotX, -999);
12959
- plotY = pick(point.plotY, -999);
12960
-
12961
- // if options.y is null, which happens by default on column charts, set the position
12962
- // above or below the column depending on the threshold
12963
- individualYDelta = options.y === null ?
12964
- (point.y >= seriesOptions.threshold ?
12965
- -fontLineHeight + fontBaseline : // below the threshold
12966
- fontBaseline) : // above the threshold
12967
- options.y;
12968
-
12969
- x = (inverted ? chart.plotWidth - plotY : plotX) + options.x;
12970
- y = mathRound((inverted ? chart.plotHeight - plotX : plotY) + individualYDelta);
12971
-
12972
- }
12945
+ // Determine if each data label is enabled
12946
+ pointOptions = point.options && point.options.dataLabels;
12947
+ enabled = generalOptions.enabled || (pointOptions && pointOptions.enabled);
12973
12948
 
12974
- // Check if the individual label must be disabled due to either falling
12975
- // ouside the plot area, or the enabled option being switched off
12976
- if (series.isCartesian && !chart.isInsidePlot(x - options.x, y)) {
12977
- enabled = false;
12978
- }
12979
12949
 
12980
12950
  // If the point is outside the plot area, destroy it. #678, #820
12981
12951
  if (dataLabel && !enabled) {
@@ -12985,23 +12955,15 @@ Series.prototype = {
12985
12955
  // in the point options, or if they fall outside the plot area.
12986
12956
  } else if (enabled) {
12987
12957
 
12988
- var align = options.align,
12989
- attr,
12990
- name;
12958
+ rotation = options.rotation;
12959
+
12960
+ // Create individual options structure that can be extended without
12961
+ // affecting others
12962
+ options = merge(generalOptions, pointOptions);
12991
12963
 
12992
12964
  // Get the string
12993
12965
  str = options.formatter.call(point.getLabelConfig(), options);
12994
12966
 
12995
- // in columns, align the string to the column
12996
- if (seriesType === 'column') {
12997
- x += { left: -1, right: 1 }[align] * point.barW / 2 || 0;
12998
- }
12999
-
13000
- if (!stacking && inverted && point.y < 0) {
13001
- align = 'right';
13002
- x -= 10;
13003
- }
13004
-
13005
12967
  // Determine the color
13006
12968
  options.style.color = pick(options.color, options.style.color, series.color, 'black');
13007
12969
 
@@ -13012,19 +12974,17 @@ Series.prototype = {
13012
12974
  dataLabel
13013
12975
  .attr({
13014
12976
  text: str
13015
- }).animate({
13016
- x: x,
13017
- y: y
13018
12977
  });
12978
+ isNew = false;
13019
12979
  // create new label
13020
12980
  } else if (defined(str)) {
13021
12981
  attr = {
13022
- align: align,
12982
+ //align: align,
13023
12983
  fill: options.backgroundColor,
13024
12984
  stroke: options.borderColor,
13025
12985
  'stroke-width': options.borderWidth,
13026
12986
  r: options.borderRadius || 0,
13027
- rotation: options.rotation,
12987
+ rotation: rotation,
13028
12988
  padding: options.padding,
13029
12989
  zIndex: 1
13030
12990
  };
@@ -13035,43 +12995,78 @@ Series.prototype = {
13035
12995
  }
13036
12996
  }
13037
12997
 
13038
- dataLabel = point.dataLabel = renderer[options.rotation ? 'text' : 'label']( // labels don't support rotation
12998
+ dataLabel = point.dataLabel = series.chart.renderer[rotation ? 'text' : 'label']( // labels don't support rotation
13039
12999
  str,
13040
- x,
13041
- y,
13000
+ 0,
13001
+ -999,
13042
13002
  null,
13043
13003
  null,
13044
13004
  null,
13045
- options.useHTML,
13046
- true // baseline for backwards compat
13005
+ options.useHTML
13047
13006
  )
13048
13007
  .attr(attr)
13049
13008
  .css(options.style)
13050
13009
  .add(dataLabelsGroup)
13051
13010
  .shadow(options.shadow);
13011
+
13052
13012
  }
13053
-
13054
- if (isBarLike && seriesOptions.stacking && dataLabel) {
13055
- var barX = point.barX,
13056
- barY = point.barY,
13057
- barW = point.barW,
13058
- barH = point.barH;
13059
-
13060
- dataLabel.align(options, null,
13061
- {
13062
- x: inverted ? chart.plotWidth - barY - barH : barX,
13063
- y: inverted ? chart.plotHeight - barX - barW : barY,
13064
- width: inverted ? barH : barW,
13065
- height: inverted ? barW : barH
13066
- });
13067
- }
13068
-
13069
13013
 
13014
+ // Now the data label is created and placed at 0,0, so we need to align it
13015
+ if (dataLabel) {
13016
+ series.alignDataLabel(point, dataLabel, options, null, isNew);
13017
+ }
13070
13018
  }
13071
13019
  });
13072
13020
  }
13073
13021
  },
13074
13022
 
13023
+ /**
13024
+ * Align each individual data label
13025
+ */
13026
+ alignDataLabel: function (point, dataLabel, options, alignTo, isNew) {
13027
+ var chart = this.chart,
13028
+ inverted = chart.inverted,
13029
+ plotX = pick(point.plotX, -999),
13030
+ plotY = pick(point.plotY, -999),
13031
+ bBox = dataLabel.getBBox(),
13032
+ alignAttr; // the final position;
13033
+
13034
+ // The alignment box is a singular point
13035
+ alignTo = extend({
13036
+ x: inverted ? chart.plotWidth - plotY : plotX,
13037
+ y: mathRound(inverted ? chart.plotHeight - plotX : plotY),
13038
+ width: 0,
13039
+ height: 0
13040
+ }, alignTo);
13041
+
13042
+ // Add the text size for alignment calculation
13043
+ extend(options, {
13044
+ width: bBox.width,
13045
+ height: bBox.height
13046
+ });
13047
+
13048
+ // Allow a hook for changing alignment in the last moment, then do the alignment
13049
+ if (options.rotation) { // Fancy box alignment isn't supported for rotated text
13050
+ alignAttr = {
13051
+ align: options.align,
13052
+ x: alignTo.x + options.x + alignTo.width / 2,
13053
+ y: alignTo.y + options.y + alignTo.height / 2
13054
+ };
13055
+ dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
13056
+ } else {
13057
+ dataLabel.align(options, null, alignTo);
13058
+ alignAttr = dataLabel.alignAttr;
13059
+ }
13060
+
13061
+ // Show or hide based on the final aligned position
13062
+ dataLabel.attr({
13063
+ visibility: options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) || chart.isInsidePlot(plotX, plotY, inverted) ?
13064
+ (hasSVG ? 'inherit' : VISIBLE) :
13065
+ HIDDEN
13066
+ });
13067
+
13068
+ },
13069
+
13075
13070
  /**
13076
13071
  * Return the graph path of a segment
13077
13072
  */
@@ -13270,8 +13265,6 @@ Series.prototype = {
13270
13265
  chartSeriesGroup
13271
13266
  );
13272
13267
 
13273
- series.drawDataLabels();
13274
-
13275
13268
  // initiate the animation
13276
13269
  if (doAnimation) {
13277
13270
  series.animate(true);
@@ -13290,6 +13283,10 @@ Series.prototype = {
13290
13283
 
13291
13284
  // draw the points
13292
13285
  series.drawPoints();
13286
+
13287
+ // draw the data labels
13288
+ series.drawDataLabels();
13289
+
13293
13290
 
13294
13291
  // draw the mouse tracking area
13295
13292
  if (series.options.enableMouseTracking !== false) {
@@ -13880,8 +13877,9 @@ defaultPlotOptions.column = merge(defaultSeriesOptions, {
13880
13877
  }
13881
13878
  },
13882
13879
  dataLabels: {
13883
- y: null,
13884
- verticalAlign: null
13880
+ align: null, // auto
13881
+ verticalAlign: null, // auto
13882
+ y: null
13885
13883
  },
13886
13884
  threshold: 0
13887
13885
  });
@@ -13981,7 +13979,7 @@ var ColumnSeries = extendClass(Series, {
13981
13979
  pointOffsetWidth - (categoryWidth / 2)) *
13982
13980
  (reversedXAxis ? -1 : 1),
13983
13981
  threshold = options.threshold,
13984
- translatedThreshold = series.yAxis.getThreshold(threshold),
13982
+ translatedThreshold = series.translatedThreshold = series.yAxis.getThreshold(threshold),
13985
13983
  minPointLength = pick(options.minPointLength, 5);
13986
13984
 
13987
13985
  // record the new values
@@ -14010,13 +14008,8 @@ var ColumnSeries = extendClass(Series, {
14010
14008
  }
14011
14009
  }
14012
14010
 
14013
- extend(point, {
14014
- barX: barX,
14015
- barY: barY,
14016
- barW: barW,
14017
- barH: barH,
14018
- pointWidth: pointWidth
14019
- });
14011
+ point.barX = barX;
14012
+ point.pointWidth = pointWidth;
14020
14013
 
14021
14014
  // create shape type and shape args that are reused in drawPoints and drawTracker
14022
14015
  point.shapeType = 'rect';
@@ -14058,15 +14051,14 @@ var ColumnSeries = extendClass(Series, {
14058
14051
  var series = this,
14059
14052
  options = series.options,
14060
14053
  renderer = series.chart.renderer,
14061
- graphic,
14062
14054
  shapeArgs;
14063
14055
 
14064
14056
 
14065
14057
  // draw the columns
14066
14058
  each(series.points, function (point) {
14067
- var plotY = point.plotY;
14068
- if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
14059
+ var plotY = point.plotY,
14069
14060
  graphic = point.graphic;
14061
+ if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
14070
14062
  shapeArgs = point.shapeArgs;
14071
14063
  if (graphic) { // update
14072
14064
  stop(graphic);
@@ -14079,6 +14071,8 @@ var ColumnSeries = extendClass(Series, {
14079
14071
  .shadow(options.shadow, null, options.stacking && !options.borderRadius);
14080
14072
  }
14081
14073
 
14074
+ } else if (graphic) {
14075
+ point.graphic = graphic.destroy(); // #1269
14082
14076
  }
14083
14077
  });
14084
14078
  },
@@ -14141,6 +14135,54 @@ var ColumnSeries = extendClass(Series, {
14141
14135
  }
14142
14136
  });
14143
14137
  },
14138
+
14139
+ /**
14140
+ * Override the basic data label alignment by adjusting for the position of the column
14141
+ */
14142
+ alignDataLabel: function (point, dataLabel, options, alignTo, isNew) {
14143
+ var chart = this.chart,
14144
+ inverted = chart.inverted,
14145
+ below = point.below || (point.plotY > (this.translatedThreshold || chart.plotSizeY)),
14146
+ inside = (this.options.stacking || options.inside); // draw it inside the box?
14147
+
14148
+ // Align to the column itself, or the top of it
14149
+ if (point.shapeArgs) { // Area range uses this method but not alignTo
14150
+ alignTo = merge(point.shapeArgs);
14151
+ if (inverted) {
14152
+ alignTo = {
14153
+ x: chart.plotWidth - alignTo.y - alignTo.height,
14154
+ y: chart.plotHeight - alignTo.x - alignTo.width,
14155
+ width: alignTo.height,
14156
+ height: alignTo.width
14157
+ };
14158
+ }
14159
+
14160
+ // Compute the alignment box
14161
+ if (!inside) {
14162
+ if (inverted) {
14163
+ alignTo.x += below ? 0 : alignTo.width;
14164
+ alignTo.width = 0;
14165
+ } else {
14166
+ alignTo.y += below ? alignTo.height : 0;
14167
+ alignTo.height = 0;
14168
+ }
14169
+ }
14170
+ }
14171
+
14172
+ // When alignment is undefined (typically columns and bars), display the individual
14173
+ // point below or above the point depending on the threshold
14174
+ options.align = pick(
14175
+ options.align,
14176
+ !inverted || inside ? 'center' : below ? 'right' : 'left'
14177
+ );
14178
+ options.verticalAlign = pick(
14179
+ options.verticalAlign,
14180
+ inverted || inside ? 'middle' : below ? 'top' : 'bottom'
14181
+ );
14182
+
14183
+ // Call the parent method
14184
+ Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
14185
+ },
14144
14186
 
14145
14187
 
14146
14188
  /**
@@ -14214,14 +14256,7 @@ seriesTypes.column = ColumnSeries;
14214
14256
  /**
14215
14257
  * Set the default options for bar
14216
14258
  */
14217
- defaultPlotOptions.bar = merge(defaultPlotOptions.column, {
14218
- dataLabels: {
14219
- align: 'left',
14220
- x: 5,
14221
- y: null,
14222
- verticalAlign: 'middle'
14223
- }
14224
- });
14259
+ defaultPlotOptions.bar = merge(defaultPlotOptions.column);
14225
14260
  /**
14226
14261
  * The Bar series class
14227
14262
  */
@@ -14334,9 +14369,9 @@ defaultPlotOptions.pie = merge(defaultSeriesOptions, {
14334
14369
  enabled: true,
14335
14370
  formatter: function () {
14336
14371
  return this.point.name;
14337
- },
14372
+ }
14338
14373
  // softConnector: true,
14339
- y: 5
14374
+ //y: 0
14340
14375
  },
14341
14376
  //innerSize: 0,
14342
14377
  legendType: 'point',
@@ -14578,7 +14613,10 @@ var PieSeries = {
14578
14613
  radiusX, // the x component of the radius vector for a given point
14579
14614
  radiusY,
14580
14615
  labelDistance = options.dataLabels.distance,
14581
- ignoreHiddenPoint = options.ignoreHiddenPoint;
14616
+ ignoreHiddenPoint = options.ignoreHiddenPoint,
14617
+ i,
14618
+ len = points.length,
14619
+ point;
14582
14620
 
14583
14621
  // get positions - either an integer or a percentage string must be given
14584
14622
  series.center = positions = series.getCenter();
@@ -14594,11 +14632,16 @@ var PieSeries = {
14594
14632
  };
14595
14633
 
14596
14634
  // get the total sum
14597
- each(points, function (point) {
14635
+ for (i = 0; i < len; i++) {
14636
+ point = points[i];
14598
14637
  total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
14599
- });
14638
+ }
14600
14639
 
14601
- each(points, function (point) {
14640
+ // Calculate the geometry for each point
14641
+ for (i = 0; i < len; i++) {
14642
+
14643
+ point = points[i];
14644
+
14602
14645
  // set start and end angle
14603
14646
  fraction = total ? point.y / total : 0;
14604
14647
  start = mathRound(cumulative * circ * precision) / precision;
@@ -14651,7 +14694,7 @@ var PieSeries = {
14651
14694
  point.percentage = fraction * 100;
14652
14695
  point.total = total;
14653
14696
 
14654
- });
14697
+ }
14655
14698
 
14656
14699
 
14657
14700
  this.setTooltipPoints();
@@ -14952,7 +14995,7 @@ var PieSeries = {
14952
14995
  })[dataLabel.moved ? 'animate' : 'attr']({
14953
14996
  x: x + options.x +
14954
14997
  ({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0),
14955
- y: y + options.y
14998
+ y: y + options.y - 10 // 10 is for the baseline (label vs text)
14956
14999
  });
14957
15000
  dataLabel.moved = true;
14958
15001
 
@@ -14996,6 +15039,8 @@ var PieSeries = {
14996
15039
  }
14997
15040
  }
14998
15041
  },
15042
+
15043
+ alignDataLabel: noop,
14999
15044
 
15000
15045
  /**
15001
15046
  * Draw point specific tracker objects. Inherit directly from column series.
@@ -15061,6 +15106,6 @@ extend(Highcharts, {
15061
15106
  canvas: useCanVG,
15062
15107
  vml: !hasSVG && !useCanVG,
15063
15108
  product: 'Highcharts',
15064
- version: '2.3.2'
15109
+ version: '2.3.3'
15065
15110
  });
15066
15111
  }());