highcharts-js-rails 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v0.1.5 (2012-04-30) ##
2
+
3
+ * Update Highcharts to 2.2.2.
4
+
1
5
  ## v0.1.4 (2012-03-17) ##
2
6
 
3
7
  * Take into account the fact that sometimes the data passed to a Series may be an array of arrays.
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'highcharts-js-rails'
6
- s.version = '0.1.4'
6
+ s.version = '0.1.5'
7
7
  s.authors = ['Alex Robbin']
8
8
  s.email = ['agrobbin@gmail.com']
9
9
  s.homepage = 'https://github.com/agrobbin/highcharts-js-rails'
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v2.2.1 (2012-03-15)
2
+ * @license @product.name@ JS v@product.version@ (@product.date@)
3
3
  * MooTools adapter
4
4
  *
5
5
  * (c) 2010-2011 Torstein Hønsi
@@ -285,6 +285,15 @@ win.HighchartsAdapter = {
285
285
  }
286
286
  },
287
287
 
288
+ /**
289
+ * Set back e.pageX and e.pageY that MooTools has abstracted away
290
+ */
291
+ washMouseEvent: function (e) {
292
+ e.pageX = e.page.x;
293
+ e.pageY = e.page.y;
294
+ return e;
295
+ },
296
+
288
297
  /**
289
298
  * Stop running animations on the object
290
299
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v2.2.1 (2012-03-15)
2
+ * @license @product.name@ JS v@product.version@ (@product.date@)
3
3
  * Prototype adapter
4
4
  *
5
5
  * @author Michael Nelson, Torstein Hønsi.
@@ -84,13 +84,13 @@ return {
84
84
 
85
85
  if (element.attr) { // SVGElement
86
86
  element.attr(this.options.attribute, position);
87
-
87
+
88
88
  } else { // HTML, #409
89
89
  obj = {};
90
90
  obj[this.options.attribute] = position;
91
91
  $(element).setStyle(obj);
92
92
  }
93
-
93
+
94
94
  },
95
95
  finish: function () {
96
96
  // Delete the property that holds this animation now that it is finished.
@@ -230,6 +230,10 @@ return {
230
230
  }
231
231
  },
232
232
 
233
+ washMouseEvent: function (e) {
234
+ return e;
235
+ },
236
+
233
237
  // um, grep
234
238
  grep: function (arr, fn) {
235
239
  return arr.findAll(fn);
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v2.2.1 (2012-03-15)
5
+ * @license Highcharts JS v2.2.2 (2012-04-26)
6
6
  *
7
7
  * (c) 2009-2011 Torstein Hønsi
8
8
  *
@@ -124,6 +124,7 @@ var UNDEFINED,
124
124
  addEvent = adapter.addEvent,
125
125
  removeEvent = adapter.removeEvent,
126
126
  fireEvent = adapter.fireEvent,
127
+ washMouseEvent = adapter.washMouseEvent,
127
128
  animate = adapter.animate,
128
129
  stop = adapter.stop,
129
130
 
@@ -497,10 +498,10 @@ function normalizeTickInterval(interval, multiples, magnitude, options) {
497
498
 
498
499
  /**
499
500
  * Get a normalized tick interval for dates. Returns a configuration object with
500
- * unit range (interval), count and name. Used to prepare data for getTimeTicks.
501
+ * unit range (interval), count and name. Used to prepare data for getTimeTicks.
501
502
  * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
502
- * of segments in stock charts, the normalizing logic was extracted in order to
503
- * prevent it for running over again for each segment having the same interval.
503
+ * of segments in stock charts, the normalizing logic was extracted in order to
504
+ * prevent it for running over again for each segment having the same interval.
504
505
  * #662, #697.
505
506
  */
506
507
  function normalizeTimeTickInterval(tickInterval, unitsOption) {
@@ -534,7 +535,7 @@ function normalizeTimeTickInterval(tickInterval, unitsOption) {
534
535
  multiples = unit[1],
535
536
  count,
536
537
  i;
537
-
538
+
538
539
  // loop through the units to find the one that best fits the tickInterval
539
540
  for (i = 0; i < units.length; i++) {
540
541
  unit = units[i];
@@ -558,7 +559,7 @@ function normalizeTimeTickInterval(tickInterval, unitsOption) {
558
559
  if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
559
560
  multiples = [1, 2, 5];
560
561
  }
561
-
562
+
562
563
  // prevent 2.5 years intervals, though 25, 250 etc. are allowed
563
564
  if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
564
565
  multiples = [1, 2, 5];
@@ -566,7 +567,7 @@ function normalizeTimeTickInterval(tickInterval, unitsOption) {
566
567
 
567
568
  // get the count
568
569
  count = normalizeTickInterval(tickInterval / interval, multiples);
569
-
570
+
570
571
  return {
571
572
  unitRange: interval,
572
573
  count: count,
@@ -595,7 +596,7 @@ function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
595
596
  interval = normalizedInterval.unitRange,
596
597
  count = normalizedInterval.count;
597
598
 
598
-
599
+
599
600
 
600
601
  if (interval >= timeUnits[SECOND]) { // second
601
602
  minDate.setMilliseconds(0);
@@ -665,7 +666,7 @@ function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
665
666
  // else, the interval is fixed and we use simple addition
666
667
  } else {
667
668
  time += interval * count;
668
-
669
+
669
670
  // mark new days if the time is dividable by day
670
671
  if (interval <= timeUnits[HOUR] && time % timeUnits[DAY] === 0) {
671
672
  higherRanks[time] = DAY;
@@ -674,7 +675,7 @@ function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
674
675
 
675
676
  i++;
676
677
  }
677
-
678
+
678
679
  // push the last time
679
680
  tickPositions.push(time);
680
681
 
@@ -720,7 +721,7 @@ ChartCounters.prototype = {
720
721
  * and not covering the point it self.
721
722
  */
722
723
  function placeBox(boxWidth, boxHeight, outerLeft, outerTop, outerWidth, outerHeight, point, distance, preferRight) {
723
-
724
+
724
725
  // keep the box within the chart area
725
726
  var pointX = point.x,
726
727
  pointY = point.y,
@@ -749,10 +750,15 @@ function placeBox(boxWidth, boxHeight, outerLeft, outerTop, outerWidth, outerHei
749
750
  if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
750
751
  y = pointY + outerTop + distance; // below
751
752
  }
752
- } else if (y + boxHeight > outerTop + outerHeight) {
753
+ }
754
+
755
+ // Now if the tooltip is below the chart, move it up. It's better to cover the
756
+ // point than to disappear outside the chart. #834.
757
+ if (y + boxHeight > outerTop + outerHeight) {
753
758
  y = outerTop + outerHeight - boxHeight - distance; // below
754
759
  }
755
760
 
761
+
756
762
  return {x: x, y: y};
757
763
  }
758
764
 
@@ -853,7 +859,7 @@ function discardElement(element) {
853
859
  }
854
860
 
855
861
  /**
856
- * Provide error messages for debugging, with links to online explanation
862
+ * Provide error messages for debugging, with links to online explanation
857
863
  */
858
864
  function error(code, stop) {
859
865
  var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
@@ -929,9 +935,10 @@ pathAnim = {
929
935
  }
930
936
 
931
937
  // if shifting points, prepend a dummy point to the end path
932
- if (shift === 1) {
933
-
934
- end = [].concat(end).splice(0, numParams).concat(end);
938
+ if (shift <= end.length / numParams) {
939
+ while (shift--) {
940
+ end = [].concat(end).splice(0, numParams).concat(end);
941
+ }
935
942
  }
936
943
  elem.shift = 0; // reset for following animations
937
944
 
@@ -1145,6 +1152,13 @@ if (!globalAdapter && win.jQuery) {
1145
1152
  }
1146
1153
  };
1147
1154
 
1155
+ /**
1156
+ * Extension method needed for MooTools
1157
+ */
1158
+ washMouseEvent = function (e) {
1159
+ return e;
1160
+ };
1161
+
1148
1162
  /**
1149
1163
  * Animate a HTML element or SVG element wrapper
1150
1164
  * @param {Object} el
@@ -1270,7 +1284,7 @@ defaultOptions = {
1270
1284
  },
1271
1285
  global: {
1272
1286
  useUTC: true,
1273
- canvasToolsURL: 'http://code.highcharts.com/2.2.1/modules/canvas-tools.js'
1287
+ canvasToolsURL: 'http://code.highcharts.com/2.2.2/modules/canvas-tools.js'
1274
1288
  },
1275
1289
  chart: {
1276
1290
  //animation: true,
@@ -1418,7 +1432,7 @@ defaultOptions = {
1418
1432
  //valueDecimals: null,
1419
1433
  //xDateFormat: '%A, %b %e, %Y',
1420
1434
  //valuePrefix: '',
1421
- //ySuffix: ''
1435
+ //ySuffix: ''
1422
1436
  //}
1423
1437
  // turboThreshold: 1000
1424
1438
  // zIndex: null
@@ -1673,8 +1687,8 @@ defaultBottomAxisOptions = { // horizontal axis
1673
1687
  labels: {
1674
1688
  align: 'center',
1675
1689
  x: 0,
1676
- y: 14,
1677
- overflow: 'justify' // docs
1690
+ y: 14
1691
+ // overflow: undefined // docs - can be 'justify'
1678
1692
  // staggerLines: null
1679
1693
  },
1680
1694
  title: {
@@ -1831,15 +1845,15 @@ function setTimeMethods() {
1831
1845
  * @param {Object} options The new custom options
1832
1846
  */
1833
1847
  function setOptions(options) {
1834
-
1835
- // Pull out axis options and apply them to the respective default axis options
1848
+
1849
+ // Pull out axis options and apply them to the respective default axis options
1836
1850
  defaultXAxisOptions = merge(defaultXAxisOptions, options.xAxis);
1837
1851
  defaultYAxisOptions = merge(defaultYAxisOptions, options.yAxis);
1838
1852
  options.xAxis = options.yAxis = UNDEFINED;
1839
-
1853
+
1840
1854
  // Merge in the default options
1841
1855
  defaultOptions = merge(defaultOptions, options);
1842
-
1856
+
1843
1857
  // Apply UTC
1844
1858
  setTimeMethods();
1845
1859
 
@@ -2008,6 +2022,7 @@ SVGElement.prototype = {
2008
2022
  nodeName = element.nodeName,
2009
2023
  renderer = wrapper.renderer,
2010
2024
  skipAttr,
2025
+ titleNode,
2011
2026
  attrSetters = wrapper.attrSetters,
2012
2027
  shadows = wrapper.shadows,
2013
2028
  hasSetSymbolSize,
@@ -2143,9 +2158,12 @@ SVGElement.prototype = {
2143
2158
 
2144
2159
  // Title requires a subnode, #431
2145
2160
  } else if (key === 'title') {
2146
- var title = doc.createElementNS(SVG_NS, 'title');
2147
- title.appendChild(doc.createTextNode(value));
2148
- element.appendChild(title);
2161
+ titleNode = element.getElementsByTagName('title')[0];
2162
+ if (!titleNode) {
2163
+ titleNode = doc.createElementNS(SVG_NS, 'title');
2164
+ element.appendChild(titleNode);
2165
+ }
2166
+ titleNode.textContent = value;
2149
2167
  }
2150
2168
 
2151
2169
  // jQuery animate changes case
@@ -2159,7 +2177,7 @@ SVGElement.prototype = {
2159
2177
  }
2160
2178
 
2161
2179
  // symbols
2162
- if (wrapper.symbolName && /^(x|y|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
2180
+ if (wrapper.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
2163
2181
 
2164
2182
 
2165
2183
  if (!hasSetSymbolSize) {
@@ -2200,14 +2218,14 @@ SVGElement.prototype = {
2200
2218
  }
2201
2219
 
2202
2220
  }
2203
-
2221
+
2204
2222
  // Workaround for our #732, WebKit's issue https://bugs.webkit.org/show_bug.cgi?id=78385
2205
2223
  // TODO: If the WebKit team fix this bug before the final release of Chrome 18, remove the workaround.
2206
2224
  if (isWebKit && /Chrome\/(18|19)/.test(userAgent)) {
2207
2225
  if (nodeName === 'text' && (hash.x !== UNDEFINED || hash.y !== UNDEFINED)) {
2208
2226
  var parent = element.parentNode,
2209
2227
  next = element.nextSibling;
2210
-
2228
+
2211
2229
  if (parent) {
2212
2230
  parent.removeChild(element);
2213
2231
  if (next) {
@@ -2219,7 +2237,7 @@ SVGElement.prototype = {
2219
2237
  }
2220
2238
  }
2221
2239
  // End of workaround for #732
2222
-
2240
+
2223
2241
  return ret;
2224
2242
  },
2225
2243
 
@@ -2678,7 +2696,7 @@ SVGElement.prototype = {
2678
2696
  // SVG elements
2679
2697
  if (element.namespaceURI === SVG_NS) {
2680
2698
  try { // Fails in Firefox if the container has display: none.
2681
-
2699
+
2682
2700
  bBox = element.getBBox ?
2683
2701
  // SVG: use extend because IE9 is not allowed to change width and height in case
2684
2702
  // of rotation (below)
@@ -2689,13 +2707,13 @@ SVGElement.prototype = {
2689
2707
  height: element.offsetHeight
2690
2708
  };
2691
2709
  } catch (e) {}
2692
-
2710
+
2693
2711
  // If the bBox is not set, the try-catch block above failed. The other condition
2694
2712
  // is for Opera that returns a width of -Infinity on hidden elements.
2695
2713
  if (!bBox || bBox.width < 0) {
2696
2714
  bBox = { width: 0, height: 0 };
2697
2715
  }
2698
-
2716
+
2699
2717
  width = bBox.width;
2700
2718
  height = bBox.height;
2701
2719
 
@@ -2951,6 +2969,32 @@ SVGRenderer.prototype = {
2951
2969
  renderer.gradients = {}; // Object where gradient SvgElements are stored
2952
2970
 
2953
2971
  renderer.setSize(width, height, false);
2972
+
2973
+
2974
+
2975
+ // Issue 110 workaround:
2976
+ // In Firefox, if a div is positioned by percentage, its pixel position may land
2977
+ // between pixels. The container itself doesn't display this, but an SVG element
2978
+ // inside this container will be drawn at subpixel precision. In order to draw
2979
+ // sharp lines, this must be compensated for. This doesn't seem to work inside
2980
+ // iframes though (like in jsFiddle).
2981
+ var subPixelFix, rect;
2982
+ if (isFirefox && container.getBoundingClientRect) {
2983
+ renderer.subPixelFix = subPixelFix = function () {
2984
+ css(container, { left: 0, top: 0 });
2985
+ rect = container.getBoundingClientRect();
2986
+ css(container, {
2987
+ left: (-(rect.left - pInt(rect.left))) + PX,
2988
+ top: (-(rect.top - pInt(rect.top))) + PX
2989
+ });
2990
+ };
2991
+
2992
+ // run the fix now
2993
+ subPixelFix();
2994
+
2995
+ // run it on resize
2996
+ addEvent(win, 'resize', subPixelFix);
2997
+ }
2954
2998
  },
2955
2999
 
2956
3000
  /**
@@ -2972,6 +3016,9 @@ SVGRenderer.prototype = {
2972
3016
  renderer.defs = rendererDefs.destroy();
2973
3017
  }
2974
3018
 
3019
+ // Remove sub pixel fix handler
3020
+ removeEvent(win, 'resize', renderer.subPixelFix);
3021
+
2975
3022
  renderer.alignedObjects = null;
2976
3023
 
2977
3024
  return null;
@@ -3014,7 +3061,14 @@ SVGRenderer.prototype = {
3014
3061
  textLineHeight = textStyles && textStyles.lineHeight,
3015
3062
  lastLine,
3016
3063
  GET_COMPUTED_STYLE = 'getComputedStyle',
3017
- i = childNodes.length;
3064
+ i = childNodes.length,
3065
+ linePositions = [];
3066
+
3067
+ // Needed in IE9 because it doesn't report tspan's offsetHeight (#893)
3068
+ function getLineHeightByBBox(lineNo) {
3069
+ linePositions[lineNo] = textNode.getBBox().height;
3070
+ return mathRound(linePositions[lineNo] - (linePositions[lineNo - 1] || 0));
3071
+ }
3018
3072
 
3019
3073
  // remove old text
3020
3074
  while (i--) {
@@ -3092,7 +3146,7 @@ SVGRenderer.prototype = {
3092
3146
  pInt(win[GET_COMPUTED_STYLE](lastLine, null).getPropertyValue('line-height'));
3093
3147
 
3094
3148
  if (!lineHeight || isNaN(lineHeight)) {
3095
- lineHeight = textLineHeight || lastLine.offsetHeight || 18;
3149
+ lineHeight = textLineHeight || lastLine.offsetHeight || getLineHeightByBBox(lineNo) || 18;
3096
3150
  }
3097
3151
  attr(tspan, 'dy', lineHeight);
3098
3152
  }
@@ -3453,7 +3507,8 @@ SVGRenderer.prototype = {
3453
3507
 
3454
3508
  imageRegex = /^url\((.*?)\)$/,
3455
3509
  imageSrc,
3456
- imageSize;
3510
+ imageSize,
3511
+ centerImage;
3457
3512
 
3458
3513
  if (path) {
3459
3514
 
@@ -3474,14 +3529,19 @@ SVGRenderer.prototype = {
3474
3529
  // image symbols
3475
3530
  } else if (imageRegex.test(symbol)) {
3476
3531
 
3477
- var centerImage = function (img, size) {
3532
+ // On image load, set the size and position
3533
+ centerImage = function (img, size) {
3478
3534
  img.attr({
3479
3535
  width: size[0],
3480
3536
  height: size[1]
3481
- }).translate(
3482
- -mathRound(size[0] / 2),
3483
- -mathRound(size[1] / 2)
3484
- );
3537
+ });
3538
+
3539
+ if (!img.alignByTranslate) { // #185
3540
+ img.translate(
3541
+ -mathRound(size[0] / 2),
3542
+ -mathRound(size[1] / 2)
3543
+ );
3544
+ }
3485
3545
  };
3486
3546
 
3487
3547
  imageSrc = symbol.match(imageRegex)[1];
@@ -3865,14 +3925,14 @@ SVGRenderer.prototype = {
3865
3925
  */
3866
3926
  fontMetrics: function (fontSize) {
3867
3927
  fontSize = pInt(fontSize || 11);
3868
-
3928
+
3869
3929
  // Empirical values found by comparing font size and bounding box height.
3870
3930
  // Applies to the default font family. http://jsfiddle.net/highcharts/7xvn7/
3871
3931
  var lineHeight = fontSize < 24 ? fontSize + 4 : mathRound(fontSize * 1.2),
3872
3932
  baseline = mathRound(lineHeight * 0.8);
3873
-
3933
+
3874
3934
  return {
3875
- h: lineHeight,
3935
+ h: lineHeight,
3876
3936
  b: baseline
3877
3937
  };
3878
3938
  },
@@ -3888,12 +3948,13 @@ SVGRenderer.prototype = {
3888
3948
  * coordinates it should be pinned to
3889
3949
  * @param {Number} anchorY
3890
3950
  * @param {Boolean} baseline Whether to position the label relative to the text baseline,
3891
- * like renderer.text, or to the upper border of the rectangle.
3951
+ * like renderer.text, or to the upper border of the rectangle.
3952
+ * @param {String} className Class name for the group
3892
3953
  */
3893
- label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline) {
3954
+ label: function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
3894
3955
 
3895
3956
  var renderer = this,
3896
- wrapper = renderer.g(),
3957
+ wrapper = renderer.g(className),
3897
3958
  text = renderer.text('', 0, 0, useHTML)
3898
3959
  .attr({
3899
3960
  zIndex: 1
@@ -3901,7 +3962,7 @@ SVGRenderer.prototype = {
3901
3962
  .add(wrapper),
3902
3963
  box,
3903
3964
  bBox,
3904
- align = 'left',
3965
+ alignFactor = 0,
3905
3966
  padding = 3,
3906
3967
  width,
3907
3968
  height,
@@ -3920,23 +3981,23 @@ SVGRenderer.prototype = {
3920
3981
  function updateBoxSize() {
3921
3982
  var boxY,
3922
3983
  style = text.element.style;
3923
-
3984
+
3924
3985
  bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) &&
3925
3986
  text.getBBox(true);
3926
- wrapper.width = (width || bBox.width) + 2 * padding;
3927
- wrapper.height = (height || bBox.height) + 2 * padding;
3928
-
3987
+ wrapper.width = (width || bBox.width || 0) + 2 * padding;
3988
+ wrapper.height = (height || bBox.height || 0) + 2 * padding;
3989
+
3929
3990
  // update the label-scoped y offset
3930
3991
  baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b;
3931
-
3932
-
3992
+
3993
+
3933
3994
  // create the border box if it is not already present
3934
3995
  if (!box) {
3935
3996
  boxY = baseline ? -baselineOffset : 0;
3936
-
3997
+
3937
3998
  wrapper.box = box = shape ?
3938
- renderer.symbol(shape, 0, boxY, wrapper.width, wrapper.height) :
3939
- renderer.rect(0, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
3999
+ renderer.symbol(shape, -alignFactor * padding, boxY, wrapper.width, wrapper.height) :
4000
+ renderer.rect(-alignFactor * padding, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
3940
4001
  box.add(wrapper);
3941
4002
  }
3942
4003
 
@@ -3954,9 +4015,9 @@ SVGRenderer.prototype = {
3954
4015
  function updateTextPadding() {
3955
4016
  var styles = wrapper.styles,
3956
4017
  textAlign = styles && styles.textAlign,
3957
- x = padding,
4018
+ x = padding * (1 - alignFactor),
3958
4019
  y;
3959
-
4020
+
3960
4021
  // determin y based on the baseline
3961
4022
  y = baseline ? 0 : baselineOffset;
3962
4023
 
@@ -4031,10 +4092,10 @@ SVGRenderer.prototype = {
4031
4092
 
4032
4093
  // change local variable and set attribue as well
4033
4094
  attrSetters.align = function (value) {
4034
- align = value;
4095
+ alignFactor = { left: 0, center: 0.5, right: 1 }[value];
4035
4096
  return false; // prevent setting text-anchor on the group
4036
4097
  };
4037
-
4098
+
4038
4099
  // apply these to the box and the text alike
4039
4100
  attrSetters.text = function (value, key) {
4040
4101
  text.attr(key, value);
@@ -4063,12 +4124,13 @@ SVGRenderer.prototype = {
4063
4124
  boxAttr(key, value - wrapperY);
4064
4125
  return false;
4065
4126
  };
4066
-
4127
+
4067
4128
  // rename attributes
4068
4129
  attrSetters.x = function (value) {
4069
- value -= { left: 0, center: 0.5, right: 1 }[align] * ((width || bBox.width) + padding);
4070
- wrapperX = wrapper.x = mathRound(value); // wrapper.x is for animation getter
4071
-
4130
+ wrapper.x = value; // for animation getter
4131
+ value -= alignFactor * ((width || bBox.width) + padding);
4132
+ wrapperX = mathRound(value);
4133
+
4072
4134
  wrapper.attr('translateX', wrapperX);
4073
4135
  return false;
4074
4136
  };
@@ -4239,12 +4301,12 @@ var VMLElement = {
4239
4301
  toggleChildren: function (element, visibility) {
4240
4302
  var childNodes = element.childNodes,
4241
4303
  i = childNodes.length;
4242
-
4304
+
4243
4305
  while (i--) {
4244
-
4306
+
4245
4307
  // apply the visibility
4246
4308
  css(childNodes[i], { visibility: visibility });
4247
-
4309
+
4248
4310
  // we have a nested group, apply it to its children again
4249
4311
  if (childNodes[i].nodeName === 'DIV') {
4250
4312
  this.toggleChildren(childNodes[i], visibility);
@@ -4377,9 +4439,9 @@ var VMLElement = {
4377
4439
 
4378
4440
  // width and height
4379
4441
  } else if (key === 'width' || key === 'height') {
4380
-
4442
+
4381
4443
  value = mathMax(0, value); // don't set width or height below zero (#311)
4382
-
4444
+
4383
4445
  this[key] = value; // used in getter
4384
4446
 
4385
4447
  // clipping rectangle special
@@ -4487,12 +4549,19 @@ var VMLElement = {
4487
4549
  */
4488
4550
  clip: function (clipRect) {
4489
4551
  var wrapper = this,
4490
- clipMembers = clipRect.members;
4552
+ clipMembers = clipRect.members,
4553
+ element = wrapper.element;
4491
4554
 
4492
4555
  clipMembers.push(wrapper);
4493
4556
  wrapper.destroyClip = function () {
4494
4557
  erase(clipMembers, wrapper);
4495
4558
  };
4559
+
4560
+ // Issue #863 workaround - related to #140, #61, #74
4561
+ if (element.parentNode.className === 'highcharts-tracker' && !docMode8) {
4562
+ css(element, { visibility: HIDDEN });
4563
+ }
4564
+
4496
4565
  return wrapper.css(clipRect.getCSS(wrapper.inverted));
4497
4566
  },
4498
4567
 
@@ -4706,6 +4775,7 @@ var VMLRendererExtension = { // inherit SVGRenderer
4706
4775
  height: bottom + PX
4707
4776
  });
4708
4777
  }
4778
+
4709
4779
  return ret;
4710
4780
  },
4711
4781
 
@@ -4772,15 +4842,15 @@ var VMLRendererExtension = { // inherit SVGRenderer
4772
4842
  (y2 - y1) / // y vector
4773
4843
  (x2 - x1) // x vector
4774
4844
  ) * 180 / mathPI;
4775
-
4776
-
4845
+
4846
+
4777
4847
  // when colors attribute is used, the meanings of opacity and o:opacity2
4778
4848
  // are reversed.
4779
4849
  markup = ['<fill colors="0% ', color1, ',100% ', color2, '" angle="', angle,
4780
4850
  '" opacity="', opacity2, '" o:opacity2="', opacity1,
4781
4851
  '" type="gradient" focus="100%" method="sigma" />'];
4782
4852
  createElement(this.prepVML(markup), null, null, elem);
4783
-
4853
+
4784
4854
  // Gradients are not supported for VML stroke, return the first color. #722.
4785
4855
  } else {
4786
4856
  return stopColor;
@@ -5175,7 +5245,7 @@ function Chart(userOptions, callback) {
5175
5245
  userOptions.series = null;
5176
5246
  options = merge(defaultOptions, userOptions); // do the merge
5177
5247
  options.series = userOptions.series = seriesOptions; // set back the series data
5178
-
5248
+
5179
5249
  var optionsChart = options.chart,
5180
5250
  optionsMargin = optionsChart.margin,
5181
5251
  margin = isObject(optionsMargin) ?
@@ -5451,10 +5521,10 @@ function Chart(userOptions, callback) {
5451
5521
  getLabelSize: function () {
5452
5522
  var label = this.label;
5453
5523
  return label ?
5454
- ((this.labelBBox = label.getBBox()))[horiz ? 'height' : 'width'] :
5524
+ ((this.labelBBox = label.getBBox(true)))[horiz ? 'height' : 'width'] :
5455
5525
  0;
5456
5526
  },
5457
-
5527
+
5458
5528
  /**
5459
5529
  * Find how far the labels extend to the right and left of the tick's x position. Used for anti-collision
5460
5530
  * detection with overflow logic.
@@ -5464,10 +5534,10 @@ function Chart(userOptions, callback) {
5464
5534
  labelOptions = options.labels,
5465
5535
  width = bBox.width,
5466
5536
  leftSide = width * { left: 0, center: 0.5, right: 1 }[labelOptions.align] - labelOptions.x;
5467
-
5468
- return [-leftSide, width - leftSide];
5537
+
5538
+ return [-leftSide, width - leftSide];
5469
5539
  },
5470
-
5540
+
5471
5541
  /**
5472
5542
  * Handle the label overflow by adjusting the labels to the left and right edge, or
5473
5543
  * hide them if they collide into the neighbour label.
@@ -5478,9 +5548,9 @@ function Chart(userOptions, callback) {
5478
5548
  isLast = this.isLast,
5479
5549
  label = this.label,
5480
5550
  x = label.x;
5481
-
5551
+
5482
5552
  if (isFirst || isLast) {
5483
-
5553
+
5484
5554
  var sides = this.getLabelSides(),
5485
5555
  leftSide = sides[0],
5486
5556
  rightSide = sides[1],
@@ -5488,41 +5558,41 @@ function Chart(userOptions, callback) {
5488
5558
  plotRight = plotLeft + axis.len,
5489
5559
  neighbour = ticks[tickPositions[index + (isFirst ? 1 : -1)]],
5490
5560
  neighbourEdge = neighbour && neighbour.label.x + neighbour.getLabelSides()[isFirst ? 0 : 1];
5491
-
5561
+
5492
5562
  if ((isFirst && !reversed) || (isLast && reversed)) {
5493
5563
  // Is the label spilling out to the left of the plot area?
5494
5564
  if (x + leftSide < plotLeft) {
5495
-
5565
+
5496
5566
  // Align it to plot left
5497
5567
  x = plotLeft - leftSide;
5498
-
5568
+
5499
5569
  // Hide it if it now overlaps the neighbour label
5500
5570
  if (neighbour && x + rightSide > neighbourEdge) {
5501
5571
  show = false;
5502
5572
  }
5503
5573
  }
5504
-
5574
+
5505
5575
  } else {
5506
5576
  // Is the label spilling out to the right of the plot area?
5507
5577
  if (x + rightSide > plotRight) {
5508
-
5578
+
5509
5579
  // Align it to plot right
5510
5580
  x = plotRight - rightSide;
5511
-
5581
+
5512
5582
  // Hide it if it now overlaps the neighbour label
5513
5583
  if (neighbour && x + leftSide < neighbourEdge) {
5514
5584
  show = false;
5515
5585
  }
5516
-
5586
+
5517
5587
  }
5518
5588
  }
5519
-
5589
+
5520
5590
  // Set the modified x position of the label
5521
5591
  label.x = x;
5522
5592
  }
5523
5593
  return show;
5524
5594
  },
5525
-
5595
+
5526
5596
  /**
5527
5597
  * Put everything in place
5528
5598
  *
@@ -5589,7 +5659,7 @@ function Chart(userOptions, callback) {
5589
5659
  // If the parameter 'old' is set, the current call will be followed
5590
5660
  // by another call, therefore do not do any animations this time
5591
5661
  if (!old && gridLine && gridLinePath) {
5592
- gridLine.animate({
5662
+ gridLine[tick.isNew ? 'attr' : 'animate']({
5593
5663
  d: gridLinePath
5594
5664
  });
5595
5665
  }
@@ -5646,7 +5716,7 @@ function Chart(userOptions, callback) {
5646
5716
  if (staggerLines) {
5647
5717
  y += (index / (step || 1) % staggerLines) * 16;
5648
5718
  }
5649
-
5719
+
5650
5720
  // Cache x and y to be able to read final position before animation
5651
5721
  label.x = x;
5652
5722
  label.y = y;
@@ -5655,9 +5725,9 @@ function Chart(userOptions, callback) {
5655
5725
  if ((tick.isFirst && !pick(options.showFirstLabel, 1)) ||
5656
5726
  (tick.isLast && !pick(options.showLastLabel, 1))) {
5657
5727
  show = false;
5658
-
5728
+
5659
5729
  // Handle label overflow and show or hide accordingly
5660
- } else if (!staggerLines && horiz && labelOptions.overflow === 'justify' && !tick.handleOverflow(index)) {
5730
+ } else if (!staggerLines && horiz && labelOptions.overflow === 'justify' && !tick.handleOverflow(index)) {
5661
5731
  show = false;
5662
5732
  }
5663
5733
 
@@ -5666,7 +5736,7 @@ function Chart(userOptions, callback) {
5666
5736
  // show those indices dividable by step
5667
5737
  show = false;
5668
5738
  }
5669
-
5739
+
5670
5740
  // Set the new position, and show or hide
5671
5741
  if (show) {
5672
5742
  label[tick.isNew ? 'attr' : 'animate']({
@@ -5680,9 +5750,9 @@ function Chart(userOptions, callback) {
5680
5750
  }
5681
5751
  }
5682
5752
 
5683
-
5753
+
5684
5754
  },
5685
-
5755
+
5686
5756
  /**
5687
5757
  * Destructor for the tick prototype
5688
5758
  */
@@ -5997,7 +6067,7 @@ function Chart(userOptions, callback) {
5997
6067
  yDataLength,
5998
6068
  activeYData = [],
5999
6069
  activeCounter = 0;
6000
-
6070
+
6001
6071
  // Validate threshold in logarithmic axes
6002
6072
  if (isLog && threshold <= 0) {
6003
6073
  threshold = seriesOptions.threshold = null;
@@ -6148,7 +6218,7 @@ function Chart(userOptions, callback) {
6148
6218
  *
6149
6219
  */
6150
6220
  translate = function (val, backwards, cvsCoord, old, handleLog) {
6151
-
6221
+
6152
6222
  var sign = 1,
6153
6223
  cvsOffset = 0,
6154
6224
  localA = old ? oldTransA : transA,
@@ -6263,28 +6333,28 @@ function Chart(userOptions, callback) {
6263
6333
  }
6264
6334
  return tickPositions;
6265
6335
  }
6266
-
6336
+
6267
6337
  /**
6268
6338
  * Set the tick positions of a logarithmic axis
6269
6339
  */
6270
6340
  function getLogTickPositions(interval, min, max, minor) {
6271
-
6341
+
6272
6342
  // Since we use this method for both major and minor ticks,
6273
6343
  // use a local variable and return the result
6274
- var positions = [];
6275
-
6344
+ var positions = [];
6345
+
6276
6346
  // Reset
6277
6347
  if (!minor) {
6278
6348
  axis._minorAutoInterval = null;
6279
6349
  }
6280
-
6350
+
6281
6351
  // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
6282
6352
  if (interval >= 0.5) {
6283
6353
  interval = mathRound(interval);
6284
6354
  positions = getLinearTickPositions(interval, min, max);
6285
-
6286
- // Second case: We need intermediary ticks. For example
6287
- // 1, 2, 4, 6, 8, 10, 20, 40 etc.
6355
+
6356
+ // Second case: We need intermediary ticks. For example
6357
+ // 1, 2, 4, 6, 8, 10, 20, 40 etc.
6288
6358
  } else if (interval >= 0.08) {
6289
6359
  var roundedMin = mathFloor(min),
6290
6360
  intermediate,
@@ -6294,7 +6364,7 @@ function Chart(userOptions, callback) {
6294
6364
  pos,
6295
6365
  lastPos,
6296
6366
  break2;
6297
-
6367
+
6298
6368
  if (interval > 0.3) {
6299
6369
  intermediate = [1, 2, 4];
6300
6370
  } else if (interval > 0.15) { // 0.2 equals five minor ticks per 1, 10, 100 etc
@@ -6302,23 +6372,23 @@ function Chart(userOptions, callback) {
6302
6372
  } else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
6303
6373
  intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
6304
6374
  }
6305
-
6375
+
6306
6376
  for (i = roundedMin; i < max + 1 && !break2; i++) {
6307
6377
  len = intermediate.length;
6308
6378
  for (j = 0; j < len && !break2; j++) {
6309
6379
  pos = log2lin(lin2log(i) * intermediate[j]);
6310
-
6380
+
6311
6381
  if (pos > min) {
6312
6382
  positions.push(lastPos);
6313
6383
  }
6314
-
6384
+
6315
6385
  if (lastPos > max) {
6316
6386
  break2 = true;
6317
6387
  }
6318
6388
  lastPos = pos;
6319
6389
  }
6320
6390
  }
6321
-
6391
+
6322
6392
  // Third case: We are so deep in between whole logarithmic values that
6323
6393
  // we might as well handle the tick positions like a linear axis. For
6324
6394
  // example 1.01, 1.02, 1.03, 1.04.
@@ -6329,37 +6399,37 @@ function Chart(userOptions, callback) {
6329
6399
  filteredTickIntervalOption = tickIntervalOption === 'auto' ? null : tickIntervalOption,
6330
6400
  tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1),
6331
6401
  totalPixelLength = minor ? axisLength / tickPositions.length : axisLength;
6332
-
6402
+
6333
6403
  interval = pick(
6334
6404
  filteredTickIntervalOption,
6335
6405
  axis._minorAutoInterval,
6336
6406
  (realMax - realMin) * tickPixelIntervalOption / (totalPixelLength || 1)
6337
6407
  );
6338
-
6408
+
6339
6409
  interval = normalizeTickInterval(
6340
- interval,
6341
- null,
6410
+ interval,
6411
+ null,
6342
6412
  math.pow(10, mathFloor(math.log(interval) / math.LN10))
6343
6413
  );
6344
-
6414
+
6345
6415
  positions = map(getLinearTickPositions(
6346
- interval,
6416
+ interval,
6347
6417
  realMin,
6348
- realMax
6418
+ realMax
6349
6419
  ), log2lin);
6350
-
6420
+
6351
6421
  if (!minor) {
6352
6422
  axis._minorAutoInterval = interval / 5;
6353
6423
  }
6354
6424
  }
6355
-
6356
- // Set the axis-level tickInterval variable
6425
+
6426
+ // Set the axis-level tickInterval variable
6357
6427
  if (!minor) {
6358
6428
  tickInterval = interval;
6359
6429
  }
6360
6430
  return positions;
6361
6431
  }
6362
-
6432
+
6363
6433
  /**
6364
6434
  * Return the minor tick positions. For logarithmic axes, reuse the same logic
6365
6435
  * as for major ticks.
@@ -6369,27 +6439,27 @@ function Chart(userOptions, callback) {
6369
6439
  pos,
6370
6440
  i,
6371
6441
  len;
6372
-
6442
+
6373
6443
  if (isLog) {
6374
6444
  len = tickPositions.length;
6375
6445
  for (i = 1; i < len; i++) {
6376
6446
  minorTickPositions = minorTickPositions.concat(
6377
6447
  getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true)
6378
- );
6448
+ );
6379
6449
  }
6380
-
6381
- } else {
6450
+
6451
+ } else {
6382
6452
  for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
6383
- minorTickPositions.push(pos);
6453
+ minorTickPositions.push(pos);
6384
6454
  }
6385
6455
  }
6386
-
6456
+
6387
6457
  return minorTickPositions;
6388
6458
  }
6389
6459
 
6390
6460
  /**
6391
- * Adjust the min and max for the minimum range. Keep in mind that the series data is
6392
- * not yet processed, so we don't have information on data cropping and grouping, or
6461
+ * Adjust the min and max for the minimum range. Keep in mind that the series data is
6462
+ * not yet processed, so we don't have information on data cropping and grouping, or
6393
6463
  * updated axis.pointRange or series.pointRange. The data can't be processed until
6394
6464
  * we have finally established min and max.
6395
6465
  */
@@ -6403,10 +6473,10 @@ function Chart(userOptions, callback) {
6403
6473
  loopLength,
6404
6474
  minArgs,
6405
6475
  maxArgs;
6406
-
6476
+
6407
6477
  // Set the automatic minimum range based on the closest point distance
6408
6478
  if (isXAxis && minRange === UNDEFINED && !isLog) {
6409
-
6479
+
6410
6480
  if (defined(options.min) || defined(options.max)) {
6411
6481
  minRange = null; // don't do this again
6412
6482
 
@@ -6426,8 +6496,14 @@ function Chart(userOptions, callback) {
6426
6496
  });
6427
6497
  minRange = mathMin(closestDataRange * 5, dataMax - dataMin);
6428
6498
  }
6499
+
6500
+ // A hook for resetting the minRange in series.setData (#878)
6501
+ // TODO: remove this in protofy, where xAxis.minRange can be set directly
6502
+ axis.setMinRange = function (newMinRange) {
6503
+ minRange = newMinRange;
6504
+ };
6429
6505
  }
6430
-
6506
+
6431
6507
  // if minRange is exceeded, adjust
6432
6508
  if (max - min < minRange) {
6433
6509
 
@@ -6444,7 +6520,7 @@ function Chart(userOptions, callback) {
6444
6520
  if (spaceAvailable) { // if space is availabe, stay within the data range
6445
6521
  maxArgs[2] = dataMax;
6446
6522
  }
6447
-
6523
+
6448
6524
  max = arrayMin(maxArgs);
6449
6525
 
6450
6526
  // now if the max is adjusted, adjust the min back
@@ -6528,10 +6604,10 @@ function Chart(userOptions, callback) {
6528
6604
  }
6529
6605
 
6530
6606
  // Now we're finished detecting min and max, crop and group series data. This
6531
- // is in turn needed in order to find tick positions in ordinal axes.
6607
+ // is in turn needed in order to find tick positions in ordinal axes.
6532
6608
  if (isXAxis && !secondPass) {
6533
6609
  each(axis.series, function (series) {
6534
- series.processData(min !== oldMin || max !== oldMax);
6610
+ series.processData(min !== oldMin || max !== oldMax);
6535
6611
  });
6536
6612
  }
6537
6613
 
@@ -6542,10 +6618,10 @@ function Chart(userOptions, callback) {
6542
6618
  if (axis.beforeSetTickPositions) {
6543
6619
  axis.beforeSetTickPositions();
6544
6620
  }
6545
-
6621
+
6546
6622
  // hook for extensions, used in Highstock ordinal axes
6547
6623
  if (axis.postProcessTickInterval) {
6548
- tickInterval = axis.postProcessTickInterval(tickInterval);
6624
+ tickInterval = axis.postProcessTickInterval(tickInterval);
6549
6625
  }
6550
6626
 
6551
6627
  // for linear axes, get magnitude and normalize the interval
@@ -6620,7 +6696,6 @@ function Chart(userOptions, callback) {
6620
6696
  * number of ticks in that group
6621
6697
  */
6622
6698
  function adjustTickAmount() {
6623
-
6624
6699
  if (maxTicks && maxTicks[xOrY] && !isDatetimeAxis && !categories && !isLinked && options.alignTicks !== false) { // only apply to linear scale
6625
6700
  var oldTickAmount = tickAmount,
6626
6701
  calculatedTickAmount = tickPositions.length;
@@ -6655,13 +6730,14 @@ function Chart(userOptions, callback) {
6655
6730
  i,
6656
6731
  isDirtyData,
6657
6732
  isDirtyAxisLength;
6658
-
6733
+
6659
6734
  oldMin = min;
6660
6735
  oldMax = max;
6661
6736
  oldAxisLength = axisLength;
6662
6737
 
6663
6738
  // set the new axisLength
6664
- axisLength = horiz ? axisWidth : axisHeight;
6739
+ axis.setAxisSize();
6740
+ //axisLength = horiz ? axisWidth : axisHeight;
6665
6741
  isDirtyAxisLength = axisLength !== oldAxisLength;
6666
6742
 
6667
6743
  // is there new data?
@@ -6709,13 +6785,13 @@ function Chart(userOptions, callback) {
6709
6785
  * @param {Boolean} redraw
6710
6786
  * @param {Boolean|Object} animation Whether to apply animation, and optionally animation
6711
6787
  * configuration
6712
- * @param {Object} eventArguments
6788
+ * @param {Object} eventArguments
6713
6789
  *
6714
6790
  */
6715
6791
  function setExtremes(newMin, newMax, redraw, animation, eventArguments) {
6716
6792
 
6717
6793
  redraw = pick(redraw, true); // defaults to true
6718
-
6794
+
6719
6795
  // Extend the arguments with min and max
6720
6796
  eventArguments = extend(eventArguments, {
6721
6797
  min: newMin,
@@ -6727,17 +6803,17 @@ function Chart(userOptions, callback) {
6727
6803
 
6728
6804
  userMin = newMin;
6729
6805
  userMax = newMax;
6730
-
6806
+
6731
6807
  // Mark for running afterSetExtremes
6732
6808
  axis.isDirtyExtremes = true;
6733
-
6809
+
6734
6810
  // redraw
6735
6811
  if (redraw) {
6736
6812
  chart.redraw(animation);
6737
6813
  }
6738
6814
  });
6739
6815
  }
6740
-
6816
+
6741
6817
  /**
6742
6818
  * Update translation information
6743
6819
  */
@@ -6746,7 +6822,7 @@ function Chart(userOptions, callback) {
6746
6822
  pointRange = 0,
6747
6823
  closestPointRange,
6748
6824
  seriesClosestPointRange;
6749
-
6825
+
6750
6826
  // adjust translation for padding
6751
6827
  if (isXAxis) {
6752
6828
  if (isLinked) {
@@ -6762,7 +6838,7 @@ function Chart(userOptions, callback) {
6762
6838
  }
6763
6839
  });
6764
6840
  }
6765
-
6841
+
6766
6842
  // pointRange means the width reserved for each point, like in a column chart
6767
6843
  axis.pointRange = pointRange;
6768
6844
 
@@ -6794,7 +6870,7 @@ function Chart(userOptions, callback) {
6794
6870
  axisHeight = pick(options.height, plotHeight);
6795
6871
  axisBottom = chartHeight - axisHeight - axisTop;
6796
6872
  axisRight = chartWidth - axisWidth - axisLeft;
6797
- axisLength = horiz ? axisWidth : axisHeight;
6873
+ axisLength = mathMax(horiz ? axisWidth : axisHeight, 0); // mathMax fixes #905
6798
6874
 
6799
6875
  // expose to use in Series object and navigator
6800
6876
  axis.left = axisLeft;
@@ -6824,7 +6900,7 @@ function Chart(userOptions, callback) {
6824
6900
  function getThreshold(threshold) {
6825
6901
  var realMin = isLog ? lin2log(min) : min,
6826
6902
  realMax = isLog ? lin2log(max) : max;
6827
-
6903
+
6828
6904
  if (realMin > threshold || threshold === null) {
6829
6905
  threshold = realMin;
6830
6906
  } else if (realMax < threshold) {
@@ -6995,10 +7071,10 @@ function Chart(userOptions, callback) {
6995
7071
  // Major ticks. Pull out the first item and render it last so that
6996
7072
  // we can get the position of the neighbour label. #808.
6997
7073
  each(tickPositions.slice(1).concat([tickPositions[0]]), function (pos, i) {
6998
-
7074
+
6999
7075
  // Reorganize the indices
7000
7076
  i = (i === tickPositions.length - 1) ? 0 : i + 1;
7001
-
7077
+
7002
7078
  // linked axes need an extra check to find out if
7003
7079
  if (!isLinked || (pos >= min && pos <= max)) {
7004
7080
 
@@ -7149,10 +7225,13 @@ function Chart(userOptions, callback) {
7149
7225
  visibility: VISIBLE,
7150
7226
  zIndex: 6
7151
7227
  })
7152
- .translate(plotLeft, plotTop)
7153
7228
  .add();
7154
7229
  }
7155
7230
 
7231
+ // plotLeft/Top will change when y axis gets wider so we need to translate the
7232
+ // stackTotalGroup at every render call. See bug #506 and #516
7233
+ stackTotalGroup.translate(plotLeft, plotTop);
7234
+
7156
7235
  // Render each stack total
7157
7236
  for (stackKey in stacks) {
7158
7237
  oneStack = stacks[stackKey];
@@ -7178,16 +7257,16 @@ function Chart(userOptions, callback) {
7178
7257
  }
7179
7258
  }
7180
7259
  }
7181
-
7260
+
7182
7261
  /**
7183
7262
  * Update the axis title by options
7184
7263
  */
7185
7264
  function setTitle(newTitleOptions, redraw) {
7186
7265
  options.title = merge(options.title, newTitleOptions);
7187
-
7266
+
7188
7267
  axisTitle = axisTitle.destroy();
7189
7268
  axis.isDirty = true;
7190
-
7269
+
7191
7270
  if (pick(redraw, true)) {
7192
7271
  chart.redraw();
7193
7272
  }
@@ -7200,7 +7279,7 @@ function Chart(userOptions, callback) {
7200
7279
 
7201
7280
  // hide tooltip and hover states
7202
7281
  if (tracker.resetTracker) {
7203
- tracker.resetTracker();
7282
+ tracker.resetTracker(true);
7204
7283
  }
7205
7284
 
7206
7285
  // render the axis
@@ -7355,7 +7434,7 @@ function Chart(userOptions, callback) {
7355
7434
  style.padding = 0;
7356
7435
 
7357
7436
  // create the label
7358
- var label = renderer.label('', 0, 0, null, null, null, options.useHTML)
7437
+ var label = renderer.label('', 0, 0, null, null, null, options.useHTML, null, 'tooltip')
7359
7438
  .attr({
7360
7439
  padding: padding,
7361
7440
  fill: options.backgroundColor,
@@ -7407,10 +7486,10 @@ function Chart(userOptions, callback) {
7407
7486
  s.push((series.tooltipFormatter && series.tooltipFormatter(item)) ||
7408
7487
  item.point.tooltipFormatter(series.tooltipOptions.pointFormat));
7409
7488
  });
7410
-
7489
+
7411
7490
  // footer
7412
7491
  s.push(options.footerFormat || '');
7413
-
7492
+
7414
7493
  return s.join('');
7415
7494
  }
7416
7495
 
@@ -7593,7 +7672,7 @@ function Chart(userOptions, callback) {
7593
7672
  axis = point.series[i ? 'yAxis' : 'xAxis'];
7594
7673
  if (crosshairsOptions[i] && axis) {
7595
7674
  path = axis.getPlotLinePath(
7596
- i ? pick(point.stackY, point.y) : point.x, // #814
7675
+ i ? pick(point.stackY, point.y) : point.x, // #814
7597
7676
  1
7598
7677
  );
7599
7678
  if (crosshairs[i]) {
@@ -7658,8 +7737,6 @@ function Chart(userOptions, callback) {
7658
7737
  */
7659
7738
  function normalizeMouseEvent(e) {
7660
7739
  var ePos,
7661
- chartPosLeft,
7662
- chartPosTop,
7663
7740
  chartX,
7664
7741
  chartY;
7665
7742
 
@@ -7684,16 +7761,14 @@ function Chart(userOptions, callback) {
7684
7761
 
7685
7762
  // get mouse position
7686
7763
  chartPosition = offset(container);
7687
- chartPosLeft = chartPosition.left;
7688
- chartPosTop = chartPosition.top;
7689
7764
 
7690
7765
  // chartX and chartY
7691
- if (isIE) { // IE including IE9 that has pageX but in a different meaning
7766
+ if (ePos.pageX === UNDEFINED) { // IE < 9. #886.
7692
7767
  chartX = e.x;
7693
7768
  chartY = e.y;
7694
7769
  } else {
7695
- chartX = ePos.pageX - chartPosLeft;
7696
- chartY = ePos.pageY - chartPosTop;
7770
+ chartX = ePos.pageX - chartPosition.left;
7771
+ chartY = ePos.pageY - chartPosition.top;
7697
7772
  }
7698
7773
 
7699
7774
  return extend(e, {
@@ -7741,7 +7816,8 @@ function Chart(userOptions, callback) {
7741
7816
  i,
7742
7817
  j,
7743
7818
  distance = chartWidth,
7744
- index = inverted ? e.chartY : e.chartX - plotLeft; // wtf?
7819
+ // the index in the tooltipPoints array, corresponding to pixel position in plot area
7820
+ index = inverted ? plotHeight + plotTop - e.chartY : e.chartX - plotLeft;
7745
7821
 
7746
7822
  // shared tooltip
7747
7823
  if (tooltip && options.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) {
@@ -7794,24 +7870,34 @@ function Chart(userOptions, callback) {
7794
7870
  /**
7795
7871
  * Reset the tracking by hiding the tooltip, the hover series state and the hover point
7796
7872
  */
7797
- function resetTracker() {
7873
+ function resetTracker(allowMove) {
7798
7874
  var hoverSeries = chart.hoverSeries,
7799
- hoverPoint = chart.hoverPoint;
7875
+ hoverPoint = chart.hoverPoint,
7876
+ tooltipPoints = chart.hoverPoints || hoverPoint;
7800
7877
 
7801
- if (hoverPoint) {
7802
- hoverPoint.onMouseOut();
7803
- }
7878
+ // Just move the tooltip, #349
7879
+ if (allowMove && tooltip && tooltipPoints) {
7880
+ tooltip.refresh(tooltipPoints);
7804
7881
 
7805
- if (hoverSeries) {
7806
- hoverSeries.onMouseOut();
7807
- }
7882
+ // Full reset
7883
+ } else {
7808
7884
 
7809
- if (tooltip) {
7810
- tooltip.hide();
7811
- tooltip.hideCrosshairs();
7812
- }
7885
+ if (hoverPoint) {
7886
+ hoverPoint.onMouseOut();
7887
+ }
7888
+
7889
+ if (hoverSeries) {
7890
+ hoverSeries.onMouseOut();
7891
+ }
7892
+
7893
+ if (tooltip) {
7894
+ tooltip.hide();
7895
+ tooltip.hideCrosshairs();
7896
+ }
7813
7897
 
7814
- hoverX = null;
7898
+ hoverX = null;
7899
+
7900
+ }
7815
7901
  }
7816
7902
 
7817
7903
  /**
@@ -7825,7 +7911,8 @@ function Chart(userOptions, callback) {
7825
7911
  },
7826
7912
  selectionBox = selectionMarker.getBBox(),
7827
7913
  selectionLeft = selectionBox.x - plotLeft,
7828
- selectionTop = selectionBox.y - plotTop;
7914
+ selectionTop = selectionBox.y - plotTop,
7915
+ runZoom;
7829
7916
 
7830
7917
 
7831
7918
  // a selection has been made
@@ -7855,23 +7942,30 @@ function Chart(userOptions, callback) {
7855
7942
  0,
7856
7943
  1
7857
7944
  );
7858
-
7859
- selectionData[isXAxis ? 'xAxis' : 'yAxis'].push({
7860
- axis: axis,
7861
- min: mathMin(selectionMin, selectionMax), // for reversed axes,
7862
- max: mathMax(selectionMin, selectionMax)
7863
- });
7945
+ if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859
7946
+ selectionData[isXAxis ? 'xAxis' : 'yAxis'].push({
7947
+ axis: axis,
7948
+ min: mathMin(selectionMin, selectionMax), // for reversed axes,
7949
+ max: mathMax(selectionMin, selectionMax)
7950
+ });
7951
+ runZoom = true;
7952
+ }
7864
7953
  }
7865
7954
  });
7866
- fireEvent(chart, 'selection', selectionData, zoom);
7955
+ if (runZoom) {
7956
+ fireEvent(chart, 'selection', selectionData, zoom);
7957
+ }
7867
7958
 
7868
7959
  }
7869
7960
  selectionMarker = selectionMarker.destroy();
7870
7961
  }
7871
7962
 
7872
- css(container, { cursor: 'auto' });
7963
+ if (chart) { // it may be destroyed on mouse up - #877
7964
+ css(container, { cursor: 'auto' });
7965
+ chart.cancelClick = hasDragged; // #370
7966
+ chart.mouseIsDown = mouseIsDown = hasDragged = false;
7967
+ }
7873
7968
 
7874
- chart.mouseIsDown = mouseIsDown = hasDragged = false;
7875
7969
  removeEvent(doc, hasTouch ? 'touchend' : 'mouseup', drop);
7876
7970
 
7877
7971
  }
@@ -7880,12 +7974,14 @@ function Chart(userOptions, callback) {
7880
7974
  * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea.
7881
7975
  */
7882
7976
  function hideTooltipOnMouseMove(e) {
7883
- var pageX = defined(e.pageX) ? e.pageX : e.page.x, // In mootools the event is wrapped and the page x/y position is named e.page.x
7884
- pageY = defined(e.pageX) ? e.pageY : e.page.y; // Ref: http://mootools.net/docs/core/Types/DOMEvent
7885
7977
 
7978
+ // Get e.pageX and e.pageY back in MooTools
7979
+ washMouseEvent(e);
7980
+
7981
+ // If we're outside, hide the tooltip
7886
7982
  if (chartPosition &&
7887
- !isInsidePlot(pageX - chartPosition.left - plotLeft,
7888
- pageY - chartPosition.top - plotTop)) {
7983
+ !isInsidePlot(e.pageX - chartPosition.left - plotLeft,
7984
+ e.pageY - chartPosition.top - plotTop)) {
7889
7985
  resetTracker();
7890
7986
  }
7891
7987
  }
@@ -7916,6 +8012,7 @@ function Chart(userOptions, callback) {
7916
8012
 
7917
8013
  // record the start position
7918
8014
  chart.mouseIsDown = mouseIsDown = true;
8015
+ chart.cancelClick = false;
7919
8016
  chart.mouseDownX = mouseDownX = e.chartX;
7920
8017
  mouseDownY = e.chartY;
7921
8018
 
@@ -8087,9 +8184,9 @@ function Chart(userOptions, callback) {
8087
8184
  e.cancelBubble = true; // IE specific
8088
8185
 
8089
8186
 
8090
- if (!hasDragged) {
8091
-
8092
- // Detect clicks on trackers or tracker groups, #783
8187
+ if (!chart.cancelClick) {
8188
+
8189
+ // Detect clicks on trackers or tracker groups, #783
8093
8190
  if (hoverPoint && (attr(e.target, 'isTracker') || attr(e.target.parentNode, 'isTracker'))) {
8094
8191
  var plotX = hoverPoint.plotX,
8095
8192
  plotY = hoverPoint.plotY;
@@ -8121,8 +8218,6 @@ function Chart(userOptions, callback) {
8121
8218
 
8122
8219
 
8123
8220
  }
8124
- // reset mouseIsDown and hasDragged
8125
- hasDragged = false;
8126
8221
  };
8127
8222
 
8128
8223
  }
@@ -8141,15 +8236,15 @@ function Chart(userOptions, callback) {
8141
8236
  container.onclick = container.onmousedown = container.onmousemove = container.ontouchstart = container.ontouchend = container.ontouchmove = null;
8142
8237
  }
8143
8238
 
8144
-
8239
+
8145
8240
  // Run MouseTracker
8146
-
8241
+
8147
8242
  if (!trackerGroup) {
8148
8243
  chart.trackerGroup = trackerGroup = renderer.g('tracker')
8149
8244
  .attr({ zIndex: 9 })
8150
8245
  .add();
8151
8246
  }
8152
-
8247
+
8153
8248
  if (options.enabled) {
8154
8249
  chart.tooltip = tooltip = Tooltip(options);
8155
8250
 
@@ -8258,7 +8353,7 @@ function Chart(userOptions, callback) {
8258
8353
  legendSymbol = item.legendSymbol,
8259
8354
  symbolX,
8260
8355
  checkbox = item.checkbox;
8261
-
8356
+
8262
8357
  if (legendItem) {
8263
8358
  legendItem.attr({
8264
8359
  x: ltr ? itemX : legendWidth - itemX,
@@ -8426,11 +8521,11 @@ function Chart(userOptions, callback) {
8426
8521
  //'stroke-width': 0,
8427
8522
  zIndex: 3
8428
8523
  }).add(legendGroup);
8429
-
8524
+
8430
8525
  if (!ltr) {
8431
8526
  symbolX += symbolWidth;
8432
8527
  }
8433
-
8528
+
8434
8529
  } else if (itemOptions && itemOptions.marker && itemOptions.marker.enabled) { // draw the marker
8435
8530
  radius = itemOptions.marker.radius;
8436
8531
  legendSymbol = renderer.symbol(
@@ -8443,14 +8538,14 @@ function Chart(userOptions, callback) {
8443
8538
  .attr(item.pointAttr[NORMAL_STATE])
8444
8539
  .attr({ zIndex: 3 })
8445
8540
  .add(legendGroup);
8446
-
8541
+
8447
8542
  if (!ltr) {
8448
8543
  symbolX += symbolWidth / 2;
8449
8544
  }
8450
8545
 
8451
8546
  }
8452
8547
  if (legendSymbol) {
8453
-
8548
+
8454
8549
  legendSymbol.xOff = symbolX + (strokeWidth % 2 / 2);
8455
8550
  legendSymbol.yOff = symbolY + (strokeWidth % 2 / 2);
8456
8551
  }
@@ -8496,7 +8591,7 @@ function Chart(userOptions, callback) {
8496
8591
  itemX = initialItemX;
8497
8592
  itemY += itemMarginTop + itemHeight + itemMarginBottom;
8498
8593
  }
8499
-
8594
+
8500
8595
  // If the item exceeds the height, start a new column
8501
8596
  if (!horizontal && itemY + options.y + itemHeight > chartHeight - spacingTop - spacingBottom) {
8502
8597
  itemY = initialItemY;
@@ -8507,7 +8602,7 @@ function Chart(userOptions, callback) {
8507
8602
  // Set the edge positions
8508
8603
  maxItemWidth = mathMax(maxItemWidth, itemWidth);
8509
8604
  lastItemY = mathMax(lastItemY, itemY + itemMarginBottom);
8510
-
8605
+
8511
8606
  // cache the position of the newly generated or reordered items
8512
8607
  item._legendItemPos = [itemX, itemY];
8513
8608
 
@@ -8539,10 +8634,10 @@ function Chart(userOptions, callback) {
8539
8634
 
8540
8635
  if (!legendGroup) {
8541
8636
  legendGroup = renderer.g('legend')
8542
- // #414, #759. Trackers will be drawn above the legend, but we have
8637
+ // #414, #759. Trackers will be drawn above the legend, but we have
8543
8638
  // to sacrifice that because tooltips need to be above the legend
8544
8639
  // and trackers above tooltips
8545
- .attr({ zIndex: 7 })
8640
+ .attr({ zIndex: 7 })
8546
8641
  .add();
8547
8642
  }
8548
8643
 
@@ -8615,8 +8710,8 @@ function Chart(userOptions, callback) {
8615
8710
  // hide the border if no items
8616
8711
  box[allItems.length ? 'show' : 'hide']();
8617
8712
  }
8618
-
8619
- // Now that the legend width and height are extablished, put the items in the
8713
+
8714
+ // Now that the legend width and height are extablished, put the items in the
8620
8715
  // final position
8621
8716
  each(allItems, positionItem);
8622
8717
 
@@ -8825,14 +8920,14 @@ function Chart(userOptions, callback) {
8825
8920
 
8826
8921
  // redraw axes
8827
8922
  each(axes, function (axis) {
8828
-
8923
+
8829
8924
  // Fire 'afterSetExtremes' only if extremes are set
8830
8925
  if (axis.isDirtyExtremes) { // #821
8831
8926
  axis.isDirtyExtremes = false;
8832
8927
  fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751
8833
8928
  }
8834
-
8835
- if (axis.isDirty || isDirtyBox) {
8929
+
8930
+ if (axis.isDirty || isDirtyBox || hasStackedSeries) {
8836
8931
  axis.redraw();
8837
8932
  isDirtyBox = true; // #792
8838
8933
  }
@@ -8866,9 +8961,9 @@ function Chart(userOptions, callback) {
8866
8961
  });
8867
8962
 
8868
8963
 
8869
- // hide tooltip and hover states
8964
+ // move tooltip or reset
8870
8965
  if (tracker && tracker.resetTracker) {
8871
- tracker.resetTracker();
8966
+ tracker.resetTracker(true);
8872
8967
  }
8873
8968
 
8874
8969
  // redraw if canvas
@@ -9098,7 +9193,7 @@ function Chart(userOptions, callback) {
9098
9193
 
9099
9194
  // Redraw
9100
9195
  if (hasZoomed) {
9101
- redraw(
9196
+ redraw(
9102
9197
  pick(optionsChart.animation, chart.pointCount < 100) // animation
9103
9198
  );
9104
9199
  }
@@ -9204,7 +9299,7 @@ function Chart(userOptions, callback) {
9204
9299
  if (isString(renderTo)) {
9205
9300
  renderTo = doc.getElementById(renderTo);
9206
9301
  }
9207
-
9302
+
9208
9303
  // Display an error if the renderTo is wrong
9209
9304
  if (!renderTo) {
9210
9305
  error(13, true);
@@ -9213,7 +9308,7 @@ function Chart(userOptions, callback) {
9213
9308
  // remove previous chart
9214
9309
  renderTo.innerHTML = '';
9215
9310
 
9216
- // If the container doesn't have an offsetWidth, it has or is a child of a node
9311
+ // If the container doesn't have an offsetWidth, it ha s or is a child of a node
9217
9312
  // that has display:none. We need to temporarily move it out to a visible
9218
9313
  // state to determine the size, else the legend and tooltips won't render
9219
9314
  // properly
@@ -9222,7 +9317,7 @@ function Chart(userOptions, callback) {
9222
9317
  css(renderToClone, {
9223
9318
  position: ABSOLUTE,
9224
9319
  top: '-9999px',
9225
- display: ''
9320
+ display: 'block'
9226
9321
  });
9227
9322
  doc.body.appendChild(renderToClone);
9228
9323
  }
@@ -9257,35 +9352,6 @@ function Chart(userOptions, callback) {
9257
9352
  // to get the tracker for translating mouse events
9258
9353
  renderer.create(chart, container, chartWidth, chartHeight);
9259
9354
  }
9260
-
9261
- // Issue 110 workaround:
9262
- // In Firefox, if a div is positioned by percentage, its pixel position may land
9263
- // between pixels. The container itself doesn't display this, but an SVG element
9264
- // inside this container will be drawn at subpixel precision. In order to draw
9265
- // sharp lines, this must be compensated for. This doesn't seem to work inside
9266
- // iframes though (like in jsFiddle).
9267
- var subPixelFix, rect;
9268
- if (isFirefox && container.getBoundingClientRect) {
9269
- subPixelFix = function () {
9270
- css(container, { left: 0, top: 0 });
9271
- rect = container.getBoundingClientRect();
9272
- css(container, {
9273
- left: (-(rect.left - pInt(rect.left))) + PX,
9274
- top: (-(rect.top - pInt(rect.top))) + PX
9275
- });
9276
- };
9277
-
9278
- // run the fix now
9279
- subPixelFix();
9280
-
9281
- // run it on resize
9282
- addEvent(win, 'resize', subPixelFix);
9283
-
9284
- // remove it on chart destroy
9285
- addEvent(chart, 'destroy', function () {
9286
- removeEvent(win, 'resize', subPixelFix);
9287
- });
9288
- }
9289
9355
  }
9290
9356
 
9291
9357
  /**
@@ -9391,11 +9457,11 @@ function Chart(userOptions, callback) {
9391
9457
  var width = optionsChart.width || renderTo.offsetWidth,
9392
9458
  height = optionsChart.height || renderTo.offsetHeight,
9393
9459
  target = e ? e.target : win; // #805 - MooTools doesn't supply e
9394
-
9460
+
9395
9461
  // Width and height checks for display:none. Target is doc in IE8 and Opera,
9396
9462
  // win in Firefox, Chrome and IE9.
9397
9463
  if (width && height && (target === win || target === doc)) {
9398
-
9464
+
9399
9465
  if (width !== containerWidth || height !== containerHeight) {
9400
9466
  clearTimeout(reflowTimeout);
9401
9467
  reflowTimeout = setTimeout(function () {
@@ -9663,6 +9729,8 @@ function Chart(userOptions, callback) {
9663
9729
  axis.setScale();
9664
9730
  });
9665
9731
  getMargins();
9732
+
9733
+ maxTicks = null; // reset for second pass
9666
9734
  each(axes, function (axis) {
9667
9735
  axis.setTickPositions(true); // update to reflect the new margins
9668
9736
  });
@@ -9893,8 +9961,8 @@ function Chart(userOptions, callback) {
9893
9961
  each(chart.callbacks, function (fn) {
9894
9962
  fn.apply(chart, [chart]);
9895
9963
  });
9896
-
9897
-
9964
+
9965
+
9898
9966
  // If the chart was rendered outside the top container, put it back in
9899
9967
  if (renderToClone) {
9900
9968
  renderTo.appendChild(container);
@@ -10050,8 +10118,8 @@ Point.prototype = {
10050
10118
  // copy options directly to point
10051
10119
  extend(point, options);
10052
10120
  point.options = options;
10053
-
10054
- // This is the fastest way to detect if there are individual point dataLabels that need
10121
+
10122
+ // This is the fastest way to detect if there are individual point dataLabels that need
10055
10123
  // to be considered in drawDataLabels. These can only occur in object configs.
10056
10124
  if (options.dataLabels) {
10057
10125
  series._hasPointLabels = true;
@@ -10060,7 +10128,7 @@ Point.prototype = {
10060
10128
  point.name = options[0];
10061
10129
  point.y = options[1];
10062
10130
  }
10063
-
10131
+
10064
10132
  /*
10065
10133
  * If no x is set by now, get auto incremented value. All points must have an
10066
10134
  * x value, however the y value can be null to create a gap in the series
@@ -10069,8 +10137,8 @@ Point.prototype = {
10069
10137
  if (point.x === UNDEFINED) {
10070
10138
  point.x = x === UNDEFINED ? series.autoIncrement() : x;
10071
10139
  }
10072
-
10073
-
10140
+
10141
+
10074
10142
 
10075
10143
  },
10076
10144
 
@@ -10080,19 +10148,23 @@ Point.prototype = {
10080
10148
  destroy: function () {
10081
10149
  var point = this,
10082
10150
  series = point.series,
10083
- hoverPoints = series.chart.hoverPoints,
10151
+ chart = series.chart,
10152
+ hoverPoints = chart.hoverPoints,
10084
10153
  prop;
10085
10154
 
10086
- series.chart.pointCount--;
10155
+ chart.pointCount--;
10087
10156
 
10088
10157
  if (hoverPoints) {
10089
10158
  point.setState();
10090
10159
  erase(hoverPoints, point);
10160
+ if (!hoverPoints.length) {
10161
+ chart.hoverPoints = null;
10162
+ }
10163
+
10091
10164
  }
10092
- if (point === series.chart.hoverPoint) {
10165
+ if (point === chart.hoverPoint) {
10093
10166
  point.onMouseOut();
10094
10167
  }
10095
- series.chart.hoverPoints = null;
10096
10168
 
10097
10169
  // remove all events
10098
10170
  if (point.graphic || point.dataLabel) { // removeEvent and destroyElements are performance expensive
@@ -10101,7 +10173,7 @@ Point.prototype = {
10101
10173
  }
10102
10174
 
10103
10175
  if (point.legendItem) { // pies have legend items
10104
- point.series.chart.legend.destroyItem(point);
10176
+ chart.legend.destroyItem(point);
10105
10177
  }
10106
10178
 
10107
10179
  for (prop in point) {
@@ -10231,28 +10303,28 @@ Point.prototype = {
10231
10303
  for (i in match) {
10232
10304
  key = match[i];
10233
10305
  if (isString(key) && key !== pointFormat) { // IE matches more than just the variables
10234
-
10306
+
10235
10307
  // Split it further into parts
10236
10308
  parts = (' ' + key).split(splitter); // add empty string because IE and the rest handles it differently
10237
10309
  obj = { 'point': point, 'series': series }[parts[1]];
10238
10310
  prop = parts[2];
10239
-
10311
+
10240
10312
  // Add some preformatting
10241
- if (obj === point && (prop === 'y' || prop === 'open' || prop === 'high' ||
10242
- prop === 'low' || prop === 'close')) {
10243
- replacement = (seriesTooltipOptions.valuePrefix || seriesTooltipOptions.yPrefix || '') +
10313
+ if (obj === point && (prop === 'y' || prop === 'open' || prop === 'high' ||
10314
+ prop === 'low' || prop === 'close')) {
10315
+ replacement = (seriesTooltipOptions.valuePrefix || seriesTooltipOptions.yPrefix || '') +
10244
10316
  numberFormat(point[prop], pick(seriesTooltipOptions.valueDecimals, seriesTooltipOptions.yDecimals, originalDecimals)) +
10245
10317
  (seriesTooltipOptions.valueSuffix || seriesTooltipOptions.ySuffix || '');
10246
-
10318
+
10247
10319
  // Automatic replacement
10248
10320
  } else {
10249
10321
  replacement = obj[prop];
10250
10322
  }
10251
-
10323
+
10252
10324
  pointFormat = pointFormat.replace(key, replacement);
10253
10325
  }
10254
10326
  }
10255
-
10327
+
10256
10328
  return pointFormat;
10257
10329
  },
10258
10330
 
@@ -10525,7 +10597,7 @@ Series.prototype = {
10525
10597
 
10526
10598
  series.chart = chart;
10527
10599
  series.options = options = series.setOptions(options); // merge with plotOptions
10528
-
10600
+
10529
10601
  // bind the axes
10530
10602
  series.bindAxes();
10531
10603
 
@@ -10538,7 +10610,7 @@ Series.prototype = {
10538
10610
  visible: options.visible !== false, // true by default
10539
10611
  selected: options.selected === true // false by default
10540
10612
  });
10541
-
10613
+
10542
10614
  // special
10543
10615
  if (useCanVG) {
10544
10616
  options.animation = false;
@@ -10564,9 +10636,9 @@ Series.prototype = {
10564
10636
  series.setData(options.data, false);
10565
10637
 
10566
10638
  },
10567
-
10568
-
10569
-
10639
+
10640
+
10641
+
10570
10642
  /**
10571
10643
  * Set the xAxis and yAxis properties of cartesian series, and register the series
10572
10644
  * in the axis.series array
@@ -10576,31 +10648,31 @@ Series.prototype = {
10576
10648
  seriesOptions = series.options,
10577
10649
  chart = series.chart,
10578
10650
  axisOptions;
10579
-
10651
+
10580
10652
  if (series.isCartesian) {
10581
-
10653
+
10582
10654
  each(['xAxis', 'yAxis'], function (AXIS) { // repeat for xAxis and yAxis
10583
-
10655
+
10584
10656
  each(chart[AXIS], function (axis) { // loop through the chart's axis objects
10585
-
10657
+
10586
10658
  axisOptions = axis.options;
10587
-
10588
- // apply if the series xAxis or yAxis option mathches the number of the
10659
+
10660
+ // apply if the series xAxis or yAxis option mathches the number of the
10589
10661
  // axis, or if undefined, use the first axis
10590
10662
  if ((seriesOptions[AXIS] === axisOptions.index) ||
10591
10663
  (seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) {
10592
-
10664
+
10593
10665
  // register this series in the axis.series lookup
10594
10666
  axis.series.push(series);
10595
-
10667
+
10596
10668
  // set this series.xAxis or series.yAxis reference
10597
10669
  series[AXIS] = axis;
10598
-
10670
+
10599
10671
  // mark dirty for redraw
10600
10672
  axis.isDirty = true;
10601
10673
  }
10602
10674
  });
10603
-
10675
+
10604
10676
  });
10605
10677
  }
10606
10678
  },
@@ -10635,7 +10707,7 @@ Series.prototype = {
10635
10707
  pointsLength = points.length;
10636
10708
 
10637
10709
  if (pointsLength) { // no action required for []
10638
-
10710
+
10639
10711
  // if connect nulls, just remove null points
10640
10712
  if (series.options.connectNulls) {
10641
10713
  i = pointsLength;
@@ -10647,7 +10719,7 @@ Series.prototype = {
10647
10719
  if (points.length) {
10648
10720
  segments = [points];
10649
10721
  }
10650
-
10722
+
10651
10723
  // else, split on null points
10652
10724
  } else {
10653
10725
  each(points, function (point, i) {
@@ -10662,7 +10734,7 @@ Series.prototype = {
10662
10734
  });
10663
10735
  }
10664
10736
  }
10665
-
10737
+
10666
10738
  // register it
10667
10739
  series.segments = segments;
10668
10740
  },
@@ -10685,13 +10757,13 @@ Series.prototype = {
10685
10757
  plotOptions.series,
10686
10758
  itemOptions
10687
10759
  );
10688
-
10760
+
10689
10761
  // Re-insert the data array to the options and the original config (#717)
10690
10762
  options.data = itemOptions.data = data;
10691
-
10763
+
10692
10764
  // the tooltip options are merged between global and series specific options
10693
10765
  series.tooltipOptions = merge(chartOptions.tooltip, options.tooltip);
10694
-
10766
+
10695
10767
  return options;
10696
10768
 
10697
10769
  },
@@ -10714,7 +10786,7 @@ Series.prototype = {
10714
10786
  defaultSymbols = chart.options.symbols,
10715
10787
  counters = chart.counters;
10716
10788
  series.symbol = seriesMarkerOption.symbol || defaultSymbols[counters.symbol++];
10717
-
10789
+
10718
10790
  // don't substract radius in image symbols (#604)
10719
10791
  if (/^url/.test(series.symbol)) {
10720
10792
  seriesMarkerOption.radius = 0;
@@ -10747,7 +10819,7 @@ Series.prototype = {
10747
10819
  setAnimation(animation, chart);
10748
10820
 
10749
10821
  // Make graph animate sideways
10750
- if (graph && shift) {
10822
+ if (graph && shift) {
10751
10823
  graph.shift = currentShift + 1;
10752
10824
  }
10753
10825
  if (area) {
@@ -10756,7 +10828,7 @@ Series.prototype = {
10756
10828
  }
10757
10829
  area.isArea = true; // needed in animation, both with and without shift
10758
10830
  }
10759
-
10831
+
10760
10832
  // Optional redraw, defaults to true
10761
10833
  redraw = pick(redraw, true);
10762
10834
 
@@ -10772,7 +10844,7 @@ Series.prototype = {
10772
10844
  // Shift the first point off the parallel arrays
10773
10845
  // todo: consider series.removePoint(i) method
10774
10846
  if (shift) {
10775
- if (data[0]) {
10847
+ if (data[0] && data[0].remove) {
10776
10848
  data[0].remove(false);
10777
10849
  } else {
10778
10850
  data.shift();
@@ -10803,16 +10875,17 @@ Series.prototype = {
10803
10875
  initialColor = series.initialColor,
10804
10876
  chart = series.chart,
10805
10877
  firstPoint = null,
10878
+ xAxis = series.xAxis,
10806
10879
  i;
10807
10880
 
10808
10881
  // reset properties
10809
10882
  series.xIncrement = null;
10810
- series.pointRange = (series.xAxis && series.xAxis.categories && 1) || options.pointRange;
10811
-
10883
+ series.pointRange = (xAxis && xAxis.categories && 1) || options.pointRange;
10884
+
10812
10885
  if (defined(initialColor)) { // reset colors for pie
10813
10886
  chart.counters.color = initialColor;
10814
10887
  }
10815
-
10888
+
10816
10889
  // parallel arrays
10817
10890
  var xData = [],
10818
10891
  yData = [],
@@ -10826,15 +10899,15 @@ Series.prototype = {
10826
10899
  // way. Although the 'for' loops are similar, they are repeated inside each
10827
10900
  // if-else conditional for max performance.
10828
10901
  if (dataLength > turboThreshold) {
10829
-
10902
+
10830
10903
  // find the first non-null point
10831
10904
  i = 0;
10832
10905
  while (firstPoint === null && i < dataLength) {
10833
10906
  firstPoint = data[i];
10834
10907
  i++;
10835
10908
  }
10836
-
10837
-
10909
+
10910
+
10838
10911
  if (isNumber(firstPoint)) { // assume all points are numbers
10839
10912
  var x = pick(options.pointStart, 0),
10840
10913
  pointInterval = pick(options.pointInterval, 1);
@@ -10884,6 +10957,13 @@ Series.prototype = {
10884
10957
  }
10885
10958
  }
10886
10959
 
10960
+ // reset minRange (#878)
10961
+ // TODO: In protofy, run this code instead:
10962
+ // if (xAxis) xAxis.minRange = UNDEFINED;
10963
+ if (xAxis && xAxis.setMinRange) {
10964
+ xAxis.setMinRange(); // to undefined
10965
+ }
10966
+
10887
10967
  // redraw
10888
10968
  series.isDirty = series.isDirtyData = chart.isDirtyBox = true;
10889
10969
  if (pick(redraw, true)) {
@@ -10948,7 +11028,7 @@ Series.prototype = {
10948
11028
  isCartesian = series.isCartesian;
10949
11029
 
10950
11030
  // If the series data or axes haven't changed, don't go through this. Return false to pass
10951
- // the message on to override methods like in data grouping.
11031
+ // the message on to override methods like in data grouping.
10952
11032
  if (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) {
10953
11033
  return false;
10954
11034
  }
@@ -10963,7 +11043,7 @@ Series.prototype = {
10963
11043
  if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
10964
11044
  processedXData = [];
10965
11045
  processedYData = [];
10966
-
11046
+
10967
11047
  // only crop if it's actually spilling out
10968
11048
  } else if (processedXData[0] < min || processedXData[dataLength - 1] > max) {
10969
11049
 
@@ -10980,15 +11060,15 @@ Series.prototype = {
10980
11060
  cropEnd = i + 1;
10981
11061
  break;
10982
11062
  }
10983
-
11063
+
10984
11064
  }
10985
11065
  processedXData = processedXData.slice(cropStart, cropEnd);
10986
11066
  processedYData = processedYData.slice(cropStart, cropEnd);
10987
11067
  cropped = true;
10988
11068
  }
10989
11069
  }
10990
-
10991
-
11070
+
11071
+
10992
11072
  // Find the closest distance between processed points
10993
11073
  for (i = processedXData.length - 1; i > 0; i--) {
10994
11074
  distance = processedXData[i] - processedXData[i - 1];
@@ -10996,18 +11076,18 @@ Series.prototype = {
10996
11076
  closestPointRange = distance;
10997
11077
  }
10998
11078
  }
10999
-
11079
+
11000
11080
  // Record the properties
11001
11081
  series.cropped = cropped; // undefined or true
11002
11082
  series.cropStart = cropStart;
11003
11083
  series.processedXData = processedXData;
11004
11084
  series.processedYData = processedYData;
11005
-
11085
+
11006
11086
  if (options.pointRange === null) { // null means auto, as for columns, candlesticks and OHLC
11007
11087
  series.pointRange = closestPointRange || 1;
11008
11088
  }
11009
11089
  series.closestPointRange = closestPointRange;
11010
-
11090
+
11011
11091
  },
11012
11092
 
11013
11093
  /**
@@ -11053,7 +11133,7 @@ Series.prototype = {
11053
11133
  }
11054
11134
 
11055
11135
  // Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when
11056
- // swithching view from non-grouped data to grouped data (#637)
11136
+ // swithching view from non-grouped data to grouped data (#637)
11057
11137
  if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) {
11058
11138
  for (i = 0; i < dataLength; i++) {
11059
11139
  if (i === cropStart && !hasGroupedData) { // when has grouped data, clear all points
@@ -11091,7 +11171,7 @@ Series.prototype = {
11091
11171
  isLastSeries,
11092
11172
  allStackSeries = yAxis.series,
11093
11173
  i = allStackSeries.length;
11094
-
11174
+
11095
11175
  // Is it the last visible series?
11096
11176
  while (i--) {
11097
11177
  if (allStackSeries[i].visible) {
@@ -11101,7 +11181,7 @@ Series.prototype = {
11101
11181
  break;
11102
11182
  }
11103
11183
  }
11104
-
11184
+
11105
11185
  // Translate each point
11106
11186
  for (i = 0; i < dataLength; i++) {
11107
11187
  var point = points[i],
@@ -11111,7 +11191,7 @@ Series.prototype = {
11111
11191
  stack = yAxis.stacks[(yValue < options.threshold ? '-' : '') + series.stackKey],
11112
11192
  pointStack,
11113
11193
  pointStackTotal;
11114
-
11194
+
11115
11195
  // get the plotX translation
11116
11196
  point.plotX = mathRound(xAxis.translate(xValue, 0, 0, 0, 1) * 10) / 10; // Math.round fixes #591
11117
11197
 
@@ -11121,11 +11201,11 @@ Series.prototype = {
11121
11201
  pointStackTotal = pointStack.total;
11122
11202
  pointStack.cum = yBottom = pointStack.cum - yValue; // start from top
11123
11203
  yValue = yBottom + yValue;
11124
-
11204
+
11125
11205
  if (isLastSeries) {
11126
11206
  yBottom = options.threshold;
11127
11207
  }
11128
-
11208
+
11129
11209
  if (stacking === 'percent') {
11130
11210
  yBottom = pointStackTotal ? yBottom * 100 / pointStackTotal : 0;
11131
11211
  yValue = pointStackTotal ? yValue * 100 / pointStackTotal : 0;
@@ -11137,17 +11217,17 @@ Series.prototype = {
11137
11217
  }
11138
11218
 
11139
11219
  // Set translated yBottom or remove it
11140
- point.yBottom = defined(yBottom) ?
11220
+ point.yBottom = defined(yBottom) ?
11141
11221
  yAxis.translate(yBottom, 0, 1, 0, 1) :
11142
11222
  null;
11143
-
11223
+
11144
11224
  // general hook, used for Highstock compare mode
11145
11225
  if (hasModifyValue) {
11146
11226
  yValue = series.modifyValue(yValue, point);
11147
11227
  }
11148
11228
 
11149
11229
  // Set the the plotY value, reset it for redraws
11150
- point.plotY = (typeof yValue === 'number') ?
11230
+ point.plotY = (typeof yValue === 'number') ?
11151
11231
  mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10 : // Math.round fixes #591
11152
11232
  UNDEFINED;
11153
11233
 
@@ -11172,10 +11252,9 @@ Series.prototype = {
11172
11252
  setTooltipPoints: function (renew) {
11173
11253
  var series = this,
11174
11254
  chart = series.chart,
11175
- inverted = chart.inverted,
11176
11255
  points = [],
11177
11256
  pointsLength,
11178
- plotSize = mathRound((inverted ? chart.plotTop : chart.plotLeft) + chart.plotSizeX),
11257
+ plotSize = chart.plotSizeX,
11179
11258
  low,
11180
11259
  high,
11181
11260
  xAxis = series.xAxis,
@@ -11201,10 +11280,10 @@ Series.prototype = {
11201
11280
  // loop the concatenated points and apply each point to all the closest
11202
11281
  // pixel positions
11203
11282
  if (xAxis && xAxis.reversed) {
11204
- points = points.reverse();//reverseArray(points);
11283
+ points = points.reverse();
11205
11284
  }
11206
11285
 
11207
- //each(points, function (point, i) {
11286
+ // Assign each pixel position to the nearest point
11208
11287
  pointsLength = points.length;
11209
11288
  for (i = 0; i < pointsLength; i++) {
11210
11289
  point = points[i];
@@ -11214,7 +11293,7 @@ Series.prototype = {
11214
11293
  plotSize;
11215
11294
 
11216
11295
  while (low <= high) {
11217
- tooltipPoints[inverted ? plotSize - low++ : low++] = point;
11296
+ tooltipPoints[low++] = point;
11218
11297
  }
11219
11298
  }
11220
11299
  series.tooltipPoints = tooltipPoints;
@@ -11229,7 +11308,7 @@ Series.prototype = {
11229
11308
  xDateFormat = tooltipOptions.xDateFormat || '%A, %b %e, %Y',
11230
11309
  xAxis = series.xAxis,
11231
11310
  isDateTime = xAxis && xAxis.options.type === 'datetime';
11232
-
11311
+
11233
11312
  return tooltipOptions.headerFormat
11234
11313
  .replace('{point.key}', isDateTime ? dateFormat(xDateFormat, key) : key)
11235
11314
  .replace('{series.name}', series.name)
@@ -11556,7 +11635,7 @@ Series.prototype = {
11556
11635
 
11557
11636
  // remove all events
11558
11637
  removeEvent(series);
11559
-
11638
+
11560
11639
  // erase from axes
11561
11640
  each(['xAxis', 'yAxis'], function (AXIS) {
11562
11641
  axis = series[AXIS];
@@ -11616,11 +11695,11 @@ Series.prototype = {
11616
11695
  * Draw the data labels
11617
11696
  */
11618
11697
  drawDataLabels: function () {
11619
-
11698
+
11620
11699
  var series = this,
11621
11700
  seriesOptions = series.options,
11622
11701
  options = seriesOptions.dataLabels;
11623
-
11702
+
11624
11703
  if (options.enabled || series._hasPointLabels) {
11625
11704
  var x,
11626
11705
  y,
@@ -11649,8 +11728,8 @@ Series.prototype = {
11649
11728
 
11650
11729
  if (isBarLike) {
11651
11730
  var defaultYs = {
11652
- top: fontBaseline,
11653
- middle: fontBaseline - fontLineHeight / 2,
11731
+ top: fontBaseline,
11732
+ middle: fontBaseline - fontLineHeight / 2,
11654
11733
  bottom: -fontLineHeight + fontBaseline
11655
11734
  };
11656
11735
  if (stacking) {
@@ -11667,12 +11746,12 @@ Series.prototype = {
11667
11746
  // In non stacked series the default label placement is on top of the bars
11668
11747
  if (vAlignIsNull) {
11669
11748
  options = merge(options, {verticalAlign: 'top'});
11670
-
11749
+
11671
11750
  // If no y delta is specified, try to create a good default (like default bar)
11672
11751
  } else if (yIsNull) {
11673
11752
  options = merge(options, { y: defaultYs[options.verticalAlign]});
11674
11753
  }
11675
-
11754
+
11676
11755
  }
11677
11756
  }
11678
11757
 
@@ -11690,13 +11769,13 @@ Series.prototype = {
11690
11769
  } else {
11691
11770
  dataLabelsGroup.translate(groupLeft, groupTop);
11692
11771
  }
11693
-
11772
+
11694
11773
  // make the labels for each point
11695
11774
  generalOptions = options;
11696
11775
  each(points, function (point) {
11697
-
11776
+
11698
11777
  dataLabel = point.dataLabel;
11699
-
11778
+
11700
11779
  // Merge in individual options from point
11701
11780
  options = generalOptions; // reset changes from previous points
11702
11781
  pointOptions = point.options;
@@ -11704,52 +11783,52 @@ Series.prototype = {
11704
11783
  options = merge(options, pointOptions.dataLabels);
11705
11784
  }
11706
11785
  enabled = options.enabled;
11707
-
11786
+
11708
11787
  // Get the positions
11709
11788
  if (enabled) {
11710
11789
  var plotX = (point.barX && point.barX + point.barW / 2) || pick(point.plotX, -999),
11711
11790
  plotY = pick(point.plotY, -999),
11712
-
11791
+
11713
11792
  // if options.y is null, which happens by default on column charts, set the position
11714
11793
  // above or below the column depending on the threshold
11715
- individualYDelta = options.y === null ?
11716
- (point.y >= seriesOptions.threshold ?
11717
- -fontLineHeight + fontBaseline : // below the threshold
11794
+ individualYDelta = options.y === null ?
11795
+ (point.y >= seriesOptions.threshold ?
11796
+ -fontLineHeight + fontBaseline : // below the threshold
11718
11797
  fontBaseline) : // above the threshold
11719
11798
  options.y;
11720
-
11799
+
11721
11800
  x = (inverted ? chart.plotWidth - plotY : plotX) + options.x;
11722
11801
  y = mathRound((inverted ? chart.plotHeight - plotX : plotY) + individualYDelta);
11723
-
11802
+
11724
11803
  }
11725
-
11804
+
11726
11805
  // If the point is outside the plot area, destroy it. #678, #820
11727
11806
  if (dataLabel && series.isCartesian && (!chart.isInsidePlot(x, y) || !enabled)) {
11728
11807
  point.dataLabel = dataLabel.destroy();
11729
-
11730
- // Individual labels are disabled if the are explicitly disabled
11808
+
11809
+ // Individual labels are disabled if the are explicitly disabled
11731
11810
  // in the point options, or if they fall outside the plot area.
11732
11811
  } else if (enabled) {
11733
-
11812
+
11734
11813
  var align = options.align;
11735
-
11814
+
11736
11815
  // Get the string
11737
11816
  str = options.formatter.call(point.getLabelConfig(), options);
11738
-
11817
+
11739
11818
  // in columns, align the string to the column
11740
11819
  if (seriesType === 'column') {
11741
11820
  x += { left: -1, right: 1 }[align] * point.barW / 2 || 0;
11742
11821
  }
11743
-
11822
+
11744
11823
  if (!stacking && inverted && point.y < 0) {
11745
11824
  align = 'right';
11746
11825
  x -= 10;
11747
11826
  }
11748
-
11827
+
11749
11828
  // Determine the color
11750
11829
  options.style.color = pick(options.color, options.style.color, series.color, 'black');
11751
-
11752
-
11830
+
11831
+
11753
11832
  // update existing label
11754
11833
  if (dataLabel) {
11755
11834
  // vertically centered
@@ -11777,7 +11856,7 @@ Series.prototype = {
11777
11856
  fill: options.backgroundColor,
11778
11857
  stroke: options.borderColor,
11779
11858
  'stroke-width': options.borderWidth,
11780
- r: options.borderRadius,
11859
+ r: options.borderRadius || 0,
11781
11860
  rotation: options.rotation,
11782
11861
  padding: options.padding,
11783
11862
  zIndex: 1
@@ -11786,13 +11865,13 @@ Series.prototype = {
11786
11865
  .add(dataLabelsGroup)
11787
11866
  .shadow(options.shadow);
11788
11867
  }
11789
-
11868
+
11790
11869
  if (isBarLike && seriesOptions.stacking && dataLabel) {
11791
11870
  var barX = point.barX,
11792
11871
  barY = point.barY,
11793
11872
  barW = point.barW,
11794
11873
  barH = point.barH;
11795
-
11874
+
11796
11875
  dataLabel.align(options, null,
11797
11876
  {
11798
11877
  x: inverted ? chart.plotWidth - barY - barH : barX,
@@ -11801,8 +11880,8 @@ Series.prototype = {
11801
11880
  height: inverted ? barW : barH
11802
11881
  });
11803
11882
  }
11804
-
11805
-
11883
+
11884
+
11806
11885
  }
11807
11886
  });
11808
11887
  }
@@ -11883,17 +11962,17 @@ Series.prototype = {
11883
11962
  areaSegmentPath.push(L, segmentPath[1], segmentPath[2]);
11884
11963
  }
11885
11964
  if (options.stacking && series.type !== 'areaspline') {
11886
-
11887
- // Follow stack back. Todo: implement areaspline. A general solution could be to
11965
+
11966
+ // Follow stack back. Todo: implement areaspline. A general solution could be to
11888
11967
  // reverse the entire graphPath of the previous series, though may be hard with
11889
11968
  // splines and with series with different extremes
11890
11969
  for (i = segment.length - 1; i >= 0; i--) {
11891
-
11970
+
11892
11971
  // step line?
11893
11972
  if (i < segment.length - 1 && options.step) {
11894
11973
  areaSegmentPath.push(segment[i + 1].plotX, segment[i].yBottom);
11895
11974
  }
11896
-
11975
+
11897
11976
  areaSegmentPath.push(segment[i].plotX, segment[i].yBottom);
11898
11977
  }
11899
11978
 
@@ -11962,17 +12041,17 @@ Series.prototype = {
11962
12041
  group = series.group,
11963
12042
  trackerGroup = series.trackerGroup,
11964
12043
  chart = series.chart;
11965
-
12044
+
11966
12045
  // A fixed size is needed for inversion to work
11967
- function setInvert() {
12046
+ function setInvert() {
11968
12047
  var size = {
11969
12048
  width: series.yAxis.len,
11970
12049
  height: series.xAxis.len
11971
12050
  };
11972
-
12051
+
11973
12052
  // Set the series.group size
11974
12053
  group.attr(size).invert();
11975
-
12054
+
11976
12055
  // Set the tracker group size
11977
12056
  if (trackerGroup) {
11978
12057
  trackerGroup.attr(size).invert();
@@ -11986,7 +12065,7 @@ Series.prototype = {
11986
12065
 
11987
12066
  // Do it now
11988
12067
  setInvert(); // do it now
11989
-
12068
+
11990
12069
  // On subsequent render and redraw, just do setInvert without setting up events again
11991
12070
  series.invertGroups = setInvert;
11992
12071
  },
@@ -12021,7 +12100,7 @@ Series.prototype = {
12021
12100
  chart.clipRect = clipRect;
12022
12101
  }
12023
12102
  }
12024
-
12103
+
12025
12104
 
12026
12105
  // the group
12027
12106
  if (!series.group) {
@@ -12057,12 +12136,12 @@ Series.prototype = {
12057
12136
  if (series.options.enableMouseTracking !== false) {
12058
12137
  series.drawTracker();
12059
12138
  }
12060
-
12139
+
12061
12140
  // Handle inverted series and tracker groups
12062
12141
  if (chart.inverted) {
12063
12142
  series.invertGroups();
12064
12143
  }
12065
-
12144
+
12066
12145
  // Do the initial clipping. This must be done after inverting for VML.
12067
12146
  if (doClip && !series.hasRendered) {
12068
12147
  group.clip(clipRect);
@@ -12070,7 +12149,7 @@ Series.prototype = {
12070
12149
  series.trackerGroup.clip(chart.clipRect);
12071
12150
  }
12072
12151
  }
12073
-
12152
+
12074
12153
 
12075
12154
  // run the animation
12076
12155
  if (doAnimation) {
@@ -12271,26 +12350,26 @@ Series.prototype = {
12271
12350
  drawTrackerGroup: function () {
12272
12351
  var trackerGroup = this.trackerGroup,
12273
12352
  chart = this.chart;
12274
-
12353
+
12275
12354
  if (this.isCartesian) {
12276
-
12355
+
12277
12356
  // Generate it on first call
12278
- if (!trackerGroup) {
12357
+ if (!trackerGroup) {
12279
12358
  this.trackerGroup = trackerGroup = chart.renderer.g()
12280
12359
  .attr({
12281
12360
  zIndex: this.options.zIndex || 1
12282
12361
  })
12283
12362
  .add(chart.trackerGroup);
12284
-
12363
+
12285
12364
  }
12286
12365
  // Place it on first and subsequent (redraw) calls
12287
12366
  trackerGroup.translate(this.xAxis.left, this.yAxis.top);
12288
-
12367
+
12289
12368
  }
12290
-
12369
+
12291
12370
  return trackerGroup;
12292
12371
  },
12293
-
12372
+
12294
12373
  /**
12295
12374
  * Draw the tracker object that sits above all data labels and markers to
12296
12375
  * track mouse events on the graph or points. For the line type charts
@@ -12333,15 +12412,15 @@ Series.prototype = {
12333
12412
  trackerPath.push(M, singlePoint.plotX - snap, singlePoint.plotY,
12334
12413
  L, singlePoint.plotX + snap, singlePoint.plotY);
12335
12414
  }
12336
-
12337
-
12415
+
12416
+
12338
12417
 
12339
12418
  // draw the tracker
12340
12419
  if (tracker) {
12341
12420
  tracker.attr({ d: trackerPath });
12342
12421
 
12343
12422
  } else { // create
12344
-
12423
+
12345
12424
  series.tracker = renderer.path(trackerPath)
12346
12425
  .attr({
12347
12426
  isTracker: true,
@@ -12612,7 +12691,7 @@ var ColumnSeries = extendClass(Series, {
12612
12691
  r: options.borderRadius,
12613
12692
  strokeWidth: borderWidth
12614
12693
  };
12615
-
12694
+
12616
12695
  if (borderWidth % 2) { // correct for shorting in crisp method, visible in stacked columns with 1px border
12617
12696
  shapeArgs.y -= 1;
12618
12697
  shapeArgs.height += 1;
@@ -12670,7 +12749,7 @@ var ColumnSeries = extendClass(Series, {
12670
12749
  .attr(point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE])
12671
12750
  .add(series.group)
12672
12751
  .shadow(options.shadow);
12673
-
12752
+
12674
12753
  }
12675
12754
 
12676
12755
  }
@@ -12691,13 +12770,17 @@ var ColumnSeries = extendClass(Series, {
12691
12770
  cursor = options.cursor,
12692
12771
  css = cursor && { cursor: cursor },
12693
12772
  trackerGroup = series.drawTrackerGroup(),
12694
- rel;
12695
-
12773
+ rel,
12774
+ plotY,
12775
+ validPlotY;
12776
+
12696
12777
  each(series.points, function (point) {
12697
12778
  tracker = point.tracker;
12698
12779
  shapeArgs = point.trackerArgs || point.shapeArgs;
12780
+ plotY = point.plotY;
12781
+ validPlotY = !series.isCartesian || (plotY !== UNDEFINED && !isNaN(plotY));
12699
12782
  delete shapeArgs.strokeWidth;
12700
- if (point.y !== null) {
12783
+ if (point.y !== null && validPlotY) {
12701
12784
  if (tracker) {// update
12702
12785
  tracker.attr(shapeArgs);
12703
12786
 
@@ -12761,7 +12844,7 @@ var ColumnSeries = extendClass(Series, {
12761
12844
  // start values
12762
12845
  graphic.attr({
12763
12846
  height: 0,
12764
- y: defined(threshold) ?
12847
+ y: defined(threshold) ?
12765
12848
  yAxis.getThreshold(threshold) :
12766
12849
  yAxis.translate(yAxis.getExtremes().min, 0, 1, 0, 1)
12767
12850
  });
@@ -12852,10 +12935,10 @@ var ScatterSeries = extendClass(Series, {
12852
12935
  while (i--) {
12853
12936
  graphic = points[i].graphic;
12854
12937
  if (graphic) { // doesn't exist for null points
12855
- graphic.element._i = i;
12938
+ graphic.element._i = i;
12856
12939
  }
12857
12940
  }
12858
-
12941
+
12859
12942
  // Add the event listeners, we need to do this only once
12860
12943
  if (!series._hasTracking) {
12861
12944
  series.group
@@ -13040,17 +13123,20 @@ var PieSeries = extendClass(Series, {
13040
13123
  * Extend the basic setData method by running processData and generatePoints immediately,
13041
13124
  * in order to access the points from the legend.
13042
13125
  */
13043
- setData: function () {
13044
- Series.prototype.setData.apply(this, arguments);
13126
+ setData: function (data, redraw) {
13127
+ Series.prototype.setData.call(this, data, false);
13045
13128
  this.processData();
13046
13129
  this.generatePoints();
13130
+ if (pick(redraw, true)) {
13131
+ this.chart.redraw();
13132
+ }
13047
13133
  },
13048
13134
  /**
13049
13135
  * Do translation for pie slices
13050
13136
  */
13051
13137
  translate: function () {
13052
13138
  this.generatePoints();
13053
-
13139
+
13054
13140
  var total = 0,
13055
13141
  series = this,
13056
13142
  cumulative = -0.25, // start at top
@@ -13315,7 +13401,7 @@ var PieSeries = extendClass(Series, {
13315
13401
  // assume equal label heights
13316
13402
  labelHeight = halves[0][0] && halves[0][0].dataLabel && halves[0][0].dataLabel.getBBox().height;
13317
13403
 
13318
- /* Loop over the points in each quartile, starting from the top and bottom
13404
+ /* Loop over the points in each half, starting from the top and bottom
13319
13405
  * of the pie to detect overlapping labels.
13320
13406
  */
13321
13407
  while (i--) {
@@ -13328,115 +13414,126 @@ var PieSeries = extendClass(Series, {
13328
13414
  length = points.length,
13329
13415
  slotIndex;
13330
13416
 
13331
-
13332
- // build the slots
13333
- for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
13334
- slots.push(pos);
13335
- // visualize the slot
13336
- /*
13337
- var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
13338
- slotY = pos + chart.plotTop;
13339
- if (!isNaN(slotX)) {
13340
- chart.renderer.rect(slotX, slotY - 7, 100, labelHeight)
13341
- .attr({
13342
- 'stroke-width': 1,
13343
- stroke: 'silver'
13344
- })
13345
- .add();
13346
- chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
13347
- .attr({
13348
- fill: 'silver'
13349
- }).add();
13350
- }
13351
- // */
13352
- }
13353
- slotsLength = slots.length;
13354
-
13355
- // if there are more values than available slots, remove lowest values
13356
- if (length > slotsLength) {
13357
- // create an array for sorting and ranking the points within each quarter
13358
- rankArr = [].concat(points);
13359
- rankArr.sort(sort);
13360
- j = length;
13361
- while (j--) {
13362
- rankArr[j].rank = j;
13363
- }
13364
- j = length;
13365
- while (j--) {
13366
- if (points[j].rank >= slotsLength) {
13367
- points.splice(j, 1);
13417
+ // Only do anti-collision when we are outside the pie and have connectors (#856)
13418
+ if (distanceOption > 0) {
13419
+
13420
+ // build the slots
13421
+ for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
13422
+ slots.push(pos);
13423
+ // visualize the slot
13424
+ /*
13425
+ var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
13426
+ slotY = pos + chart.plotTop;
13427
+ if (!isNaN(slotX)) {
13428
+ chart.renderer.rect(slotX, slotY - 7, 100, labelHeight)
13429
+ .attr({
13430
+ 'stroke-width': 1,
13431
+ stroke: 'silver'
13432
+ })
13433
+ .add();
13434
+ chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
13435
+ .attr({
13436
+ fill: 'silver'
13437
+ }).add();
13438
+ }
13439
+ // */
13440
+ }
13441
+ slotsLength = slots.length;
13442
+
13443
+ // if there are more values than available slots, remove lowest values
13444
+ if (length > slotsLength) {
13445
+ // create an array for sorting and ranking the points within each quarter
13446
+ rankArr = [].concat(points);
13447
+ rankArr.sort(sort);
13448
+ j = length;
13449
+ while (j--) {
13450
+ rankArr[j].rank = j;
13451
+ }
13452
+ j = length;
13453
+ while (j--) {
13454
+ if (points[j].rank >= slotsLength) {
13455
+ points.splice(j, 1);
13456
+ }
13368
13457
  }
13458
+ length = points.length;
13369
13459
  }
13370
- length = points.length;
13371
- }
13372
13460
 
13373
- // The label goes to the nearest open slot, but not closer to the edge than
13374
- // the label's index.
13375
- for (j = 0; j < length; j++) {
13461
+ // The label goes to the nearest open slot, but not closer to the edge than
13462
+ // the label's index.
13463
+ for (j = 0; j < length; j++) {
13376
13464
 
13377
- point = points[j];
13378
- labelPos = point.labelPos;
13465
+ point = points[j];
13466
+ labelPos = point.labelPos;
13379
13467
 
13380
- var closest = 9999,
13381
- distance,
13382
- slotI;
13468
+ var closest = 9999,
13469
+ distance,
13470
+ slotI;
13383
13471
 
13384
- // find the closest slot index
13385
- for (slotI = 0; slotI < slotsLength; slotI++) {
13386
- distance = mathAbs(slots[slotI] - labelPos[1]);
13387
- if (distance < closest) {
13388
- closest = distance;
13389
- slotIndex = slotI;
13472
+ // find the closest slot index
13473
+ for (slotI = 0; slotI < slotsLength; slotI++) {
13474
+ distance = mathAbs(slots[slotI] - labelPos[1]);
13475
+ if (distance < closest) {
13476
+ closest = distance;
13477
+ slotIndex = slotI;
13478
+ }
13390
13479
  }
13391
- }
13392
13480
 
13393
- // if that slot index is closer to the edges of the slots, move it
13394
- // to the closest appropriate slot
13395
- if (slotIndex < j && slots[j] !== null) { // cluster at the top
13396
- slotIndex = j;
13397
- } else if (slotsLength < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
13398
- slotIndex = slotsLength - length + j;
13399
- while (slots[slotIndex] === null) { // make sure it is not taken
13400
- slotIndex++;
13401
- }
13402
- } else {
13403
- // Slot is taken, find next free slot below. In the next run, the next slice will find the
13404
- // slot above these, because it is the closest one
13405
- while (slots[slotIndex] === null) { // make sure it is not taken
13406
- slotIndex++;
13481
+ // if that slot index is closer to the edges of the slots, move it
13482
+ // to the closest appropriate slot
13483
+ if (slotIndex < j && slots[j] !== null) { // cluster at the top
13484
+ slotIndex = j;
13485
+ } else if (slotsLength < length - j + slotIndex && slots[j] !== null) { // cluster at the bottom
13486
+ slotIndex = slotsLength - length + j;
13487
+ while (slots[slotIndex] === null) { // make sure it is not taken
13488
+ slotIndex++;
13489
+ }
13490
+ } else {
13491
+ // Slot is taken, find next free slot below. In the next run, the next slice will find the
13492
+ // slot above these, because it is the closest one
13493
+ while (slots[slotIndex] === null) { // make sure it is not taken
13494
+ slotIndex++;
13495
+ }
13407
13496
  }
13408
- }
13409
13497
 
13410
- usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
13411
- slots[slotIndex] = null; // mark as taken
13498
+ usedSlots.push({ i: slotIndex, y: slots[slotIndex] });
13499
+ slots[slotIndex] = null; // mark as taken
13500
+ }
13501
+ // sort them in order to fill in from the top
13502
+ usedSlots.sort(sort);
13412
13503
  }
13413
- // sort them in order to fill in from the top
13414
- usedSlots.sort(sort);
13415
-
13416
13504
 
13417
13505
  // now the used slots are sorted, fill them up sequentially
13418
13506
  for (j = 0; j < length; j++) {
13419
13507
 
13508
+ var slot, naturalY;
13509
+
13420
13510
  point = points[j];
13421
13511
  labelPos = point.labelPos;
13422
13512
  dataLabel = point.dataLabel;
13423
- var slot = usedSlots.pop(),
13424
- naturalY = labelPos[1];
13425
-
13426
13513
  visibility = point.visible === false ? HIDDEN : VISIBLE;
13427
- slotIndex = slot.i;
13514
+ naturalY = labelPos[1];
13515
+
13516
+ if (distanceOption > 0) {
13517
+ slot = usedSlots.pop();
13518
+ slotIndex = slot.i;
13519
+
13520
+ // if the slot next to currrent slot is free, the y value is allowed
13521
+ // to fall back to the natural position
13522
+ y = slot.y;
13523
+ if ((naturalY > y && slots[slotIndex + 1] !== null) ||
13524
+ (naturalY < y && slots[slotIndex - 1] !== null)) {
13525
+ y = naturalY;
13526
+ }
13428
13527
 
13429
- // if the slot next to currrent slot is free, the y value is allowed
13430
- // to fall back to the natural position
13431
- y = slot.y;
13432
- if ((naturalY > y && slots[slotIndex + 1] !== null) ||
13433
- (naturalY < y && slots[slotIndex - 1] !== null)) {
13528
+ } else {
13434
13529
  y = naturalY;
13435
13530
  }
13436
13531
 
13437
13532
  // get the x - use the natural x position for first and last slot, to prevent the top
13438
13533
  // and botton slice connectors from touching each other on either side
13439
- x = series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
13534
+ x = options.justify ?
13535
+ seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
13536
+ series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
13440
13537
 
13441
13538
  // move or place the data label
13442
13539
  dataLabel
@@ -13538,6 +13635,6 @@ extend(Highcharts, {
13538
13635
  extendClass: extendClass,
13539
13636
  placeBox: placeBox,
13540
13637
  product: 'Highcharts',
13541
- version: '2.2.1'
13638
+ version: '2.2.2'
13542
13639
  });
13543
13640
  }());