highcharts-js-rails 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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
  }());