highcharts-rails 4.0.1 → 4.0.3

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.
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.0.1 (2014-04-24)
5
+ * @license Highcharts JS v4.0.3 (2014-07-03)
6
6
  *
7
7
  * (c) 2009-2014 Torstein Honsi
8
8
  *
@@ -108,7 +108,7 @@ extend(Pane.prototype, {
108
108
  [1, '#DDD']
109
109
  ]
110
110
  },
111
- from: Number.MIN_VALUE, // corrected to axis min
111
+ from: -Number.MAX_VALUE, // corrected to axis min
112
112
  innerRadius: 0,
113
113
  to: Number.MAX_VALUE, // corrected to axis max
114
114
  outerRadius: '105%'
@@ -658,6 +658,7 @@ defaultPlotOptions.arearange = merge(defaultPlotOptions.area, {
658
658
  },
659
659
  trackByArea: true,
660
660
  dataLabels: {
661
+ align: null,
661
662
  verticalAlign: null,
662
663
  xLow: 0,
663
664
  xHigh: 0,
@@ -795,6 +796,7 @@ seriesTypes.arearange = extendClass(seriesTypes.area, {
795
796
  originalDataLabels = [],
796
797
  seriesProto = Series.prototype,
797
798
  dataLabelOptions = this.options.dataLabels,
799
+ align = dataLabelOptions.align,
798
800
  point,
799
801
  inverted = this.chart.inverted;
800
802
 
@@ -818,7 +820,9 @@ seriesTypes.arearange = extendClass(seriesTypes.area, {
818
820
  // Set the default offset
819
821
  point.below = false;
820
822
  if (inverted) {
821
- dataLabelOptions.align = 'left';
823
+ if (!align) {
824
+ dataLabelOptions.align = 'left';
825
+ }
822
826
  dataLabelOptions.x = dataLabelOptions.xHigh;
823
827
  } else {
824
828
  dataLabelOptions.y = dataLabelOptions.yHigh;
@@ -845,7 +849,9 @@ seriesTypes.arearange = extendClass(seriesTypes.area, {
845
849
  // Set the default offset
846
850
  point.below = true;
847
851
  if (inverted) {
848
- dataLabelOptions.align = 'right';
852
+ if (!align) {
853
+ dataLabelOptions.align = 'right';
854
+ }
849
855
  dataLabelOptions.x = dataLabelOptions.xLow;
850
856
  } else {
851
857
  dataLabelOptions.y = dataLabelOptions.yLow;
@@ -855,6 +861,8 @@ seriesTypes.arearange = extendClass(seriesTypes.area, {
855
861
  seriesProto.drawDataLabels.apply(this, arguments);
856
862
  }
857
863
  }
864
+
865
+ dataLabelOptions.align = align;
858
866
 
859
867
  },
860
868
 
@@ -862,7 +870,7 @@ seriesTypes.arearange = extendClass(seriesTypes.area, {
862
870
  seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
863
871
  },
864
872
 
865
- getSymbol: seriesTypes.column.prototype.getSymbol,
873
+ getSymbol: noop,
866
874
 
867
875
  drawPoints: noop
868
876
  });/**
@@ -931,7 +939,7 @@ seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, {
931
939
  shapeArgs.y = y;
932
940
  });
933
941
  },
934
- trackerGroups: ['group', 'dataLabels'],
942
+ trackerGroups: ['group', 'dataLabelsGroup'],
935
943
  drawGraph: noop,
936
944
  pointAttrToOptions: colProto.pointAttrToOptions,
937
945
  drawPoints: colProto.drawPoints,
@@ -1013,7 +1021,7 @@ var GaugeSeries = {
1013
1021
  drawGraph: noop,
1014
1022
  fixedBox: true,
1015
1023
  forceDL: true,
1016
- trackerGroups: ['group', 'dataLabels'],
1024
+ trackerGroups: ['group', 'dataLabelsGroup'],
1017
1025
 
1018
1026
  /**
1019
1027
  * Calculate paths etc
@@ -1504,7 +1512,12 @@ defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, {
1504
1512
  lineWidth: 1,
1505
1513
  lineColor: '#333',
1506
1514
  dashStyle: 'dot',
1507
- borderColor: '#333'
1515
+ borderColor: '#333',
1516
+ states: {
1517
+ hover: {
1518
+ lineWidthPlus: 0 // #3126
1519
+ }
1520
+ }
1508
1521
  });
1509
1522
 
1510
1523
 
@@ -1535,7 +1548,7 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1535
1548
  translate: function () {
1536
1549
  var series = this,
1537
1550
  options = series.options,
1538
- axis = series.yAxis,
1551
+ yAxis = series.yAxis,
1539
1552
  len,
1540
1553
  i,
1541
1554
  points,
@@ -1544,13 +1557,15 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1544
1557
  stack,
1545
1558
  y,
1546
1559
  previousY,
1560
+ previousIntermediate,
1547
1561
  stackPoint,
1548
- threshold = options.threshold;
1562
+ threshold = options.threshold,
1563
+ tooltipY;
1549
1564
 
1550
1565
  // run column series translate
1551
1566
  seriesTypes.column.prototype.translate.apply(this);
1552
1567
 
1553
- previousY = threshold;
1568
+ previousY = previousIntermediate = threshold;
1554
1569
  points = series.points;
1555
1570
 
1556
1571
  for (i = 0, len = points.length; i < len; i++) {
@@ -1569,13 +1584,18 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1569
1584
 
1570
1585
  // up points
1571
1586
  y = mathMax(previousY, previousY + point.y) + stackPoint[0];
1572
- shapeArgs.y = axis.translate(y, 0, 1);
1587
+ shapeArgs.y = yAxis.translate(y, 0, 1);
1573
1588
 
1574
1589
 
1575
1590
  // sum points
1576
- if (point.isSum || point.isIntermediateSum) {
1577
- shapeArgs.y = axis.translate(stackPoint[1], 0, 1);
1578
- shapeArgs.height = axis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
1591
+ if (point.isSum) {
1592
+ shapeArgs.y = yAxis.translate(stackPoint[1], 0, 1);
1593
+ shapeArgs.height = yAxis.translate(stackPoint[0], 0, 1) - shapeArgs.y;
1594
+
1595
+ } else if (point.isIntermediateSum) {
1596
+ shapeArgs.y = yAxis.translate(stackPoint[1], 0, 1);
1597
+ shapeArgs.height = yAxis.translate(previousIntermediate, 0, 1) - shapeArgs.y;
1598
+ previousIntermediate = stackPoint[1];
1579
1599
 
1580
1600
  // if it's not the sum point, update previous stack end position
1581
1601
  } else {
@@ -1589,8 +1609,17 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1589
1609
  }
1590
1610
 
1591
1611
  point.plotY = shapeArgs.y = mathRound(shapeArgs.y) - (series.borderWidth % 2) / 2;
1592
- shapeArgs.height = mathRound(shapeArgs.height);
1612
+ shapeArgs.height = mathMax(mathRound(shapeArgs.height), 0.001); // #3151
1593
1613
  point.yBottom = shapeArgs.y + shapeArgs.height;
1614
+
1615
+ // Correct tooltip placement (#3014)
1616
+ tooltipY = point.plotY + (point.negative ? shapeArgs.height : 0);
1617
+ if (series.chart.inverted) {
1618
+ point.tooltipPos[0] = yAxis.len - tooltipY;
1619
+ } else {
1620
+ point.tooltipPos[1] = tooltipY;
1621
+ }
1622
+
1594
1623
  }
1595
1624
  },
1596
1625
 
@@ -1622,7 +1651,6 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1622
1651
  yData[i] = sum;
1623
1652
  } else if (y === "intermediateSum" || point.isIntermediateSum) {
1624
1653
  yData[i] = subSum;
1625
- subSum = threshold;
1626
1654
  } else {
1627
1655
  sum += y;
1628
1656
  subSum += y;
@@ -1749,7 +1777,9 @@ seriesTypes.waterfall = extendClass(seriesTypes.column, {
1749
1777
  // 1 - set default options
1750
1778
  defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, {
1751
1779
  dataLabels: {
1752
- format: '{point.z}',
1780
+ formatter: function () { // #2945
1781
+ return this.point.z;
1782
+ },
1753
1783
  inside: true,
1754
1784
  style: {
1755
1785
  color: 'white',
@@ -2011,14 +2041,14 @@ Axis.prototype.beforePadding = function () {
2011
2041
  // Find the min and max Z
2012
2042
  zData = series.zData;
2013
2043
  if (zData.length) { // #1735
2014
- zMin = math.min(
2044
+ zMin = pick(seriesOptions.zMin, math.min(
2015
2045
  zMin,
2016
2046
  math.max(
2017
2047
  arrayMin(zData),
2018
2048
  seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE
2019
2049
  )
2020
- );
2021
- zMax = math.max(zMax, arrayMax(zData));
2050
+ ));
2051
+ zMax = pick(seriesOptions.zMax, math.max(zMax, arrayMax(zData)));
2022
2052
  }
2023
2053
  }
2024
2054
  }
@@ -2908,7 +2908,7 @@ if (CanvasRenderingContext2D) {
2908
2908
  });
2909
2909
  }
2910
2910
  }/**
2911
- * @license Highcharts JS v4.0.1 (2014-04-24)
2911
+ * @license Highcharts JS v4.0.3 (2014-07-03)
2912
2912
  * CanVGRenderer Extension module
2913
2913
  *
2914
2914
  * (c) 2011-2012 Torstein Honsi, Erik Olsson
@@ -24,8 +24,9 @@
24
24
  * - complete : Function(chartOptions)
25
25
  * The callback that is evaluated when the data is finished loading, optionally from an
26
26
  * external source, and parsed. The first argument passed is a finished chart options
27
- * object, containing series and an xAxis with categories if applicable. Thise options
28
- * can be extended with additional options and passed directly to the chart constructor.
27
+ * object, containing the series. Thise options
28
+ * can be extended with additional options and passed directly to the chart constructor. This is
29
+ * related to the parsed callback, that goes in at an earlier stage.
29
30
  *
30
31
  * - csv : String
31
32
  * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn
@@ -57,7 +58,9 @@
57
58
  *
58
59
  * - parsed : Function
59
60
  * A callback function to access the parsed columns, the two-dimentional input data
60
- * array directly, before they are interpreted into series data and categories.
61
+ * array directly, before they are interpreted into series data and categories. See also
62
+ * the complete callback, that goes in on a later stage where the raw columns are interpreted
63
+ * into a Highcharts option structure.
61
64
  *
62
65
  * - parseDate : Function
63
66
  * A callback function to parse string representations of dates into JavaScript timestamps.
@@ -84,7 +87,7 @@
84
87
  // JSLint options:
85
88
  /*global jQuery */
86
89
 
87
- (function (Highcharts) {
90
+ (function (Highcharts) { // docs
88
91
 
89
92
  // Utilities
90
93
  var each = Highcharts.each;
@@ -339,7 +342,7 @@
339
342
  headerRow = null;
340
343
  }
341
344
  });
342
- this.headerRow = 0;
345
+ this.headerRow = 0;
343
346
  },
344
347
 
345
348
  /**
@@ -485,10 +488,11 @@
485
488
  data,
486
489
  i,
487
490
  j,
488
- seriesIndex;
491
+ seriesIndex,
492
+ chartOptions;
489
493
 
490
494
 
491
- if (options.complete) {
495
+ if (options.complete || options.afterComplete) {
492
496
 
493
497
  this.getColumnDistribution();
494
498
 
@@ -557,12 +561,20 @@
557
561
  }
558
562
 
559
563
  // Do the callback
560
- options.complete({
564
+ chartOptions = {
561
565
  xAxis: {
562
566
  type: type
563
567
  },
564
568
  series: series
565
- });
569
+ };
570
+ if (options.complete) {
571
+ options.complete(chartOptions);
572
+ }
573
+ // The afterComplete hook is used internally to avoid conflict with the externally
574
+ // available complete option.
575
+ if (options.afterComplete) {
576
+ options.afterComplete(chartOptions);
577
+ }
566
578
  }
567
579
  }
568
580
  });
@@ -580,14 +592,17 @@
580
592
 
581
593
  if (userOptions && userOptions.data) {
582
594
  Highcharts.data(Highcharts.extend(userOptions.data, {
583
- complete: function (dataOptions) {
595
+ afterComplete: function (dataOptions) {
596
+ var i, series;
584
597
 
585
598
  // Merge series configs
586
599
  if (userOptions.hasOwnProperty('series')) {
587
600
  if (typeof userOptions.series === 'object') {
588
- each(userOptions.series, function (series, i) {
601
+ i = Math.max(userOptions.series.length, dataOptions.series.length);
602
+ while (i--) {
603
+ series = userOptions.series[i] || {};
589
604
  userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]);
590
- });
605
+ }
591
606
  } else { // Allow merging in dataOptions.series (#2856)
592
607
  delete userOptions.series;
593
608
  }
@@ -17,6 +17,7 @@
17
17
  each = H.each,
18
18
  extend = H.extend,
19
19
  format = H.format,
20
+ pick = H.pick,
20
21
  wrap = H.wrap,
21
22
  Chart = H.Chart,
22
23
  seriesTypes = H.seriesTypes,
@@ -77,7 +78,7 @@
77
78
  visibility: 'inherit'
78
79
  })
79
80
  .animate({
80
- opacity: 1
81
+ opacity: pick(this.newOpacity, 1) // newOpacity used in maps
81
82
  }, animation || {
82
83
  duration: 250
83
84
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.1 (2014-04-24)
2
+ * @license Highcharts JS v4.0.3 (2014-07-03)
3
3
  * Exporting module
4
4
  *
5
5
  * (c) 2010-2014 Torstein Honsi
@@ -250,6 +250,7 @@ extend(Chart.prototype, {
250
250
  each(chart.series, function (serie) {
251
251
  seriesOptions = merge(serie.options, {
252
252
  animation: false, // turn off animation
253
+ enableMouseTracking: false,
253
254
  showCheckbox: false,
254
255
  visible: serie.visible
255
256
  });
@@ -294,7 +295,10 @@ extend(Chart.prototype, {
294
295
  .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
295
296
  .replace(/ href=/g, ' xlink:href=')
296
297
  .replace(/\n/, ' ')
297
- .replace(/<\/svg>.*?$/, '</svg>') // any HTML added to the container after the SVG (#894)
298
+ // Any HTML added to the container after the SVG (#894)
299
+ .replace(/<\/svg>.*?$/, '</svg>')
300
+ // Batik doesn't support rgba fills and strokes (#3095)
301
+ .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
298
302
  /* This fails in IE < 8
299
303
  .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
300
304
  return s2 +'.'+ s3[0];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.0.1 (2014-04-24)
2
+ * @license Highcharts JS v4.0.3 (2014-07-03)
3
3
  *
4
4
  * (c) 2011-2014 Torstein Honsi
5
5
  *
@@ -17,6 +17,7 @@ var UNDEFINED,
17
17
  Legend = Highcharts.Legend,
18
18
  LegendSymbolMixin = Highcharts.LegendSymbolMixin,
19
19
  Series = Highcharts.Series,
20
+ SVGRenderer = Highcharts.SVGRenderer,
20
21
 
21
22
  defaultOptions = Highcharts.getOptions(),
22
23
  each = Highcharts.each,
@@ -48,7 +49,7 @@ extend(ColorAxis.prototype, {
48
49
  startOnTick: true,
49
50
  endOnTick: true,
50
51
  offset: 0,
51
- marker: { // docs: use another name?
52
+ marker: {
52
53
  animation: {
53
54
  duration: 50
54
55
  },
@@ -117,6 +118,7 @@ extend(ColorAxis.prototype, {
117
118
  colorCounter = 0,
118
119
  options = this.options;
119
120
  this.dataClasses = dataClasses = [];
121
+ this.legendItems = [];
120
122
 
121
123
  each(userOptions.dataClasses, function (dataClass, i) {
122
124
  var colors;
@@ -213,7 +215,7 @@ extend(ColorAxis.prototype, {
213
215
  if (this.isLog) {
214
216
  value = this.val2lin(value);
215
217
  }
216
- pos = 1 - ((this.max - value) / (this.max - this.min));
218
+ pos = 1 - ((this.max - value) / ((this.max - this.min) || 1));
217
219
  i = stops.length;
218
220
  while (i--) {
219
221
  if (pos > stops[i][0]) {
@@ -225,7 +227,7 @@ extend(ColorAxis.prototype, {
225
227
 
226
228
  // The position within the gradient
227
229
  pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
228
-
230
+
229
231
  color = this.tweenColors(
230
232
  from.color,
231
233
  to.color,
@@ -236,7 +238,9 @@ extend(ColorAxis.prototype, {
236
238
  },
237
239
 
238
240
  getOffset: function () {
239
- var group = this.legendGroup;
241
+ var group = this.legendGroup,
242
+ sideOffset = this.chart.axisOffset[this.side];
243
+
240
244
  if (group) {
241
245
 
242
246
  Axis.prototype.getOffset.call(this);
@@ -250,6 +254,8 @@ extend(ColorAxis.prototype, {
250
254
 
251
255
  this.added = true;
252
256
  }
257
+ // Reset it to avoid color axis reserving space
258
+ this.chart.axisOffset[this.side] = sideOffset;
253
259
  }
254
260
  },
255
261
 
@@ -281,7 +287,8 @@ extend(ColorAxis.prototype, {
281
287
  box,
282
288
  width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
283
289
  height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
284
- labelPadding = pick(legendOptions.labelPadding, horiz ? 10 : 30);
290
+ labelPadding = pick(legendOptions.labelPadding, horiz ? 16 : 30),
291
+ itemDistance = pick(legendOptions.itemDistance, 10);
285
292
 
286
293
  this.setLegendColor();
287
294
 
@@ -297,7 +304,7 @@ extend(ColorAxis.prototype, {
297
304
  box = item.legendSymbol.getBBox();
298
305
 
299
306
  // Set how much space this legend item takes up
300
- this.legendItemWidth = width + padding + (horiz ? 0 : labelPadding);
307
+ this.legendItemWidth = width + padding + (horiz ? itemDistance : labelPadding);
301
308
  this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
302
309
  },
303
310
  /**
@@ -372,56 +379,58 @@ extend(ColorAxis.prototype, {
372
379
  getDataClassLegendSymbols: function () {
373
380
  var axis = this,
374
381
  chart = this.chart,
375
- legendItems = [],
382
+ legendItems = this.legendItems,
376
383
  legendOptions = chart.options.legend,
377
384
  valueDecimals = legendOptions.valueDecimals,
378
385
  valueSuffix = legendOptions.valueSuffix || '',
379
386
  name;
380
387
 
381
- each(this.dataClasses, function (dataClass, i) {
382
- var vis = true,
383
- from = dataClass.from,
384
- to = dataClass.to;
385
-
386
- // Assemble the default name. This can be overridden by legend.options.labelFormatter
387
- name = '';
388
- if (from === UNDEFINED) {
389
- name = '< ';
390
- } else if (to === UNDEFINED) {
391
- name = '> ';
392
- }
393
- if (from !== UNDEFINED) {
394
- name += numberFormat(from, valueDecimals) + valueSuffix;
395
- }
396
- if (from !== UNDEFINED && to !== UNDEFINED) {
397
- name += ' - ';
398
- }
399
- if (to !== UNDEFINED) {
400
- name += numberFormat(to, valueDecimals) + valueSuffix;
401
- }
402
-
403
- // Add a mock object to the legend items
404
- legendItems.push(extend({
405
- chart: chart,
406
- name: name,
407
- options: {},
408
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
409
- visible: true,
410
- setState: noop,
411
- setVisible: function () {
412
- vis = this.visible = !vis;
413
- each(axis.series, function (series) {
414
- each(series.points, function (point) {
415
- if (point.dataClass === i) {
416
- point.setVisible(vis);
417
- }
418
- });
419
- });
420
-
421
- chart.legend.colorizeItem(this, vis);
388
+ if (!legendItems.length) {
389
+ each(this.dataClasses, function (dataClass, i) {
390
+ var vis = true,
391
+ from = dataClass.from,
392
+ to = dataClass.to;
393
+
394
+ // Assemble the default name. This can be overridden by legend.options.labelFormatter
395
+ name = '';
396
+ if (from === UNDEFINED) {
397
+ name = '< ';
398
+ } else if (to === UNDEFINED) {
399
+ name = '> ';
422
400
  }
423
- }, dataClass));
424
- });
401
+ if (from !== UNDEFINED) {
402
+ name += numberFormat(from, valueDecimals) + valueSuffix;
403
+ }
404
+ if (from !== UNDEFINED && to !== UNDEFINED) {
405
+ name += ' - ';
406
+ }
407
+ if (to !== UNDEFINED) {
408
+ name += numberFormat(to, valueDecimals) + valueSuffix;
409
+ }
410
+
411
+ // Add a mock object to the legend items
412
+ legendItems.push(extend({
413
+ chart: chart,
414
+ name: name,
415
+ options: {},
416
+ drawLegendSymbol: LegendSymbolMixin.drawRectangle,
417
+ visible: true,
418
+ setState: noop,
419
+ setVisible: function () {
420
+ vis = this.visible = !vis;
421
+ each(axis.series, function (series) {
422
+ each(series.points, function (point) {
423
+ if (point.dataClass === i) {
424
+ point.setVisible(vis);
425
+ }
426
+ });
427
+ });
428
+
429
+ chart.legend.colorizeItem(this, vis);
430
+ }
431
+ }, dataClass));
432
+ });
433
+ }
425
434
  return legendItems;
426
435
  },
427
436
  name: '' // Prevents 'undefined' in legend in IE8
@@ -489,6 +498,7 @@ var colorSeriesMixin = {
489
498
  trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
490
499
  getSymbol: noop,
491
500
  parallelArrays: ['x', 'y', 'value'],
501
+ colorKey: 'value',
492
502
 
493
503
  /**
494
504
  * In choropleth maps, the color is a result of the value, so this needs translation too
@@ -496,13 +506,14 @@ var colorSeriesMixin = {
496
506
  translateColors: function () {
497
507
  var series = this,
498
508
  nullColor = this.options.nullColor,
499
- colorAxis = this.colorAxis;
509
+ colorAxis = this.colorAxis,
510
+ colorKey = this.colorKey;
500
511
 
501
512
  each(this.data, function (point) {
502
- var value = point.value,
513
+ var value = point[colorKey],
503
514
  color;
504
515
 
505
- color = value === null ? nullColor : colorAxis ? colorAxis.toColor(value, point) : (point.color) || series.color;
516
+ color = value === null ? nullColor : (colorAxis && value !== undefined) ? colorAxis.toColor(value, point) : point.color || series.color;
506
517
 
507
518
  if (color) {
508
519
  point.color = color;
@@ -510,6 +521,55 @@ var colorSeriesMixin = {
510
521
  });
511
522
  }
512
523
  };
524
+
525
+
526
+ /**
527
+ * Wrap the buildText method and add the hook for add text stroke
528
+ */
529
+ wrap(SVGRenderer.prototype, 'buildText', function (proceed, wrapper) {
530
+
531
+ var textStroke = wrapper.styles && wrapper.styles.HcTextStroke;
532
+
533
+ proceed.call(this, wrapper);
534
+
535
+ // Apply the text stroke
536
+ if (textStroke && wrapper.applyTextStroke) {
537
+ wrapper.applyTextStroke(textStroke);
538
+ }
539
+ });
540
+
541
+ /**
542
+ * Apply an outside text stroke to data labels, based on the custom CSS property, HcTextStroke.
543
+ * Consider moving this to Highcharts core, also makes sense on stacked columns etc.
544
+ */
545
+ SVGRenderer.prototype.Element.prototype.applyTextStroke = function (textStroke) {
546
+ var elem = this.element,
547
+ tspans,
548
+ firstChild;
549
+
550
+ textStroke = textStroke.split(' ');
551
+ tspans = elem.getElementsByTagName('tspan');
552
+ firstChild = elem.firstChild;
553
+
554
+ // In order to get the right y position of the clones,
555
+ // copy over the y setter
556
+ this.ySetter = this.xSetter;
557
+
558
+ each([].slice.call(tspans), function (tspan, y) {
559
+ var clone;
560
+ if (y === 0) {
561
+ tspan.setAttribute('x', elem.getAttribute('x'));
562
+ if ((y = elem.getAttribute('y')) !== null) {
563
+ tspan.setAttribute('y', y);
564
+ }
565
+ }
566
+ clone = tspan.cloneNode(1);
567
+ clone.setAttribute('stroke', textStroke[1]);
568
+ clone.setAttribute('stroke-width', textStroke[0]);
569
+ clone.setAttribute('stroke-linejoin', 'round');
570
+ elem.insertBefore(clone, firstChild);
571
+ });
572
+ };
513
573
  /**
514
574
  * Extend the default options with map options
515
575
  */
@@ -518,14 +578,16 @@ defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
518
578
  borderWidth: 0,
519
579
  nullColor: '#F8F8F8',
520
580
  dataLabels: {
521
- format: '{point.value}',
581
+ formatter: function () { // #2945
582
+ return this.point.value;
583
+ },
522
584
  verticalAlign: 'middle',
523
585
  crop: false,
524
586
  overflow: false,
525
587
  style: {
526
588
  color: 'white',
527
589
  fontWeight: 'bold',
528
- textShadow: '0 0 5px black'
590
+ HcTextStroke: '1px rgba(0,0,0,0.5)'
529
591
  }
530
592
  },
531
593
  marker: null,
@@ -584,6 +646,13 @@ seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
584
646
  });
585
647
 
586
648
  series.translateColors();
649
+
650
+ // Make sure colors are updated on colorAxis update (#2893)
651
+ if (this.chart.hasRendered) {
652
+ each(series.points, function (point) {
653
+ point.shapeArgs.fill = point.color;
654
+ });
655
+ }
587
656
  },
588
657
  drawPoints: seriesTypes.column.prototype.drawPoints,
589
658
  animate: noop,