highcharts-rails 4.1.5 → 4.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4cc9e5622951314ffef63262c82a65c0497f3fdb
4
- data.tar.gz: 36185a5fe7cca92c015c733997b09003bb39f841
3
+ metadata.gz: 20d000bd52b08e8dd4a7376d96a3a9f76e41276a
4
+ data.tar.gz: ef3d6781527923bce73e358926c4befc2e85d4eb
5
5
  SHA512:
6
- metadata.gz: 8a739bc1dde01881470be2af26db1d39a1f8392c18bbe6855c8b4517819dcd013e29b6de70ce704328cc74a63787cd9f6322851fdae08af65a33a464b5dae520
7
- data.tar.gz: 2cd5ffd46e58ea78a37cbb27c5a94d1fe92193fcd99bf3357e8eec612a9f68b18002087a3792f5f97d30c4095e9dc6e04271563a5a98a64e439a54f930969cfd
6
+ metadata.gz: 2a94fc86214d938de0f8cab679f489978cc6ff272656dd7c2172f62dcf192b92ceea747f39e427519b11da95487807f6d3323ba3813b28107e15a9246846d4a3
7
+ data.tar.gz: e9c7523d2c44dba8d99512eaabc532b8c977b5a3d1633ca05449fa57c75b19c52b361a65ea8cf90972ab2c81acba8c9e5d4b74db3ae53c3b8b3b9d7c5e8e9c9f
@@ -1,3 +1,50 @@
1
+ # 4.1.6 / 2015-08-31
2
+
3
+ * Updated Highcharts to 4.1.6 (2015-06-12)
4
+ * Added new option, series.getExtremesFromAll, that tells the y axis to be scaled to the whole series range, not only the visible part.
5
+ * Added scaling support for Z axis on 3D charts.
6
+ * Added xAxis.title.x and xAxis.title.y options for positioning.
7
+ * Fixed #4160, 3D should be disabled for inverted charts
8
+ * Fixed #4160, 3D should be disabled for inverted charts.
9
+ * Fixed #1457, columnrange did not render with reversed Y axis.
10
+ * Fixed #182, browser hangs when updating chart from series mouseOut event.
11
+ * Fixed #2077, innerSize of pies was not correct when size was dynamic.
12
+ * Fixed #2088, crosshair shown on multiple axes.
13
+ * Fixed #3923, crash in Chrome when extending a dashed line to extreme length.
14
+ * Fixed #3974, duplicated data labels on Retina displays when text-shadow is applied.
15
+ * Fixed #3977, tooltip content sometimes displayed outside tooltip.
16
+ * Fixed #4069, slow updating of multiple pie slices.
17
+ * Fixed #4108, line wrap dataLabels in tree maps.
18
+ * Fixed #4116, cannot disable tooltip on polar chart.
19
+ * Fixed #4117, broken tooltips in pie charts when useHTML was enabled.
20
+ * Fixed #4124, treemap click on legend item returned error.
21
+ * Fixed #4128, tooltip only looked at X value for line series
22
+ * Fixed #4146, point marker stayed after updating point to null when connectNulls was true.
23
+ * Fixed #4161, a regression causing wrong position of tooltip in top row of heatmap.
24
+ * Fixed #4163, tooltip broken on pie in combo chart.
25
+ * Fixed #4166, resetting point state was coupled to tooltip, resulting in state not reset when the tooltip was empty.
26
+ * Fixed #4167, chart crashed on Y axis breaks in stock chart.
27
+ * Fixed #4177, X axis label ellipsis overlapped when in the middle of the axis.
28
+ * Fixed #4197, ignoreHiddenPoint didn't work.
29
+ * Fixed #4200, unresponsive tooltip on tight column chart with shared tooltip.
30
+ * Fixed #4201, redundant tick alignment to empty axes.
31
+ * Fixed #4203, radial gradient rendered wrong in 3D Pies
32
+ * Fixed #4208, click events did not bubble.
33
+ * Fixed #4210, touch scroll was trapped when zoomType enabled.
34
+ * Fixed #4217, tickInterval on linked axis did not follow that of parent.
35
+ * Fixed #4221, negative color and zones not working with logarithmic axis.
36
+ * Fixed #4223, tooltips showed year when data resolution was less than 1 millisecond.
37
+ * Fixed #4247, X zooming within an Y axis break resulted in empty chart.
38
+ * Fixed #4256, summary columns on waterfall, including data labels, extended below the plot area if an Y axis minimum was set.
39
+ * Fixed #4261, added namespace to drillToNode on click event in tree maps.
40
+ * Fixed #4264, column in stacked chart was mispositioned in some cases.
41
+ * Fixed issue with wrong data label being hidden on overlap in columns.
42
+ * Fixed issues with ellipsis on first and last label on X axis. Closes #3975.
43
+ * Export: Added missing treemaps.js, fixes #4092.
44
+ * Export: Added support for Map constructor.
45
+ * Export: Added treemaps for serverside rendering.
46
+ * Export: Enable loading of maps.js.
47
+
1
48
  # 4.1.5 / 2015-04-13
2
49
 
3
50
  * Updated Highcharts to 4.1.5
data/Rakefile CHANGED
@@ -1,35 +1 @@
1
1
  require 'bundler/gem_tasks'
2
-
3
- desc "Update to the latest version of Highcharts"
4
- task :update, :version do |t, args|
5
- version = args[:version]
6
- url = "http://code.highcharts.com/zips/Highcharts-#{version}.zip"
7
- puts "Fetching #{url}"
8
- `curl -# #{url} -o tmp/#{version}.zip`
9
- `unzip tmp/#{version}.zip -d tmp/#{version}`
10
-
11
- mappings = {
12
- "highcharts.src.js" => "highcharts.js",
13
- "highcharts-more.src.js" => "highcharts/highcharts-more.js",
14
- "highcharts-3d.src.js" => "highcharts/highcharts-3d.js",
15
- "standalone-framework.src.js" => "highcharts/adapters/standalone-framework.js",
16
- "annotations.src.js" => "highcharts/modules/annotations.js",
17
- "canvas-tools.src.js" => "highcharts/modules/canvas-tools.js",
18
- "data.src.js" => "highcharts/modules/data.js",
19
- "drilldown.src.js" => "highcharts/modules/drilldown.js",
20
- "exporting.src.js" => "highcharts/modules/exporting.js",
21
- "funnel.src.js" => "highcharts/modules/funnel.js",
22
- "heatmap.src.js" => "highcharts/modules/heatmap.js",
23
- "no-data-to-display.src.js" => "highcharts/modules/no-data-to-display.js",
24
- "solid-gauge.src.js" => "highcharts/modules/solid-gauge.js",
25
- "treemap.src.js" => "highcharts/modules/treemap.js",
26
- "broken-axis.src.js" => "highcharts/modules/broken-axis.js",
27
- }
28
- dest = "app/assets/javascripts/"
29
- Dir.glob("tmp/#{version}/js/**/*.src.js").each do |file|
30
- name = File.basename(file)
31
- FileUtils.cp file, "#{dest}#{mappings[name]}", verbose: true
32
- end
33
- FileUtils.cp Dir.glob("tmp/#{version}/js/themes/*.js"), "#{dest}highcharts/themes/", verbose: true
34
- FileUtils.cp Dir.glob("tmp/#{version}/graphics/*.png"), "app/assets/images/highcharts", verbose: true
35
- end
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.1.5 (2015-04-13)
5
+ * @license Highcharts JS v4.1.6 (2015-06-12)
6
6
  *
7
7
  * (c) 2009-2014 Torstein Honsi
8
8
  *
@@ -56,7 +56,7 @@ var UNDEFINED,
56
56
  charts = [],
57
57
  chartCount = 0,
58
58
  PRODUCT = 'Highcharts',
59
- VERSION = '4.1.5',
59
+ VERSION = '4.1.6',
60
60
 
61
61
  // some constants for frequently used strings
62
62
  DIV = 'div',
@@ -368,6 +368,13 @@ function pad(number, length) {
368
368
  return new Array((length || 2) + 1 - String(number).length).join(0) + number;
369
369
  }
370
370
 
371
+ /**
372
+ * Return a length based on either the integer value, or a percentage of a base.
373
+ */
374
+ function relativeLength (value, base) {
375
+ return (/%$/).test(value) ? base * parseFloat(value) / 100 : parseFloat(value);
376
+ }
377
+
371
378
  /**
372
379
  * Wrap a method with extended functionality, preserving the original function
373
380
  * @param {Object} obj The context object that the method belongs to
@@ -1261,8 +1268,8 @@ defaultOptions = {
1261
1268
  global: {
1262
1269
  useUTC: true,
1263
1270
  //timezoneOffset: 0,
1264
- canvasToolsURL: 'http://code.highcharts.com/4.1.5/modules/canvas-tools.js',
1265
- VMLRadialGradientURL: 'http://code.highcharts.com/4.1.5/gfx/vml-radial-gradient.png'
1271
+ canvasToolsURL: 'http://code.highcharts.com/4.1.6/modules/canvas-tools.js',
1272
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.1.6/gfx/vml-radial-gradient.png'
1266
1273
  },
1267
1274
  chart: {
1268
1275
  //animation: true,
@@ -1944,13 +1951,20 @@ SVGElement.prototype = {
1944
1951
  var elem = this.element,
1945
1952
  tspans,
1946
1953
  hasContrast = textShadow.indexOf('contrast') !== -1,
1954
+ styles = {},
1947
1955
  // IE10 and IE11 report textShadow in elem.style even though it doesn't work. Check
1948
1956
  // this again with new IE release. In exports, the rendering is passed to PhantomJS.
1949
1957
  supports = this.renderer.forExport || (elem.style.textShadow !== UNDEFINED && !isIE);
1950
1958
 
1951
1959
  // When the text shadow is set to contrast, use dark stroke for light text and vice versa
1952
1960
  if (hasContrast) {
1953
- textShadow = textShadow.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
1961
+ styles.textShadow = textShadow = textShadow.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
1962
+ }
1963
+
1964
+ // Safari with retina displays as well as PhantomJS bug (#3974). Firefox does not tolerate this,
1965
+ // it removes the text shadows.
1966
+ if (isWebKit) {
1967
+ styles.textRendering = 'geometricPrecision';
1954
1968
  }
1955
1969
 
1956
1970
  /* Selective side-by-side testing in supported browser (http://jsfiddle.net/highcharts/73L1ptrh/)
@@ -1962,11 +1976,7 @@ SVGElement.prototype = {
1962
1976
 
1963
1977
  // No reason to polyfill, we've got native support
1964
1978
  if (supports) {
1965
- if (hasContrast) { // Apply the altered style
1966
- css(elem, {
1967
- textShadow: textShadow
1968
- });
1969
- }
1979
+ css(elem, styles); // Apply altered textShadow or textRendering workaround
1970
1980
  } else {
1971
1981
 
1972
1982
  this.fakeTS = true; // Fake text shadow
@@ -3269,7 +3279,7 @@ SVGRenderer.prototype = {
3269
3279
  wasTooLong = true;
3270
3280
  }
3271
3281
  wordStr = span.substring(0, wordStr.length + (tooLong ? -1 : 1) * mathCeil(cursor));
3272
- words = [wordStr + '\u2026'];
3282
+ words = [wordStr + (width > 3 ? '\u2026' : '')];
3273
3283
  tspan.removeChild(tspan.firstChild);
3274
3284
  }
3275
3285
 
@@ -3365,7 +3375,7 @@ SVGRenderer.prototype = {
3365
3375
  */
3366
3376
  getContrast: function (color) {
3367
3377
  color = Color(color).rgba;
3368
- return color[0] + color[1] + color[2] > 384 ? '#000' : '#FFF';
3378
+ return color[0] + color[1] + color[2] > 384 ? '#000000' : '#FFFFFF';
3369
3379
  },
3370
3380
 
3371
3381
  /**
@@ -3889,11 +3899,8 @@ SVGRenderer.prototype = {
3889
3899
  safeDistance = r + halfDistance,
3890
3900
  anchorX = options && options.anchorX,
3891
3901
  anchorY = options && options.anchorY,
3892
- path,
3893
- normalizer = mathRound(options.strokeWidth || 0) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors;
3902
+ path;
3894
3903
 
3895
- x += normalizer;
3896
- y += normalizer;
3897
3904
  path = [
3898
3905
  'M', x + r, y,
3899
3906
  'L', x + w - r, y, // top side
@@ -4121,8 +4128,8 @@ SVGRenderer.prototype = {
4121
4128
 
4122
4129
  // create the border box if it is not already present
4123
4130
  if (!box) {
4124
- boxX = mathRound(-alignFactor * padding);
4125
- boxY = baseline ? -baselineOffset : 0;
4131
+ boxX = mathRound(-alignFactor * padding) + crispAdjust;
4132
+ boxY = (baseline ? -baselineOffset : 0) + crispAdjust;
4126
4133
 
4127
4134
  wrapper.box = box = shape ?
4128
4135
  renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height, deferredAttr) :
@@ -4162,11 +4169,7 @@ SVGRenderer.prototype = {
4162
4169
  if (x !== text.x || y !== text.y) {
4163
4170
  text.attr('x', x);
4164
4171
  if (y !== UNDEFINED) {
4165
- // As a workaround for #3649, use translation instead of y attribute. #3649
4166
- // is a rendering bug in WebKit for Retina (Mac, iOS, PhantomJS) that
4167
- // results in duplicated text when an y attribute is used in combination
4168
- // with a CSS text-style.
4169
- text.attr(text.element.nodeName === 'SPAN' ? 'y' : 'translateY', y);
4172
+ text.attr('y', y);
4170
4173
  }
4171
4174
  }
4172
4175
 
@@ -4263,7 +4266,7 @@ SVGRenderer.prototype = {
4263
4266
  };
4264
4267
  wrapper.anchorXSetter = function (value, key) {
4265
4268
  anchorX = value;
4266
- boxAttr(key, value + crispAdjust - wrapperX);
4269
+ boxAttr(key, mathRound(value) - crispAdjust - wrapperX);
4267
4270
  };
4268
4271
  wrapper.anchorYSetter = function (value, key) {
4269
4272
  anchorY = value;
@@ -5896,6 +5899,8 @@ Tick.prototype = {
5896
5899
  factor = { left: 0, center: 0.5, right: 1 }[axis.labelAlign],
5897
5900
  labelWidth = label.getBBox().width,
5898
5901
  slotWidth = axis.slotWidth,
5902
+ xCorrection = factor,
5903
+ goRight = 1,
5899
5904
  leftPos,
5900
5905
  rightPos,
5901
5906
  textWidth;
@@ -5904,25 +5909,25 @@ Tick.prototype = {
5904
5909
  // If it now overshoots the slotWidth, add ellipsis.
5905
5910
  if (!rotation) {
5906
5911
  leftPos = pxPos - factor * labelWidth;
5907
- rightPos = pxPos + factor * labelWidth;
5912
+ rightPos = pxPos + (1 - factor) * labelWidth;
5908
5913
 
5909
5914
  if (leftPos < leftBound) {
5910
- slotWidth -= leftBound - leftPos;
5911
- xy.x = leftBound;
5912
- label.attr({ align: 'left' });
5915
+ slotWidth = xy.x + slotWidth * (1 - factor) - leftBound;
5913
5916
  } else if (rightPos > rightBound) {
5914
- slotWidth -= rightPos - rightBound;
5915
- xy.x = rightBound;
5916
- label.attr({ align: 'right' });
5917
+ slotWidth = rightBound - xy.x + slotWidth * factor;
5918
+ goRight = -1;
5917
5919
  }
5918
5920
 
5921
+ slotWidth = mathMin(axis.slotWidth, slotWidth); // #4177
5922
+ if (slotWidth < axis.slotWidth && axis.labelAlign === 'center') {
5923
+ xy.x += goRight * (axis.slotWidth - slotWidth - xCorrection * (axis.slotWidth - mathMin(labelWidth, slotWidth)));
5924
+ }
5919
5925
  // If the label width exceeds the available space, set a text width to be
5920
5926
  // picked up below. Also, if a width has been set before, we need to set a new
5921
5927
  // one because the reported labelWidth will be limited by the box (#3938).
5922
5928
  if (labelWidth > slotWidth || (axis.autoRotation && label.styles.width)) {
5923
5929
  textWidth = slotWidth;
5924
5930
  }
5925
-
5926
5931
 
5927
5932
  // Add ellipsis to prevent rotated labels to be clipped against the edge of the chart
5928
5933
  } else if (rotation < 0 && pxPos - factor * labelWidth < leftBound) {
@@ -6909,7 +6914,7 @@ Axis.prototype = {
6909
6914
  *
6910
6915
  */
6911
6916
  translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
6912
- var axis = this,
6917
+ var axis = this.linkedParent || this, // #1417
6913
6918
  sign = 1,
6914
6919
  cvsOffset = 0,
6915
6920
  localA = old ? axis.oldTransA : axis.transA,
@@ -7371,7 +7376,7 @@ Axis.prototype = {
7371
7376
  axis.tickInterval = 1;
7372
7377
  } else if (isLinked && !tickIntervalOption &&
7373
7378
  tickPixelIntervalOption === axis.linkedParent.options.tickPixelInterval) {
7374
- axis.tickInterval = axis.linkedParent.tickInterval;
7379
+ axis.tickInterval = tickIntervalOption = axis.linkedParent.tickInterval;
7375
7380
  } else {
7376
7381
  axis.tickInterval = pick(
7377
7382
  tickIntervalOption,
@@ -7416,20 +7421,18 @@ Axis.prototype = {
7416
7421
  }
7417
7422
 
7418
7423
  // for linear axes, get magnitude and normalize the interval
7419
- if (!isDatetimeAxis && !isLog) { // linear
7420
- if (!tickIntervalOption) {
7421
- axis.tickInterval = normalizeTickInterval(
7422
- axis.tickInterval,
7423
- null,
7424
- getMagnitude(axis.tickInterval),
7425
- // If the tick interval is between 0.5 and 5 and the axis max is in the order of
7426
- // thousands, chances are we are dealing with years. Don't allow decimals. #3363.
7427
- pick(options.allowDecimals, !(axis.tickInterval > 0.5 && axis.tickInterval < 5 && axis.max > 1000 && axis.max < 9999)),
7428
- !!this.tickAmount
7429
- );
7430
- }
7424
+ if (!isDatetimeAxis && !isLog && !tickIntervalOption) {
7425
+ axis.tickInterval = normalizeTickInterval(
7426
+ axis.tickInterval,
7427
+ null,
7428
+ getMagnitude(axis.tickInterval),
7429
+ // If the tick interval is between 0.5 and 5 and the axis max is in the order of
7430
+ // thousands, chances are we are dealing with years. Don't allow decimals. #3363.
7431
+ pick(options.allowDecimals, !(axis.tickInterval > 0.5 && axis.tickInterval < 5 && axis.max > 1000 && axis.max < 9999)),
7432
+ !!this.tickAmount
7433
+ );
7431
7434
  }
7432
-
7435
+
7433
7436
  // Prevent ticks from getting so close that we can't draw the labels
7434
7437
  if (!this.tickAmount && this.len) { // Color axis with disabled legend has no length
7435
7438
  axis.tickInterval = axis.unsquish();
@@ -7461,7 +7464,7 @@ Axis.prototype = {
7461
7464
  this.tickInterval / 5 : options.minorTickInterval;
7462
7465
 
7463
7466
  // Find the tick positions
7464
- this.tickPositions = tickPositions = options.tickPositions && options.tickPositions.slice(); // Work on a copy (#1565)
7467
+ this.tickPositions = tickPositions = tickPositionsOption && tickPositionsOption.slice(); // Work on a copy (#1565)
7465
7468
  if (!tickPositions) {
7466
7469
 
7467
7470
  if (this.isDatetimeAxis) {
@@ -7563,7 +7566,9 @@ Axis.prototype = {
7563
7566
  key = [horiz ? options.left : options.top, horiz ? options.width : options.height, options.pane].join(',');
7564
7567
 
7565
7568
  if (others[key]) {
7566
- hasOther = true;
7569
+ if (axis.series.length) {
7570
+ hasOther = true; // #4201
7571
+ }
7567
7572
  } else {
7568
7573
  others[key] = 1;
7569
7574
  }
@@ -7939,8 +7944,9 @@ Axis.prototype = {
7939
7944
  labelOptions = this.options.labels,
7940
7945
  horiz = this.horiz,
7941
7946
  margin = chart.margin,
7947
+ slotCount = this.categories ? tickPositions.length : tickPositions.length - 1,
7942
7948
  slotWidth = this.slotWidth = (horiz && !labelOptions.step && !labelOptions.rotation &&
7943
- ((this.staggerLines || 1) * chart.plotWidth) / tickPositions.length) ||
7949
+ ((this.staggerLines || 1) * chart.plotWidth) / slotCount) ||
7944
7950
  (!horiz && ((margin[3] && (margin[3] - chart.spacing[3])) || chart.chartWidth * 0.33)), // #1580, #1931,
7945
7951
  innerWidth = mathMax(1, mathRound(slotWidth - 2 * (labelOptions.padding || 5))),
7946
7952
  attr = {},
@@ -8208,6 +8214,8 @@ Axis.prototype = {
8208
8214
  margin = horiz ? axisLeft : axisTop,
8209
8215
  opposite = this.opposite,
8210
8216
  offset = this.offset,
8217
+ xOption = axisTitleOptions.x || 0,
8218
+ yOption = axisTitleOptions.y || 0,
8211
8219
  fontSize = pInt(axisTitleOptions.style.fontSize || 12),
8212
8220
 
8213
8221
  // the position in the length direction of the axis
@@ -8226,12 +8234,11 @@ Axis.prototype = {
8226
8234
 
8227
8235
  return {
8228
8236
  x: horiz ?
8229
- alongAxis :
8230
- offAxis + (opposite ? this.width : 0) + offset +
8231
- (axisTitleOptions.x || 0), // x
8237
+ alongAxis + xOption :
8238
+ offAxis + (opposite ? this.width : 0) + offset + xOption,
8232
8239
  y: horiz ?
8233
- offAxis - (opposite ? this.height : 0) + offset :
8234
- alongAxis + (axisTitleOptions.y || 0) // y
8240
+ offAxis + yOption - (opposite ? this.height : 0) + offset :
8241
+ alongAxis + yOption
8235
8242
  };
8236
8243
  },
8237
8244
 
@@ -8511,7 +8518,9 @@ Axis.prototype = {
8511
8518
  // Disabled in options
8512
8519
  !this.crosshair ||
8513
8520
  // Snap
8514
- ((defined(point) || !pick(this.crosshair.snap, true)) === false)
8521
+ ((defined(point) || !pick(this.crosshair.snap, true)) === false) ||
8522
+ // Not on this axis (#4095, #2888)
8523
+ (point && point.series && point.series[this.coll] !== this)
8515
8524
  ) {
8516
8525
  this.hideCrosshair();
8517
8526
 
@@ -9000,16 +9009,6 @@ Tooltip.prototype = {
9000
9009
  tooltip.label.fadeOut();
9001
9010
  tooltip.isHidden = true;
9002
9011
  }, pick(delay, this.options.hideDelay, 500));
9003
-
9004
- // hide previous hoverPoints and set new
9005
- if (hoverPoints) {
9006
- each(hoverPoints, function (point) {
9007
- point.setState();
9008
- });
9009
- }
9010
-
9011
- this.chart.hoverPoints = null;
9012
- this.chart.hoverSeries = null;
9013
9012
  }
9014
9013
  },
9015
9014
 
@@ -9076,7 +9075,7 @@ Tooltip.prototype = {
9076
9075
  var chart = this.chart,
9077
9076
  distance = this.distance,
9078
9077
  ret = {},
9079
- h = point.h,
9078
+ h = point.h || 0, // #4117
9080
9079
  swapped,
9081
9080
  first = ['y', chart.chartHeight, boxHeight, point.plotY + chart.plotTop],
9082
9081
  second = ['x', chart.chartWidth, boxWidth, point.plotX + chart.plotLeft],
@@ -9270,7 +9269,7 @@ Tooltip.prototype = {
9270
9269
  plotY: y,
9271
9270
  negative: point.negative,
9272
9271
  ttBelow: point.ttBelow,
9273
- h: (point.shapeArgs && point.shapeArgs.height) || 0
9272
+ h: anchor[2] || 0
9274
9273
  });
9275
9274
 
9276
9275
  this.isHidden = false;
@@ -9299,7 +9298,7 @@ Tooltip.prototype = {
9299
9298
  // do the move
9300
9299
  this.move(
9301
9300
  mathRound(pos.x),
9302
- mathRound(pos.y),
9301
+ mathRound(pos.y || 0), // can be undefined (#3977)
9303
9302
  point.plotX + chart.plotLeft,
9304
9303
  point.plotY + chart.plotTop
9305
9304
  );
@@ -9322,7 +9321,7 @@ Tooltip.prototype = {
9322
9321
  day: 3
9323
9322
  },
9324
9323
  date,
9325
- lastN;
9324
+ lastN = 'millisecond'; // for sub-millisecond data, #4223
9326
9325
 
9327
9326
  if (closestPointRange) {
9328
9327
  date = dateFormat('%m-%d %H:%M:%S.%L', point.x);
@@ -9532,16 +9531,13 @@ Pointer.prototype = {
9532
9531
  tooltip = chart.tooltip,
9533
9532
  shared = tooltip ? tooltip.shared : false,
9534
9533
  followPointer,
9535
- //point,
9536
- //points,
9537
9534
  hoverPoint = chart.hoverPoint,
9538
9535
  hoverSeries = chart.hoverSeries,
9539
9536
  i,
9540
- //j,
9541
9537
  distance = chart.chartWidth,
9542
- rdistance = chart.chartWidth,
9543
9538
  anchor,
9544
9539
  noSharedTooltip,
9540
+ directTouch,
9545
9541
  kdpoints = [],
9546
9542
  kdpoint,
9547
9543
  kdpointT;
@@ -9557,7 +9553,7 @@ Pointer.prototype = {
9557
9553
  }
9558
9554
 
9559
9555
  // If it has a hoverPoint and that series requires direct touch (like columns),
9560
- // use the hoverPoint (#3899). Otherwise, search the k-d tree.
9556
+ // use the hoverPoint (#3899). Otherwise, search the k-d tree.
9561
9557
  if (!shared && hoverSeries && hoverSeries.directTouch && hoverPoint) {
9562
9558
  kdpoint = hoverPoint;
9563
9559
 
@@ -9567,8 +9563,9 @@ Pointer.prototype = {
9567
9563
  each(series, function (s) {
9568
9564
  // Skip hidden series
9569
9565
  noSharedTooltip = s.noSharedTooltip && shared;
9570
- if (s.visible && !noSharedTooltip && pick(s.options.enableMouseTracking, true)) { // #3821
9571
- kdpointT = s.searchPoint(e); // #3828
9566
+ directTouch = !shared && s.directTouch;
9567
+ if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
9568
+ kdpointT = s.searchPoint(e, !noSharedTooltip && s.kdDimensions === 1); // #3828
9572
9569
  if (kdpointT) {
9573
9570
  kdpoints.push(kdpointT);
9574
9571
  }
@@ -9576,19 +9573,15 @@ Pointer.prototype = {
9576
9573
  });
9577
9574
  // Find absolute nearest point
9578
9575
  each(kdpoints, function (p) {
9579
- if (p && defined(p.plotX) && defined(p.plotY)) {
9580
- if ((p.dist.distX < distance) || ((p.dist.distX === distance || p.series.kdDimensions > 1) &&
9581
- p.dist.distR < rdistance)) {
9582
- distance = p.dist.distX;
9583
- rdistance = p.dist.distR;
9584
- kdpoint = p;
9585
- }
9576
+ if (p && typeof p.dist === 'number' && p.dist < distance) {
9577
+ distance = p.dist;
9578
+ kdpoint = p;
9586
9579
  }
9587
9580
  });
9588
9581
  }
9589
9582
 
9590
- // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926
9591
- if (kdpoint && (kdpoint !== hoverPoint || (tooltip && tooltip.isHidden))) {
9583
+ // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200
9584
+ if (kdpoint && (kdpoint !== this.prevKDPoint || (tooltip && tooltip.isHidden))) {
9592
9585
  // Draw tooltip if necessary
9593
9586
  if (shared && !kdpoint.series.noSharedTooltip) {
9594
9587
  i = kdpoints.length;
@@ -9615,6 +9608,7 @@ Pointer.prototype = {
9615
9608
  }
9616
9609
  kdpoint.onMouseOver(e);
9617
9610
  }
9611
+ this.prevKDPoint = kdpoint;
9618
9612
 
9619
9613
  // Update positions (regardless of kdpoint or hoverPoint)
9620
9614
  } else {
@@ -9639,7 +9633,8 @@ Pointer.prototype = {
9639
9633
  each(chart.axes, function (axis) {
9640
9634
  axis.drawCrosshair(e, pick(kdpoint, hoverPoint));
9641
9635
  });
9642
-
9636
+
9637
+
9643
9638
  },
9644
9639
 
9645
9640
 
@@ -9654,8 +9649,9 @@ Pointer.prototype = {
9654
9649
  chart = pointer.chart,
9655
9650
  hoverSeries = chart.hoverSeries,
9656
9651
  hoverPoint = chart.hoverPoint,
9652
+ hoverPoints = chart.hoverPoints,
9657
9653
  tooltip = chart.tooltip,
9658
- tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint;
9654
+ tooltipPoints = tooltip && tooltip.shared ? hoverPoints : hoverPoint;
9659
9655
 
9660
9656
  // Narrow in allowMove
9661
9657
  allowMove = allowMove && tooltip && tooltipPoints;
@@ -9671,7 +9667,7 @@ Pointer.prototype = {
9671
9667
  hoverPoint.setState(hoverPoint.state, true);
9672
9668
  each(chart.axes, function (axis) {
9673
9669
  if (pick(axis.options.crosshair && axis.options.crosshair.snap, true)) {
9674
- axis.drawCrosshair(null, allowMove);
9670
+ axis.drawCrosshair(null, hoverPoint);
9675
9671
  } else {
9676
9672
  axis.hideCrosshair();
9677
9673
  }
@@ -9686,6 +9682,12 @@ Pointer.prototype = {
9686
9682
  hoverPoint.onMouseOut();
9687
9683
  }
9688
9684
 
9685
+ if (hoverPoints) {
9686
+ each(hoverPoints, function (point) {
9687
+ point.setState();
9688
+ });
9689
+ }
9690
+
9689
9691
  if (hoverSeries) {
9690
9692
  hoverSeries.onMouseOut();
9691
9693
  }
@@ -9704,7 +9706,7 @@ Pointer.prototype = {
9704
9706
  axis.hideCrosshair();
9705
9707
  });
9706
9708
 
9707
- pointer.hoverX = null;
9709
+ pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
9708
9710
 
9709
9711
  }
9710
9712
  },
@@ -10007,7 +10009,6 @@ Pointer.prototype = {
10007
10009
 
10008
10010
  e = this.normalize(e);
10009
10011
  e.originalEvent = e; // #3913
10010
- e.cancelBubble = true; // IE specific
10011
10012
 
10012
10013
  if (!chart.cancelClick) {
10013
10014
 
@@ -10208,8 +10209,14 @@ extend(Highcharts.Pointer.prototype, {
10208
10209
  chart.runTrackerClick) || self.runChartClick),
10209
10210
  clip = {};
10210
10211
 
10212
+ // Don't initiate panning until the user has pinched. This prevents us from
10213
+ // blocking page scrolling as users scroll down a long page (#4210).
10214
+ if (touchesLength > 1) {
10215
+ self.initiated = true;
10216
+ }
10217
+
10211
10218
  // On touch devices, only proceed to trigger click if a handler is defined
10212
- if (hasZoom && !fireClickEvent) {
10219
+ if (hasZoom && self.initiated && !fireClickEvent) {
10213
10220
  e.preventDefault();
10214
10221
  }
10215
10222
 
@@ -10271,7 +10278,10 @@ extend(Highcharts.Pointer.prototype, {
10271
10278
  }
10272
10279
  },
10273
10280
 
10274
- onContainerTouchStart: function (e) {
10281
+ /**
10282
+ * General touch handler shared by touchstart and touchmove.
10283
+ */
10284
+ touch: function (e, start) {
10275
10285
  var chart = this.chart;
10276
10286
 
10277
10287
  hoverChartIndex = chart.index;
@@ -10283,24 +10293,28 @@ extend(Highcharts.Pointer.prototype, {
10283
10293
  if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && !chart.openMenu) {
10284
10294
 
10285
10295
  // Run mouse events and display tooltip etc
10286
- this.runPointActions(e);
10296
+ if (start) {
10297
+ this.runPointActions(e);
10298
+ }
10287
10299
 
10288
10300
  this.pinch(e);
10289
10301
 
10290
- } else {
10302
+ } else if (start) {
10291
10303
  // Hide the tooltip on touching outside the plot area (#1203)
10292
10304
  this.reset();
10293
10305
  }
10294
10306
 
10295
10307
  } else if (e.touches.length === 2) {
10296
10308
  this.pinch(e);
10297
- }
10309
+ }
10310
+ },
10311
+
10312
+ onContainerTouchStart: function (e) {
10313
+ this.touch(e, true);
10298
10314
  },
10299
10315
 
10300
10316
  onContainerTouchMove: function (e) {
10301
- if (e.touches.length === 1 || e.touches.length === 2) {
10302
- this.pinch(e);
10303
- }
10317
+ this.touch(e);
10304
10318
  },
10305
10319
 
10306
10320
  onDocumentTouchEnd: function (e) {
@@ -10505,10 +10519,11 @@ Legend.prototype = {
10505
10519
  legendItemPos = item._legendItemPos,
10506
10520
  itemX = legendItemPos[0],
10507
10521
  itemY = legendItemPos[1],
10508
- checkbox = item.checkbox;
10522
+ checkbox = item.checkbox,
10523
+ legendGroup = item.legendGroup;
10509
10524
 
10510
- if (item.legendGroup) {
10511
- item.legendGroup.translate(
10525
+ if (legendGroup && legendGroup.element) {
10526
+ legendGroup.translate(
10512
10527
  ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4,
10513
10528
  itemY
10514
10529
  );
@@ -10539,16 +10554,6 @@ Legend.prototype = {
10539
10554
  }
10540
10555
  },
10541
10556
 
10542
- /**
10543
- * Destroy all items.
10544
- */
10545
- clearItems: function () {
10546
- var legend = this;
10547
- each(legend.getAllItems(), function (item) {
10548
- legend.destroyItem(item);
10549
- });
10550
- },
10551
-
10552
10557
  /**
10553
10558
  * Destroys the legend.
10554
10559
  */
@@ -10617,6 +10622,16 @@ Legend.prototype = {
10617
10622
  this.titleHeight = titleHeight;
10618
10623
  },
10619
10624
 
10625
+ /**
10626
+ * Set the legend item text
10627
+ */
10628
+ setText: function (item) {
10629
+ var options = this.options;
10630
+ item.legendItem.attr({
10631
+ text: options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item)
10632
+ });
10633
+ },
10634
+
10620
10635
  /**
10621
10636
  * Render a single specific legend item
10622
10637
  * @param {Object} item A series or point
@@ -10657,7 +10672,7 @@ Legend.prototype = {
10657
10672
 
10658
10673
  // Generate the list item text and add it to the group
10659
10674
  item.legendItem = li = renderer.text(
10660
- options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item),
10675
+ '',
10661
10676
  ltr ? symbolWidth + symbolPadding : -symbolPadding,
10662
10677
  legend.baseline || 0,
10663
10678
  useHTML
@@ -10692,6 +10707,9 @@ Legend.prototype = {
10692
10707
  }
10693
10708
  }
10694
10709
 
10710
+ // Always update the text
10711
+ legend.setText(item);
10712
+
10695
10713
  // calculate the positions for the next line
10696
10714
  bBox = li.getBBox();
10697
10715
 
@@ -11402,10 +11420,13 @@ Chart.prototype = {
11402
11420
  }
11403
11421
  }
11404
11422
 
11405
- // handle updated data in the series
11423
+ // Handle updated data in the series
11406
11424
  each(series, function (serie) {
11407
- if (serie.isDirty) { // prepare the data so axis can read it
11425
+ if (serie.isDirty) {
11408
11426
  if (serie.options.legendType === 'point') {
11427
+ if (serie.updateTotals) {
11428
+ serie.updateTotals();
11429
+ }
11409
11430
  redrawLegend = true;
11410
11431
  }
11411
11432
  }
@@ -12638,22 +12659,20 @@ var CenteredSeriesMixin = Highcharts.CenteredSeriesMixin = {
12638
12659
  centerOption = options.center,
12639
12660
  positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0],
12640
12661
  smallestSize = mathMin(plotWidth, plotHeight),
12641
- isPercent,
12642
12662
  i,
12643
12663
  value;
12644
12664
 
12645
12665
  for (i = 0; i < 4; ++i) {
12646
12666
  value = positions[i];
12647
- isPercent = /%$/.test(value);
12648
- handleSlicingRoom = i < 2 || (i === 2 && isPercent);
12649
- positions[i] = (isPercent ?
12650
- // i == 0: centerX, relative to width
12651
- // i == 1: centerY, relative to height
12652
- // i == 2: size, relative to smallestSize
12653
- // i == 3: innerSize, relative to size
12654
- [plotWidth, plotHeight, smallestSize, positions[2]][i] *
12655
- pInt(value) / 100 :
12656
- pInt(value)) + (handleSlicingRoom ? slicingRoom : 0);
12667
+ handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
12668
+
12669
+ // i == 0: centerX, relative to width
12670
+ // i == 1: centerY, relative to height
12671
+ // i == 2: size, relative to smallestSize
12672
+ // i == 3: innerSize, relative to size
12673
+ positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) +
12674
+ (handleSlicingRoom ? slicingRoom : 0);
12675
+
12657
12676
  }
12658
12677
  return positions;
12659
12678
  }
@@ -12728,7 +12747,7 @@ Point.prototype = {
12728
12747
  optionsToObject: function (options) {
12729
12748
  var ret = {},
12730
12749
  series = this.series,
12731
- keys = series.options.keys, // docs: http://jsfiddle.net/ch4v7n8v/1
12750
+ keys = series.options.keys,
12732
12751
  pointArrayMap = keys || series.pointArrayMap || ['y'],
12733
12752
  valueCount = pointArrayMap.length,
12734
12753
  firstItemType,
@@ -13591,8 +13610,8 @@ Series.prototype = {
13591
13610
  // For points within the visible range, including the first point outside the
13592
13611
  // visible range, consider y extremes
13593
13612
  validValue = y !== null && y !== UNDEFINED && (!yAxis.isLog || (y.length || y > 0));
13594
- withinRange = this.getExtremesFromAll || this.cropped || ((xData[i + 1] || x) >= xMin &&
13595
- (xData[i - 1] || x) <= xMax);
13613
+ withinRange = this.getExtremesFromAll || this.options.getExtremesFromAll || this.cropped ||
13614
+ ((xData[i + 1] || x) >= xMin && (xData[i - 1] || x) <= xMax);
13596
13615
 
13597
13616
  if (validValue && withinRange) {
13598
13617
 
@@ -13634,6 +13653,7 @@ Series.prototype = {
13634
13653
  pointPlacement = options.pointPlacement,
13635
13654
  dynamicallyPlaced = pointPlacement === 'between' || isNumber(pointPlacement),
13636
13655
  threshold = options.threshold,
13656
+ stackThreshold = options.startFromThreshold ? threshold : 0,
13637
13657
  plotX,
13638
13658
  plotY,
13639
13659
  lastPlotX,
@@ -13645,7 +13665,7 @@ Series.prototype = {
13645
13665
  xValue = point.x,
13646
13666
  yValue = point.y,
13647
13667
  yBottom = point.low,
13648
- stack = stacking && yAxis.stacks[(series.negStacks && yValue < threshold ? '-' : '') + series.stackKey],
13668
+ stack = stacking && yAxis.stacks[(series.negStacks && yValue < (stackThreshold ? 0 : threshold) ? '-' : '') + series.stackKey],
13649
13669
  pointStack,
13650
13670
  stackValues;
13651
13671
 
@@ -13656,7 +13676,7 @@ Series.prototype = {
13656
13676
  }
13657
13677
 
13658
13678
  // Get the plotX translation
13659
- point.plotX = plotX = xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags'); // Math.round fixes #591
13679
+ point.plotX = plotX = mathMin(mathMax(-1e5, xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')), 1e5); // #3923
13660
13680
 
13661
13681
 
13662
13682
  // Calculate the bottom y value for stacked series
@@ -13667,7 +13687,7 @@ Series.prototype = {
13667
13687
  yBottom = stackValues[0];
13668
13688
  yValue = stackValues[1];
13669
13689
 
13670
- if (yBottom === 0) {
13690
+ if (yBottom === stackThreshold) {
13671
13691
  yBottom = pick(threshold, yAxis.min);
13672
13692
  }
13673
13693
  if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
@@ -14320,8 +14340,13 @@ Series.prototype = {
14320
14340
  chartSizeMax = mathMax(chart.chartWidth, chart.chartHeight),
14321
14341
  zoneAxis = this.zoneAxis || 'y',
14322
14342
  axis = this[zoneAxis + 'Axis'],
14343
+ extremes,
14323
14344
  reversed = axis.reversed,
14345
+ inverted = chart.inverted,
14324
14346
  horiz = axis.horiz,
14347
+ pxRange,
14348
+ pxPosMin,
14349
+ pxPosMax,
14325
14350
  ignoreZones = false;
14326
14351
 
14327
14352
  if (zones.length && (graph || area)) {
@@ -14335,25 +14360,27 @@ Series.prototype = {
14335
14360
  }
14336
14361
 
14337
14362
  // Create the clips
14363
+ extremes = axis.getExtremes();
14338
14364
  each(zones, function (threshold, i) {
14339
- translatedFrom = pick(translatedTo, (reversed ? (horiz ? chart.plotWidth : 0) : (horiz ? 0 : axis.toPixels(axis.min))));
14340
- translatedTo = mathRound(axis.toPixels(pick(threshold.value, axis.max), true));
14341
-
14342
- if (axis.isXAxis) {
14343
- translatedFrom = translatedFrom > translatedTo ? translatedTo : translatedFrom; //#4006 from should be less or equal then to
14344
- } else {
14345
- translatedFrom = translatedFrom < translatedTo ? translatedTo : translatedFrom; //#4006 from should be less or equal then to
14346
- }
14347
14365
 
14366
+ translatedFrom = reversed ?
14367
+ (horiz ? chart.plotWidth : 0) :
14368
+ (horiz ? 0 : axis.toPixels(extremes.min));
14369
+ translatedFrom = mathMin(mathMax(pick(translatedTo, translatedFrom), 0), chartSizeMax);
14370
+ translatedTo = mathMin(mathMax(mathRound(axis.toPixels(pick(threshold.value, extremes.max), true)), 0), chartSizeMax);
14371
+
14348
14372
  if (ignoreZones) {
14349
- translatedFrom = translatedTo = axis.toPixels(axis.max);
14373
+ translatedFrom = translatedTo = axis.toPixels(extremes.max);
14350
14374
  }
14351
14375
 
14376
+ pxRange = Math.abs(translatedFrom - translatedTo);
14377
+ pxPosMin = mathMin(translatedFrom, translatedTo);
14378
+ pxPosMax = mathMax(translatedFrom, translatedTo);
14352
14379
  if (axis.isXAxis) {
14353
14380
  clipAttr = {
14354
- x: reversed ? translatedTo : translatedFrom,
14381
+ x: inverted ? pxPosMax : pxPosMin,
14355
14382
  y: 0,
14356
- width: Math.abs(translatedFrom - translatedTo),
14383
+ width: pxRange,
14357
14384
  height: chartSizeMax
14358
14385
  };
14359
14386
  if (!horiz) {
@@ -14362,21 +14389,21 @@ Series.prototype = {
14362
14389
  } else {
14363
14390
  clipAttr = {
14364
14391
  x: 0,
14365
- y: reversed ? translatedFrom : translatedTo,
14392
+ y: inverted ? pxPosMax : pxPosMin,
14366
14393
  width: chartSizeMax,
14367
- height: Math.abs(translatedFrom - translatedTo)
14368
- };
14394
+ height: pxRange
14395
+ };
14369
14396
  if (horiz) {
14370
14397
  clipAttr.y = chart.plotWidth - clipAttr.y;
14371
14398
  }
14372
- }
14399
+ }
14373
14400
 
14374
14401
  /// VML SUPPPORT
14375
14402
  if (chart.inverted && renderer.isVML) {
14376
14403
  if (axis.isXAxis) {
14377
14404
  clipAttr = {
14378
14405
  x: 0,
14379
- y: reversed ? translatedFrom : translatedTo,
14406
+ y: reversed ? pxPosMin : pxPosMax,
14380
14407
  height: clipAttr.width,
14381
14408
  width: chart.chartWidth
14382
14409
  };
@@ -14405,7 +14432,7 @@ Series.prototype = {
14405
14432
  }
14406
14433
  }
14407
14434
  // if this zone extends out of the axis, ignore the others
14408
- ignoreZones = threshold.value > axis.max;
14435
+ ignoreZones = threshold.value > extremes.max;
14409
14436
  });
14410
14437
  this.clips = clips;
14411
14438
  }
@@ -14639,11 +14666,9 @@ Series.prototype = {
14639
14666
  */
14640
14667
 
14641
14668
  kdDimensions: 1,
14642
- kdTree: null,
14643
14669
  kdAxisArray: ['clientX', 'plotY'],
14644
- kdComparer: 'distX',
14645
14670
 
14646
- searchPoint: function (e) {
14671
+ searchPoint: function (e, compareX) {
14647
14672
  var series = this,
14648
14673
  xAxis = series.xAxis,
14649
14674
  yAxis = series.yAxis,
@@ -14652,7 +14677,7 @@ Series.prototype = {
14652
14677
  return this.searchKDTree({
14653
14678
  clientX: inverted ? xAxis.len - e.chartY + xAxis.pos : e.chartX - xAxis.pos,
14654
14679
  plotY: inverted ? yAxis.len - e.chartX + yAxis.pos : e.chartY - yAxis.pos
14655
- });
14680
+ }, compareX);
14656
14681
  },
14657
14682
 
14658
14683
  buildKDTree: function () {
@@ -14675,7 +14700,7 @@ Series.prototype = {
14675
14700
 
14676
14701
  median = Math.floor(length / 2);
14677
14702
 
14678
- // build and return node
14703
+ // build and return nod
14679
14704
  return {
14680
14705
  point: points[median],
14681
14706
  left: _kdtree(points.slice(0, median), depth + 1, dimensions),
@@ -14690,9 +14715,9 @@ Series.prototype = {
14690
14715
  var points = grep(series.points, function (point) {
14691
14716
  return point.y !== null;
14692
14717
  });
14693
- series.kdTree = _kdtree(points, dimensions, dimensions);
14694
- }
14695
14718
 
14719
+ series.kdTree = _kdtree(points, dimensions, dimensions);
14720
+ }
14696
14721
  delete series.kdTree;
14697
14722
 
14698
14723
  if (series.options.kdSync) { // For testing tooltips, don't build async
@@ -14702,23 +14727,20 @@ Series.prototype = {
14702
14727
  }
14703
14728
  },
14704
14729
 
14705
- searchKDTree: function (point) {
14730
+ searchKDTree: function (point, compareX) {
14706
14731
  var series = this,
14707
- kdComparer = this.kdComparer,
14708
14732
  kdX = this.kdAxisArray[0],
14709
- kdY = this.kdAxisArray[1];
14733
+ kdY = this.kdAxisArray[1],
14734
+ kdComparer = compareX ? 'distX' : 'dist';
14710
14735
 
14711
- // Internal function
14712
- function _distance(p1, p2) {
14736
+ // Set the one and two dimensional distance on the point object
14737
+ function setDistance(p1, p2) {
14713
14738
  var x = (defined(p1[kdX]) && defined(p2[kdX])) ? Math.pow(p1[kdX] - p2[kdX], 2) : null,
14714
14739
  y = (defined(p1[kdY]) && defined(p2[kdY])) ? Math.pow(p1[kdY] - p2[kdY], 2) : null,
14715
14740
  r = (x || 0) + (y || 0);
14716
-
14717
- return {
14718
- distX: defined(x) ? Math.sqrt(x) : Number.MAX_VALUE,
14719
- distY: defined(y) ? Math.sqrt(y) : Number.MAX_VALUE,
14720
- distR: defined(r) ? Math.sqrt(r) : Number.MAX_VALUE
14721
- };
14741
+
14742
+ p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
14743
+ p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
14722
14744
  }
14723
14745
  function _search(search, tree, depth, dimensions) {
14724
14746
  var point = tree.point,
@@ -14729,27 +14751,28 @@ Series.prototype = {
14729
14751
  ret = point,
14730
14752
  nPoint1,
14731
14753
  nPoint2;
14732
- point.dist = _distance(search, point);
14754
+
14755
+ setDistance(search, point);
14733
14756
 
14734
14757
  // Pick side based on distance to splitting point
14735
14758
  tdist = search[axis] - point[axis];
14736
14759
  sideA = tdist < 0 ? 'left' : 'right';
14760
+ sideB = tdist < 0 ? 'right' : 'left';
14737
14761
 
14738
14762
  // End of tree
14739
14763
  if (tree[sideA]) {
14740
14764
  nPoint1 =_search(search, tree[sideA], depth + 1, dimensions);
14741
14765
 
14742
- ret = (nPoint1.dist[kdComparer] < ret.dist[kdComparer] ? nPoint1 : point);
14743
-
14744
- sideB = tdist < 0 ? 'right' : 'left';
14745
- if (tree[sideB]) {
14746
- // compare distance to current best to splitting point to decide wether to check side B or not
14747
- if (Math.sqrt(tdist*tdist) < ret.dist[kdComparer]) {
14748
- nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
14749
- ret = (nPoint2.dist[kdComparer] < ret.dist[kdComparer] ? nPoint2 : ret);
14750
- }
14766
+ ret = (nPoint1[kdComparer] < ret[kdComparer] ? nPoint1 : point);
14767
+ }
14768
+ if (tree[sideB]) {
14769
+ // compare distance to current best to splitting point to decide wether to check side B or not
14770
+ if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
14771
+ nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
14772
+ ret = (nPoint2[kdComparer] < ret[kdComparer] ? nPoint2 : ret);
14751
14773
  }
14752
14774
  }
14775
+
14753
14776
  return ret;
14754
14777
  }
14755
14778
 
@@ -14947,6 +14970,7 @@ Series.prototype.setStackedPoints = function () {
14947
14970
  yDataLength = yData.length,
14948
14971
  seriesOptions = series.options,
14949
14972
  threshold = seriesOptions.threshold,
14973
+ stackThreshold = seriesOptions.startFromThreshold ? threshold : 0,
14950
14974
  stackOption = seriesOptions.stack,
14951
14975
  stacking = seriesOptions.stacking,
14952
14976
  stackKey = series.stackKey,
@@ -14972,7 +14996,7 @@ Series.prototype.setStackedPoints = function () {
14972
14996
 
14973
14997
  // Read stacked values into a stack based on the x value,
14974
14998
  // the sign of y and the stack key. Stacking is also handled for null values (#739)
14975
- isNegative = negStacks && y < threshold;
14999
+ isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
14976
15000
  key = isNegative ? negKey : stackKey;
14977
15001
 
14978
15002
  // Create empty object for this stack if it doesn't exist yet
@@ -14992,7 +15016,10 @@ Series.prototype.setStackedPoints = function () {
14992
15016
 
14993
15017
  // If the StackItem doesn't exist, create it first
14994
15018
  stack = stacks[key][x];
14995
- stack.points[pointKey] = [stack.cum || 0];
15019
+ //stack.points[pointKey] = [stack.cum || stackThreshold];
15020
+ stack.points[pointKey] = [pick(stack.cum, stackThreshold)];
15021
+
15022
+
14996
15023
 
14997
15024
  // Add value to the stack total
14998
15025
  if (stacking === 'percent') {
@@ -15011,7 +15038,7 @@ Series.prototype.setStackedPoints = function () {
15011
15038
  stack.total = correctFloat(stack.total + (y || 0));
15012
15039
  }
15013
15040
 
15014
- stack.cum = (stack.cum || 0) + (y || 0);
15041
+ stack.cum = pick(stack.cum, stackThreshold) + (y || 0);
15015
15042
 
15016
15043
  stack.points[pointKey].push(stack.cum);
15017
15044
  stackedYData[i] = stack.cum;
@@ -15223,6 +15250,9 @@ extend(Point.prototype, {
15223
15250
  point.applyOptions(options);
15224
15251
 
15225
15252
  // Update visuals
15253
+ if (point.y === null && graphic) { // #4146
15254
+ point.graphic = graphic.destroy();
15255
+ }
15226
15256
  if (isObject(options) && !isArray(options)) {
15227
15257
  // Defer the actual redraw until getAttribs has been called (#3260)
15228
15258
  point.redraw = function () {
@@ -15230,7 +15260,7 @@ extend(Point.prototype, {
15230
15260
  if (options && options.marker && options.marker.symbol) {
15231
15261
  point.graphic = graphic.destroy();
15232
15262
  } else {
15233
- graphic.attr(point.pointAttr[point.state || '']);
15263
+ graphic.attr(point.pointAttr[point.state || ''])[point.visible ? 'show' : 'hide'](true); // #2430
15234
15264
  }
15235
15265
  }
15236
15266
  if (options && options.dataLabels && point.dataLabel) { // #2468
@@ -15255,9 +15285,8 @@ extend(Point.prototype, {
15255
15285
  chart.isDirtyBox = true;
15256
15286
  }
15257
15287
 
15258
- if (chart.legend.display && seriesOptions.legendType === 'point') { // #1831, #1885, #3934
15259
- series.updateTotals();
15260
- chart.legend.clearItems();
15288
+ if (seriesOptions.legendType === 'point') { // #1831, #1885
15289
+ chart.isDirtyLegend = true;
15261
15290
  }
15262
15291
  if (redraw) {
15263
15292
  chart.redraw(animation);
@@ -15986,6 +16015,7 @@ defaultPlotOptions.column = merge(defaultSeriesOptions, {
15986
16015
  verticalAlign: null, // auto
15987
16016
  y: null
15988
16017
  },
16018
+ startFromThreshold: true, // docs: http://jsfiddle.net/highcharts/hz8fopan/14/
15989
16019
  stickyTracking: false,
15990
16020
  tooltip: {
15991
16021
  distance: 6
@@ -16137,7 +16167,8 @@ var ColumnSeries = extendClass(Series, {
16137
16167
  // Record the new values
16138
16168
  each(series.points, function (point) {
16139
16169
  var yBottom = pick(point.yBottom, translatedThreshold),
16140
- plotY = mathMin(mathMax(-999 - yBottom, point.plotY), yAxis.len + 999 + yBottom), // Don't draw too far outside plot area (#1303, #2241)
16170
+ safeDistance = 999 + mathAbs(yBottom),
16171
+ plotY = mathMin(mathMax(-safeDistance, point.plotY), yAxis.len + safeDistance), // Don't draw too far outside plot area (#1303, #2241, #4264)
16141
16172
  barX = point.plotX + pointXOffset,
16142
16173
  barW = seriesBarW,
16143
16174
  barY = mathMin(plotY, yBottom),
@@ -16163,11 +16194,6 @@ var ColumnSeries = extendClass(Series, {
16163
16194
  point.barX = barX;
16164
16195
  point.pointWidth = pointWidth;
16165
16196
 
16166
- // Fix the tooltip on center of grouped columns (#1216, #424, #3648)
16167
- point.tooltipPos = chart.inverted ?
16168
- [yAxis.len + yAxis.pos - chart.plotLeft - plotY, series.xAxis.len - barX - barW / 2] :
16169
- [barX + barW / 2, plotY + yAxis.pos - chart.plotTop];
16170
-
16171
16197
  // Round off to obtain crisp edges and avoid overlapping with neighbours (#2694)
16172
16198
  right = mathRound(barX + barW) + xCrisp;
16173
16199
  barX = mathRound(barX) + xCrisp;
@@ -16184,6 +16210,11 @@ var ColumnSeries = extendClass(Series, {
16184
16210
  barH += 1;
16185
16211
  }
16186
16212
 
16213
+ // Fix the tooltip on center of grouped columns (#1216, #424, #3648)
16214
+ point.tooltipPos = chart.inverted ?
16215
+ [yAxis.len + yAxis.pos - chart.plotLeft - plotY, series.xAxis.len - barX - barW / 2, barH] :
16216
+ [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
16217
+
16187
16218
  // Register shape type and arguments to be used in drawPoints
16188
16219
  point.shapeType = 'rect';
16189
16220
  point.shapeArgs = {
@@ -16192,7 +16223,6 @@ var ColumnSeries = extendClass(Series, {
16192
16223
  width: barW,
16193
16224
  height: barH
16194
16225
  };
16195
-
16196
16226
  });
16197
16227
 
16198
16228
  },
@@ -16351,7 +16381,6 @@ var ScatterSeries = extendClass(Series, {
16351
16381
  trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
16352
16382
  takeOrdinalPosition: false, // #2342
16353
16383
  kdDimensions: 2,
16354
- kdComparer: 'distR',
16355
16384
  drawGraph: function () {
16356
16385
  if (this.options.lineWidth) {
16357
16386
  Series.prototype.drawGraph.call(this);
@@ -16437,20 +16466,22 @@ var PiePoint = extendClass(Point, {
16437
16466
  * @param {Boolean} vis Whether to show the slice or not. If undefined, the
16438
16467
  * visibility is toggled
16439
16468
  */
16440
- setVisible: function (vis, force) {
16469
+ setVisible: function (vis, redraw) {
16441
16470
  var point = this,
16442
16471
  series = point.series,
16443
16472
  chart = series.chart,
16444
- doRedraw = !series.isDirty && series.options.ignoreHiddenPoint;
16473
+ ignoreHiddenPoint = series.options.ignoreHiddenPoint;
16474
+
16475
+ redraw = pick(redraw, ignoreHiddenPoint);
16476
+
16477
+ if (vis !== point.visible) {
16445
16478
 
16446
- // Only if the value has changed
16447
- if (vis !== point.visible || force) {
16448
-
16449
16479
  // If called without an argument, toggle visibility
16450
16480
  point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis;
16451
16481
  series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
16452
16482
 
16453
- // Show and hide associated elements
16483
+ // Show and hide associated elements. This is performed regardless of redraw or not,
16484
+ // because chart.redraw only handles full series.
16454
16485
  each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) {
16455
16486
  if (point[key]) {
16456
16487
  point[key][vis ? 'show' : 'hide'](true);
@@ -16458,19 +16489,15 @@ var PiePoint = extendClass(Point, {
16458
16489
  });
16459
16490
 
16460
16491
  if (point.legendItem) {
16461
- if (chart.hasRendered) {
16462
- series.updateTotals();
16463
- chart.legend.clearItems();
16464
- if (!doRedraw) {
16465
- chart.legend.render();
16466
- }
16467
- }
16468
16492
  chart.legend.colorizeItem(point, vis);
16469
16493
  }
16470
-
16494
+
16471
16495
  // Handle ignore hidden slices
16472
- if (doRedraw) {
16496
+ if (ignoreHiddenPoint) {
16473
16497
  series.isDirty = true;
16498
+ }
16499
+
16500
+ if (redraw) {
16474
16501
  chart.redraw();
16475
16502
  }
16476
16503
  }
@@ -16529,6 +16556,7 @@ var PieSeries = {
16529
16556
  isCartesian: false,
16530
16557
  pointClass: PiePoint,
16531
16558
  requireSorting: false,
16559
+ directTouch: true,
16532
16560
  noSharedTooltip: true,
16533
16561
  trackerGroups: ['group', 'dataLabelsGroup'],
16534
16562
  axisTypes: [],
@@ -16597,24 +16625,14 @@ var PieSeries = {
16597
16625
  updateTotals: function () {
16598
16626
  var i,
16599
16627
  total = 0,
16600
- points,
16601
- len,
16628
+ points = this.points,
16629
+ len = points.length,
16602
16630
  point,
16603
16631
  ignoreHiddenPoint = this.options.ignoreHiddenPoint;
16604
16632
 
16605
- // Populate local vars
16606
- points = this.points;
16607
- len = points.length;
16608
-
16609
16633
  // Get the total sum
16610
16634
  for (i = 0; i < len; i++) {
16611
16635
  point = points[i];
16612
-
16613
- // Disallow negative values (#1530, #3623)
16614
- if (point.y < 0) {
16615
- point.y = null;
16616
- }
16617
-
16618
16636
  total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
16619
16637
  }
16620
16638
  this.total = total;
@@ -16622,7 +16640,6 @@ var PieSeries = {
16622
16640
  // Set each point's properties
16623
16641
  for (i = 0; i < len; i++) {
16624
16642
  point = points[i];
16625
- //point.percentage = (total <= 0 || ignoreHiddenPoint && !point.visible) ? 0 : point.y / total * 100;
16626
16643
  point.percentage = (total > 0 && (point.visible || !ignoreHiddenPoint)) ? point.y / total * 100 : 0;
16627
16644
  point.total = total;
16628
16645
  }
@@ -16762,7 +16779,8 @@ var PieSeries = {
16762
16779
  //group,
16763
16780
  shadow = series.options.shadow,
16764
16781
  shadowGroup,
16765
- shapeArgs;
16782
+ shapeArgs,
16783
+ attr;
16766
16784
 
16767
16785
  if (shadow && !series.shadowGroup) {
16768
16786
  series.shadowGroup = renderer.g('shadow')
@@ -16771,9 +16789,6 @@ var PieSeries = {
16771
16789
 
16772
16790
  // draw the slices
16773
16791
  each(series.points, function (point) {
16774
-
16775
- var visible = point.options.visible;
16776
-
16777
16792
  graphic = point.graphic;
16778
16793
  shapeArgs = point.shapeArgs;
16779
16794
  shadowGroup = point.shadowGroup;
@@ -16797,27 +16812,24 @@ var PieSeries = {
16797
16812
 
16798
16813
  // draw the slice
16799
16814
  if (graphic) {
16800
- graphic.animate(extend(shapeArgs, groupTranslation));
16815
+ graphic.animate(extend(shapeArgs, groupTranslation));
16801
16816
  } else {
16817
+ attr = { 'stroke-linejoin': 'round' };
16818
+ if (!point.visible) {
16819
+ attr.visibility = 'hidden';
16820
+ }
16821
+
16802
16822
  point.graphic = graphic = renderer[point.shapeType](shapeArgs)
16803
16823
  .setRadialReference(series.center)
16804
16824
  .attr(
16805
16825
  point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]
16806
16826
  )
16807
- .attr({
16808
- 'stroke-linejoin': 'round'
16809
- //zIndex: 1 // #2722 (reversed)
16810
- })
16827
+ .attr(attr)
16811
16828
  .attr(groupTranslation)
16812
16829
  .add(series.group)
16813
16830
  .shadow(shadow, shadowGroup);
16814
16831
  }
16815
16832
 
16816
- // Detect point specific visibility (#2430)
16817
- if (visible !== undefined) {
16818
- point.setVisible(visible, true);
16819
- }
16820
-
16821
16833
  });
16822
16834
 
16823
16835
  },
@@ -17370,7 +17382,6 @@ if (seriesTypes.pie) {
17370
17382
 
17371
17383
  // get the x - use the natural x position for first and last slot, to prevent the top
17372
17384
  // and botton slice connectors from touching each other on either side
17373
- // Problem: Should check that it makes sense - http://jsfiddle.net/highcharts/n1y6ngxz/
17374
17385
  x = options.justify ?
17375
17386
  seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
17376
17387
  series.getX(y === centerY - radius - distanceOption || y === centerY + radius + distanceOption ? naturalY : y, i);
@@ -17428,7 +17439,7 @@ if (seriesTypes.pie) {
17428
17439
  labelPos = point.labelPos;
17429
17440
  dataLabel = point.dataLabel;
17430
17441
 
17431
- if (dataLabel && dataLabel._pos) {
17442
+ if (dataLabel && dataLabel._pos && point.visible) {
17432
17443
  visibility = dataLabel._attr.visibility;
17433
17444
  x = dataLabel.connX;
17434
17445
  y = dataLabel.connY;
@@ -17479,7 +17490,7 @@ if (seriesTypes.pie) {
17479
17490
  var dataLabel = point.dataLabel,
17480
17491
  _pos;
17481
17492
 
17482
- if (dataLabel) {
17493
+ if (dataLabel && point.visible) {
17483
17494
  _pos = dataLabel._pos;
17484
17495
  if (_pos) {
17485
17496
  dataLabel.attr(dataLabel._attr);
@@ -17538,6 +17549,7 @@ if (seriesTypes.pie) {
17538
17549
  // If the size must be decreased, we need to run translate and drawDataLabels again
17539
17550
  if (newSize < center[2]) {
17540
17551
  center[2] = newSize;
17552
+ center[3] = relativeLength(options.innerSize || 0, newSize);
17541
17553
  this.translate(center);
17542
17554
  each(this.points, function (point) {
17543
17555
  if (point.dataLabel) {
@@ -17565,7 +17577,7 @@ if (seriesTypes.column) {
17565
17577
  var inverted = this.chart.inverted,
17566
17578
  series = point.series,
17567
17579
  dlBox = point.dlBox || point.shapeArgs, // data label box for alignment
17568
- below = point.below || (point.plotY > pick(this.translatedThreshold, series.yAxis.len)),
17580
+ below = pick(point.below, point.plotY > pick(this.translatedThreshold, series.yAxis.len)), // point.below is used in range series
17569
17581
  inside = pick(options.inside, !!this.options.stacking); // draw it inside the box?
17570
17582
 
17571
17583
  // Align to the column itself, or the top of it
@@ -17613,7 +17625,7 @@ if (seriesTypes.column) {
17613
17625
 
17614
17626
 
17615
17627
  /**
17616
- * Highcharts JS v4.1.5 (2015-04-13)
17628
+ * Highcharts JS v4.1.6 (2015-06-12)
17617
17629
  * Highcharts module to hide overlapping data labels. This module is included by default in Highmaps.
17618
17630
  *
17619
17631
  * (c) 2010-2014 Torstein Honsi
@@ -17625,6 +17637,7 @@ if (seriesTypes.column) {
17625
17637
  (function (H) {
17626
17638
  var Chart = H.Chart,
17627
17639
  each = H.each,
17640
+ pick = H.pick,
17628
17641
  addEvent = HighchartsAdapter.addEvent;
17629
17642
 
17630
17643
  // Collect potensial overlapping data labels. Stack labels probably don't need to be
@@ -17638,7 +17651,7 @@ if (seriesTypes.column) {
17638
17651
  if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866
17639
17652
  each(series.points, function (point) {
17640
17653
  if (point.dataLabel) {
17641
- point.dataLabel.labelrank = point.labelrank;
17654
+ point.dataLabel.labelrank = pick(point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
17642
17655
  labels.push(point.dataLabel);
17643
17656
  }
17644
17657
  });
@@ -17685,6 +17698,13 @@ if (seriesTypes.column) {
17685
17698
  }
17686
17699
  }
17687
17700
 
17701
+ // Prevent a situation in a gradually rising slope, that each label
17702
+ // will hide the previous one because the previous one always has
17703
+ // lower rank.
17704
+ labels.sort(function (a, b) {
17705
+ return b.labelrank - a.labelrank;
17706
+ });
17707
+
17688
17708
  // Detect overlapping labels
17689
17709
  for (i = 0; i < len; i++) {
17690
17710
  label1 = labels[i];
@@ -18353,6 +18373,8 @@ extend(Series.prototype, {
18353
18373
  tooltip = chart.tooltip,
18354
18374
  hoverPoint = chart.hoverPoint;
18355
18375
 
18376
+ chart.hoverSeries = null; // #182, set to null before the mouseOut event fires
18377
+
18356
18378
  // trigger mouse out on the point, which must be in this series
18357
18379
  if (hoverPoint) {
18358
18380
  hoverPoint.onMouseOut();
@@ -18371,7 +18393,6 @@ extend(Series.prototype, {
18371
18393
 
18372
18394
  // set normal state
18373
18395
  series.setState();
18374
- chart.hoverSeries = null;
18375
18396
  },
18376
18397
 
18377
18398
  /**