highcharts-rails 4.1.5 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  /**