highcharts-rails 4.0.1 → 4.0.3

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: 8f8d206def106fb6c373fc89d3ef802cd616e055
4
- data.tar.gz: cabae3745451a60e09475e9a53264e596a1264ff
3
+ metadata.gz: 246fa26d563f5ebbff59b1fad5fd84c66c8139ed
4
+ data.tar.gz: c866482da07406051a1cf89456b8ffb981b8a2b7
5
5
  SHA512:
6
- metadata.gz: 4d878e9ce4d16f2db87415380c3d3086c5b1ce5cd111a554493de850dadbfed601981dd130e350b0cf68b0eefeef8308cf6a09b7d6c77a4ad867f3cb8d6ab458
7
- data.tar.gz: ad2ecd78ae400af9200bb431f478b3bcffb78d3f865fc4fa65a1055dc8199363d1d110d86e3ba2dea2e789d1c3038af93696cc97bef5186ab1f80ff30258a6bb
6
+ metadata.gz: 1b22d1db2a1152606cb5fab1e3c3a7b073e6ee8f45bf1d84d686f4b7f251bc9d34373bff24336a14fd55be63ee55cdd4f32befa0c1f676bd4df8a3e3cbbd6b9f
7
+ data.tar.gz: 9a7c6ac887b3282e97490c4050b95505fd2c927a57937c3966b9c8648bd4a6b1448c8acd3a43503e615906deb4295c867936516889eb248acc1ec41e8daa3b2c
@@ -1,3 +1,63 @@
1
+ # 4.0.3 / 2014-07-03
2
+
3
+ * Updated Highcharts to 4.0.3
4
+ * Added 3d options edgeColor and edgeWidth to distinguish from borders that have different defaults.
5
+ * Added option, chart.panKey, to allow panning and zooming on the same chart. The chart can now be configured so the user can pan by holding down the shift key while dragging.
6
+ * Added features zMin and zMax for bubble series, to set the Z value corresponding to minSize and maxSize independently from the data.
7
+ * Changed default top position for loading label to 45%, which results in a vertically centered label.
8
+ * Better handling of data label heights on pie charts, related to #2630.
9
+ * Better handling of dynamic font sizes. Adjust tooltip text-wrapping. Apply dynamic font size (em) on line breaks. Fixed placement of title and axis labels when using ems for font size. Added support for em font-size in legend.itemStyle.
10
+ * Fixed #2251, a regression since 3.0.8 causing text and graphic elements to become selected while zooming in old IE.
11
+ * Fixed #3195 where a short axis with startOnTick and endOnTick set to false would display no ticks at all.
12
+ * Fixed #2694 causing columns to overlap by one pixel in some cases when pointPadding is 0.
13
+ * Fixed #2227, waterfall breaking with more than one intermediate sum.
14
+ * Fixed #3163, pie chart data labels displaying outside plot area.
15
+ * Fixed #1991, a regression causing area stacks with null or missing values to draw incorrectly.
16
+ * Fixed #3158 causing wrong line wraps on text containing markup.
17
+ * Fixed #3151, waterfall columns not displaying a border when the rendered height was 0.
18
+ * Downloadbuilder missing metatags in SVG, added filterset, fixes #3106
19
+ * Fixed #3072 causing unexpected borders after hover on column series when borderWidth was 0.
20
+ * Fixed #3132 causing wrong line breaks in SVG text (pseudo HTML) containing markup.
21
+ * Enabled mouse tracking on data labels for some series types where it had been mistakenly disabled.
22
+ * Fixed #2697, JavaScript error in IE11 on destroying a chart containing useHTML elements after the chart container has first been emptied.
23
+ * Fixed #3126, waterfall graph getting wider on hover. Introduced new options, lineWidthPlus, radiusPlus for hover state of graphs and markers.
24
+ * Fixed #3116, halo failing to animate when a point was updated to a new position. Removed redundant pointer.reset call on Axis.redraw because Chart.redraw also calls the same method.
25
+ * Fixed #3113, roundoff error throwing off calculations of extremes on log axis.
26
+ * Fixed #3104, touch panning not being able to pan outside the data range even if the axis min and max options were outside the range.
27
+ * Fixed #3098, plot area size of a pie chart not being recalculated after the legend size changed.
28
+ * Fixed #3090 causing default 3d options not to be interpreted correctly.
29
+ * Fixed #3094 causing series Z index to change after Series.update.
30
+ * Fixed regression issue #3095, semi-transparent fills coming out as black in exports created by Batik.
31
+ * Fixed #3082, positioning of 3d pie datalabels.
32
+ * Fixed #3080 causing JS error on low chart due to negative height of the legend clip rectangle.
33
+ * Fixed #3051 causing wrong Z indexes for slices in 3d pie charts.
34
+ * Fixed #3075 causing touch panning on a categorized axis to alter range.
35
+ * Fixed #3039, series options was not read for 3D.
36
+ * Fixed #2893 causing heatmap point colors not to update after updating the color axis coloring.
37
+ * Fixed #1080, loading text not reflowing to changed chart size.
38
+ * Fixed #3056 with tooltip chevron not pointing exactly on the right point of the graph when animating between close positions.
39
+ * Fixed #3044 causing JS errors in some cases when axis label formatter returned null or undefined. Closes #3050.
40
+ * Fixed #3053 causing series to be cropped too tight on logarithmic X axis when the number of points exceeded the crop threshold.
41
+ * Fixed #2240 causing the point mouse out event not to fire when the tooltip is shared.
42
+ * Fixed #2997, solidgauge issue with points past ±360° or below yAxis.min.
43
+ * Fixed #3028 causing the last series to become unlinked when calling Series.update on several linked series in succession.
44
+ * Fixed #3027 causing truncation of axis labels when the axis title offset is set and the axis title is shorter than the labels.
45
+ * Fixed #3023 causing defered data labels to show even if series was hidden in the meantime while animating.
46
+ * Fixed #3003 causing rotated data labels to be misaligned after redraw when using Standalone Framework.
47
+ * Fixed #3017 causing centering of data labels on inverted range series not to work.
48
+ * Fixed #3007, with halo showing on invisible points in pies.
49
+ * Fixed #3016 causing halo on sliced pie series to diplay incorrectly.
50
+ * Fixed #3000, labels wrongly aligned on plot lines with multiple axes.
51
+ * Fixed #3014 causing wrong tooltip position in waterfall charts in Highcharts 4.
52
+ * Fixed #3006, prevent null and 0 points from rendering in 3D Pies.
53
+ * Fixed #2982 by returning the added plot line or plot band object after Axis.addPlotLine or Axis.addPlotBand.
54
+ * Fixed #2977, set mimimum view point distance for 3D.
55
+ * Fixed #2975 with clipping not being updated on column series and derived series types after chart resize.
56
+ * Fixed #2968, 3d columns not drawn correctly when equal to max.
57
+ * Fixed #2963 where columns were badly drawn in Chrome due to a rendering bug.
58
+ * Fixed #2962 causing data labels on gauges not to display unless defer is set to true.
59
+ * Fixed #2954 that caused invalid SVG attributes on animating in and out grid lines when running older jQuery versions or the Standalone Framework.
60
+
1
61
  # 4.0.1 / 2014-04-24
2
62
 
3
63
  * Updated Highcharts to 4.0.1
@@ -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
  *
@@ -11,7 +11,7 @@
11
11
 
12
12
  // JSLint options:
13
13
  /*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
14
-
14
+ /*jslint ass: true, sloppy: true, forin: true, plusplus: true, nomen: true, vars: true, regexp: true, newcap: true, browser: true, continue: true, white: true */
15
15
  (function () {
16
16
  // encapsulated variables
17
17
  var UNDEFINED,
@@ -52,11 +52,12 @@ var UNDEFINED,
52
52
  globalAnimation,
53
53
  pathAnim,
54
54
  timeUnits,
55
- noop = function () {},
55
+ error,
56
+ noop = function () { return UNDEFINED; },
56
57
  charts = [],
57
58
  chartCount = 0,
58
59
  PRODUCT = 'Highcharts',
59
- VERSION = '4.0.1',
60
+ VERSION = '4.0.3',
60
61
 
61
62
  // some constants for frequently used strings
62
63
  DIV = 'div',
@@ -73,14 +74,6 @@ var UNDEFINED,
73
74
  NORMAL_STATE = '',
74
75
  HOVER_STATE = 'hover',
75
76
  SELECT_STATE = 'select',
76
- MILLISECOND = 'millisecond',
77
- SECOND = 'second',
78
- MINUTE = 'minute',
79
- HOUR = 'hour',
80
- DAY = 'day',
81
- WEEK = 'week',
82
- MONTH = 'month',
83
- YEAR = 'year',
84
77
 
85
78
  // Object for extending Axis
86
79
  AxisPlotLineOrBandExtension,
@@ -105,11 +98,15 @@ var UNDEFINED,
105
98
 
106
99
 
107
100
  // lookup over the types and the associated classes
108
- seriesTypes = {};
101
+ seriesTypes = {},
102
+ Highcharts;
109
103
 
110
104
  // The Highcharts namespace
111
- var Highcharts = win.Highcharts = win.Highcharts ? error(16, true) : {};
112
-
105
+ if (win.Highcharts) {
106
+ error(16, true);
107
+ } else {
108
+ Highcharts = win.Highcharts = {};
109
+ }
113
110
  /**
114
111
  * Extend an object with the members of another
115
112
  * @param {Object} a The object to be extended
@@ -179,22 +176,6 @@ function merge() {
179
176
  return ret;
180
177
  }
181
178
 
182
- /**
183
- * Take an array and turn into a hash with even number arguments as keys and odd numbers as
184
- * values. Allows creating constants for commonly used style properties, attributes etc.
185
- * Avoid it in performance critical situations like looping
186
- */
187
- function hash() {
188
- var i = 0,
189
- args = arguments,
190
- length = args.length,
191
- obj = {};
192
- for (; i < length; i++) {
193
- obj[args[i++]] = args[i];
194
- }
195
- return obj;
196
- }
197
-
198
179
  /**
199
180
  * Shortcut for parseInt
200
181
  * @param {Object} s
@@ -217,7 +198,7 @@ function isString(s) {
217
198
  * @param {Object} obj
218
199
  */
219
200
  function isObject(obj) {
220
- return typeof obj === 'object';
201
+ return obj && typeof obj === 'object';
221
202
  }
222
203
 
223
204
  /**
@@ -317,7 +298,7 @@ function pick() {
317
298
  length = args.length;
318
299
  for (i = 0; i < length; i++) {
319
300
  arg = args[i];
320
- if (typeof arg !== 'undefined' && arg !== null) {
301
+ if (arg !== UNDEFINED && arg !== null) {
321
302
  return arg;
322
303
  }
323
304
  }
@@ -368,7 +349,7 @@ function createElement(tag, attribs, styles, parent, nopad) {
368
349
  * @param {Object} members
369
350
  */
370
351
  function extendClass(parent, members) {
371
- var object = function () {};
352
+ var object = function () { return UNDEFINED; };
372
353
  object.prototype = new parent();
373
354
  extend(object.prototype, members);
374
355
  return object;
@@ -618,35 +599,6 @@ function normalizeTickInterval(interval, multiples, magnitude, options) {
618
599
  }
619
600
 
620
601
 
621
- /**
622
- * Helper class that contains variuos counters that are local to the chart.
623
- */
624
- function ChartCounters() {
625
- this.color = 0;
626
- this.symbol = 0;
627
- }
628
-
629
- ChartCounters.prototype = {
630
- /**
631
- * Wraps the color counter if it reaches the specified length.
632
- */
633
- wrapColor: function (length) {
634
- if (this.color >= length) {
635
- this.color = 0;
636
- }
637
- },
638
-
639
- /**
640
- * Wraps the symbol counter if it reaches the specified length.
641
- */
642
- wrapSymbol: function (length) {
643
- if (this.symbol >= length) {
644
- this.symbol = 0;
645
- }
646
- }
647
- };
648
-
649
-
650
602
  /**
651
603
  * Utility method that sorts an object array and keeping the order of equal items.
652
604
  * ECMA script standard does not specify the behaviour when items are equal.
@@ -748,14 +700,16 @@ function discardElement(element) {
748
700
  /**
749
701
  * Provide error messages for debugging, with links to online explanation
750
702
  */
751
- function error(code, stop) {
703
+ error = function (code, stop) {
752
704
  var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
753
705
  if (stop) {
754
706
  throw msg;
755
- } else if (win.console) {
707
+ }
708
+ // else ...
709
+ if (win.console) {
756
710
  console.log(msg);
757
711
  }
758
- }
712
+ };
759
713
 
760
714
  /**
761
715
  * Fix JS round off float errors
@@ -780,18 +734,16 @@ function setAnimation(animation, chart) {
780
734
  /**
781
735
  * The time unit lookup
782
736
  */
783
- /*jslint white: true*/
784
- timeUnits = hash(
785
- MILLISECOND, 1,
786
- SECOND, 1000,
787
- MINUTE, 60000,
788
- HOUR, 3600000,
789
- DAY, 24 * 3600000,
790
- WEEK, 7 * 24 * 3600000,
791
- MONTH, 31 * 24 * 3600000,
792
- YEAR, 31556952000
793
- );
794
- /*jslint white: false*/
737
+ timeUnits = {
738
+ millisecond: 1,
739
+ second: 1000,
740
+ minute: 60000,
741
+ hour: 3600000,
742
+ day: 24 * 3600000,
743
+ week: 7 * 24 * 3600000,
744
+ month: 31 * 24 * 3600000,
745
+ year: 31556952000
746
+ };
795
747
  /**
796
748
  * Path interpolation algorithm used across adapters
797
749
  */
@@ -1006,9 +958,9 @@ pathAnim = {
1006
958
 
1007
959
  } :
1008
960
  function (arr, fn) { // legacy
1009
- var i = 0,
961
+ var i,
1010
962
  len = arr.length;
1011
- for (; i < len; i++) {
963
+ for (i = 0; i < len; i++) {
1012
964
  if (fn.call(arr[i], arr[i], i, arr) === false) {
1013
965
  return i;
1014
966
  }
@@ -1298,7 +1250,7 @@ defaultLabelOptions = {
1298
1250
 
1299
1251
  defaultOptions = {
1300
1252
  colors: ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c',
1301
- '#8085e9', '#f15c80', '#e4d354', '#8085e8', '#8d4653', '#91e8e1'], // docs
1253
+ '#8085e9', '#f15c80', '#e4d354', '#8085e8', '#8d4653', '#91e8e1'],
1302
1254
  symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
1303
1255
  lang: {
1304
1256
  loading: 'Loading...',
@@ -1315,8 +1267,8 @@ defaultOptions = {
1315
1267
  global: {
1316
1268
  useUTC: true,
1317
1269
  //timezoneOffset: 0,
1318
- canvasToolsURL: 'http://code.highcharts.com/4.0.1/modules/canvas-tools.js',
1319
- VMLRadialGradientURL: 'http://code.highcharts.com/4.0.1/gfx/vml-radial-gradient.png'
1270
+ canvasToolsURL: 'http://code.highcharts.com/4.0.3/modules/canvas-tools.js',
1271
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.0.3/gfx/vml-radial-gradient.png'
1320
1272
  },
1321
1273
  chart: {
1322
1274
  //animation: true,
@@ -1373,7 +1325,7 @@ defaultOptions = {
1373
1325
  // verticalAlign: 'top',
1374
1326
  // y: null,
1375
1327
  style: {
1376
- color: '#333333', // docs
1328
+ color: '#333333',
1377
1329
  fontSize: '18px'
1378
1330
  }
1379
1331
 
@@ -1386,7 +1338,7 @@ defaultOptions = {
1386
1338
  // verticalAlign: 'top',
1387
1339
  // y: null,
1388
1340
  style: {
1389
- color: '#555555' // docs
1341
+ color: '#555555'
1390
1342
  }
1391
1343
  },
1392
1344
 
@@ -1417,8 +1369,9 @@ defaultOptions = {
1417
1369
  //fillColor: null,
1418
1370
  states: { // states for a single point
1419
1371
  hover: {
1420
- enabled: true
1421
- //radius: base + 2
1372
+ enabled: true,
1373
+ lineWidthPlus: 1,
1374
+ radiusPlus: 2
1422
1375
  },
1423
1376
  select: {
1424
1377
  fillColor: '#FFFFFF',
@@ -1454,7 +1407,7 @@ defaultOptions = {
1454
1407
  states: { // states for the entire series
1455
1408
  hover: {
1456
1409
  //enabled: false,
1457
- //lineWidth: base + 1,
1410
+ lineWidthPlus: 1,
1458
1411
  marker: {
1459
1412
  // lineWidth: base + 1,
1460
1413
  // radius: base + 1
@@ -1498,7 +1451,7 @@ defaultOptions = {
1498
1451
  },
1499
1452
  //borderWidth: 0,
1500
1453
  borderColor: '#909090',
1501
- borderRadius: 0, // docs
1454
+ borderRadius: 0,
1502
1455
  navigation: {
1503
1456
  // animation: true,
1504
1457
  activeColor: '#274b6d',
@@ -1514,9 +1467,9 @@ defaultOptions = {
1514
1467
  padding: '5px'
1515
1468
  },*/
1516
1469
  itemStyle: {
1517
- color: '#333333', // docs
1470
+ color: '#333333',
1518
1471
  fontSize: '12px',
1519
- fontWeight: 'bold' // docs
1472
+ fontWeight: 'bold'
1520
1473
  },
1521
1474
  itemHoverStyle: {
1522
1475
  //cursor: 'pointer', removed as of #601
@@ -1551,7 +1504,7 @@ defaultOptions = {
1551
1504
  labelStyle: {
1552
1505
  fontWeight: 'bold',
1553
1506
  position: RELATIVE,
1554
- top: '1em'
1507
+ top: '45%'
1555
1508
  },
1556
1509
  // showDuration: 0,
1557
1510
  style: {
@@ -1581,9 +1534,9 @@ defaultOptions = {
1581
1534
  },
1582
1535
  //formatter: defaultFormatter,
1583
1536
  headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
1584
- pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>', // docs
1537
+ pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
1585
1538
  shadow: true,
1586
- //shape: 'calout',
1539
+ //shape: 'callout',
1587
1540
  //shared: false,
1588
1541
  snap: isTouchDevice ? 25 : 10,
1589
1542
  style: {
@@ -1816,6 +1769,13 @@ var Color = function (input) {
1816
1769
  function SVGElement() {}
1817
1770
 
1818
1771
  SVGElement.prototype = {
1772
+
1773
+ // Default base for animation
1774
+ opacity: 1,
1775
+ // For labels, these CSS properties are applied to the <text> node directly
1776
+ textProps: ['fontSize', 'fontWeight', 'fontFamily', 'color',
1777
+ 'lineHeight', 'width', 'textDecoration', 'textShadow', 'HcTextStroke'],
1778
+
1819
1779
  /**
1820
1780
  * Initialize the SVG renderer
1821
1781
  * @param {Object} renderer
@@ -1828,10 +1788,7 @@ SVGElement.prototype = {
1828
1788
  doc.createElementNS(SVG_NS, nodeName);
1829
1789
  wrapper.renderer = renderer;
1830
1790
  },
1831
- /**
1832
- * Default base for animation
1833
- */
1834
- opacity: 1,
1791
+
1835
1792
  /**
1836
1793
  * Animate a given attribute
1837
1794
  * @param {Object} params
@@ -1853,6 +1810,7 @@ SVGElement.prototype = {
1853
1810
  complete();
1854
1811
  }
1855
1812
  }
1813
+ return this;
1856
1814
  },
1857
1815
 
1858
1816
  /**
@@ -2108,7 +2066,7 @@ SVGElement.prototype = {
2108
2066
  key,
2109
2067
  attribs = {},
2110
2068
  normalizer,
2111
- strokeWidth = rect.strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
2069
+ strokeWidth = rect.strokeWidth || wrapper.strokeWidth || 0;
2112
2070
 
2113
2071
  normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
2114
2072
 
@@ -2624,8 +2582,8 @@ SVGElement.prototype = {
2624
2582
  });
2625
2583
  }
2626
2584
 
2627
- // In case of useHTML, clean up empty containers emulating SVG groups (#1960, #2393).
2628
- while (parentToClean && parentToClean.div.childNodes.length === 0) {
2585
+ // In case of useHTML, clean up empty containers emulating SVG groups (#1960, #2393, #2697).
2586
+ while (parentToClean && parentToClean.div && parentToClean.div.childNodes.length === 0) {
2629
2587
  grandParent = parentToClean.parentGroup;
2630
2588
  wrapper.safeRemoveChild(parentToClean.div);
2631
2589
  delete parentToClean.div;
@@ -2712,7 +2670,7 @@ SVGElement.prototype = {
2712
2670
  _defaultGetter: function (key) {
2713
2671
  var ret = pick(this[key], this.element ? this.element.getAttribute(key) : null, 0);
2714
2672
 
2715
- if (/^[0-9\.]+$/.test(ret)) { // is numerical
2673
+ if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
2716
2674
  ret = parseFloat(ret);
2717
2675
  }
2718
2676
  return ret;
@@ -2743,11 +2701,12 @@ SVGElement.prototype = {
2743
2701
  .replace(/dot/g, '1,3,')
2744
2702
  .replace('dash', '4,3,')
2745
2703
  .replace(/,$/, '')
2704
+ .replace('solid', 1)
2746
2705
  .split(','); // ending comma
2747
2706
 
2748
2707
  i = value.length;
2749
2708
  while (i--) {
2750
- value[i] = pInt(value[i]) * this.element.getAttribute('stroke-width');
2709
+ value[i] = pInt(value[i]) * this['stroke-width'];
2751
2710
  }
2752
2711
  value = value.join(',');
2753
2712
  this.element.setAttribute('stroke-dasharray', value);
@@ -2760,15 +2719,6 @@ SVGElement.prototype = {
2760
2719
  this[key] = value;
2761
2720
  element.setAttribute(key, value);
2762
2721
  },
2763
- // In Chrome/Win < 6 as well as Batik and PhantomJS as of 1.9.7, the stroke attribute can't be set when the stroke-
2764
- // width is 0. #1369
2765
- 'stroke-widthSetter': function (value, key, element) {
2766
- if (value === 0) {
2767
- value = 0.00001;
2768
- }
2769
- this.strokeWidth = value; // read in symbol paths like 'callout'
2770
- element.setAttribute(key, value);
2771
- },
2772
2722
  titleSetter: function (value) {
2773
2723
  var titleNode = this.element.getElementsByTagName('title')[0];
2774
2724
  if (!titleNode) {
@@ -2789,7 +2739,6 @@ SVGElement.prototype = {
2789
2739
  }
2790
2740
  },
2791
2741
  fillSetter: function (value, key, element) {
2792
-
2793
2742
  if (typeof value === 'string') {
2794
2743
  element.setAttribute(key, value);
2795
2744
  } else if (value) {
@@ -2813,24 +2762,22 @@ SVGElement.prototype.translateXSetter = SVGElement.prototype.translateYSetter =
2813
2762
  this[key] = value;
2814
2763
  this.doTransform = true;
2815
2764
  };
2816
- SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
2817
-
2818
2765
 
2819
-
2820
- // In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke-
2821
- // width is 0. #1369
2822
- /*SVGElement.prototype['stroke-widthSetter'] = SVGElement.prototype.strokeSetter = function (value, key) {
2766
+ // WebKit and Batik have problems with a stroke-width of zero, so in this case we remove the
2767
+ // stroke attribute altogether. #1270, #1369, #3065, #3072.
2768
+ SVGElement.prototype['stroke-widthSetter'] = SVGElement.prototype.strokeSetter = function (value, key, element) {
2823
2769
  this[key] = value;
2824
2770
  // Only apply the stroke attribute if the stroke width is defined and larger than 0
2825
2771
  if (this.stroke && this['stroke-width']) {
2826
- this.element.setAttribute('stroke', this.stroke);
2827
- this.element.setAttribute('stroke-width', this['stroke-width']);
2772
+ this.strokeWidth = this['stroke-width'];
2773
+ SVGElement.prototype.fillSetter.call(this, this.stroke, 'stroke', element); // use prototype as instance may be overridden
2774
+ element.setAttribute('stroke-width', this['stroke-width']);
2828
2775
  this.hasStroke = true;
2829
2776
  } else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
2830
- this.element.removeAttribute('stroke');
2777
+ element.removeAttribute('stroke');
2831
2778
  this.hasStroke = false;
2832
2779
  }
2833
- };*/
2780
+ };
2834
2781
 
2835
2782
 
2836
2783
  /**
@@ -3002,6 +2949,7 @@ SVGRenderer.prototype = {
3002
2949
  textStyles = wrapper.styles,
3003
2950
  width = wrapper.textWidth,
3004
2951
  textLineHeight = textStyles && textStyles.lineHeight,
2952
+ textStroke = textStyles && textStyles.HcTextStroke,
3005
2953
  i = childNodes.length,
3006
2954
  getLineHeight = function (tspan) {
3007
2955
  return textLineHeight ?
@@ -3009,7 +2957,8 @@ SVGRenderer.prototype = {
3009
2957
  renderer.fontMetrics(
3010
2958
  /(px|em)$/.test(tspan && tspan.style.fontSize) ?
3011
2959
  tspan.style.fontSize :
3012
- ((textStyles && textStyles.fontSize) || renderer.style.fontSize || 12)
2960
+ ((textStyles && textStyles.fontSize) || renderer.style.fontSize || 12),
2961
+ tspan
3013
2962
  ).h;
3014
2963
  };
3015
2964
 
@@ -3018,8 +2967,9 @@ SVGRenderer.prototype = {
3018
2967
  textNode.removeChild(childNodes[i]);
3019
2968
  }
3020
2969
 
3021
- // Skip tspans, add text directly to text node
3022
- if (!hasMarkup && textStr.indexOf(' ') === -1) {
2970
+ // Skip tspans, add text directly to text node. The forceTSpan is a hook
2971
+ // used in text outline hack.
2972
+ if (!hasMarkup && !textStroke && textStr.indexOf(' ') === -1) {
3023
2973
  textNode.appendChild(doc.createTextNode(textStr));
3024
2974
  return;
3025
2975
 
@@ -3094,6 +3044,9 @@ SVGRenderer.prototype = {
3094
3044
  // add attributes
3095
3045
  attr(tspan, attributes);
3096
3046
 
3047
+ // Append it
3048
+ textNode.appendChild(tspan);
3049
+
3097
3050
  // first span on subsequent line, add the line height
3098
3051
  if (!spanNo && lineNo) {
3099
3052
 
@@ -3107,27 +3060,19 @@ SVGRenderer.prototype = {
3107
3060
  attr(
3108
3061
  tspan,
3109
3062
  'dy',
3110
- getLineHeight(tspan),
3111
- // Safari 6.0.2 - too optimized for its own good (#1539)
3112
- // TODO: revisit this with future versions of Safari
3113
- isWebKit && tspan.offsetHeight
3063
+ getLineHeight(tspan)
3114
3064
  );
3115
3065
  }
3116
3066
 
3117
- // Append it
3118
- textNode.appendChild(tspan);
3119
-
3120
- spanNo++;
3121
-
3122
3067
  // check width and apply soft breaks
3123
3068
  if (width) {
3124
3069
  var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
3125
- hasWhiteSpace = words.length > 1 && textStyles.whiteSpace !== 'nowrap',
3070
+ hasWhiteSpace = spans.length > 1 || (words.length > 1 && textStyles.whiteSpace !== 'nowrap'),
3126
3071
  tooLong,
3127
3072
  actualWidth,
3128
- clipHeight = wrapper._clipHeight,
3073
+ hcHeight = textStyles.HcHeight,
3129
3074
  rest = [],
3130
- dy = getLineHeight(),
3075
+ dy = getLineHeight(tspan),
3131
3076
  softLineNo = 1,
3132
3077
  bBox;
3133
3078
 
@@ -3147,8 +3092,7 @@ SVGRenderer.prototype = {
3147
3092
  rest = [];
3148
3093
  if (words.length) {
3149
3094
  softLineNo++;
3150
-
3151
- if (clipHeight && softLineNo * dy > clipHeight) {
3095
+ if (hcHeight && softLineNo * dy > hcHeight) {
3152
3096
  words = ['...'];
3153
3097
  wrapper.attr('title', wrapper.textStr);
3154
3098
  } else {
@@ -3162,12 +3106,11 @@ SVGRenderer.prototype = {
3162
3106
  attr(tspan, 'style', spanStyle);
3163
3107
  }
3164
3108
  textNode.appendChild(tspan);
3165
-
3166
- if (actualWidth > width) { // a single word is pressing it out
3167
- width = actualWidth;
3168
- }
3169
3109
  }
3170
3110
  }
3111
+ if (actualWidth > width) { // a single word is pressing it out
3112
+ width = actualWidth;
3113
+ }
3171
3114
  } else { // append to existing line tspan
3172
3115
  tspan.removeChild(tspan.firstChild);
3173
3116
  rest.unshift(words.pop());
@@ -3177,6 +3120,8 @@ SVGRenderer.prototype = {
3177
3120
  }
3178
3121
  }
3179
3122
  }
3123
+
3124
+ spanNo++;
3180
3125
  }
3181
3126
  }
3182
3127
  });
@@ -3503,8 +3448,7 @@ SVGRenderer.prototype = {
3503
3448
  // could be exporting in IE
3504
3449
  // using href throws "not supported" in ie7 and under, requries regex shim to fix later
3505
3450
  elemWrapper.element.setAttribute('hc-svg-href', src);
3506
- }
3507
-
3451
+ }
3508
3452
  return elemWrapper;
3509
3453
  },
3510
3454
 
@@ -3823,14 +3767,15 @@ SVGRenderer.prototype = {
3823
3767
 
3824
3768
  if (!useHTML) {
3825
3769
  wrapper.xSetter = function (value, key, element) {
3826
- var childNodes = element.childNodes,
3827
- child,
3770
+ var tspans = element.getElementsByTagName('tspan'),
3771
+ tspan,
3772
+ parentVal = element.getAttribute(key),
3828
3773
  i;
3829
- for (i = 1; i < childNodes.length; i++) {
3830
- child = childNodes[i];
3831
- // if the x values are equal, the tspan represents a linebreak
3832
- if (child.getAttribute('x') === element.getAttribute('x')) {
3833
- child.setAttribute('x', value);
3774
+ for (i = 0; i < tspans.length; i++) {
3775
+ tspan = tspans[i];
3776
+ // If the x values are equal, the tspan represents a linebreak
3777
+ if (tspan.getAttribute(key) === parentVal) {
3778
+ tspan.setAttribute(key, value);
3834
3779
  }
3835
3780
  }
3836
3781
  element.setAttribute(key, value);
@@ -3843,8 +3788,12 @@ SVGRenderer.prototype = {
3843
3788
  /**
3844
3789
  * Utility to return the baseline offset and total line height from the font size
3845
3790
  */
3846
- fontMetrics: function (fontSize) {
3791
+ fontMetrics: function (fontSize, elem) {
3847
3792
  fontSize = fontSize || this.style.fontSize;
3793
+ if (elem && win.getComputedStyle) {
3794
+ elem = elem.element || elem; // SVGElement
3795
+ fontSize = win.getComputedStyle(elem, "").fontSize;
3796
+ }
3848
3797
  fontSize = /px/.test(fontSize) ? pInt(fontSize) : /em/.test(fontSize) ? parseFloat(fontSize) * 12 : 12;
3849
3798
 
3850
3799
  // Empirical values found by comparing font size and bounding box height.
@@ -3854,7 +3803,8 @@ SVGRenderer.prototype = {
3854
3803
 
3855
3804
  return {
3856
3805
  h: lineHeight,
3857
- b: baseline
3806
+ b: baseline,
3807
+ f: fontSize
3858
3808
  };
3859
3809
  },
3860
3810
 
@@ -3911,7 +3861,7 @@ SVGRenderer.prototype = {
3911
3861
  wrapper.height = (height || bBox.height || 0) + 2 * padding;
3912
3862
 
3913
3863
  // update the label-scoped y offset
3914
- baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b;
3864
+ baselineOffset = padding + renderer.fontMetrics(style && style.fontSize, text).b;
3915
3865
 
3916
3866
 
3917
3867
  if (needsBox) {
@@ -4087,7 +4037,7 @@ SVGRenderer.prototype = {
4087
4037
  if (styles) {
4088
4038
  var textStyles = {};
4089
4039
  styles = merge(styles); // create a copy to avoid altering the original object (#537)
4090
- each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration', 'textShadow'], function (prop) {
4040
+ each(wrapper.textProps, function (prop) {
4091
4041
  if (styles[prop] !== UNDEFINED) {
4092
4042
  textStyles[prop] = styles[prop];
4093
4043
  delete styles[prop];
@@ -4460,7 +4410,7 @@ if (!hasSVG && !useCanVG) {
4460
4410
  /**
4461
4411
  * The VML element wrapper.
4462
4412
  */
4463
- Highcharts.VMLElement = VMLElement = {
4413
+ VMLElement = {
4464
4414
 
4465
4415
  /**
4466
4416
  * Initialize a new VML element wrapper. It builds the markup as a string
@@ -4825,7 +4775,7 @@ Highcharts.VMLElement = VMLElement = {
4825
4775
  var i,
4826
4776
  shadows = this.shadows;
4827
4777
  value = value || [];
4828
- this.d = value.join(' '); // used in getter for animation
4778
+ this.d = value.join && value.join(' '); // used in getter for animation
4829
4779
 
4830
4780
  element.path = value = this.pathToVML(value);
4831
4781
 
@@ -4923,7 +4873,7 @@ Highcharts.VMLElement = VMLElement = {
4923
4873
  element.style[key] = value;
4924
4874
  }
4925
4875
  };
4926
- VMLElement = extendClass(SVGElement, VMLElement);
4876
+ Highcharts.VMLElement = VMLElement = extendClass(SVGElement, VMLElement);
4927
4877
 
4928
4878
  // Some shared setters
4929
4879
  VMLElement.prototype.ySetter =
@@ -5605,6 +5555,7 @@ Tick.prototype = {
5605
5555
  names = axis.names,
5606
5556
  pos = tick.pos,
5607
5557
  labelOptions = options.labels,
5558
+ rotation = labelOptions.rotation,
5608
5559
  str,
5609
5560
  tickPositions = axis.tickPositions,
5610
5561
  width = (horiz && categories &&
@@ -5651,14 +5602,14 @@ Tick.prototype = {
5651
5602
  attr = {
5652
5603
  align: axis.labelAlign
5653
5604
  };
5654
- if (isNumber(labelOptions.rotation)) {
5655
- attr.rotation = labelOptions.rotation;
5605
+ if (isNumber(rotation)) {
5606
+ attr.rotation = rotation;
5656
5607
  }
5657
5608
  if (width && labelOptions.ellipsis) {
5658
- attr._clipHeight = axis.len / tickPositions.length;
5609
+ css.HcHeight = axis.len / tickPositions.length;
5659
5610
  }
5660
5611
 
5661
- tick.label =
5612
+ tick.label = label =
5662
5613
  defined(str) && labelOptions.enabled ?
5663
5614
  chart.renderer.text(
5664
5615
  str,
@@ -5672,6 +5623,13 @@ Tick.prototype = {
5672
5623
  .add(axis.labelGroup) :
5673
5624
  null;
5674
5625
 
5626
+ // Set the tick baseline and correct for rotation (#1764)
5627
+ axis.tickBaseline = chart.renderer.fontMetrics(labelOptions.style.fontSize, label).b;
5628
+ if (rotation && axis.side === 2) {
5629
+ axis.tickBaseline *= mathCos(rotation * deg2rad);
5630
+ }
5631
+
5632
+
5675
5633
  // update
5676
5634
  } else if (label) {
5677
5635
  label.attr({
@@ -5679,6 +5637,7 @@ Tick.prototype = {
5679
5637
  })
5680
5638
  .css(css);
5681
5639
  }
5640
+ tick.yOffset = label ? pick(labelOptions.y, axis.tickBaseline + (axis.side === 2 ? 8 : -(label.getBBox().height / 2))) : 0;
5682
5641
  },
5683
5642
 
5684
5643
  /**
@@ -5755,7 +5714,7 @@ Tick.prototype = {
5755
5714
  do {
5756
5715
  index += (isFirst ? 1 : -1);
5757
5716
  neighbour = axis.ticks[tickPositions[index]];
5758
- } while (tickPositions[index] && (!neighbour || neighbour.label.line !== line));
5717
+ } while (tickPositions[index] && (!neighbour || !neighbour.label || neighbour.label.line !== line)); // #3044
5759
5718
 
5760
5719
  neighbourEdge = neighbour && neighbour.label.xy && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1];
5761
5720
 
@@ -5820,25 +5779,13 @@ Tick.prototype = {
5820
5779
  var axis = this.axis,
5821
5780
  transA = axis.transA,
5822
5781
  reversed = axis.reversed,
5823
- staggerLines = axis.staggerLines,
5824
- baseline = axis.chart.renderer.fontMetrics(labelOptions.style.fontSize).b,
5825
- rotation = labelOptions.rotation;
5782
+ staggerLines = axis.staggerLines;
5826
5783
 
5827
5784
  x = x + labelOptions.x - (tickmarkOffset && horiz ?
5828
5785
  tickmarkOffset * transA * (reversed ? -1 : 1) : 0);
5829
- y = y + labelOptions.y - (tickmarkOffset && !horiz ?
5786
+ y = y + this.yOffset - (tickmarkOffset && !horiz ?
5830
5787
  tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
5831
5788
 
5832
- // Correct for rotation (#1764)
5833
- if (rotation && axis.side === 2) {
5834
- y -= baseline - baseline * mathCos(rotation * deg2rad);
5835
- }
5836
-
5837
- // Vertically centered
5838
- if (!defined(labelOptions.y) && !rotation) { // #1951
5839
- y += baseline - label.getBBox().height / 2;
5840
- }
5841
-
5842
5789
  // Correct for staggered labels
5843
5790
  if (staggerLines) {
5844
5791
  label.line = (index / (step || 1) % staggerLines);
@@ -5904,6 +5851,7 @@ Tick.prototype = {
5904
5851
  y = xy.y,
5905
5852
  reverseCrisp = ((horiz && x === axis.pos + axis.len) || (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
5906
5853
 
5854
+ opacity = pick(opacity, 1);
5907
5855
  this.isActive = true;
5908
5856
 
5909
5857
  // create the grid line
@@ -6159,8 +6107,9 @@ Highcharts.PlotLineOrBand.prototype = {
6159
6107
  }
6160
6108
 
6161
6109
  // get the bounding box and align the label
6162
- xs = [path[1], path[4], pick(path[6], path[1])];
6163
- ys = [path[2], path[5], pick(path[7], path[2])];
6110
+ // #3000 changed to better handle choice between plotband or plotline
6111
+ xs = [path[1], path[4], (isBand ? path[6] : path[1])];
6112
+ ys = [path[2], path[5], (isBand ? path[7] : path[2])];
6164
6113
  x = arrayMin(xs);
6165
6114
  y = arrayMin(ys);
6166
6115
 
@@ -6220,11 +6169,11 @@ AxisPlotLineOrBandExtension = {
6220
6169
  },
6221
6170
 
6222
6171
  addPlotBand: function (options) {
6223
- this.addPlotBandOrLine(options, 'plotBands');
6172
+ return this.addPlotBandOrLine(options, 'plotBands');
6224
6173
  },
6225
6174
 
6226
6175
  addPlotLine: function (options) {
6227
- this.addPlotBandOrLine(options, 'plotLines');
6176
+ return this.addPlotBandOrLine(options, 'plotLines');
6228
6177
  },
6229
6178
 
6230
6179
  /**
@@ -6433,7 +6382,7 @@ Axis.prototype = {
6433
6382
  defaultBottomAxisOptions: {
6434
6383
  labels: {
6435
6384
  x: 0,
6436
- y: 20
6385
+ y: null // based on font size
6437
6386
  // overflow: undefined,
6438
6387
  // staggerLines: null
6439
6388
  },
@@ -7121,6 +7070,8 @@ Axis.prototype = {
7121
7070
  var axis = this,
7122
7071
  chart = axis.chart,
7123
7072
  options = axis.options,
7073
+ startOnTick = options.startOnTick,
7074
+ endOnTick = options.endOnTick,
7124
7075
  isLog = axis.isLog,
7125
7076
  isDatetimeAxis = axis.isDatetimeAxis,
7126
7077
  isXAxis = axis.isXAxis,
@@ -7213,7 +7164,7 @@ Axis.prototype = {
7213
7164
  );
7214
7165
  // For squished axes, set only two ticks
7215
7166
  if (!defined(tickIntervalOption) && axis.len < tickPixelIntervalOption && !this.isRadial &&
7216
- !this.isLog && !categories && options.startOnTick && options.endOnTick) {
7167
+ !this.isLog && !categories && startOnTick && endOnTick) {
7217
7168
  keepTwoTicksOnly = true;
7218
7169
  axis.tickInterval /= 4; // tick extremes closer to the real values
7219
7170
  }
@@ -7303,13 +7254,18 @@ Axis.prototype = {
7303
7254
  minPointOffset = axis.minPointOffset || 0,
7304
7255
  singlePad;
7305
7256
 
7306
- if (options.startOnTick) {
7257
+ // Prevent all ticks from being removed (#3195)
7258
+ if (!startOnTick && !endOnTick && !categories && tickPositions.length === 2) {
7259
+ tickPositions.splice(1, 0, (roundedMax + roundedMin) / 2);
7260
+ }
7261
+
7262
+ if (startOnTick) {
7307
7263
  axis.min = roundedMin;
7308
7264
  } else if (axis.min - minPointOffset > roundedMin) {
7309
7265
  tickPositions.shift();
7310
7266
  }
7311
7267
 
7312
- if (options.endOnTick) {
7268
+ if (endOnTick) {
7313
7269
  axis.max = roundedMax;
7314
7270
  } else if (axis.max + minPointOffset < roundedMax) {
7315
7271
  tickPositions.pop();
@@ -7539,7 +7495,7 @@ Axis.prototype = {
7539
7495
  height = pick(options.height, chart.plotHeight),
7540
7496
  top = pick(options.top, chart.plotTop),
7541
7497
  left = pick(options.left, chart.plotLeft + offsetLeft),
7542
- percentRegex = /%$/; // docs
7498
+ percentRegex = /%$/;
7543
7499
 
7544
7500
  // Check for percentage based input values
7545
7501
  if (percentRegex.test(height)) {
@@ -7638,6 +7594,7 @@ Axis.prototype = {
7638
7594
  axisTitleOptions = options.title,
7639
7595
  labelOptions = options.labels,
7640
7596
  labelOffset = 0, // reset
7597
+ labelOffsetPadded,
7641
7598
  axisOffset = chart.axisOffset,
7642
7599
  clipOffset = chart.clipOffset,
7643
7600
  directionFactor = [-1, 1, 1, -1][side],
@@ -7653,7 +7610,7 @@ Axis.prototype = {
7653
7610
  x,
7654
7611
  w,
7655
7612
  lineNo,
7656
- lineHeightCorrection = side === 2 ? renderer.fontMetrics(labelOptions.style.fontSize).b : 0;
7613
+ lineHeightCorrection;
7657
7614
 
7658
7615
  // For reuse in Axis.render
7659
7616
  axis.hasData = hasData = (axis.hasVisibleSeries || (defined(axis.min) && defined(axis.max) && !!tickPositions));
@@ -7734,8 +7691,8 @@ Axis.prototype = {
7734
7691
  labelOffset
7735
7692
  );
7736
7693
  }
7737
-
7738
7694
  });
7695
+
7739
7696
  if (axis.staggerLines) {
7740
7697
  labelOffset *= axis.staggerLines;
7741
7698
  axis.labelOffset = labelOffset;
@@ -7772,8 +7729,8 @@ Axis.prototype = {
7772
7729
 
7773
7730
  if (showAxis) {
7774
7731
  titleOffset = axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
7775
- titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10);
7776
7732
  titleOffsetOption = axisTitleOptions.offset;
7733
+ titleMargin = defined(titleOffsetOption) ? 0 : pick(axisTitleOptions.margin, horiz ? 5 : 10);
7777
7734
  }
7778
7735
 
7779
7736
  // hide or show the title depending on whether showEmpty is set
@@ -7783,15 +7740,15 @@ Axis.prototype = {
7783
7740
  // handle automatic or user set offset
7784
7741
  axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
7785
7742
 
7786
- axis.axisTitleMargin =
7787
- pick(titleOffsetOption,
7788
- labelOffset + titleMargin +
7789
- (labelOffset && (directionFactor * options.labels[horiz ? 'y' : 'x'] - lineHeightCorrection))
7790
- );
7743
+ lineHeightCorrection = side === 2 ? axis.tickBaseline : 0;
7744
+ labelOffsetPadded = labelOffset + titleMargin +
7745
+ (labelOffset && (directionFactor * (horiz ? pick(labelOptions.y, axis.tickBaseline + 8) : labelOptions.x) - lineHeightCorrection));
7746
+ axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
7791
7747
 
7792
7748
  axisOffset[side] = mathMax(
7793
7749
  axisOffset[side],
7794
- axis.axisTitleMargin + titleOffset + directionFactor * axis.offset
7750
+ axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
7751
+ labelOffsetPadded // #3027
7795
7752
  );
7796
7753
  clipOffset[invertedSide] = mathMax(clipOffset[invertedSide], mathFloor(options.lineWidth / 2) * 2);
7797
7754
  },
@@ -7961,7 +7918,7 @@ Axis.prototype = {
7961
7918
  ticks[pos].render(i, true, 0.1);
7962
7919
  }
7963
7920
 
7964
- ticks[pos].render(i, false, 1);
7921
+ ticks[pos].render(i);
7965
7922
  }
7966
7923
 
7967
7924
  });
@@ -8085,25 +8042,17 @@ Axis.prototype = {
8085
8042
  * Redraw the axis to reflect changes in the data or axis extremes
8086
8043
  */
8087
8044
  redraw: function () {
8088
- var axis = this,
8089
- chart = axis.chart,
8090
- pointer = chart.pointer;
8091
-
8092
- // hide tooltip and hover states
8093
- if (pointer) {
8094
- pointer.reset(true);
8095
- }
8096
-
8045
+
8097
8046
  // render the axis
8098
- axis.render();
8047
+ this.render();
8099
8048
 
8100
8049
  // move plot lines and bands
8101
- each(axis.plotLinesAndBands, function (plotLine) {
8050
+ each(this.plotLinesAndBands, function (plotLine) {
8102
8051
  plotLine.render();
8103
8052
  });
8104
8053
 
8105
8054
  // mark associated series as dirty and ready for redraw
8106
- each(axis.series, function (series) {
8055
+ each(this.series, function (series) {
8107
8056
  series.isDirty = true;
8108
8057
  });
8109
8058
 
@@ -8240,40 +8189,40 @@ Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWee
8240
8189
  count = normalizedInterval.count;
8241
8190
 
8242
8191
  if (defined(min)) { // #1300
8243
- if (interval >= timeUnits[SECOND]) { // second
8192
+ if (interval >= timeUnits.second) { // second
8244
8193
  minDate.setMilliseconds(0);
8245
- minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
8194
+ minDate.setSeconds(interval >= timeUnits.minute ? 0 :
8246
8195
  count * mathFloor(minDate.getSeconds() / count));
8247
8196
  }
8248
8197
 
8249
- if (interval >= timeUnits[MINUTE]) { // minute
8250
- minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
8198
+ if (interval >= timeUnits.minute) { // minute
8199
+ minDate[setMinutes](interval >= timeUnits.hour ? 0 :
8251
8200
  count * mathFloor(minDate[getMinutes]() / count));
8252
8201
  }
8253
8202
 
8254
- if (interval >= timeUnits[HOUR]) { // hour
8255
- minDate[setHours](interval >= timeUnits[DAY] ? 0 :
8203
+ if (interval >= timeUnits.hour) { // hour
8204
+ minDate[setHours](interval >= timeUnits.day ? 0 :
8256
8205
  count * mathFloor(minDate[getHours]() / count));
8257
8206
  }
8258
8207
 
8259
- if (interval >= timeUnits[DAY]) { // day
8260
- minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
8208
+ if (interval >= timeUnits.day) { // day
8209
+ minDate[setDate](interval >= timeUnits.month ? 1 :
8261
8210
  count * mathFloor(minDate[getDate]() / count));
8262
8211
  }
8263
8212
 
8264
- if (interval >= timeUnits[MONTH]) { // month
8265
- minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
8213
+ if (interval >= timeUnits.month) { // month
8214
+ minDate[setMonth](interval >= timeUnits.year ? 0 :
8266
8215
  count * mathFloor(minDate[getMonth]() / count));
8267
8216
  minYear = minDate[getFullYear]();
8268
8217
  }
8269
8218
 
8270
- if (interval >= timeUnits[YEAR]) { // year
8219
+ if (interval >= timeUnits.year) { // year
8271
8220
  minYear -= minYear % count;
8272
8221
  minDate[setFullYear](minYear);
8273
8222
  }
8274
8223
 
8275
8224
  // week is a special case that runs outside the hierarchy
8276
- if (interval === timeUnits[WEEK]) {
8225
+ if (interval === timeUnits.week) {
8277
8226
  // get start of current week, independent of count
8278
8227
  minDate[setDate](minDate[getDate]() - minDate[getDay]() +
8279
8228
  pick(startOfWeek, 1));
@@ -8298,18 +8247,18 @@ Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWee
8298
8247
  tickPositions.push(time);
8299
8248
 
8300
8249
  // if the interval is years, use Date.UTC to increase years
8301
- if (interval === timeUnits[YEAR]) {
8250
+ if (interval === timeUnits.year) {
8302
8251
  time = makeTime(minYear + i * count, 0);
8303
8252
 
8304
8253
  // if the interval is months, use Date.UTC to increase months
8305
- } else if (interval === timeUnits[MONTH]) {
8254
+ } else if (interval === timeUnits.month) {
8306
8255
  time = makeTime(minYear, minMonth + i * count);
8307
8256
 
8308
8257
  // if we're using global time, the interval is not fixed as it jumps
8309
8258
  // one hour at the DST crossover
8310
- } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) {
8259
+ } else if (!useUTC && (interval === timeUnits.day || interval === timeUnits.week)) {
8311
8260
  time = makeTime(minYear, minMonth, minDateDate +
8312
- i * count * (interval === timeUnits[DAY] ? 1 : 7));
8261
+ i * count * (interval === timeUnits.day ? 1 : 7));
8313
8262
 
8314
8263
  // else, the interval is fixed and we use simple addition
8315
8264
  } else {
@@ -8325,9 +8274,9 @@ Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWee
8325
8274
 
8326
8275
  // mark new days if the time is dividible by day (#1649, #1760)
8327
8276
  each(grep(tickPositions, function (time) {
8328
- return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === localTimezoneOffset;
8277
+ return interval <= timeUnits.hour && time % timeUnits.day === localTimezoneOffset;
8329
8278
  }), function (time) {
8330
- higherRanks[time] = DAY;
8279
+ higherRanks[time] = 'day';
8331
8280
  });
8332
8281
  }
8333
8282
 
@@ -8351,28 +8300,28 @@ Axis.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWee
8351
8300
  */
8352
8301
  Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
8353
8302
  var units = unitsOption || [[
8354
- MILLISECOND, // unit name
8303
+ 'millisecond', // unit name
8355
8304
  [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
8356
8305
  ], [
8357
- SECOND,
8306
+ 'second',
8358
8307
  [1, 2, 5, 10, 15, 30]
8359
8308
  ], [
8360
- MINUTE,
8309
+ 'minute',
8361
8310
  [1, 2, 5, 10, 15, 30]
8362
8311
  ], [
8363
- HOUR,
8312
+ 'hour',
8364
8313
  [1, 2, 3, 4, 6, 8, 12]
8365
8314
  ], [
8366
- DAY,
8315
+ 'day',
8367
8316
  [1, 2]
8368
8317
  ], [
8369
- WEEK,
8318
+ 'week',
8370
8319
  [1, 2]
8371
8320
  ], [
8372
- MONTH,
8321
+ 'month',
8373
8322
  [1, 2, 3, 4, 6]
8374
8323
  ], [
8375
- YEAR,
8324
+ 'year',
8376
8325
  null
8377
8326
  ]],
8378
8327
  unit = units[units.length - 1], // default unit is years
@@ -8401,7 +8350,7 @@ Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption)
8401
8350
  }
8402
8351
 
8403
8352
  // prevent 2.5 years intervals, though 25, 250 etc. are allowed
8404
- if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
8353
+ if (interval === timeUnits.year && tickInterval < 5 * interval) {
8405
8354
  multiples = [1, 2, 5];
8406
8355
  }
8407
8356
 
@@ -8409,7 +8358,7 @@ Axis.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption)
8409
8358
  count = normalizeTickInterval(
8410
8359
  tickInterval / interval,
8411
8360
  multiples,
8412
- unit[0] === YEAR ? mathMax(getMagnitude(tickInterval / interval), 1) : 1 // #1913, #2360
8361
+ unit[0] === 'year' ? mathMax(getMagnitude(tickInterval / interval), 1) : 1 // #1913, #2360
8413
8362
  );
8414
8363
 
8415
8364
  return {
@@ -8466,8 +8415,7 @@ Axis.prototype.getLogTickPositions = function (interval, min, max, minor) {
8466
8415
  len = intermediate.length;
8467
8416
  for (j = 0; j < len && !break2; j++) {
8468
8417
  pos = log2lin(lin2log(i) * intermediate[j]);
8469
-
8470
- if (pos > min && (!minor || lastPos <= max)) { // #1670
8418
+ if (pos > min && (!minor || lastPos <= max) && lastPos !== UNDEFINED) { // #1670, lastPos is #3113
8471
8419
  positions.push(lastPos);
8472
8420
  }
8473
8421
 
@@ -8597,10 +8545,12 @@ Tooltip.prototype = {
8597
8545
  move: function (x, y, anchorX, anchorY) {
8598
8546
  var tooltip = this,
8599
8547
  now = tooltip.now,
8600
- animate = tooltip.options.animation !== false && !tooltip.isHidden,
8548
+ animate = tooltip.options.animation !== false && !tooltip.isHidden &&
8549
+ // When we get close to the target position, abort animation and land on the right place (#3056)
8550
+ (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1),
8601
8551
  skipAnchor = tooltip.followPointer || tooltip.len > 1;
8602
8552
 
8603
- // get intermediate values for animation
8553
+ // Get intermediate values for animation
8604
8554
  extend(now, {
8605
8555
  x: animate ? (2 * now.x + x) / 3 : x,
8606
8556
  y: animate ? (now.y + y) / 2 : y,
@@ -8608,17 +8558,17 @@ Tooltip.prototype = {
8608
8558
  anchorY: skipAnchor ? UNDEFINED : animate ? (now.anchorY + anchorY) / 2 : anchorY
8609
8559
  });
8610
8560
 
8611
- // move to the intermediate value
8561
+ // Move to the intermediate value
8612
8562
  tooltip.label.attr(now);
8613
8563
 
8614
8564
 
8615
- // run on next tick of the mouse tracker
8616
- if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) {
8565
+ // Run on next tick of the mouse tracker
8566
+ if (animate) {
8617
8567
 
8618
- // never allow two timeouts
8568
+ // Never allow two timeouts
8619
8569
  clearTimeout(this.tooltipTimeout);
8620
8570
 
8621
- // set the fixed interval ticking for the smooth tooltip
8571
+ // Set the fixed interval ticking for the smooth tooltip
8622
8572
  this.tooltipTimeout = setTimeout(function () {
8623
8573
  // The interval function may still be running during destroy, so check that the chart is really there before calling.
8624
8574
  if (tooltip) {
@@ -8967,7 +8917,7 @@ Tooltip.prototype = {
8967
8917
  // If the point is placed every day at 23:59, we need to show
8968
8918
  // the minutes as well. This logic only works for time units less than
8969
8919
  // a day, since all higher time units are dividable by those. #2637.
8970
- (timeUnits[n] <= timeUnits[DAY] && point.key % timeUnits[n] > 0)) {
8920
+ (timeUnits[n] <= timeUnits.day && point.key % timeUnits[n] > 0)) {
8971
8921
  xDateFormat = dateTimeLabelFormats[n];
8972
8922
  break;
8973
8923
  }
@@ -9326,7 +9276,8 @@ Pointer.prototype = {
9326
9276
  clickedInside,
9327
9277
  size,
9328
9278
  mouseDownX = this.mouseDownX,
9329
- mouseDownY = this.mouseDownY;
9279
+ mouseDownY = this.mouseDownY,
9280
+ panKey = chartOptions.panKey && e[chartOptions.panKey + 'Key'];
9330
9281
 
9331
9282
  // If the mouse is outside the plot area, adjust to cooordinates
9332
9283
  // inside to prevent the selection marker from going outside
@@ -9352,7 +9303,7 @@ Pointer.prototype = {
9352
9303
  clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
9353
9304
 
9354
9305
  // make a selection
9355
- if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside) {
9306
+ if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside && !panKey) {
9356
9307
  if (!this.selectionMarker) {
9357
9308
  this.selectionMarker = chart.renderer.rect(
9358
9309
  plotLeft,
@@ -9420,8 +9371,9 @@ Pointer.prototype = {
9420
9371
  each(chart.axes, function (axis) {
9421
9372
  if (axis.zoomEnabled) {
9422
9373
  var horiz = axis.horiz,
9423
- selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)),
9424
- selectionMax = axis.toValue((horiz ? selectionLeft + selectionWidth : selectionTop + selectionHeight));
9374
+ minPixelPadding = e.type === 'touchend' ? axis.minPixelPadding: 0, // #1207, #3075
9375
+ selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop) + minPixelPadding),
9376
+ selectionMax = axis.toValue((horiz ? selectionLeft + selectionWidth : selectionTop + selectionHeight) - minPixelPadding);
9425
9377
 
9426
9378
  if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859
9427
9379
  selectionData[axis.coll].push({
@@ -9513,8 +9465,8 @@ Pointer.prototype = {
9513
9465
 
9514
9466
  hoverChartIndex = chart.index;
9515
9467
 
9516
- // normalize
9517
9468
  e = this.normalize(e);
9469
+ e.returnValue = false; // #2251, #3224
9518
9470
 
9519
9471
  if (chart.mouseIsDown === 'mousedown') {
9520
9472
  this.drag(e);
@@ -9790,8 +9742,8 @@ extend(Highcharts.Pointer.prototype, {
9790
9742
  if (axis.zoomEnabled) {
9791
9743
  var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
9792
9744
  minPixelPadding = axis.minPixelPadding,
9793
- min = axis.toPixels(axis.dataMin),
9794
- max = axis.toPixels(axis.dataMax),
9745
+ min = axis.toPixels(pick(axis.options.min, axis.dataMin)),
9746
+ max = axis.toPixels(pick(axis.options.max, axis.dataMax)),
9795
9747
  absMin = mathMin(min, max),
9796
9748
  absMax = mathMax(min, max);
9797
9749
 
@@ -9981,7 +9933,6 @@ Legend.prototype = {
9981
9933
  return;
9982
9934
  }
9983
9935
 
9984
- legend.baseline = pInt(itemStyle.fontSize) + 3 + itemMarginTop; // used in Series prototype
9985
9936
  legend.itemStyle = itemStyle;
9986
9937
  legend.itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle);
9987
9938
  legend.itemMarginTop = itemMarginTop;
@@ -10179,7 +10130,7 @@ Legend.prototype = {
10179
10130
  itemStyle = legend.itemStyle,
10180
10131
  itemHiddenStyle = legend.itemHiddenStyle,
10181
10132
  padding = legend.padding,
10182
- itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, // docs
10133
+ itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
10183
10134
  ltr = !options.rtl,
10184
10135
  itemHeight,
10185
10136
  widthOption = options.width,
@@ -10202,14 +10153,11 @@ Legend.prototype = {
10202
10153
  .attr({ zIndex: 1 })
10203
10154
  .add(legend.scrollGroup);
10204
10155
 
10205
- // Draw the legend symbol inside the group box
10206
- series.drawLegendSymbol(legend, item);
10207
-
10208
10156
  // Generate the list item text and add it to the group
10209
10157
  item.legendItem = li = renderer.text(
10210
10158
  options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item),
10211
10159
  ltr ? symbolWidth + symbolPadding : -symbolPadding,
10212
- legend.baseline,
10160
+ legend.baseline || 0,
10213
10161
  useHTML
10214
10162
  )
10215
10163
  .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
@@ -10219,6 +10167,15 @@ Legend.prototype = {
10219
10167
  })
10220
10168
  .add(item.legendGroup);
10221
10169
 
10170
+ // Get the baseline for the first item - the font size is equal for all
10171
+ if (!legend.baseline) {
10172
+ legend.baseline = renderer.fontMetrics(itemStyle.fontSize, li).f + 3 + itemMarginTop;
10173
+ li.attr('y', legend.baseline);
10174
+ }
10175
+
10176
+ // Draw the legend symbol inside the group box
10177
+ series.drawLegendSymbol(legend, item);
10178
+
10222
10179
  if (legend.setItemEvents) {
10223
10180
  legend.setItemEvents(item, li, useHTML, itemStyle, itemHiddenStyle);
10224
10181
  }
@@ -10471,7 +10428,7 @@ Legend.prototype = {
10471
10428
  pages.length = 0;
10472
10429
  if (legendHeight > spaceHeight && !options.useHTML) {
10473
10430
 
10474
- this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight - this.padding;
10431
+ this.clipHeight = clipHeight = mathMax(spaceHeight - 20 - this.titleHeight - this.padding, 0);
10475
10432
  this.currentPage = pick(this.currentPage, 1);
10476
10433
  this.fullHeight = legendHeight;
10477
10434
 
@@ -10648,9 +10605,9 @@ var LegendSymbolMixin = Highcharts.LegendSymbolMixin = {
10648
10605
  symbolWidth = legend.symbolWidth,
10649
10606
  renderer = this.chart.renderer,
10650
10607
  legendItemGroup = this.legendGroup,
10651
- verticalCenter = legend.baseline - mathRound(renderer.fontMetrics(legendOptions.itemStyle.fontSize).b * 0.3),
10608
+ verticalCenter = legend.baseline - mathRound(renderer.fontMetrics(legendOptions.itemStyle.fontSize, this.legendItem).b * 0.3),
10652
10609
  attr;
10653
-
10610
+
10654
10611
  // Draw the line
10655
10612
  if (options.lineWidth) {
10656
10613
  attr = {
@@ -10808,8 +10765,7 @@ Chart.prototype = {
10808
10765
 
10809
10766
  // Expose methods and variables
10810
10767
  chart.animation = useCanVG ? false : pick(optionsChart.animation, true);
10811
- chart.pointCount = 0;
10812
- chart.counters = new ChartCounters();
10768
+ chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
10813
10769
 
10814
10770
  chart.firstRender();
10815
10771
  },
@@ -10878,6 +10834,7 @@ Chart.prototype = {
10878
10834
  redrawLegend = chart.isDirtyLegend,
10879
10835
  hasStackedSeries,
10880
10836
  hasDirtyStacks,
10837
+ hasCartesianSeries = chart.hasCartesianSeries,
10881
10838
  isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed?
10882
10839
  seriesLength = series.length,
10883
10840
  i = seriesLength,
@@ -10941,7 +10898,7 @@ Chart.prototype = {
10941
10898
  }
10942
10899
 
10943
10900
 
10944
- if (chart.hasCartesianSeries) {
10901
+ if (hasCartesianSeries) {
10945
10902
  if (!chart.isResizing) {
10946
10903
 
10947
10904
  // reset maxTicks
@@ -10954,8 +10911,11 @@ Chart.prototype = {
10954
10911
  }
10955
10912
 
10956
10913
  chart.adjustTickAmounts();
10957
- chart.getMargins();
10914
+ }
10915
+
10916
+ chart.getMargins(); // #3098
10958
10917
 
10918
+ if (hasCartesianSeries) {
10959
10919
  // If one axis is dirty, all axes must be redrawn (#792, #2169)
10960
10920
  each(axes, function (axis) {
10961
10921
  if (axis.isDirty) {
@@ -10979,9 +10939,8 @@ Chart.prototype = {
10979
10939
  axis.redraw();
10980
10940
  }
10981
10941
  });
10982
-
10983
-
10984
10942
  }
10943
+
10985
10944
  // the plot areas size has changed
10986
10945
  if (isDirtyBox) {
10987
10946
  chart.drawChartBox();
@@ -11190,12 +11149,15 @@ Chart.prototype = {
11190
11149
  titleOptions = options.title,
11191
11150
  subtitleOptions = options.subtitle,
11192
11151
  requiresDirtyBox,
11152
+ renderer = this.renderer,
11193
11153
  autoWidth = this.spacingBox.width - 44; // 44 makes room for default context button
11194
11154
 
11195
11155
  if (title) {
11196
11156
  title
11197
11157
  .css({ width: (titleOptions.width || autoWidth) + PX })
11198
- .align(extend({ y: 15 }, titleOptions), false, 'spacingBox');
11158
+ .align(extend({
11159
+ y: renderer.fontMetrics(titleOptions.style.fontSize, title).b - 3
11160
+ }, titleOptions), false, 'spacingBox');
11199
11161
 
11200
11162
  if (!titleOptions.floating && !titleOptions.verticalAlign) {
11201
11163
  titleOffset = title.getBBox().height;
@@ -11204,7 +11166,9 @@ Chart.prototype = {
11204
11166
  if (subtitle) {
11205
11167
  subtitle
11206
11168
  .css({ width: (subtitleOptions.width || autoWidth) + PX })
11207
- .align(extend({ y: titleOffset + titleOptions.margin }, subtitleOptions), false, 'spacingBox');
11169
+ .align(extend({
11170
+ y: titleOffset + (titleOptions.margin - 13) + renderer.fontMetrics(titleOptions.style.fontSize, subtitle).b
11171
+ }, subtitleOptions), false, 'spacingBox');
11208
11172
 
11209
11173
  if (!subtitleOptions.floating && !subtitleOptions.verticalAlign) {
11210
11174
  titleOffset = mathCeil(titleOffset + subtitle.getBBox().height);
@@ -11647,7 +11611,7 @@ Chart.prototype = {
11647
11611
  x: clipX,
11648
11612
  y: clipY,
11649
11613
  width: mathFloor(chart.plotSizeX - mathMax(plotBorderWidth, clipOffset[1]) / 2 - clipX),
11650
- height: mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY)
11614
+ height: mathMax(0, mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY))
11651
11615
  };
11652
11616
 
11653
11617
  if (!skipAxes) {
@@ -11867,6 +11831,35 @@ Chart.prototype = {
11867
11831
  serie.render();
11868
11832
  });
11869
11833
  },
11834
+
11835
+ /**
11836
+ * Render labels for the chart
11837
+ */
11838
+ renderLabels: function () {
11839
+ var chart = this,
11840
+ labels = chart.options.labels;
11841
+ if (labels.items) {
11842
+ each(labels.items, function (label) {
11843
+ var style = extend(labels.style, label.style),
11844
+ x = pInt(style.left) + chart.plotLeft,
11845
+ y = pInt(style.top) + chart.plotTop + 12;
11846
+
11847
+ // delete to prevent rewriting in IE
11848
+ delete style.left;
11849
+ delete style.top;
11850
+
11851
+ chart.renderer.text(
11852
+ label.html,
11853
+ x,
11854
+ y
11855
+ )
11856
+ .attr({ zIndex: 2 })
11857
+ .css(style)
11858
+ .add();
11859
+
11860
+ });
11861
+ }
11862
+ },
11870
11863
 
11871
11864
  /**
11872
11865
  * Render all graphics for the chart
@@ -11877,10 +11870,6 @@ Chart.prototype = {
11877
11870
  renderer = chart.renderer,
11878
11871
  options = chart.options;
11879
11872
 
11880
- var labels = options.labels,
11881
- credits = options.credits,
11882
- creditsHref;
11883
-
11884
11873
  // Title
11885
11874
  chart.setTitle();
11886
11875
 
@@ -11927,39 +11916,29 @@ Chart.prototype = {
11927
11916
  chart.renderSeries();
11928
11917
 
11929
11918
  // Labels
11930
- if (labels.items) {
11931
- each(labels.items, function (label) {
11932
- var style = extend(labels.style, label.style),
11933
- x = pInt(style.left) + chart.plotLeft,
11934
- y = pInt(style.top) + chart.plotTop + 12;
11919
+ chart.renderLabels();
11935
11920
 
11936
- // delete to prevent rewriting in IE
11937
- delete style.left;
11938
- delete style.top;
11921
+ // Credits
11922
+ chart.showCredits(options.credits);
11939
11923
 
11940
- renderer.text(
11941
- label.html,
11942
- x,
11943
- y
11944
- )
11945
- .attr({ zIndex: 2 })
11946
- .css(style)
11947
- .add();
11924
+ // Set flag
11925
+ chart.hasRendered = true;
11948
11926
 
11949
- });
11950
- }
11927
+ },
11951
11928
 
11952
- // Credits
11953
- if (credits.enabled && !chart.credits) {
11954
- creditsHref = credits.href;
11955
- chart.credits = renderer.text(
11929
+ /**
11930
+ * Show chart credits based on config options
11931
+ */
11932
+ showCredits: function (credits) {
11933
+ if (credits.enabled && !this.credits) {
11934
+ this.credits = this.renderer.text(
11956
11935
  credits.text,
11957
11936
  0,
11958
11937
  0
11959
11938
  )
11960
11939
  .on('click', function () {
11961
- if (creditsHref) {
11962
- location.href = creditsHref;
11940
+ if (credits.href) {
11941
+ location.href = credits.href;
11963
11942
  }
11964
11943
  })
11965
11944
  .attr({
@@ -11970,10 +11949,6 @@ Chart.prototype = {
11970
11949
  .add()
11971
11950
  .align(credits.position);
11972
11951
  }
11973
-
11974
- // Set flag
11975
- chart.hasRendered = true;
11976
-
11977
11952
  },
11978
11953
 
11979
11954
  /**
@@ -12223,7 +12198,7 @@ Point.prototype = {
12223
12198
  applyOptions: function (options, x) {
12224
12199
  var point = this,
12225
12200
  series = point.series,
12226
- pointValKey = series.pointValKey;
12201
+ pointValKey = series.options.pointValKey || series.pointValKey;
12227
12202
 
12228
12203
  options = Point.prototype.optionsToObject.call(this, options);
12229
12204
 
@@ -12694,60 +12669,45 @@ Series.prototype = {
12694
12669
  return options;
12695
12670
 
12696
12671
  },
12697
- /**
12698
- * Get the series' color
12699
- */
12700
- getColor: function () {
12701
- var options = this.options,
12702
- userOptions = this.userOptions,
12703
- defaultColors = this.chart.options.colors,
12704
- counters = this.chart.counters,
12705
- color,
12706
- colorIndex;
12707
12672
 
12708
- color = options.color || defaultPlotOptions[this.type].color;
12673
+ getCyclic: function (prop, value, defaults) {
12674
+ var i,
12675
+ userOptions = this.userOptions,
12676
+ indexName = '_' + prop + 'Index',
12677
+ counterName = prop + 'Counter';
12709
12678
 
12710
- if (!color && !options.colorByPoint) {
12711
- if (defined(userOptions._colorIndex)) { // after Series.update()
12712
- colorIndex = userOptions._colorIndex;
12679
+ if (!value) {
12680
+ if (defined(userOptions[indexName])) { // after Series.update()
12681
+ i = userOptions[indexName];
12713
12682
  } else {
12714
- userOptions._colorIndex = counters.color;
12715
- colorIndex = counters.color++;
12683
+ userOptions[indexName] = i = this.chart[counterName] % defaults.length;
12684
+ this.chart[counterName] += 1;
12716
12685
  }
12717
- color = defaultColors[colorIndex];
12686
+ value = defaults[i];
12718
12687
  }
12688
+ this[prop] = value;
12689
+ },
12719
12690
 
12720
- this.color = color;
12721
- counters.wrapColor(defaultColors.length);
12691
+ /**
12692
+ * Get the series' color
12693
+ */
12694
+ getColor: function () {
12695
+ if (!this.options.colorByPoint) {
12696
+ this.getCyclic('color', this.options.color || defaultPlotOptions[this.type].color, this.chart.options.colors);
12697
+ }
12722
12698
  },
12723
12699
  /**
12724
12700
  * Get the series' symbol
12725
12701
  */
12726
12702
  getSymbol: function () {
12727
- var series = this,
12728
- userOptions = series.userOptions,
12729
- seriesMarkerOption = series.options.marker,
12730
- chart = series.chart,
12731
- defaultSymbols = chart.options.symbols,
12732
- counters = chart.counters,
12733
- symbolIndex;
12734
-
12735
- series.symbol = seriesMarkerOption.symbol;
12736
- if (!series.symbol) {
12737
- if (defined(userOptions._symbolIndex)) { // after Series.update()
12738
- symbolIndex = userOptions._symbolIndex;
12739
- } else {
12740
- userOptions._symbolIndex = counters.symbol;
12741
- symbolIndex = counters.symbol++;
12742
- }
12743
- series.symbol = defaultSymbols[symbolIndex];
12744
- }
12703
+ var seriesMarkerOption = this.options.marker;
12704
+
12705
+ this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
12745
12706
 
12746
12707
  // don't substract radius in image symbols (#604)
12747
- if (/^url/.test(series.symbol)) {
12708
+ if (/^url/.test(this.symbol)) {
12748
12709
  seriesMarkerOption.radius = 0;
12749
12710
  }
12750
- counters.wrapSymbol(defaultSymbols.length);
12751
12711
  },
12752
12712
 
12753
12713
  drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
@@ -12909,6 +12869,7 @@ Series.prototype = {
12909
12869
  cropThreshold = options.cropThreshold,
12910
12870
  activePointCount = 0,
12911
12871
  isCartesian = series.isCartesian,
12872
+ xExtremes,
12912
12873
  min,
12913
12874
  max;
12914
12875
 
@@ -12922,8 +12883,9 @@ Series.prototype = {
12922
12883
  // optionally filter out points outside the plot area
12923
12884
  if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
12924
12885
 
12925
- min = xAxis.min;
12926
- max = xAxis.max;
12886
+ xExtremes = xAxis.getExtremes(); // corrected for log axis (#3053)
12887
+ min = xExtremes.min;
12888
+ max = xExtremes.max;
12927
12889
 
12928
12890
  // it's outside current extremes
12929
12891
  if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
@@ -13453,8 +13415,8 @@ Series.prototype = {
13453
13415
  if (seriesOptions.marker) { // line, spline, area, areaspline, scatter
13454
13416
 
13455
13417
  // if no hover radius is given, default to normal radius + 2
13456
- stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2;
13457
- stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1;
13418
+ stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + stateOptionsHover.radiusPlus;
13419
+ stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + stateOptionsHover.lineWidthPlus;
13458
13420
 
13459
13421
  } else { // column, bar, pie
13460
13422
 
@@ -14432,9 +14394,18 @@ extend(Chart.prototype, {
14432
14394
  showLoading: function (str) {
14433
14395
  var chart = this,
14434
14396
  options = chart.options,
14435
- loadingDiv = chart.loadingDiv;
14436
-
14437
- var loadingOptions = options.loading;
14397
+ loadingDiv = chart.loadingDiv,
14398
+ loadingOptions = options.loading,
14399
+ setLoadingSize = function () {
14400
+ if (loadingDiv) {
14401
+ css(loadingDiv, {
14402
+ left: chart.plotLeft + PX,
14403
+ top: chart.plotTop + PX,
14404
+ width: chart.plotWidth + PX,
14405
+ height: chart.plotHeight + PX
14406
+ });
14407
+ }
14408
+ };
14438
14409
 
14439
14410
  // create the layer at the first call
14440
14411
  if (!loadingDiv) {
@@ -14451,7 +14422,7 @@ extend(Chart.prototype, {
14451
14422
  loadingOptions.labelStyle,
14452
14423
  loadingDiv
14453
14424
  );
14454
-
14425
+ addEvent(chart, 'redraw', setLoadingSize); // #1080
14455
14426
  }
14456
14427
 
14457
14428
  // update text
@@ -14461,11 +14432,7 @@ extend(Chart.prototype, {
14461
14432
  if (!chart.loadingShown) {
14462
14433
  css(loadingDiv, {
14463
14434
  opacity: 0,
14464
- display: '',
14465
- left: chart.plotLeft + PX,
14466
- top: chart.plotTop + PX,
14467
- width: chart.plotWidth + PX,
14468
- height: chart.plotHeight + PX
14435
+ display: ''
14469
14436
  });
14470
14437
  animate(loadingDiv, {
14471
14438
  opacity: loadingOptions.style.opacity
@@ -14474,6 +14441,7 @@ extend(Chart.prototype, {
14474
14441
  });
14475
14442
  chart.loadingShown = true;
14476
14443
  }
14444
+ setLoadingSize();
14477
14445
  },
14478
14446
 
14479
14447
  /**
@@ -14742,14 +14710,22 @@ extend(Series.prototype, {
14742
14710
  * Update the series with a new set of options
14743
14711
  */
14744
14712
  update: function (newOptions, redraw) {
14745
- var chart = this.chart,
14713
+ var series = this,
14714
+ chart = this.chart,
14746
14715
  // must use user options when changing type because this.options is merged
14747
14716
  // in with type specific plotOptions
14748
14717
  oldOptions = this.userOptions,
14749
14718
  oldType = this.type,
14750
14719
  proto = seriesTypes[oldType].prototype,
14720
+ preserve = ['group', 'markerGroup', 'dataLabelsGroup'],
14751
14721
  n;
14752
14722
 
14723
+ // Make sure groups are not destroyed (#3094)
14724
+ each(preserve, function (prop) {
14725
+ preserve[prop] = series[prop];
14726
+ delete series[prop];
14727
+ });
14728
+
14753
14729
  // Do the merge, with some forced options
14754
14730
  newOptions = merge(oldOptions, {
14755
14731
  animation: false,
@@ -14766,8 +14742,14 @@ extend(Series.prototype, {
14766
14742
  }
14767
14743
  extend(this, seriesTypes[newOptions.type || oldType].prototype);
14768
14744
 
14745
+ // Re-register groups (#3094)
14746
+ each(preserve, function (prop) {
14747
+ series[prop] = preserve[prop];
14748
+ });
14749
+
14769
14750
 
14770
14751
  this.init(chart, newOptions);
14752
+ chart.linkSeries(); // Links are lost in this.remove (#3028)
14771
14753
  if (pick(redraw, true)) {
14772
14754
  chart.redraw(false);
14773
14755
  }
@@ -14874,7 +14856,8 @@ var AreaSeries = extendClass(Series, {
14874
14856
  * in the stack.
14875
14857
  */
14876
14858
  getSegments: function () {
14877
- var segments = [],
14859
+ var series = this,
14860
+ segments = [],
14878
14861
  segment = [],
14879
14862
  keys = [],
14880
14863
  xAxis = this.xAxis,
@@ -14885,7 +14868,6 @@ var AreaSeries = extendClass(Series, {
14885
14868
  plotY,
14886
14869
  points = this.points,
14887
14870
  connectNulls = this.options.connectNulls,
14888
- val,
14889
14871
  i,
14890
14872
  x;
14891
14873
 
@@ -14906,6 +14888,9 @@ var AreaSeries = extendClass(Series, {
14906
14888
  });
14907
14889
 
14908
14890
  each(keys, function (x) {
14891
+ var y = 0,
14892
+ stackPoint;
14893
+
14909
14894
  if (connectNulls && (!pointMap[x] || pointMap[x].y === null)) { // #1836
14910
14895
  return;
14911
14896
 
@@ -14917,9 +14902,19 @@ var AreaSeries = extendClass(Series, {
14917
14902
  // insert a dummy point in order for the areas to be drawn
14918
14903
  // correctly.
14919
14904
  } else {
14905
+
14906
+ // Loop down the stack to find the series below this one that has
14907
+ // a value (#1991)
14908
+ for (i = series.index; i <= yAxis.series.length; i++) {
14909
+ stackPoint = stack[x].points[i + ',' + x];
14910
+ if (stackPoint) {
14911
+ y = stackPoint[1];
14912
+ break;
14913
+ }
14914
+ }
14915
+
14920
14916
  plotX = xAxis.translate(x);
14921
- val = stack[x].percent ? (stack[x].total ? stack[x].cum * 100 / stack[x].total : 0) : stack[x].cum; // #1991
14922
- plotY = yAxis.toPixels(val, true);
14917
+ plotY = yAxis.toPixels(y, true);
14923
14918
  segment.push({
14924
14919
  y: null,
14925
14920
  plotX: plotX,
@@ -15355,7 +15350,7 @@ var ColumnSeries = extendClass(Series, {
15355
15350
  minPointLength = pick(options.minPointLength, 5),
15356
15351
  metrics = series.getColumnMetrics(),
15357
15352
  pointWidth = metrics.width,
15358
- seriesBarW = series.barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width
15353
+ seriesBarW = series.barW = mathMax(pointWidth, 1 + 2 * borderWidth), // postprocessed for border width
15359
15354
  pointXOffset = series.pointXOffset = metrics.offset,
15360
15355
  xCrisp = -(borderWidth % 2 ? 0.5 : 0),
15361
15356
  yCrisp = borderWidth % 2 ? 0.5 : 1;
@@ -15364,9 +15359,16 @@ var ColumnSeries = extendClass(Series, {
15364
15359
  yCrisp += 1;
15365
15360
  }
15366
15361
 
15362
+ // When the pointPadding is 0, we want the columns to be packed tightly, so we allow individual
15363
+ // columns to have individual sizes. When pointPadding is greater, we strive for equal-width
15364
+ // columns (#2694).
15365
+ if (options.pointPadding) {
15366
+ seriesBarW = mathCeil(seriesBarW);
15367
+ }
15368
+
15367
15369
  Series.prototype.translate.apply(series);
15368
15370
 
15369
- // record the new values
15371
+ // Record the new values
15370
15372
  each(series.points, function (point) {
15371
15373
  var yBottom = pick(point.yBottom, translatedThreshold),
15372
15374
  plotY = mathMin(mathMax(-999 - yBottom, point.plotY), yAxis.len + 999 + yBottom), // Don't draw too far outside plot area (#1303, #2241)
@@ -15376,7 +15378,6 @@ var ColumnSeries = extendClass(Series, {
15376
15378
  right,
15377
15379
  bottom,
15378
15380
  fromTop,
15379
- fromLeft,
15380
15381
  barH = mathMax(plotY, yBottom) - barY;
15381
15382
 
15382
15383
  // Handle options.minPointLength
@@ -15397,8 +15398,7 @@ var ColumnSeries = extendClass(Series, {
15397
15398
  // Fix the tooltip on center of grouped columns (#1216)
15398
15399
  point.tooltipPos = chart.inverted ? [yAxis.len - plotY, series.xAxis.len - barX - barW / 2] : [barX + barW / 2, plotY];
15399
15400
 
15400
- // Round off to obtain crisp edges
15401
- fromLeft = mathAbs(barX) < 0.5;
15401
+ // Round off to obtain crisp edges and avoid overlapping with neighbours (#2694)
15402
15402
  right = mathRound(barX + barW) + xCrisp;
15403
15403
  barX = mathRound(barX) + xCrisp;
15404
15404
  barW = right - barX;
@@ -15408,11 +15408,7 @@ var ColumnSeries = extendClass(Series, {
15408
15408
  barY = mathRound(barY) + yCrisp;
15409
15409
  barH = bottom - barY;
15410
15410
 
15411
- // Top and left edges are exceptions
15412
- if (fromLeft) {
15413
- barX += 1;
15414
- barW -= 1;
15415
- }
15411
+ // Top edges are exceptions
15416
15412
  if (fromTop) {
15417
15413
  barY -= 1;
15418
15414
  barH += 1;
@@ -15426,6 +15422,7 @@ var ColumnSeries = extendClass(Series, {
15426
15422
  width: barW,
15427
15423
  height: barH
15428
15424
  };
15425
+
15429
15426
  });
15430
15427
 
15431
15428
  },
@@ -15455,20 +15452,23 @@ var ColumnSeries = extendClass(Series, {
15455
15452
  renderer = chart.renderer,
15456
15453
  animationLimit = options.animationLimit || 250,
15457
15454
  shapeArgs,
15458
- pointAttr,
15459
- borderAttr;
15455
+ pointAttr;
15460
15456
 
15461
15457
  // draw the columns
15462
15458
  each(series.points, function (point) {
15463
15459
  var plotY = point.plotY,
15464
- graphic = point.graphic;
15460
+ graphic = point.graphic,
15461
+ borderAttr;
15465
15462
 
15466
15463
  if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
15467
15464
  shapeArgs = point.shapeArgs;
15465
+
15468
15466
  borderAttr = defined(series.borderWidth) ? {
15469
15467
  'stroke-width': series.borderWidth
15470
15468
  } : {};
15469
+
15471
15470
  pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] || series.pointAttr[NORMAL_STATE];
15471
+
15472
15472
  if (graphic) { // update
15473
15473
  stop(graphic);
15474
15474
  graphic.attr(borderAttr)[chart.pointCount < animationLimit ? 'animate' : 'attr'](merge(shapeArgs));
@@ -15562,7 +15562,7 @@ seriesTypes.bar = BarSeries;
15562
15562
  defaultPlotOptions.scatter = merge(defaultSeriesOptions, {
15563
15563
  lineWidth: 0,
15564
15564
  tooltip: {
15565
- headerFormat: '<span style="color:{series.color}">\u25CF</span> <span style="font-size: 10px;"> {series.name}</span><br/>', // docs
15565
+ headerFormat: '<span style="color:{series.color}">\u25CF</span> <span style="font-size: 10px;"> {series.name}</span><br/>',
15566
15566
  pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
15567
15567
  },
15568
15568
  stickyTracking: false
@@ -15576,7 +15576,7 @@ var ScatterSeries = extendClass(Series, {
15576
15576
  sorted: false,
15577
15577
  requireSorting: false,
15578
15578
  noSharedTooltip: true,
15579
- trackerGroups: ['markerGroup'],
15579
+ trackerGroups: ['markerGroup', 'dataLabelsGroup'],
15580
15580
  takeOrdinalPosition: false, // #2342
15581
15581
  singularTooltips: true,
15582
15582
  drawGraph: function () {
@@ -15604,7 +15604,7 @@ defaultPlotOptions.pie = merge(defaultSeriesOptions, {
15604
15604
  // connectorPadding: 5,
15605
15605
  distance: 30,
15606
15606
  enabled: true,
15607
- formatter: function () {
15607
+ formatter: function () { // #2945
15608
15608
  return this.point.name;
15609
15609
  }
15610
15610
  // softConnector: true,
@@ -15733,7 +15733,7 @@ var PiePoint = extendClass(Point, {
15733
15733
  var shapeArgs = this.shapeArgs,
15734
15734
  chart = this.series.chart;
15735
15735
 
15736
- return this.series.chart.renderer.symbols.arc(chart.plotLeft + shapeArgs.x, chart.plotTop + shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
15736
+ return this.sliced || !this.visible ? [] : this.series.chart.renderer.symbols.arc(chart.plotLeft + shapeArgs.x, chart.plotTop + shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
15737
15737
  innerR: this.shapeArgs.r,
15738
15738
  start: shapeArgs.start,
15739
15739
  end: shapeArgs.end
@@ -16082,14 +16082,17 @@ Series.prototype.drawDataLabels = function () {
16082
16082
  dataLabelsGroup = series.plotGroup(
16083
16083
  'dataLabelsGroup',
16084
16084
  'data-labels',
16085
- HIDDEN,
16085
+ options.defer ? HIDDEN : VISIBLE,
16086
16086
  options.zIndex || 6
16087
16087
  );
16088
16088
 
16089
16089
  if (!series.hasRendered && pick(options.defer, true)) {
16090
16090
  dataLabelsGroup.attr({ opacity: 0 });
16091
16091
  addEvent(series, 'afterAnimate', function () {
16092
- series.dataLabelsGroup.show()[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: 200 });
16092
+ if (series.visible) { // #3023, #3024
16093
+ dataLabelsGroup.show();
16094
+ }
16095
+ dataLabelsGroup[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: 200 });
16093
16096
  });
16094
16097
  }
16095
16098
 
@@ -16228,12 +16231,13 @@ Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo,
16228
16231
 
16229
16232
  // Allow a hook for changing alignment in the last moment, then do the alignment
16230
16233
  if (options.rotation) { // Fancy box alignment isn't supported for rotated text
16231
- alignAttr = {
16232
- align: options.align,
16233
- x: alignTo.x + options.x + alignTo.width / 2,
16234
- y: alignTo.y + options.y + alignTo.height / 2
16235
- };
16236
- dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
16234
+ dataLabel[isNew ? 'attr' : 'animate']({
16235
+ x: alignTo.x + options.x + alignTo.width / 2,
16236
+ y: alignTo.y + options.y + alignTo.height / 2
16237
+ })
16238
+ .attr({ // #3003
16239
+ align: options.align
16240
+ });
16237
16241
  } else {
16238
16242
  dataLabel.align(options, null, alignTo);
16239
16243
  alignAttr = dataLabel.alignAttr;
@@ -16375,13 +16379,6 @@ if (seriesTypes.pie) {
16375
16379
  }
16376
16380
  });
16377
16381
 
16378
- // assume equal label heights
16379
- i = 0;
16380
- while (!labelHeight && data[i]) { // #1569
16381
- labelHeight = data[i] && data[i].dataLabel && (data[i].dataLabel.getBBox().height || 21); // 21 is for #968
16382
- i++;
16383
- }
16384
-
16385
16382
  /* Loop over the points in each half, starting from the top and bottom
16386
16383
  * of the pie to detect overlapping labels.
16387
16384
  */
@@ -16393,38 +16390,65 @@ if (seriesTypes.pie) {
16393
16390
  usedSlots = [],
16394
16391
  points = halves[i],
16395
16392
  pos,
16393
+ bottom,
16396
16394
  length = points.length,
16397
16395
  slotIndex;
16398
16396
 
16397
+ if (!length) {
16398
+ continue;
16399
+ }
16400
+
16399
16401
  // Sort by angle
16400
16402
  series.sortByAngle(points, i - 0.5);
16401
16403
 
16404
+ // Assume equal label heights on either hemisphere (#2630)
16405
+ j = labelHeight = 0;
16406
+ while (!labelHeight && points[j]) { // #1569
16407
+ labelHeight = points[j] && points[j].dataLabel && (points[j].dataLabel.getBBox().height || 21); // 21 is for #968
16408
+ j++;
16409
+ }
16410
+
16402
16411
  // Only do anti-collision when we are outside the pie and have connectors (#856)
16403
16412
  if (distanceOption > 0) {
16404
16413
 
16405
- // build the slots
16406
- for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) {
16414
+ // Build the slots
16415
+ bottom = mathMin(centerY + radius + distanceOption, chart.plotHeight);
16416
+ for (pos = mathMax(0, centerY - radius - distanceOption); pos <= bottom; pos += labelHeight) {
16407
16417
  slots.push(pos);
16418
+ }
16419
+ slotsLength = slots.length;
16420
+
16408
16421
 
16409
- // visualize the slot
16410
- /*
16422
+ /* Visualize the slots
16423
+ if (!series.slotElements) {
16424
+ series.slotElements = [];
16425
+ }
16426
+ if (i === 1) {
16427
+ series.slotElements.forEach(function (elem) {
16428
+ elem.destroy();
16429
+ });
16430
+ series.slotElements.length = 0;
16431
+ }
16432
+
16433
+ slots.forEach(function (pos, no) {
16411
16434
  var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0),
16412
16435
  slotY = pos + chart.plotTop;
16436
+
16413
16437
  if (!isNaN(slotX)) {
16414
- chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1)
16438
+ series.slotElements.push(chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1)
16415
16439
  .attr({
16416
16440
  'stroke-width': 1,
16417
- stroke: 'silver'
16441
+ stroke: 'silver',
16442
+ fill: 'rgba(0,0,255,0.1)'
16418
16443
  })
16419
- .add();
16420
- chart.renderer.text('Slot '+ (slots.length - 1), slotX, slotY + 4)
16444
+ .add());
16445
+ series.slotElements.push(chart.renderer.text('Slot '+ no, slotX, slotY + 4)
16421
16446
  .attr({
16422
16447
  fill: 'silver'
16423
- }).add();
16448
+ }).add());
16424
16449
  }
16425
- */
16426
- }
16427
- slotsLength = slots.length;
16450
+ });
16451
+ // */
16428
16452
 
16429
16453
  // if there are more values than available slots, remove lowest values
16430
16454
  if (length > slotsLength) {
@@ -16508,7 +16532,7 @@ if (seriesTypes.pie) {
16508
16532
  y = slot.y;
16509
16533
  if ((naturalY > y && slots[slotIndex + 1] !== null) ||
16510
16534
  (naturalY < y && slots[slotIndex - 1] !== null)) {
16511
- y = naturalY;
16535
+ y = mathMin(mathMax(0, naturalY), chart.plotHeight);
16512
16536
  }
16513
16537
 
16514
16538
  } else {
@@ -16519,7 +16543,7 @@ if (seriesTypes.pie) {
16519
16543
  // and botton slice connectors from touching each other on either side
16520
16544
  x = options.justify ?
16521
16545
  seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
16522
- series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i);
16546
+ series.getX(y === centerY - radius - distanceOption || y === centerY + radius + distanceOption ? naturalY : y, i);
16523
16547
 
16524
16548
 
16525
16549
  // Record the placement and visibility
@@ -17192,9 +17216,9 @@ extend(Point.prototype, {
17192
17216
  var chart = this.series.chart,
17193
17217
  hoverPoints = chart.hoverPoints;
17194
17218
 
17195
- if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887
17196
- this.firePointEvent('mouseOut');
17219
+ this.firePointEvent('mouseOut');
17197
17220
 
17221
+ if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887, #2240
17198
17222
  this.setState();
17199
17223
  chart.hoverPoint = null;
17200
17224
  }
@@ -17439,7 +17463,7 @@ extend(Series.prototype, {
17439
17463
  }
17440
17464
 
17441
17465
  if (state) {
17442
- lineWidth = stateOptions[state].lineWidth || lineWidth + 1;
17466
+ lineWidth = stateOptions[state].lineWidth || lineWidth + (stateOptions[state].lineWidthPlus || 0);
17443
17467
  }
17444
17468
 
17445
17469
  if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML