highcharts-rails 4.2.3 → 4.2.4

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: 69fd18344b70b32bbf3bfbfea024615e2b1f5f76
4
- data.tar.gz: 14eb3b76281b1bdd2afe3e3e198839f295c8c169
3
+ metadata.gz: 97714de447dedd03f4f7d06964a96824433b7915
4
+ data.tar.gz: e14b8a7a89bf5de5a48bb2c2118036292fc4e96d
5
5
  SHA512:
6
- metadata.gz: f82932eb7563813afeb7122ea8054aa8f8175deca56159eac6c8a6f0f29ea03d9b91ffb562361169298a50c4537edee41ebe565585bf98c9d366234a830dd902
7
- data.tar.gz: e3c0c82eed6937aefba1bf1bffa01ab55d5e92ff2d13996a21ea9dd6e9da72bbae9ec9165700e04e7369995614464a57e8c3a73fb57e2c2731b089bb2b47d2f8
6
+ metadata.gz: 38bb75286a35565c01ae20b6785d962a7ba5f4b9d2792807d490a89950838b2f1db2c1ba67bc3f3b9f3beeadaeea32cc0ddcbe1d4341ac6e674ce8c3b20cbe40
7
+ data.tar.gz: 8d4f48c2627791d0ca196f4084adea4c9ab94306d7efa1373996e113c62ea76b36946121755b08c0beb4d4669008d4ad425a02016f7e388efa0ccf187b435f15
@@ -1,3 +1,59 @@
1
+ # 4.2.4 / 2016-05-06
2
+
3
+ * Updated Highcharts to 4.2.4
4
+ * Added support for polar columnrange series.
5
+ * Added e.originalEvent to drilldown event in order to catch modifier keys and other properties. Closes #5113.
6
+ * Added new drilldown event, chart.events.drillupall, that is triggered after multiple single drillup events. Closes #5158. Closes #5159.
7
+ * Added new option, chart.options3d.fitToPlot to fit 3D charts into the available plotting area. This may affect the layout of existing 3D charts if the layout was adjusted by chart.margin. The chart.margin setting can now normally be removed. Closes #4933.
8
+ * Added new option, lang.shortWeekdays, to specify short weekdays other than the first three letters of the weekday.
9
+ * Added new value, day, for pointIntervalUnit.
10
+ * Added option, legend.navigation.enabled, to allow disabling legend scroll.
11
+ * Fixed #3450, tooltip did not display after zooming on Android.
12
+ * Fixed #3916, halo and tooltip visible for points outside the plot area.
13
+ * Fixed #4552, mouse interaction and tooltip positions didn't match on 3D scatter chart.
14
+ * Fixed #4631, treemap series displayed no data even when data existed.
15
+ * Fixed #4679, when series are overlapping, the tooltip should show the topmost point.
16
+ * Fixed #4700, users can set and modify opacity on parent nodes in treemap.
17
+ * Fixed #474, vertical label alignment on top axis was off when labels were on multiple lines.
18
+ * Fixed #4751, inconsistent data label behaviour after redraw.
19
+ * Fixed #4784, setting allAreas to false does not center map.
20
+ * Fixed #4812, dataLabels which was supposed to be hidden were still visible.
21
+ * Fixed #4856, zooming did not work in Treemap.
22
+ * Fixed #4866, space was not reserved for tick marks, allowing ticks to overflow the chart when axis labels were not present.
23
+ * Fixed #4868, axis breaks were not applied for a range series.
24
+ * Fixed #4891, pie slice's graphics were not fully hidden, when slice was hidden.
25
+ * Fixed #4954, corrected floating values with isSum and isIntermediateSum in Waterfall series.
26
+ * Fixed #4961, placement of tooltip on flags for inverted charts.
27
+ * Fixed #4984, tooltip jiggling on arearange even when followPointer was true.
28
+ * Fixed #5007, JS error from tooltip when running in strict mode.
29
+ * Fixed #5010, stacked areas didn't work with Date objects as X values.
30
+ * Fixed #5029, runtime error on empty data set.
31
+ * Fixed #5030, bars were shaking on initial animation.
32
+ * Fixed #5034, a regression causing axis labels on vertical axis not to wrap.
33
+ * Fixed #5053, regressions related to chart callbacks firing async.
34
+ * Fixed #5060, date format for %e was not consistent with docs.
35
+ * Fixed #5063, solid gauge tooltip showed wrong series the first time.
36
+ * Fixed #5066, crosshair with snap: false only appeared when hovering a point.
37
+ * Fixed #5075, display was not deferred on data labels with useHTML.
38
+ * Fixed #5079, a regression causing vertical axis title to disappear in IE8.
39
+ * Fixed #5084, wrong Heatmap Intro was set in parts.js, which made the Download Builder fail.
40
+ * Fixed #5085, tooltip showed for points immediately outside the plot area on the X axis.
41
+ * Fixed #5086, X axis labels overlapped when added via Chart.addSeries.
42
+ * Fixed #5087, container with padding caused error with printing in IE8.
43
+ * Fixed #5099, single point marker was not displaying when surrounded by null points.
44
+ * Fixed #5101, click events did not fire on pie slices after Point.update.
45
+ * Fixed #5112, returning false from event handlers didn't cancel default function.
46
+ * Fixed #5133, data labels disappeared on chart redraw when defer was explicitly set to true.
47
+ * Fixed #5134, a regression causing wrong aniation on pie labels when moving from one side to the other.
48
+ * Fixed #5137, boost module threw JS error when doing mouse interaction after removing series.
49
+ * Fixed #5144, problem with percentage stack with null points.
50
+ * Fixed #5146, columns with zero value and no border on negative chart were visible even though they shouldn't.
51
+ * Fixed #5156, funnel did not respect the color set for selected state.
52
+ * Fixed #5160, data grouping and boost module did not play well together.
53
+ * Fixed #5167, wrong padding in bubble charts made point disappear after setting axis extremes.
54
+ * Fixed #5171, log scale transformation functions were not accessible from the outside.
55
+ * Fixed #5174, invalid CSS because of division by zero.
56
+
1
57
  # 4.2.3 / 2016-04-09
2
58
 
3
59
  * Updated Highcharts to 4.2.3 (2016-02-08)
@@ -9,7 +9,7 @@ If you are looking to include Highstock, Justin Kuepper has made [highstock-rail
9
9
 
10
10
  Add the gem to the Gemfile
11
11
 
12
- gem "highcharts-rails", "~> 3.0.0"
12
+ gem "highcharts-rails"
13
13
  # The gem version mirrors the included version of Highcharts
14
14
 
15
15
  ## Changes
@@ -27,8 +27,6 @@ In your JavaScript manifest (e.g. `application.js`)
27
27
 
28
28
  To include one of the other adapters
29
29
 
30
- //= require highcharts/adapters/mootools-adapter
31
- //= require highcharts/adapters/prototype-adapter
32
30
  //= require highcharts/adapters/standalone-framework
33
31
 
34
32
  Or the modules
@@ -41,6 +39,7 @@ Or the modules
41
39
  //= require highcharts/modules/funnel
42
40
  //= require highcharts/modules/heatmap
43
41
  //= require highcharts/modules/no-data-to-display
42
+ //= require highcharts/modules/offline-exporting
44
43
 
45
44
  Or one of the themes
46
45
 
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.2.3 (2016-02-08)
5
+ * @license Highcharts JS v4.2.4 (2016-04-14)
6
6
  *
7
7
  * (c) 2009-2016 Torstein Honsi
8
8
  *
@@ -59,7 +59,7 @@
59
59
  charts = [],
60
60
  chartCount = 0,
61
61
  PRODUCT = 'Highcharts',
62
- VERSION = '4.2.3',
62
+ VERSION = '4.2.4',
63
63
 
64
64
  // some constants for frequently used strings
65
65
  DIV = 'div',
@@ -486,13 +486,6 @@
486
486
  return typeof n === 'number';
487
487
  }
488
488
 
489
- function log2lin(num) {
490
- return math.log(num) / math.LN10;
491
- }
492
- function lin2log(num) {
493
- return math.pow(10, num);
494
- }
495
-
496
489
  /**
497
490
  * Remove last occurence of an item from an array
498
491
  * @param {Array} arr
@@ -643,8 +636,8 @@
643
636
  * @param {Number} number
644
637
  * @param {Number} length
645
638
  */
646
- function pad(number, length) {
647
- return new Array((length || 2) + 1 - String(number).length).join(0) + number;
639
+ function pad(number, length, padder) {
640
+ return new Array((length || 2) + 1 - String(number).length).join(padder || 0) + number;
648
641
  }
649
642
 
650
643
  /**
@@ -698,15 +691,16 @@
698
691
  fullYear = date[getFullYear](),
699
692
  lang = defaultOptions.lang,
700
693
  langWeekdays = lang.weekdays,
694
+ shortWeekdays = lang.shortWeekdays,
701
695
 
702
696
  // List all format keys. Custom formats can be added from the outside.
703
697
  replacements = extend({
704
698
 
705
699
  // Day
706
- 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
700
+ 'a': shortWeekdays ? shortWeekdays[day] : langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
707
701
  'A': langWeekdays[day], // Long weekday, like 'Monday'
708
702
  'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31
709
- 'e': dayOfMonth, // Day of the month, 1 through 31
703
+ 'e': pad(dayOfMonth, 2, ' '), // Day of the month, 1 through 31
710
704
  'w': day,
711
705
 
712
706
  // Week (none implemented)
@@ -992,6 +986,14 @@
992
986
  chart.renderer.globalAnimation = pick(animation, chart.animation);
993
987
  }
994
988
 
989
+ /**
990
+ * Get the animation in object form, where a disabled animation is always
991
+ * returned with duration: 0
992
+ */
993
+ function animObject(animation) {
994
+ return isObject(animation) ? merge(animation) : { duration: animation ? 500 : 0 };
995
+ }
996
+
995
997
  /**
996
998
  * The time unit lookup
997
999
  */
@@ -1107,7 +1109,9 @@
1107
1109
  * Map an array
1108
1110
  */
1109
1111
  map = function (arr, fn) {
1110
- var results = [], i = 0, len = arr.length;
1112
+ var results = [],
1113
+ i = 0,
1114
+ len = arr.length;
1111
1115
 
1112
1116
  for (; i < len; i++) {
1113
1117
  results[i] = fn.call(arr[i], arr[i], i, arr);
@@ -1269,7 +1273,6 @@
1269
1273
  events,
1270
1274
  len,
1271
1275
  i,
1272
- preventDefault,
1273
1276
  fn;
1274
1277
 
1275
1278
  eventArguments = eventArguments || {};
@@ -1292,36 +1295,33 @@
1292
1295
  events = hcEvents[type] || [];
1293
1296
  len = events.length;
1294
1297
 
1295
- // Attach a simple preventDefault function to skip default handler if called
1296
- preventDefault = function () {
1297
- eventArguments.defaultPrevented = true;
1298
- };
1298
+ // Attach a simple preventDefault function to skip default handler if called.
1299
+ // The built-in defaultPrevented property is not overwritable (#5112)
1300
+ if (!eventArguments.preventDefault) {
1301
+ eventArguments.preventDefault = function () {
1302
+ eventArguments.defaultPrevented = true;
1303
+ };
1304
+ }
1305
+
1306
+ eventArguments.target = el;
1307
+
1308
+ // If the type is not set, we're running a custom event (#2297). If it is set,
1309
+ // we're running a browser event, and setting it will cause en error in
1310
+ // IE8 (#2465).
1311
+ if (!eventArguments.type) {
1312
+ eventArguments.type = type;
1313
+ }
1299
1314
 
1300
1315
  for (i = 0; i < len; i++) {
1301
1316
  fn = events[i];
1302
1317
 
1303
- // eventArguments is never null here
1304
- if (eventArguments.stopped) {
1305
- return;
1306
- }
1307
-
1308
- eventArguments.preventDefault = preventDefault;
1309
- eventArguments.target = el;
1310
-
1311
- // If the type is not set, we're running a custom event (#2297). If it is set,
1312
- // we're running a browser event, and setting it will cause en error in
1313
- // IE8 (#2465).
1314
- if (!eventArguments.type) {
1315
- eventArguments.type = type;
1316
- }
1317
-
1318
1318
  // If the event handler return false, prevent the default handler from executing
1319
1319
  if (fn.call(el, eventArguments) === false) {
1320
1320
  eventArguments.preventDefault();
1321
1321
  }
1322
1322
  }
1323
1323
  }
1324
-
1324
+
1325
1325
  // Run the default if not prevented
1326
1326
  if (defaultFunction && !eventArguments.defaultPrevented) {
1327
1327
  defaultFunction(eventArguments);
@@ -1350,7 +1350,7 @@
1350
1350
  if (!isNumber(opt.duration)) {
1351
1351
  opt.duration = 400;
1352
1352
  }
1353
- opt.easing = Math[opt.easing] || Math.easeInOutSine;
1353
+ opt.easing = typeof opt.easing === 'function' ? opt.easing : (Math[opt.easing] || Math.easeInOutSine);
1354
1354
  opt.curAnim = merge(params);
1355
1355
 
1356
1356
  for (prop in params) {
@@ -1428,7 +1428,7 @@
1428
1428
  // Getting the rendered width and height
1429
1429
  if (alias) {
1430
1430
  el.style.zoom = 1;
1431
- return el[alias] - 2 * getStyle(el, 'padding');
1431
+ return Math.max(el[alias] - 2 * getStyle(el, 'padding'), 0);
1432
1432
  }
1433
1433
 
1434
1434
  val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b) {
@@ -1507,6 +1507,7 @@
1507
1507
  Highcharts.removeEvent = removeEvent;
1508
1508
  Highcharts.fireEvent = fireEvent;
1509
1509
  Highcharts.animate = animate;
1510
+ Highcharts.animObject = animObject;
1510
1511
  Highcharts.stop = stop;
1511
1512
 
1512
1513
  /* ****************************************************************************
@@ -1533,7 +1534,7 @@
1533
1534
  useUTC: true,
1534
1535
  //timezoneOffset: 0,
1535
1536
  canvasToolsURL: 'http://code.highcharts.com/modules/canvas-tools.js',
1536
- VMLRadialGradientURL: 'http://code.highcharts.com/4.2.3/gfx/vml-radial-gradient.png'
1537
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.2.4/gfx/vml-radial-gradient.png'
1537
1538
  },
1538
1539
  chart: {
1539
1540
  //animation: true,
@@ -2104,7 +2105,6 @@
2104
2105
  var animOptions = pick(options, this.renderer.globalAnimation, true);
2105
2106
  stop(this); // stop regardless of animation actually running, or reverting to .attr (#607)
2106
2107
  if (animOptions) {
2107
- animOptions = merge(animOptions, {}); //#2625
2108
2108
  if (complete) { // allows using a callback with the global animation without overwriting it
2109
2109
  animOptions.complete = complete;
2110
2110
  }
@@ -2403,7 +2403,7 @@
2403
2403
 
2404
2404
  while (i--) {
2405
2405
  setter.call(
2406
- null,
2406
+ shadows[i],
2407
2407
  key === 'height' ?
2408
2408
  Math.max(value - (shadows[i].cutHeight || 0), 0) :
2409
2409
  key === 'd' ? this.d : value,
@@ -3136,7 +3136,14 @@
3136
3136
  this[key] = value;
3137
3137
  },
3138
3138
  dashstyleSetter: function (value) {
3139
- var i;
3139
+ var i,
3140
+ strokeWidth = this['stroke-width'];
3141
+
3142
+ // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new strokeWidth
3143
+ // function, we should be able to use that instead.
3144
+ if (strokeWidth === 'inherit') {
3145
+ strokeWidth = 1;
3146
+ }
3140
3147
  value = value && value.toLowerCase();
3141
3148
  if (value) {
3142
3149
  value = value
@@ -3152,10 +3159,10 @@
3152
3159
 
3153
3160
  i = value.length;
3154
3161
  while (i--) {
3155
- value[i] = pInt(value[i]) * this['stroke-width'];
3162
+ value[i] = pInt(value[i]) * strokeWidth;
3156
3163
  }
3157
3164
  value = value.join(',')
3158
- .replace('NaN', 'none'); // #3226
3165
+ .replace(/NaN/g, 'none'); // #3226
3159
3166
  this.element.setAttribute('stroke-dasharray', value);
3160
3167
  }
3161
3168
  },
@@ -3218,7 +3225,7 @@
3218
3225
  i;
3219
3226
 
3220
3227
  if (defined(value)) {
3221
- element.setAttribute(key, value); // So we can read it for other elements in the group
3228
+ element.zIndex = value; // So we can read it for other elements in the group
3222
3229
  value = +value;
3223
3230
  if (this[key] === value) { // Only update when needed (#3865)
3224
3231
  run = false;
@@ -3239,7 +3246,7 @@
3239
3246
  childNodes = parentNode.childNodes;
3240
3247
  for (i = 0; i < childNodes.length && !inserted; i++) {
3241
3248
  otherElement = childNodes[i];
3242
- otherZIndex = attr(otherElement, 'zIndex');
3249
+ otherZIndex = otherElement.zIndex;
3243
3250
  if (otherElement !== element && (
3244
3251
  // Insert before the first element with a higher zIndex
3245
3252
  pInt(otherZIndex) > value ||
@@ -3529,13 +3536,14 @@
3529
3536
 
3530
3537
 
3531
3538
  // build the lines
3532
- each(lines, function (line, lineNo) {
3533
- var spans, spanNo = 0;
3539
+ each(lines, function buildTextLines(line, lineNo) {
3540
+ var spans,
3541
+ spanNo = 0;
3534
3542
 
3535
3543
  line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
3536
3544
  spans = line.split('|||');
3537
3545
 
3538
- each(spans, function (span) {
3546
+ each(spans, function buildTextSpans(span) {
3539
3547
  if (span !== '' || spans.length === 1) {
3540
3548
  var attributes = {},
3541
3549
  tspan = doc.createElementNS(SVG_NS, 'tspan'),
@@ -3720,7 +3728,7 @@
3720
3728
  pos += increment;
3721
3729
  }
3722
3730
  }
3723
- console.log(finalPos, node.getSubStringLength(0, finalPos))
3731
+ console.log('width', width, 'stringWidth', node.getSubStringLength(0, finalPos))
3724
3732
  },
3725
3733
  */
3726
3734
 
@@ -4157,14 +4165,14 @@
4157
4165
 
4158
4166
  // Fire the load event when all external images are loaded
4159
4167
  ren.imgCount--;
4160
- if (!ren.imgCount) {
4168
+ if (!ren.imgCount && charts[ren.chartIndex].onload) {
4161
4169
  charts[ren.chartIndex].onload();
4162
4170
  }
4163
4171
  },
4164
4172
  src: imageSrc
4165
4173
  });
4174
+ this.imgCount++;
4166
4175
  }
4167
- this.imgCount++;
4168
4176
  }
4169
4177
 
4170
4178
  return obj;
@@ -4625,7 +4633,7 @@
4625
4633
  if (value !== alignFactor) {
4626
4634
  alignFactor = value;
4627
4635
  if (bBox) { // Bounding box exists, means we're dynamically changing
4628
- wrapper.attr({ x: x });
4636
+ wrapper.attr({ x: wrapperX }); // #5134
4629
4637
  }
4630
4638
  }
4631
4639
  };
@@ -4929,6 +4937,7 @@
4929
4937
  var wrapper = this.createElement('span'),
4930
4938
  element = wrapper.element,
4931
4939
  renderer = wrapper.renderer,
4940
+ isSVG = renderer.isSVG,
4932
4941
  addSetters = function (element, style) {
4933
4942
  // These properties are set as attributes on the SVG group, and as
4934
4943
  // identical CSS properties on the div. (#3542)
@@ -4948,7 +4957,11 @@
4948
4957
  element.innerHTML = this.textStr = value;
4949
4958
  wrapper.htmlUpdateTransform();
4950
4959
  };
4951
- addSetters(wrapper, wrapper.element.style);
4960
+
4961
+ // Add setters for the element itself (#4938)
4962
+ if (isSVG) { // #4938, only for HTML within SVG
4963
+ addSetters(wrapper, wrapper.element.style);
4964
+ }
4952
4965
 
4953
4966
  // Various setters which rely on update transform
4954
4967
  wrapper.xSetter = wrapper.ySetter = wrapper.alignSetter = wrapper.rotationSetter = function (value, key) {
@@ -4979,7 +4992,7 @@
4979
4992
  wrapper.css = wrapper.htmlCss;
4980
4993
 
4981
4994
  // This is specific for HTML within SVG
4982
- if (renderer.isSVG) {
4995
+ if (isSVG) {
4983
4996
  wrapper.add = function (svgGroupWrapper) {
4984
4997
 
4985
4998
  var htmlGroup,
@@ -5018,7 +5031,8 @@
5018
5031
  htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, cls, {
5019
5032
  position: ABSOLUTE,
5020
5033
  left: (parentGroup.translateX || 0) + PX,
5021
- top: (parentGroup.translateY || 0) + PX
5034
+ top: (parentGroup.translateY || 0) + PX,
5035
+ opacity: parentGroup.opacity // #5075
5022
5036
  }, htmlGroup || container); // the top group is appended to container
5023
5037
 
5024
5038
  // Shortcut
@@ -6283,7 +6297,7 @@
6283
6297
  isFirst: isFirst,
6284
6298
  isLast: isLast,
6285
6299
  dateTimeLabelFormat: dateTimeLabelFormat,
6286
- value: axis.isLog ? correctFloat(lin2log(value)) : value
6300
+ value: axis.isLog ? correctFloat(axis.lin2log(value)) : value
6287
6301
  });
6288
6302
 
6289
6303
  // prepare CSS
@@ -6338,7 +6352,8 @@
6338
6352
  rotation = this.rotation,
6339
6353
  factor = { left: 0, center: 0.5, right: 1 }[axis.labelAlign],
6340
6354
  labelWidth = label.getBBox().width,
6341
- slotWidth = axis.slotWidth,
6355
+ slotWidth = axis.getSlotWidth(),
6356
+ modifiedSlotWidth = slotWidth,
6342
6357
  xCorrection = factor,
6343
6358
  goRight = 1,
6344
6359
  leftPos,
@@ -6353,21 +6368,21 @@
6353
6368
  rightPos = pxPos + (1 - factor) * labelWidth;
6354
6369
 
6355
6370
  if (leftPos < leftBound) {
6356
- slotWidth = xy.x + slotWidth * (1 - factor) - leftBound;
6371
+ modifiedSlotWidth = xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
6357
6372
  } else if (rightPos > rightBound) {
6358
- slotWidth = rightBound - xy.x + slotWidth * factor;
6373
+ modifiedSlotWidth = rightBound - xy.x + modifiedSlotWidth * factor;
6359
6374
  goRight = -1;
6360
6375
  }
6361
6376
 
6362
- slotWidth = mathMin(axis.slotWidth, slotWidth); // #4177
6363
- if (slotWidth < axis.slotWidth && axis.labelAlign === 'center') {
6364
- xy.x += goRight * (axis.slotWidth - slotWidth - xCorrection * (axis.slotWidth - mathMin(labelWidth, slotWidth)));
6377
+ modifiedSlotWidth = mathMin(slotWidth, modifiedSlotWidth); // #4177
6378
+ if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
6379
+ xy.x += goRight * (slotWidth - modifiedSlotWidth - xCorrection * (slotWidth - mathMin(labelWidth, modifiedSlotWidth)));
6365
6380
  }
6366
6381
  // If the label width exceeds the available space, set a text width to be
6367
6382
  // picked up below. Also, if a width has been set before, we need to set a new
6368
6383
  // one because the reported labelWidth will be limited by the box (#3938).
6369
- if (labelWidth > slotWidth || (axis.autoRotation && label.styles.width)) {
6370
- textWidth = slotWidth;
6384
+ if (labelWidth > modifiedSlotWidth || (axis.autoRotation && label.styles.width)) {
6385
+ textWidth = modifiedSlotWidth;
6371
6386
  }
6372
6387
 
6373
6388
  // Add ellipsis to prevent rotated labels to be clipped against the edge of the chart
@@ -6419,10 +6434,14 @@
6419
6434
  line;
6420
6435
 
6421
6436
  if (!defined(yOffset)) {
6422
- yOffset = axis.side === 2 ?
6423
- rotCorr.y + 8 :
6437
+ if (axis.side === 0) {
6438
+ yOffset = label.rotation ? -8 : -label.getBBox().height;
6439
+ } else if (axis.side === 2) {
6440
+ yOffset = rotCorr.y + 8;
6441
+ } else {
6424
6442
  // #3140, #3140
6425
6443
  yOffset = mathCos(label.rotation * deg2rad) * (rotCorr.y - label.getBBox(false, 0).height / 2);
6444
+ }
6426
6445
  }
6427
6446
 
6428
6447
  x = x + labelOptions.x + rotCorr.x - (tickmarkOffset && horiz ?
@@ -6482,10 +6501,8 @@
6482
6501
  gridLineWidth = options[gridPrefix + 'LineWidth'],
6483
6502
  gridLineColor = options[gridPrefix + 'LineColor'],
6484
6503
  dashStyle = options[gridPrefix + 'LineDashStyle'],
6485
- tickLength = options[tickPrefix + 'Length'],
6486
- tickWidth = pick(options[tickPrefix + 'Width'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
6504
+ tickSize = axis.tickSize(tickPrefix),
6487
6505
  tickColor = options[tickPrefix + 'Color'],
6488
- tickPosition = options[tickPrefix + 'Position'],
6489
6506
  gridLinePath,
6490
6507
  mark = tick.mark,
6491
6508
  markPath,
@@ -6537,17 +6554,11 @@
6537
6554
  }
6538
6555
 
6539
6556
  // create the tick mark
6540
- if (tickWidth && tickLength) {
6541
-
6542
- // negate the length
6543
- if (tickPosition === 'inside') {
6544
- tickLength = -tickLength;
6545
- }
6557
+ if (tickSize) {
6546
6558
  if (axis.opposite) {
6547
- tickLength = -tickLength;
6559
+ tickSize[0] = -tickSize[0];
6548
6560
  }
6549
-
6550
- markPath = tick.getMarkPath(x, y, tickLength, tickWidth * reverseCrisp, horiz, renderer);
6561
+ markPath = tick.getMarkPath(x, y, tickSize[0], tickSize[1] * reverseCrisp, horiz, renderer);
6551
6562
  if (mark) { // updating
6552
6563
  mark.animate({
6553
6564
  d: markPath,
@@ -6558,7 +6569,7 @@
6558
6569
  markPath
6559
6570
  ).attr({
6560
6571
  stroke: tickColor,
6561
- 'stroke-width': tickWidth,
6572
+ 'stroke-width': tickSize[1],
6562
6573
  opacity: opacity
6563
6574
  }).add(axis.axisGroup);
6564
6575
  }
@@ -6644,7 +6655,8 @@
6644
6655
  zIndex = pick(options.zIndex, 0),
6645
6656
  events = options.events,
6646
6657
  attribs = {},
6647
- renderer = axis.chart.renderer;
6658
+ renderer = axis.chart.renderer,
6659
+ log2lin = axis.log2lin;
6648
6660
 
6649
6661
  // logarithmic conversion
6650
6662
  if (axis.isLog) {
@@ -6919,8 +6931,8 @@
6919
6931
  cursor: 'default',
6920
6932
  fontSize: '11px'
6921
6933
  },
6922
- x: 0,
6923
- y: 15
6934
+ x: 0
6935
+ //y: undefined
6924
6936
  /*formatter: function () {
6925
6937
  return this.value;
6926
6938
  },*/
@@ -6991,8 +7003,7 @@
6991
7003
  tickPixelInterval: 72,
6992
7004
  showLastLabel: true,
6993
7005
  labels: {
6994
- x: -8,
6995
- y: 3
7006
+ x: -8
6996
7007
  },
6997
7008
  lineWidth: 0,
6998
7009
  maxPadding: 0.05,
@@ -7023,8 +7034,7 @@
7023
7034
  */
7024
7035
  defaultLeftAxisOptions: {
7025
7036
  labels: {
7026
- x: -15,
7027
- y: null
7037
+ x: -15
7028
7038
  },
7029
7039
  title: {
7030
7040
  rotation: 270
@@ -7036,8 +7046,7 @@
7036
7046
  */
7037
7047
  defaultRightAxisOptions: {
7038
7048
  labels: {
7039
- x: 15,
7040
- y: null
7049
+ x: 15
7041
7050
  },
7042
7051
  title: {
7043
7052
  rotation: 90
@@ -7050,8 +7059,7 @@
7050
7059
  defaultBottomAxisOptions: {
7051
7060
  labels: {
7052
7061
  autoRotation: [-45],
7053
- x: 0,
7054
- y: null // based on font size
7062
+ x: 0
7055
7063
  // overflow: undefined,
7056
7064
  // staggerLines: null
7057
7065
  },
@@ -7065,8 +7073,7 @@
7065
7073
  defaultTopAxisOptions: {
7066
7074
  labels: {
7067
7075
  autoRotation: [-45],
7068
- x: 0,
7069
- y: -15
7076
+ x: 0
7070
7077
  // overflow: undefined
7071
7078
  // staggerLines: null
7072
7079
  },
@@ -7230,8 +7237,8 @@
7230
7237
 
7231
7238
  // extend logarithmic axis
7232
7239
  if (axis.isLog) {
7233
- axis.val2lin = log2lin;
7234
- axis.lin2val = lin2log;
7240
+ axis.val2lin = axis.log2lin;
7241
+ axis.lin2val = axis.lin2log;
7235
7242
  }
7236
7243
  },
7237
7244
 
@@ -7670,6 +7677,22 @@
7670
7677
  axis.max = max;
7671
7678
  },
7672
7679
 
7680
+ /**
7681
+ * Find the closestPointRange across all series
7682
+ */
7683
+ getClosest: function () {
7684
+ var ret;
7685
+ each(this.series, function (series) {
7686
+ var seriesClosest = series.closestPointRange;
7687
+ if (!series.noSharedTooltip && defined(seriesClosest)) {
7688
+ ret = defined(ret) ?
7689
+ mathMin(ret, seriesClosest) :
7690
+ seriesClosest;
7691
+ }
7692
+ });
7693
+ return ret;
7694
+ },
7695
+
7673
7696
  /**
7674
7697
  * Update translation information
7675
7698
  */
@@ -7693,15 +7716,9 @@
7693
7716
  pointRangePadding = linkedParent.pointRangePadding;
7694
7717
 
7695
7718
  } else {
7696
- // Find the closestPointRange across all series
7697
- each(axis.series, function (series) {
7698
- var seriesClosest = series.closestPointRange;
7699
- if (!series.noSharedTooltip && defined(seriesClosest)) {
7700
- closestPointRange = defined(closestPointRange) ?
7701
- mathMin(closestPointRange, seriesClosest) :
7702
- seriesClosest;
7703
- }
7704
- });
7719
+
7720
+ // Get the closest points
7721
+ closestPointRange = axis.getClosest();
7705
7722
 
7706
7723
  each(axis.series, function (series) {
7707
7724
  var seriesPointRange = hasCategories ?
@@ -7770,6 +7787,7 @@
7770
7787
  chart = axis.chart,
7771
7788
  options = axis.options,
7772
7789
  isLog = axis.isLog,
7790
+ log2lin = axis.log2lin,
7773
7791
  isDatetimeAxis = axis.isDatetimeAxis,
7774
7792
  isXAxis = axis.isXAxis,
7775
7793
  isLinked = axis.isLinked,
@@ -8345,7 +8363,8 @@
8345
8363
  */
8346
8364
  getExtremes: function () {
8347
8365
  var axis = this,
8348
- isLog = axis.isLog;
8366
+ isLog = axis.isLog,
8367
+ lin2log = axis.lin2log;
8349
8368
 
8350
8369
  return {
8351
8370
  min: isLog ? correctFloat(lin2log(axis.min)) : axis.min,
@@ -8364,6 +8383,7 @@
8364
8383
  getThreshold: function (threshold) {
8365
8384
  var axis = this,
8366
8385
  isLog = axis.isLog,
8386
+ lin2log = axis.lin2log,
8367
8387
  realMin = isLog ? lin2log(axis.min) : axis.min,
8368
8388
  realMax = isLog ? lin2log(axis.max) : axis.max;
8369
8389
 
@@ -8398,22 +8418,50 @@
8398
8418
  return ret;
8399
8419
  },
8400
8420
 
8421
+ /**
8422
+ * Get the tick length and width for the axis.
8423
+ * @param {String} prefix 'tick' or 'minorTick'
8424
+ * @returns {Array} An array of tickLength and tickWidth
8425
+ */
8426
+ tickSize: function (prefix) {
8427
+ var options = this.options,
8428
+ tickLength = options[prefix + 'Length'],
8429
+ tickWidth = pick(options[prefix + 'Width'], prefix === 'tick' && this.isXAxis ? 1 : 0); // X axis defaults to 1
8430
+
8431
+ if (tickWidth && tickLength) {
8432
+ // Negate the length
8433
+ if (options[prefix + 'Position'] === 'inside') {
8434
+ tickLength = -tickLength;
8435
+ }
8436
+ return [tickLength, tickWidth];
8437
+ }
8438
+
8439
+ },
8440
+
8441
+ /**
8442
+ * Return the size of the labels
8443
+ */
8444
+ labelMetrics: function () {
8445
+ return this.chart.renderer.fontMetrics(
8446
+ this.options.labels.style.fontSize,
8447
+ this.ticks[0] && this.ticks[0].label
8448
+ );
8449
+ },
8450
+
8401
8451
  /**
8402
8452
  * Prevent the ticks from getting so close we can't draw the labels. On a horizontal
8403
8453
  * axis, this is handled by rotating the labels, removing ticks and adding ellipsis.
8404
8454
  * On a vertical axis remove ticks and add ellipsis.
8405
8455
  */
8406
8456
  unsquish: function () {
8407
- var chart = this.chart,
8408
- ticks = this.ticks,
8409
- labelOptions = this.options.labels,
8457
+ var labelOptions = this.options.labels,
8410
8458
  horiz = this.horiz,
8411
8459
  tickInterval = this.tickInterval,
8412
8460
  newTickInterval = tickInterval,
8413
8461
  slotSize = this.len / (((this.categories ? 1 : 0) + this.max - this.min) / tickInterval),
8414
8462
  rotation,
8415
8463
  rotationOption = labelOptions.rotation,
8416
- labelMetrics = chart.renderer.fontMetrics(labelOptions.style.fontSize, ticks[0] && ticks[0].label),
8464
+ labelMetrics = this.labelMetrics(),
8417
8465
  step,
8418
8466
  bestScore = Number.MAX_VALUE,
8419
8467
  autoRotation,
@@ -8463,6 +8511,26 @@
8463
8511
  return newTickInterval;
8464
8512
  },
8465
8513
 
8514
+ /**
8515
+ * Get the general slot width for this axis. This may change between the pre-render (from Axis.getOffset)
8516
+ * and the final tick rendering and placement (#5086).
8517
+ */
8518
+ getSlotWidth: function () {
8519
+ var chart = this.chart,
8520
+ horiz = this.horiz,
8521
+ labelOptions = this.options.labels,
8522
+ slotCount = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1),
8523
+ marginLeft = chart.margin[3];
8524
+
8525
+ return (horiz && (labelOptions.step || 0) < 2 && !labelOptions.rotation && // #4415
8526
+ ((this.staggerLines || 1) * chart.plotWidth) / slotCount) ||
8527
+ (!horiz && ((marginLeft && (marginLeft - chart.spacing[3])) || chart.chartWidth * 0.33)); // #1580, #1931
8528
+
8529
+ },
8530
+
8531
+ /**
8532
+ * Render the axis labels and determine whether ellipsis or rotation need to be applied
8533
+ */
8466
8534
  renderUnsquish: function () {
8467
8535
  var chart = this.chart,
8468
8536
  renderer = chart.renderer,
@@ -8470,14 +8538,10 @@
8470
8538
  ticks = this.ticks,
8471
8539
  labelOptions = this.options.labels,
8472
8540
  horiz = this.horiz,
8473
- margin = chart.margin,
8474
- slotCount = this.categories ? tickPositions.length : tickPositions.length - 1,
8475
- slotWidth = this.slotWidth = (horiz && (labelOptions.step || 0) < 2 && !labelOptions.rotation && // #4415
8476
- ((this.staggerLines || 1) * chart.plotWidth) / slotCount) ||
8477
- (!horiz && ((margin[3] && (margin[3] - chart.spacing[3])) || chart.chartWidth * 0.33)), // #1580, #1931,
8541
+ slotWidth = this.getSlotWidth(),
8478
8542
  innerWidth = mathMax(1, mathRound(slotWidth - 2 * (labelOptions.padding || 5))),
8479
8543
  attr = {},
8480
- labelMetrics = renderer.fontMetrics(labelOptions.style.fontSize, ticks[0] && ticks[0].label),
8544
+ labelMetrics = this.labelMetrics(),
8481
8545
  textOverflowOption = labelOptions.style.textOverflow,
8482
8546
  css,
8483
8547
  labelLength = 0,
@@ -8526,9 +8590,13 @@
8526
8590
  // Reset ellipsis in order to get the correct bounding box (#4070)
8527
8591
  if (label.styles.textOverflow === 'ellipsis') {
8528
8592
  label.css({ textOverflow: 'clip' });
8593
+
8594
+ // Set the correct width in order to read the bounding box height (#4678, #5034)
8595
+ } else if (ticks[pos].labelLength > slotWidth) {
8596
+ label.css({ width: slotWidth + 'px' });
8529
8597
  }
8530
- if (label.getBBox().height > this.len / tickPositions.length - (labelMetrics.h - labelMetrics.f) ||
8531
- ticks[pos].labelLength > slotWidth) { // #4678
8598
+
8599
+ if (label.getBBox().height > this.len / tickPositions.length - (labelMetrics.h - labelMetrics.f)) {
8532
8600
  label.specCss = { textOverflow: 'ellipsis' };
8533
8601
  }
8534
8602
  }
@@ -8607,7 +8675,8 @@
8607
8675
  directionFactor = [-1, 1, 1, -1][side],
8608
8676
  n,
8609
8677
  axisParent = axis.axisParent, // Used in color axis
8610
- lineHeightCorrection;
8678
+ lineHeightCorrection,
8679
+ tickSize = this.tickSize('tick');
8611
8680
 
8612
8681
  // For reuse in Axis.render
8613
8682
  hasData = axis.hasData();
@@ -8682,8 +8751,7 @@
8682
8751
  zIndex: 7,
8683
8752
  rotation: axisTitleOptions.rotation || 0,
8684
8753
  align:
8685
- axisTitleOptions.textAlign ||
8686
- {
8754
+ axisTitleOptions.textAlign || {
8687
8755
  low: opposite ? 'right' : 'left',
8688
8756
  middle: 'center',
8689
8757
  high: opposite ? 'left' : 'right'
@@ -8709,15 +8777,27 @@
8709
8777
  axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
8710
8778
 
8711
8779
  axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
8712
- lineHeightCorrection = side === 2 ? axis.tickRotCorr.y : 0;
8713
- labelOffsetPadded = Math.abs(labelOffset) + titleMargin +
8714
- (labelOffset && (directionFactor * (horiz ? pick(labelOptions.y, axis.tickRotCorr.y + 8) : labelOptions.x) - lineHeightCorrection));
8780
+ if (side === 0) {
8781
+ lineHeightCorrection = -axis.labelMetrics().h;
8782
+ } else if (side === 2) {
8783
+ lineHeightCorrection = axis.tickRotCorr.y;
8784
+ } else {
8785
+ lineHeightCorrection = 0;
8786
+ }
8787
+
8788
+ // Find the padded label offset
8789
+ labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
8790
+ if (labelOffset) {
8791
+ labelOffsetPadded -= lineHeightCorrection;
8792
+ labelOffsetPadded += directionFactor * (horiz ? pick(labelOptions.y, axis.tickRotCorr.y + directionFactor * 8) : labelOptions.x);
8793
+ }
8715
8794
  axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
8716
8795
 
8717
8796
  axisOffset[side] = mathMax(
8718
8797
  axisOffset[side],
8719
8798
  axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
8720
- labelOffsetPadded // #3027
8799
+ labelOffsetPadded, // #3027
8800
+ hasData && tickPositions.length && tickSize ? tickSize[0] : 0 // #4866
8721
8801
  );
8722
8802
 
8723
8803
  // Decide the clipping needed to keep the graph inside the plot area and axis lines
@@ -8809,6 +8889,7 @@
8809
8889
  renderer = chart.renderer,
8810
8890
  options = axis.options,
8811
8891
  isLog = axis.isLog,
8892
+ lin2log = axis.lin2log,
8812
8893
  isLinked = axis.isLinked,
8813
8894
  tickPositions = axis.tickPositions,
8814
8895
  axisTitle = axis.axisTitle,
@@ -8823,7 +8904,7 @@
8823
8904
  hasRendered = chart.hasRendered,
8824
8905
  slideInTicks = hasRendered && defined(axis.oldMin) && !isNaN(axis.oldMin),
8825
8906
  showAxis = axis.showAxis,
8826
- globalAnimation = renderer.globalAnimation,
8907
+ animation = animObject(renderer.globalAnimation),
8827
8908
  from,
8828
8909
  to;
8829
8910
 
@@ -8926,7 +9007,7 @@
8926
9007
  var pos,
8927
9008
  i,
8928
9009
  forDestruction = [],
8929
- delay = globalAnimation ? globalAnimation.duration || 500 : 0,
9010
+ delay = animation.duration,
8930
9011
  destroyInactiveItems = function () {
8931
9012
  i = forDestruction.length;
8932
9013
  while (i--) {
@@ -9358,6 +9439,8 @@
9358
9439
  var axis = this,
9359
9440
  options = axis.options,
9360
9441
  axisLength = axis.len,
9442
+ lin2log = axis.lin2log,
9443
+ log2lin = axis.log2lin,
9361
9444
  // Since we use this method for both major and minor ticks,
9362
9445
  // use a local variable and return the result
9363
9446
  positions = [];
@@ -9446,7 +9529,16 @@
9446
9529
  axis.tickInterval = interval;
9447
9530
  }
9448
9531
  return positions;
9449
- };/**
9532
+ };
9533
+
9534
+ Axis.prototype.log2lin = function (num) {
9535
+ return math.log(num) / math.LN10;
9536
+ };
9537
+
9538
+ Axis.prototype.lin2log = function (num) {
9539
+ return math.pow(10, num);
9540
+ };
9541
+ /**
9450
9542
  * The tooltip object
9451
9543
  * @param {Object} chart The chart instance
9452
9544
  * @param {Object} options Tooltip options
@@ -9643,7 +9735,7 @@
9643
9735
  first = ['y', chart.chartHeight, boxHeight, point.plotY + chart.plotTop, chart.plotTop, chart.plotTop + chart.plotHeight],
9644
9736
  second = ['x', chart.chartWidth, boxWidth, point.plotX + chart.plotLeft, chart.plotLeft, chart.plotLeft + chart.plotWidth],
9645
9737
  // The far side is right or bottom
9646
- preferFarSide = pick(point.ttBelow, (chart.inverted && !point.negative) || (!chart.inverted && point.negative)),
9738
+ preferFarSide = !this.followPointer && pick(point.ttBelow, !chart.inverted === !!point.negative), // #4984
9647
9739
  /**
9648
9740
  * Handle the preferred dimension. When the preferred dimension is tooltip
9649
9741
  * on top or bottom of the point, it will look for space there.
@@ -10141,9 +10233,17 @@
10141
10233
  if (p) {
10142
10234
  // Store both closest points, using point.dist and point.distX comparisons (#4645):
10143
10235
  each(['dist', 'distX'], function (dist, k) {
10144
- if (typeof p[dist] === 'number' && p[dist] < distance[k]) {
10145
- distance[k] = p[dist];
10146
- kdpoint[k] = p;
10236
+ if (typeof p[dist] === 'number') {
10237
+ var
10238
+ // It is closer than the reference point
10239
+ isCloser = p[dist] < distance[k],
10240
+ // It is equally close, but above the reference point (#4679)
10241
+ isAbove = p[dist] === distance[k] && p.series.group.zIndex >= kdpoint[k].series.group.zIndex;
10242
+
10243
+ if (isCloser || isAbove) {
10244
+ distance[k] = p[dist];
10245
+ kdpoint[k] = p;
10246
+ }
10147
10247
  }
10148
10248
  });
10149
10249
  }
@@ -10202,22 +10302,18 @@
10202
10302
  addEvent(doc, 'mousemove', pointer._onDocumentMouseMove);
10203
10303
  }
10204
10304
 
10205
- // Crosshair
10305
+ // Crosshair. For each hover point, loop over axes and draw cross if that point
10306
+ // belongs to the axis (#4927).
10206
10307
  each(shared ? kdpoints : [pick(kdpoint[1], hoverPoint)], function (point) {
10207
- var series = point && point.series;
10208
- if (series) {
10209
- each(['xAxis', 'yAxis', 'colorAxis'], function (coll) {
10210
- if (series[coll]) {
10211
- series[coll].drawCrosshair(e, point);
10212
- }
10213
- });
10214
- }
10308
+ each(chart.axes, function (axis) {
10309
+ // In case of snap = false, point is undefined, and we draw the crosshair anyway (#5066)
10310
+ if (!point || point.series[axis.coll] === axis) {
10311
+ axis.drawCrosshair(e, point);
10312
+ }
10313
+ });
10215
10314
  });
10216
-
10217
10315
  },
10218
10316
 
10219
-
10220
-
10221
10317
  /**
10222
10318
  * Reset the tracking by hiding the tooltip, the hover series state and the hover point
10223
10319
  *
@@ -10232,13 +10328,10 @@
10232
10328
  tooltip = chart.tooltip,
10233
10329
  tooltipPoints = tooltip && tooltip.shared ? hoverPoints : hoverPoint;
10234
10330
 
10235
- // Narrow in allowMove
10236
- allowMove = allowMove && tooltip && tooltipPoints;
10237
-
10238
- // Check if the points have moved outside the plot area (#1003, #4736)
10239
- if (allowMove) {
10331
+ // Check if the points have moved outside the plot area (#1003, #4736, #5101)
10332
+ if (allowMove && tooltipPoints) {
10240
10333
  each(splat(tooltipPoints), function (point) {
10241
- if (point.plotX === undefined) {
10334
+ if (point.series.isCartesian && point.plotX === undefined) {
10242
10335
  allowMove = false;
10243
10336
  }
10244
10337
  });
@@ -10246,17 +10339,19 @@
10246
10339
 
10247
10340
  // Just move the tooltip, #349
10248
10341
  if (allowMove) {
10249
- tooltip.refresh(tooltipPoints);
10250
- if (hoverPoint) { // #2500
10251
- hoverPoint.setState(hoverPoint.state, true);
10252
- each(chart.axes, function (axis) {
10253
- if (pick(axis.options.crosshair && axis.options.crosshair.snap, true)) {
10254
- axis.drawCrosshair(null, hoverPoint);
10255
- } else {
10256
- axis.hideCrosshair();
10257
- }
10258
- });
10342
+ if (tooltip && tooltipPoints) {
10343
+ tooltip.refresh(tooltipPoints);
10344
+ if (hoverPoint) { // #2500
10345
+ hoverPoint.setState(hoverPoint.state, true);
10346
+ each(chart.axes, function (axis) {
10347
+ if (pick(axis.crosshair && axis.crosshair.snap, true)) {
10348
+ axis.drawCrosshair(null, hoverPoint);
10349
+ } else {
10350
+ axis.hideCrosshair();
10351
+ }
10352
+ });
10259
10353
 
10354
+ }
10260
10355
  }
10261
10356
 
10262
10357
  // Full reset
@@ -10876,7 +10971,9 @@
10876
10971
  * General touch handler shared by touchstart and touchmove.
10877
10972
  */
10878
10973
  touch: function (e, start) {
10879
- var chart = this.chart;
10974
+ var chart = this.chart,
10975
+ hasMoved,
10976
+ pinchDown;
10880
10977
 
10881
10978
  hoverChartIndex = chart.index;
10882
10979
 
@@ -10891,7 +10988,22 @@
10891
10988
  this.runPointActions(e);
10892
10989
  }
10893
10990
 
10894
- this.pinch(e);
10991
+ // Android fires touchmove events after the touchstart even if the
10992
+ // finger hasn't moved, or moved only a pixel or two. In iOS however,
10993
+ // the touchmove doesn't fire unless the finger moves more than ~4px.
10994
+ // So we emulate this behaviour in Android by checking how much it
10995
+ // moved, and cancelling on small distances. #3450.
10996
+ if (e.type === 'touchmove') {
10997
+ pinchDown = this.pinchDown;
10998
+ hasMoved = Math.sqrt(
10999
+ Math.pow(pinchDown[0].chartX - e.chartX, 2) +
11000
+ Math.pow(pinchDown[0].chartY - e.chartY, 2)
11001
+ ) >= 4;
11002
+ }
11003
+
11004
+ if (pick(hasMoved, true)) {
11005
+ this.pinch(e);
11006
+ }
10895
11007
 
10896
11008
  } else if (start) {
10897
11009
  // Hide the tooltip on touching outside the plot area (#1203)
@@ -10924,7 +11036,8 @@
10924
11036
  var touches = {},
10925
11037
  hasPointerEvent = !!win.PointerEvent,
10926
11038
  getWebkitTouches = function () {
10927
- var key, fake = [];
11039
+ var key,
11040
+ fake = [];
10928
11041
  fake.item = function (i) {
10929
11042
  return this[i];
10930
11043
  };
@@ -11587,7 +11700,7 @@
11587
11700
 
11588
11701
  // Reset the legend height and adjust the clipping rectangle
11589
11702
  pages.length = 0;
11590
- if (legendHeight > spaceHeight) {
11703
+ if (legendHeight > spaceHeight && navOptions.enabled !== false) {
11591
11704
 
11592
11705
  this.clipHeight = clipHeight = mathMax(spaceHeight - 20 - this.titleHeight - padding, 0);
11593
11706
  this.currentPage = pick(this.currentPage, 1);
@@ -12666,14 +12779,13 @@
12666
12779
  fireEvent(chart, 'resize');
12667
12780
 
12668
12781
  // Fire endResize and set isResizing back. If animation is disabled, fire without delay
12669
- globalAnimation = renderer.globalAnimation; // Reassign it before using it, it may have changed since the top of this function.
12670
12782
  syncTimeout(function () {
12671
12783
  if (chart) {
12672
12784
  fireEvent(chart, 'endResize', null, function () {
12673
12785
  chart.isResizing -= 1;
12674
12786
  });
12675
12787
  }
12676
- }, globalAnimation === false ? 0 : ((globalAnimation && globalAnimation.duration) || 500));
12788
+ }, animObject(globalAnimation).duration);
12677
12789
  },
12678
12790
 
12679
12791
  /**
@@ -13227,7 +13339,7 @@
13227
13339
  chart.renderer.draw();
13228
13340
 
13229
13341
  // Fire the load event if there are no external images
13230
- if (!chart.renderer.imgCount) {
13342
+ if (!chart.renderer.imgCount && chart.onload) {
13231
13343
  chart.onload();
13232
13344
  }
13233
13345
 
@@ -13249,10 +13361,10 @@
13249
13361
  }
13250
13362
  });
13251
13363
 
13252
- // Fire the load event if there are no external images
13253
- if (!chart.renderer.imgCount) {
13254
- fireEvent(chart, 'load');
13255
- }
13364
+ fireEvent(chart, 'load');
13365
+
13366
+ // Don't run again
13367
+ this.onload = null;
13256
13368
  },
13257
13369
 
13258
13370
  /**
@@ -13365,7 +13477,7 @@
13365
13477
 
13366
13478
  // If no x is set by now, get auto incremented value. All points must have an
13367
13479
  // x value, however the y value can be null to create a gap in the series
13368
- if (typeof point.x !== 'number' && series) {
13480
+ if (point.x === undefined && series) {
13369
13481
  point.x = x === undefined ? series.autoIncrement() : x;
13370
13482
  }
13371
13483
 
@@ -13746,11 +13858,16 @@
13746
13858
  this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
13747
13859
 
13748
13860
  // Added code for pointInterval strings
13749
- if (pointIntervalUnit === 'month' || pointIntervalUnit === 'year') {
13861
+ if (pointIntervalUnit) {
13750
13862
  date = new Date(xIncrement);
13751
- date = (pointIntervalUnit === 'month') ?
13752
- +date[setMonth](date[getMonth]() + pointInterval) :
13753
- +date[setFullYear](date[getFullYear]() + pointInterval);
13863
+
13864
+ if (pointIntervalUnit === 'day') {
13865
+ date = +date[setDate](date[getDate]() + pointInterval);
13866
+ } else if (pointIntervalUnit === 'month') {
13867
+ date = +date[setMonth](date[getMonth]() + pointInterval);
13868
+ } else if (pointIntervalUnit === 'year') {
13869
+ date = +date[setFullYear](date[getFullYear]() + pointInterval);
13870
+ }
13754
13871
  pointInterval = date - xIncrement;
13755
13872
  }
13756
13873
 
@@ -14165,6 +14282,7 @@
14165
14282
  } else {
14166
14283
  // splat the y data in case of ohlc data array
14167
14284
  points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
14285
+ points[i].dataGroup = series.groupMap[i];
14168
14286
  }
14169
14287
  points[i].index = cursor; // For faster access in Point.update
14170
14288
  }
@@ -14339,11 +14457,13 @@
14339
14457
  point.category = categories && categories[point.x] !== UNDEFINED ?
14340
14458
  categories[point.x] : point.x;
14341
14459
 
14342
- // Determine auto enabling of markers (#3635)
14343
- if (i) {
14344
- closestPointRangePx = mathMin(closestPointRangePx, mathAbs(plotX - lastPlotX));
14460
+ // Determine auto enabling of markers (#3635, #5099)
14461
+ if (!point.isNull) {
14462
+ if (lastPlotX !== undefined) {
14463
+ closestPointRangePx = mathMin(closestPointRangePx, mathAbs(plotX - lastPlotX));
14464
+ }
14465
+ lastPlotX = plotX;
14345
14466
  }
14346
- lastPlotX = plotX;
14347
14467
 
14348
14468
  }
14349
14469
  series.closestPointRangePx = closestPointRangePx;
@@ -14352,8 +14472,12 @@
14352
14472
  /**
14353
14473
  * Return the series points with null points filtered out
14354
14474
  */
14355
- getValidPoints: function (points) {
14356
- return grep(points || this.points, function (point) {
14475
+ getValidPoints: function (points, insideOnly) {
14476
+ var chart = this.chart;
14477
+ return grep(points || this.points || [], function isValidPoint(point) { // #3916, #5029
14478
+ if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) { // #5085
14479
+ return false;
14480
+ }
14357
14481
  return !point.isNull;
14358
14482
  });
14359
14483
  },
@@ -15123,7 +15247,7 @@
15123
15247
  if (isNew) {
15124
15248
  this[prop] = group = this.chart.renderer.g(name)
15125
15249
  .attr({
15126
- zIndex: zIndex || 0.1 // IE8 needs this
15250
+ zIndex: zIndex || 0.1 // IE8 and pointer logic use this
15127
15251
  })
15128
15252
  .add(parent);
15129
15253
 
@@ -15164,10 +15288,9 @@
15164
15288
  chart = series.chart,
15165
15289
  group,
15166
15290
  options = series.options,
15167
- animation = options.animation,
15168
15291
  // Animation doesn't work in IE8 quirks when the group div is hidden,
15169
15292
  // and looks bad in other oldIE
15170
- animDuration = (animation && !!series.animate && chart.renderer.isSVG && pick(animation.duration, 500)) || 0,
15293
+ animDuration = !!series.animate && chart.renderer.isSVG && animObject(options.animation).duration,
15171
15294
  visibility = series.visible ? 'inherit' : 'hidden', // #2597
15172
15295
  zIndex = options.zIndex,
15173
15296
  hasRendered = series.hasRendered,
@@ -15319,7 +15442,9 @@
15319
15442
 
15320
15443
  // Internal function
15321
15444
  function _kdtree(points, depth, dimensions) {
15322
- var axis, median, length = points && points.length;
15445
+ var axis,
15446
+ median,
15447
+ length = points && points.length;
15323
15448
 
15324
15449
  if (length) {
15325
15450
 
@@ -15345,7 +15470,14 @@
15345
15470
 
15346
15471
  // Start the recursive build process with a clone of the points array and null points filtered out (#3873)
15347
15472
  function startRecursive() {
15348
- series.kdTree = _kdtree(series.getValidPoints(), dimensions, dimensions);
15473
+ series.kdTree = _kdtree(
15474
+ series.getValidPoints(
15475
+ null,
15476
+ !series.directTouch // For line-type series restrict to plot area, but column-type series not (#3916, #4511)
15477
+ ),
15478
+ dimensions,
15479
+ dimensions
15480
+ );
15349
15481
  }
15350
15482
  delete series.kdTree;
15351
15483
 
@@ -15755,8 +15887,8 @@
15755
15887
 
15756
15888
  if (y !== null) {
15757
15889
  stack.points[pointKey].push(stack.cum);
15890
+ stackedYData[i] = stack.cum;
15758
15891
  }
15759
- stackedYData[i] = stack.cum;
15760
15892
 
15761
15893
  }
15762
15894
 
@@ -15861,16 +15993,17 @@
15861
15993
  */
15862
15994
  addAxis: function (options, isX, redraw, animation) {
15863
15995
  var key = isX ? 'xAxis' : 'yAxis',
15864
- chartOptions = this.options;
15996
+ chartOptions = this.options,
15997
+ userOptions = merge(options, {
15998
+ index: this[key].length,
15999
+ isX: isX
16000
+ });
15865
16001
 
15866
- new Axis(this, merge(options, { // eslint-disable-line no-new
15867
- index: this[key].length,
15868
- isX: isX
15869
- }));
16002
+ new Axis(this, userOptions); // eslint-disable-line no-new
15870
16003
 
15871
16004
  // Push the new axis options to the chart options
15872
16005
  chartOptions[key] = splat(chartOptions[key] || {});
15873
- chartOptions[key].push(options);
16006
+ chartOptions[key].push(userOptions);
15874
16007
 
15875
16008
  if (pick(redraw, true)) {
15876
16009
  this.redraw(animation);
@@ -16009,7 +16142,7 @@
16009
16142
 
16010
16143
  // Record the options to options.data. If there is an object from before,
16011
16144
  // use point options, otherwise use raw options. (#4701)
16012
- seriesOptions.data[i] = isObject(seriesOptions.data[i]) ? point.options : options;
16145
+ seriesOptions.data[i] = (isObject(seriesOptions.data[i]) && !isArray(seriesOptions.data[i])) ? point.options : options;
16013
16146
 
16014
16147
  // redraw
16015
16148
  series.isDirty = series.isDirtyData = true;
@@ -16395,7 +16528,7 @@
16395
16528
  // Sort the keys (#1651)
16396
16529
  for (x in stack) {
16397
16530
  if (stack[x].total !== null) { // nulled after switching between grouping and not (#1651, #2336)
16398
- keys.push(+x);
16531
+ keys.push(x);
16399
16532
  }
16400
16533
  }
16401
16534
  keys.sort(function (a, b) {
@@ -16531,12 +16664,12 @@
16531
16664
  if (top !== undefined) {
16532
16665
  graphPoints.push({
16533
16666
  plotX: plotX,
16534
- plotY: top === null ? translatedThreshold : yAxis.toPixels(top, true),
16667
+ plotY: top === null ? translatedThreshold : yAxis.getThreshold(top),
16535
16668
  isNull: isNull
16536
16669
  });
16537
16670
  bottomPoints.push({
16538
16671
  plotX: plotX,
16539
- plotY: bottom === null ? translatedThreshold : yAxis.toPixels(bottom, true)
16672
+ plotY: bottom === null ? translatedThreshold : yAxis.getThreshold(bottom)
16540
16673
  });
16541
16674
  }
16542
16675
  };
@@ -16957,7 +17090,7 @@
16957
17090
  h = bottom - y;
16958
17091
 
16959
17092
  // Top edges are exceptions
16960
- if (fromTop) {
17093
+ if (fromTop && h) { // #5146
16961
17094
  y -= 1;
16962
17095
  h += 1;
16963
17096
  }
@@ -17126,9 +17259,15 @@
17126
17259
 
17127
17260
  } else { // run the animation
17128
17261
 
17129
- attr.scaleY = 1;
17130
17262
  attr[inverted ? 'translateX' : 'translateY'] = yAxis.pos;
17131
- series.group.animate(attr, series.options.animation);
17263
+ series.group.animate(attr, extend(animObject(series.options.animation), {
17264
+ // Do the scale synchronously to ensure smooth updating (#5030)
17265
+ step: function (val, fx) {
17266
+ series.group.attr({
17267
+ scaleY: fx.pos
17268
+ });
17269
+ }
17270
+ }));
17132
17271
 
17133
17272
  // delete this function to allow it only once
17134
17273
  series.animate = null;
@@ -17685,6 +17824,7 @@
17685
17824
  hasRendered = series.hasRendered || 0,
17686
17825
  str,
17687
17826
  dataLabelsGroup,
17827
+ defer = pick(options.defer, true),
17688
17828
  renderer = series.chart.renderer;
17689
17829
 
17690
17830
  if (options.enabled || series._hasPointLabels) {
@@ -17698,11 +17838,11 @@
17698
17838
  dataLabelsGroup = series.plotGroup(
17699
17839
  'dataLabelsGroup',
17700
17840
  'data-labels',
17701
- options.defer ? HIDDEN : VISIBLE,
17841
+ defer && !hasRendered ? 'hidden' : 'visible', // #5133
17702
17842
  options.zIndex || 6
17703
17843
  );
17704
17844
 
17705
- if (pick(options.defer, true)) {
17845
+ if (defer) {
17706
17846
  dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
17707
17847
  if (!hasRendered) {
17708
17848
  addEvent(series, 'afterAnimate', function () {
@@ -17877,8 +18017,7 @@
17877
18017
  x: alignTo.x + options.x + alignTo.width / 2 + rotCorr.x,
17878
18018
  y: alignTo.y + options.y + alignTo.height / 2
17879
18019
  };
17880
- dataLabel
17881
- [isNew ? 'attr' : 'animate'](alignAttr)
18020
+ dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
17882
18021
  .attr({ // #3003
17883
18022
  align: options.align
17884
18023
  });
@@ -19452,6 +19591,7 @@
19452
19591
  arrayMin: arrayMin,
19453
19592
  arrayMax: arrayMax,
19454
19593
  charts: charts,
19594
+ correctFloat: correctFloat,
19455
19595
  dateFormat: dateFormat,
19456
19596
  error: error,
19457
19597
  format: format,