highcharts-rails 3.0.7 → 3.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2908,10 +2908,10 @@ if (CanvasRenderingContext2D) {
2908
2908
  });
2909
2909
  }
2910
2910
  }/**
2911
- * @license Highcharts JS v3.0.7 (2013-10-24)
2911
+ * @license Highcharts JS v3.0.8 (2014-01-09)
2912
2912
  * CanVGRenderer Extension module
2913
2913
  *
2914
- * (c) 2011-2012 Torstein Hønsi, Erik Olsson
2914
+ * (c) 2011-2012 Torstein Honsi, Erik Olsson
2915
2915
  *
2916
2916
  * License: www.highcharts.com/license
2917
2917
  */
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * @license Data plugin for Highcharts
3
3
  *
4
- * (c) 2012-2013 Torstein Hønsi
5
- * Last revision 2013-06-07
4
+ * (c) 2012-2014 Torstein Honsi
6
5
  *
7
6
  * License: www.highcharts.com/license
8
7
  */
@@ -50,7 +49,8 @@
50
49
  * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic
51
50
  *
52
51
  * - itemDelimiter : String
53
- * Item or cell delimiter for parsing CSV. Defaults to ",".
52
+ * Item or cell delimiter for parsing CSV. Defaults to the tab character "\t" if a tab character
53
+ * is found in the CSV string, if not it defaults to ",".
54
54
  *
55
55
  * - lineDelimiter : String
56
56
  * Line delimiter for parsing CSV. Defaults to "\n".
@@ -172,6 +172,7 @@
172
172
  endRow = options.endRow || Number.MAX_VALUE,
173
173
  startColumn = options.startColumn || 0,
174
174
  endColumn = options.endColumn || Number.MAX_VALUE,
175
+ itemDelimiter,
175
176
  lines,
176
177
  activeRowNo = 0;
177
178
 
@@ -181,6 +182,8 @@
181
182
  .replace(/\r\n/g, "\n") // Unix
182
183
  .replace(/\r/g, "\n") // Mac
183
184
  .split(options.lineDelimiter || "\n");
185
+
186
+ itemDelimiter = options.itemDelimiter || (csv.indexOf('\t') !== -1 ? '\t' : ',');
184
187
 
185
188
  each(lines, function (line, rowNo) {
186
189
  var trimmed = self.trim(line),
@@ -189,7 +192,7 @@
189
192
  items;
190
193
 
191
194
  if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
192
- items = line.split(options.itemDelimiter || ',');
195
+ items = line.split(itemDelimiter);
193
196
  each(items, function (item, colNo) {
194
197
  if (colNo >= startColumn && colNo <= endColumn) {
195
198
  if (!columns[colNo - startColumn]) {
@@ -2,7 +2,6 @@
2
2
  * Highcharts Drilldown plugin
3
3
  *
4
4
  * Author: Torstein Honsi
5
- * Last revision: 2013-02-18
6
5
  * License: MIT License
7
6
  *
8
7
  * Demo: http://jsfiddle.net/highcharts/Vf3yT/
@@ -43,13 +42,13 @@
43
42
  defaultOptions.drilldown = {
44
43
  activeAxisLabelStyle: {
45
44
  cursor: 'pointer',
46
- color: '#039',
45
+ color: '#0d233a',
47
46
  fontWeight: 'bold',
48
47
  textDecoration: 'underline'
49
48
  },
50
49
  activeDataLabelStyle: {
51
50
  cursor: 'pointer',
52
- color: '#039',
51
+ color: '#0d233a',
53
52
  fontWeight: 'bold',
54
53
  textDecoration: 'underline'
55
54
  },
@@ -70,7 +69,7 @@
70
69
  /**
71
70
  * A general fadeIn method
72
71
  */
73
- H.SVGRenderer.prototype.Element.prototype.fadeIn = function () {
72
+ H.SVGRenderer.prototype.Element.prototype.fadeIn = function (animation) {
74
73
  this
75
74
  .attr({
76
75
  opacity: 0.1,
@@ -78,7 +77,7 @@
78
77
  })
79
78
  .animate({
80
79
  opacity: 1
81
- }, {
80
+ }, animation || {
82
81
  duration: 250
83
82
  });
84
83
  };
@@ -301,6 +300,9 @@
301
300
  point.graphic
302
301
  .attr(animateFrom)
303
302
  .animate(point.shapeArgs, animationOptions);
303
+ if (point.dataLabel) {
304
+ point.dataLabel.fadeIn(animationOptions);
305
+ }
304
306
  });
305
307
  }
306
308
 
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @license Highcharts JS v3.0.7 (2013-10-24)
2
+ * @license Highcharts JS v3.0.8 (2014-01-09)
3
3
  * Exporting module
4
4
  *
5
- * (c) 2010-2013 Torstein Hønsi
5
+ * (c) 2010-2014 Torstein Honsi
6
6
  *
7
7
  * License: www.highcharts.com/license
8
8
  */
@@ -77,7 +77,7 @@ defaultOptions.navigation = {
77
77
  symbolX: 12.5,
78
78
  symbolY: 10.5,
79
79
  align: 'right',
80
- buttonSpacing: 3,
80
+ buttonSpacing: 3,
81
81
  height: 22,
82
82
  // text: null,
83
83
  theme: {
@@ -158,16 +158,16 @@ defaultOptions.exporting = {
158
158
  };
159
159
 
160
160
  // Add the Highcharts.post utility
161
- Highcharts.post = function (url, data) {
161
+ Highcharts.post = function (url, data, formAttributes) {
162
162
  var name,
163
163
  form;
164
-
164
+
165
165
  // create the form
166
- form = createElement('form', {
166
+ form = createElement('form', merge({
167
167
  method: 'post',
168
168
  action: url,
169
169
  enctype: 'multipart/form-data'
170
- }, {
170
+ }, formAttributes), { // docs
171
171
  display: NONE
172
172
  }, doc.body);
173
173
 
@@ -222,7 +222,7 @@ extend(Chart.prototype, {
222
222
  width: chart.chartWidth + PX,
223
223
  height: chart.chartHeight + PX
224
224
  }, doc.body);
225
-
225
+
226
226
  // get the source size
227
227
  cssWidth = chart.renderTo.style.width;
228
228
  cssHeight = chart.renderTo.style.height;
@@ -244,7 +244,7 @@ extend(Chart.prototype, {
244
244
  height: sourceHeight
245
245
  });
246
246
  options.exporting.enabled = false; // hide buttons in print
247
-
247
+
248
248
  // prepare for replicating the chart
249
249
  options.series = [];
250
250
  each(chart.series, function (serie) {
@@ -331,13 +331,13 @@ extend(Chart.prototype, {
331
331
  */
332
332
  exportChart: function (options, chartOptions) {
333
333
  options = options || {};
334
-
334
+
335
335
  var chart = this,
336
336
  chartExportingOptions = chart.options.exporting,
337
337
  svg = chart.getSVG(merge(
338
338
  { chart: { borderRadius: 0 } },
339
339
  chartExportingOptions.chartOptions,
340
- chartOptions,
340
+ chartOptions,
341
341
  {
342
342
  exporting: {
343
343
  sourceWidth: options.sourceWidth || chartExportingOptions.sourceWidth,
@@ -348,7 +348,7 @@ extend(Chart.prototype, {
348
348
 
349
349
  // merge the options
350
350
  options = merge(chart.options.exporting, options);
351
-
351
+
352
352
  // do the post
353
353
  Highcharts.post(options.url, {
354
354
  filename: options.filename || 'chart',
@@ -356,10 +356,10 @@ extend(Chart.prototype, {
356
356
  width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
357
357
  scale: options.scale || 2,
358
358
  svg: svg
359
- });
359
+ }, options.formAttributes);
360
360
 
361
361
  },
362
-
362
+
363
363
  /**
364
364
  * Print the chart
365
365
  */
@@ -435,7 +435,12 @@ extend(Chart.prototype, {
435
435
  innerMenu,
436
436
  hide,
437
437
  hideTimer,
438
- menuStyle;
438
+ menuStyle,
439
+ docMouseUpHandler = function (e) {
440
+ if (!chart.pointer.inClass(e.target, className)) {
441
+ hide();
442
+ }
443
+ };
439
444
 
440
445
  // create the menu only the first time
441
446
  if (!menu) {
@@ -472,18 +477,19 @@ extend(Chart.prototype, {
472
477
  addEvent(menu, 'mouseenter', function () {
473
478
  clearTimeout(hideTimer);
474
479
  });
475
- // Hide it on clicking or touching outside the menu (#2258, #2335)
476
- addEvent(document, 'mouseup', function (e) {
477
- if (!chart.pointer.inClass(e.target, className)) {
478
- hide();
479
- }
480
+
481
+
482
+ // Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
483
+ addEvent(document, 'mouseup', docMouseUpHandler);
484
+ addEvent(chart, 'destroy', function () {
485
+ removeEvent(document, 'mouseup', docMouseUpHandler);
480
486
  });
481
487
 
482
488
 
483
489
  // create the items
484
490
  each(items, function (item) {
485
491
  if (item) {
486
- var element = item.separator ?
492
+ var element = item.separator ?
487
493
  createElement('hr', null, null, innerMenu) :
488
494
  createElement(DIV, {
489
495
  onmouseover: function () {
@@ -580,11 +586,11 @@ extend(Chart.prototype, {
580
586
  } else if (menuItems) {
581
587
  callback = function () {
582
588
  chart.contextMenu(
583
- button.menuClassName,
584
- menuItems,
585
- button.translateX,
586
- button.translateY,
587
- button.width,
589
+ button.menuClassName,
590
+ menuItems,
591
+ button.translateX,
592
+ button.translateY,
593
+ button.width,
588
594
  button.height,
589
595
  button
590
596
  );
@@ -595,7 +601,7 @@ extend(Chart.prototype, {
595
601
 
596
602
  if (btnOptions.text && btnOptions.symbol) {
597
603
  attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);
598
-
604
+
599
605
  } else if (!btnOptions.text) {
600
606
  extend(attr, {
601
607
  width: btnOptions.width,
@@ -616,7 +622,7 @@ extend(Chart.prototype, {
616
622
  btnOptions.symbol,
617
623
  btnOptions.symbolX - (symbolSize / 2),
618
624
  btnOptions.symbolY - (symbolSize / 2),
619
- symbolSize,
625
+ symbolSize,
620
626
  symbolSize
621
627
  )
622
628
  .attr(extend(symbolAttr, {
@@ -648,7 +654,7 @@ extend(Chart.prototype, {
648
654
  // Destroy the extra buttons added
649
655
  for (i = 0; i < chart.exportSVGElements.length; i++) {
650
656
  elem = chart.exportSVGElements[i];
651
-
657
+
652
658
  // Destroy and null the svg/vml elements
653
659
  if (elem) { // #1822
654
660
  elem.onclick = elem.ontouchstart = null;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @license
3
- * Highcharts funnel module, Beta
3
+ * Highcharts funnel module
4
4
  *
5
- * (c) 2010-2012 Torstein Hønsi
5
+ * (c) 2010-2014 Torstein Honsi
6
6
  *
7
7
  * License: www.highcharts.com/license
8
8
  */
@@ -3,51 +3,57 @@
3
3
  each = H.each;
4
4
 
5
5
  seriesTypes.heatmap = H.extendClass(seriesTypes.map, {
6
- colorKey: 'z',
7
6
  useMapGeometry: false,
8
- pointArrayMap: ['y', 'z'],
7
+ pointArrayMap: ['y', 'value'],
8
+ init: function () {
9
+ seriesTypes.map.prototype.init.apply(this, arguments);
10
+ this.pointRange = this.options.colsize || 1;
11
+ // TODO: similar logic for the Y axis
12
+ },
9
13
  translate: function () {
10
14
  var series = this,
11
15
  options = series.options,
12
- dataMin = Number.MAX_VALUE,
13
- dataMax = Number.MIN_VALUE;
16
+ xAxis = series.xAxis,
17
+ yAxis = series.yAxis;
14
18
 
15
19
  series.generatePoints();
16
20
 
17
- each(series.data, function (point) {
18
- var x = point.x,
19
- y = point.y,
20
- value = point.z,
21
- xPad = (options.colsize || 1) / 2,
22
- yPad = (options.rowsize || 1) / 2;
21
+ each(series.points, function (point) {
22
+ var xPad = (options.colsize || 1) / 2,
23
+ yPad = (options.rowsize || 1) / 2,
24
+ x1 = Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)),
25
+ x2 = Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)),
26
+ y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
27
+ y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1));
28
+
29
+
30
+ point.plotY = 1; // Pass test in Column.drawPoints
23
31
 
24
- point.path = [
25
- 'M', x - xPad, y - yPad,
26
- 'L', x + xPad, y - yPad,
27
- 'L', x + xPad, y + yPad,
28
- 'L', x - xPad, y + yPad,
29
- 'Z'
30
- ];
31
-
32
- point.shapeType = 'path';
32
+ point.shapeType = 'rect';
33
33
  point.shapeArgs = {
34
- d: series.translatePath(point.path)
34
+ x: Math.min(x1, x2),
35
+ y: Math.min(y1, y2),
36
+ width: Math.abs(x2 - x1),
37
+ height: Math.abs(y2 - y1)
35
38
  };
36
-
37
- if (typeof value === 'number') {
38
- if (value > dataMax) {
39
- dataMax = value;
40
- } else if (value < dataMin) {
41
- dataMin = value;
42
- }
43
- }
44
39
  });
45
40
 
46
- series.translateColors(dataMin, dataMax);
41
+ series.pointRange = options.colsize || 1;
42
+ series.translateColors();
47
43
  },
48
44
 
45
+ animate: function () {},
49
46
  getBox: function () {},
50
- getExtremes: H.Series.prototype.getExtremes
47
+
48
+ getExtremes: function () {
49
+ // Get the extremes from the value data
50
+ H.Series.prototype.getExtremes.call(this, this.valueData);
51
+ this.valueMin = this.dataMin;
52
+ this.valueMax = this.dataMax;
53
+
54
+ // Get the extremes from the y data
55
+ H.Series.prototype.getExtremes.call(this);
56
+ }
51
57
 
52
58
  });
53
59
 
@@ -1,27 +1,24 @@
1
1
  /**
2
- * @license Map plugin v0.1 for Highcharts
2
+ * @license Map plugin v0.2 for Highcharts
3
3
  *
4
- * (c) 2011-2013 Torstein Hønsi
4
+ * (c) 2011-2014 Torstein Honsi
5
5
  *
6
6
  * License: www.highcharts.com/license
7
7
  */
8
8
 
9
- /*
10
- * See www.H.com/studies/world-map.htm for use case.
11
- *
12
- * To do:
13
- * - Optimize long variable names and alias adapter methods and Highcharts namespace variables
14
- * - Zoom and pan GUI
15
- */
16
9
  /*global HighchartsAdapter*/
17
10
  (function (H) {
18
11
  var UNDEFINED,
19
12
  Axis = H.Axis,
20
13
  Chart = H.Chart,
14
+ Color = H.Color,
21
15
  Point = H.Point,
22
16
  Pointer = H.Pointer,
17
+ Legend = H.Legend,
18
+ Series = H.Series,
23
19
  SVGRenderer = H.SVGRenderer,
24
20
  VMLRenderer = H.VMLRenderer,
21
+
25
22
  symbols = SVGRenderer.prototype.symbols,
26
23
  each = H.each,
27
24
  extend = H.extend,
@@ -31,10 +28,8 @@
31
28
  numberFormat = H.numberFormat,
32
29
  defaultOptions = H.getOptions(),
33
30
  seriesTypes = H.seriesTypes,
34
- inArray = HighchartsAdapter.inArray,
35
31
  plotOptions = defaultOptions.plotOptions,
36
32
  wrap = H.wrap,
37
- Color = H.Color,
38
33
  noop = function () {};
39
34
 
40
35
  // Add language
@@ -166,7 +161,7 @@
166
161
  this.dataMax = dataMax;
167
162
  }
168
163
  });
169
-
164
+
170
165
  /**
171
166
  * Override axis translation to make sure the aspect ratio is always kept
172
167
  */
@@ -174,55 +169,51 @@
174
169
  var chart = this.chart,
175
170
  mapRatio,
176
171
  plotRatio = chart.plotWidth / chart.plotHeight,
177
- isXAxis = this.isXAxis,
178
172
  adjustedAxisLength,
179
173
  xAxis = chart.xAxis[0],
180
- padAxis;
174
+ padAxis,
175
+ fixTo,
176
+ fixDiff;
177
+
181
178
 
182
179
  // Run the parent method
183
180
  proceed.call(this);
184
181
 
185
182
  // On Y axis, handle both
186
- if (chart.options.chart.type === 'map' && !isXAxis && xAxis.transA !== UNDEFINED) {
183
+ if (chart.options.chart.preserveAspectRatio && this.coll === 'yAxis' && xAxis.transA !== UNDEFINED) {
187
184
 
188
185
  // Use the same translation for both axes
189
186
  this.transA = xAxis.transA = Math.min(this.transA, xAxis.transA);
190
187
 
191
- mapRatio = (xAxis.max - xAxis.min) / (this.max - this.min);
188
+ mapRatio = chart.mapRatio = plotRatio / ((xAxis.max - xAxis.min) / (this.max - this.min));
192
189
 
193
190
  // What axis to pad to put the map in the middle
194
- padAxis = mapRatio > plotRatio ? this : xAxis;
195
-
191
+ padAxis = mapRatio < 1 ? this : xAxis;
192
+
196
193
  // Pad it
197
194
  adjustedAxisLength = (padAxis.max - padAxis.min) * padAxis.transA;
198
- padAxis.minPixelPadding = (padAxis.len - adjustedAxisLength) / 2;
195
+ padAxis.pixelPadding = padAxis.len - adjustedAxisLength;
196
+ padAxis.minPixelPadding = padAxis.pixelPadding / 2;
197
+
198
+ fixTo = padAxis.fixTo;
199
+ if (fixTo) {
200
+ fixDiff = fixTo[1] - padAxis.toValue(fixTo[0], true);
201
+ fixDiff *= padAxis.transA;
202
+ if (Math.abs(fixDiff) > padAxis.minPixelPadding) { // zooming out again, keep within restricted area
203
+ fixDiff = 0;
204
+ }
205
+ padAxis.minPixelPadding -= fixDiff;
206
+
207
+ }
199
208
  }
200
209
  });
201
210
 
202
-
203
- //--- Start zooming and panning features
204
- wrap(Chart.prototype, 'render', function (proceed) {
205
- var chart = this,
206
- mapNavigation = chart.options.mapNavigation;
207
-
208
- proceed.call(chart);
209
-
210
- // Render the plus and minus buttons
211
- chart.renderMapNavigation();
212
-
213
- // Add the double click event
214
- if (pick(mapNavigation.enableDoubleClickZoom, mapNavigation.enabled) || mapNavigation.enableDoubleClickZoomTo) {
215
- H.addEvent(chart.container, 'dblclick', function (e) {
216
- chart.pointer.onContainerDblClick(e);
217
- });
218
- }
219
-
220
- // Add the mousewheel event
221
- if (pick(mapNavigation.enableMouseWheelZoom, mapNavigation.enabled)) {
222
- H.addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) {
223
- chart.pointer.onContainerMouseWheel(e);
224
- });
225
- }
211
+ /**
212
+ * Override Axis.render in order to delete the fixTo prop
213
+ */
214
+ wrap(Axis.prototype, 'render', function (proceed) {
215
+ proceed.call(this);
216
+ this.fixTo = null;
226
217
  });
227
218
 
228
219
  // Extend the Pointer
@@ -238,13 +229,15 @@
238
229
 
239
230
  if (chart.options.mapNavigation.enableDoubleClickZoomTo) {
240
231
  if (chart.pointer.inClass(e.target, 'highcharts-tracker')) {
241
- chart.zoomToShape(chart.hoverPoint);
232
+ chart.hoverPoint.zoomTo();
242
233
  }
243
234
  } else if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
244
235
  chart.mapZoom(
245
236
  0.5,
246
237
  chart.xAxis[0].toValue(e.chartX),
247
- chart.yAxis[0].toValue(e.chartY)
238
+ chart.yAxis[0].toValue(e.chartY),
239
+ e.chartX,
240
+ e.chartY
248
241
  );
249
242
  }
250
243
  },
@@ -262,9 +255,11 @@
262
255
  delta = e.detail || -(e.wheelDelta / 120);
263
256
  if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
264
257
  chart.mapZoom(
265
- delta > 0 ? 2 : 0.5,
258
+ delta > 0 ? 2 : 1 / 2,
266
259
  chart.xAxis[0].toValue(e.chartX),
267
- chart.yAxis[0].toValue(e.chartY)
260
+ chart.yAxis[0].toValue(e.chartY),
261
+ delta > 0 ? undefined : e.chartX,
262
+ delta > 0 ? undefined : e.chartY
268
263
  );
269
264
  }
270
265
  }
@@ -304,6 +299,402 @@
304
299
  }
305
300
  });
306
301
 
302
+
303
+
304
+
305
+ /**
306
+ * The ColorAxis object for inclusion in gradient legends
307
+ */
308
+ var ColorAxis = H.ColorAxis = function () {
309
+ this.init.apply(this, arguments);
310
+ };
311
+ extend(ColorAxis.prototype, Axis.prototype);
312
+ extend(ColorAxis.prototype, {
313
+ defaultColorAxisOptions: {
314
+ lineWidth: 0,
315
+ gridLineWidth: 1,
316
+ tickPixelInterval: 72,
317
+ startOnTick: true,
318
+ endOnTick: true,
319
+ offset: 0,
320
+ marker: { // docs: use another name?
321
+ animation: {
322
+ duration: 50
323
+ },
324
+ color: 'gray',
325
+ width: 0.01
326
+ },
327
+ labels: {
328
+ overflow: 'justify'
329
+ },
330
+ minColor: '#EFEFFF',
331
+ maxColor: '#102d4c'
332
+ },
333
+ init: function (chart, userOptions) {
334
+ var horiz = chart.options.legend.layout !== 'vertical',
335
+ options;
336
+
337
+ // Build the options
338
+ options = merge(this.defaultColorAxisOptions, {
339
+ side: horiz ? 2 : 1,
340
+ reversed: !horiz
341
+ }, userOptions, {
342
+ isX: horiz,
343
+ opposite: !horiz,
344
+ showEmpty: false,
345
+ title: null
346
+ });
347
+
348
+ Axis.prototype.init.call(this, chart, options);
349
+
350
+ // Base init() pushes it to the xAxis array, now pop it again
351
+ //chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
352
+
353
+ // Prepare data classes
354
+ if (userOptions.dataClasses) {
355
+ this.initDataClasses(userOptions);
356
+ }
357
+
358
+ // Override original axis properties
359
+ this.isXAxis = true;
360
+ this.horiz = horiz;
361
+ },
362
+
363
+ initDataClasses: function (userOptions) {
364
+ var chart = this.chart,
365
+ dataClasses,
366
+ colorCounter = 0,
367
+ options = this.options;
368
+ this.dataClasses = dataClasses = [];
369
+
370
+ each(userOptions.dataClasses, function (dataClass, i) {
371
+ var colors;
372
+
373
+ dataClass = merge(dataClass);
374
+ dataClasses.push(dataClass);
375
+ if (!dataClass.color) {
376
+ if (options.dataClassColor === 'category') {
377
+ colors = chart.options.colors;
378
+ dataClass.color = colors[colorCounter++];
379
+ // loop back to zero
380
+ if (colorCounter === colors.length) {
381
+ colorCounter = 0;
382
+ }
383
+ } else {
384
+ dataClass.color = tweenColors(Color(options.minColor), Color(options.maxColor), i / (userOptions.dataClasses.length - 1));
385
+ }
386
+ }
387
+ });
388
+ },
389
+
390
+ /**
391
+ * Extend the setOptions method to process extreme colors and color
392
+ * stops.
393
+ */
394
+ setOptions: function (userOptions) {
395
+ Axis.prototype.setOptions.call(this, userOptions);
396
+
397
+ this.options.crosshair = this.options.marker;
398
+
399
+ this.stops = userOptions.stops || [
400
+ [0, this.options.minColor],
401
+ [1, this.options.maxColor]
402
+ ];
403
+ each(this.stops, function (stop) {
404
+ stop.color = Color(stop[1]);
405
+ });
406
+ this.coll = 'colorAxis';
407
+ },
408
+
409
+ setAxisSize: function () {
410
+ var symbol = this.legendSymbol,
411
+ chart = this.chart;
412
+
413
+ if (symbol) {
414
+ this.left = symbol.x;
415
+ this.top = symbol.y;
416
+ this.width = symbol.width;
417
+ this.height = symbol.height;
418
+ this.right = chart.chartWidth - this.left - this.width;
419
+ this.bottom = chart.chartHeight - this.top - this.height;
420
+
421
+ this.len = this.horiz ? this.width : this.height;
422
+ this.pos = this.horiz ? this.left : this.top;
423
+ }
424
+ },
425
+
426
+ /**
427
+ * Translate from a value to a color
428
+ */
429
+ toColor: function (value, point) {
430
+ var pos,
431
+ stops = this.stops,
432
+ from,
433
+ to,
434
+ color,
435
+ dataClasses = this.dataClasses,
436
+ dataClass,
437
+ i;
438
+
439
+ if (dataClasses) {
440
+ i = dataClasses.length;
441
+ while (i--) {
442
+ dataClass = dataClasses[i];
443
+ from = dataClass.from;
444
+ to = dataClass.to;
445
+ if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
446
+ color = dataClass.color;
447
+ if (point) {
448
+ point.dataClass = i;
449
+ }
450
+ break;
451
+ }
452
+ }
453
+
454
+ } else {
455
+
456
+ if (this.isLog) {
457
+ value = this.val2lin(value);
458
+ }
459
+ pos = 1 - ((this.max - value) / (this.max - this.min));
460
+ i = stops.length;
461
+ while (i--) {
462
+ if (pos > stops[i][0]) {
463
+ break;
464
+ }
465
+ }
466
+ from = stops[i] || stops[i + 1];
467
+ to = stops[i + 1] || from;
468
+
469
+ // The position within the gradient
470
+ pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
471
+
472
+ color = tweenColors(
473
+ from.color,
474
+ to.color,
475
+ pos
476
+ );
477
+ }
478
+ return color;
479
+ },
480
+
481
+ getOffset: function () {
482
+ var group = this.legendGroup;
483
+ if (group) {
484
+
485
+ Axis.prototype.getOffset.call(this);
486
+
487
+ if (!this.axisGroup.parentGroup) {
488
+
489
+ // Move the axis elements inside the legend group
490
+ this.axisGroup.add(group);
491
+ this.gridGroup.add(group);
492
+ this.labelGroup.add(group);
493
+
494
+ this.added = true;
495
+ }
496
+ }
497
+ },
498
+
499
+ /**
500
+ * Create the color gradient
501
+ */
502
+ setLegendColor: function () {
503
+ var grad,
504
+ horiz = this.horiz,
505
+ options = this.options;
506
+
507
+ grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1];
508
+ this.legendColor = {
509
+ linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
510
+ stops: options.stops || [
511
+ [0, options.minColor],
512
+ [1, options.maxColor]
513
+ ]
514
+ };
515
+ },
516
+
517
+ /**
518
+ * The color axis appears inside the legend and has its own legend symbol
519
+ */
520
+ drawLegendSymbol: function (legend, item) {
521
+ var padding = legend.padding,
522
+ legendOptions = legend.options,
523
+ horiz = this.horiz,
524
+ box,
525
+ width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
526
+ height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
527
+ labelPadding = pick(legendOptions.labelPadding, horiz ? 10 : 30);
528
+
529
+ this.setLegendColor();
530
+
531
+ // Create the gradient
532
+ item.legendSymbol = this.chart.renderer.rect(
533
+ 0,
534
+ legend.baseline - 11,
535
+ width,
536
+ height
537
+ ).attr({
538
+ zIndex: 1
539
+ }).add(item.legendGroup);
540
+ box = item.legendSymbol.getBBox();
541
+
542
+ // Set how much space this legend item takes up
543
+ this.legendItemWidth = width + padding + (horiz ? 0 : labelPadding);
544
+ this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
545
+ },
546
+ /**
547
+ * Fool the legend
548
+ */
549
+ setState: noop,
550
+ visible: true,
551
+ setVisible: noop,
552
+ getSeriesExtremes: function () {
553
+ var series;
554
+ if (this.series.length) {
555
+ series = this.series[0];
556
+ this.dataMin = series.valueMin;
557
+ this.dataMax = series.valueMax;
558
+ }
559
+ },
560
+ drawCrosshair: function (e, point) {
561
+ var newCross = !this.cross,
562
+ plotX = point && point.plotX,
563
+ plotY = point && point.plotY,
564
+ crossPos,
565
+ axisPos = this.pos,
566
+ axisLen = this.len;
567
+
568
+ if (point) {
569
+ crossPos = this.toPixels(point.value);
570
+ if (crossPos < axisPos) {
571
+ crossPos = axisPos - 2;
572
+ } else if (crossPos > axisPos + axisLen) {
573
+ crossPos = axisPos + axisLen + 2;
574
+ }
575
+
576
+ point.plotX = crossPos;
577
+ point.plotY = this.len - crossPos;
578
+ Axis.prototype.drawCrosshair.call(this, e, point);
579
+ point.plotX = plotX;
580
+ point.plotY = plotY;
581
+
582
+ if (!newCross && this.cross) {
583
+ this.cross
584
+ .attr({
585
+ fill: this.crosshair.color
586
+ })
587
+ .add(this.labelGroup);
588
+ }
589
+ }
590
+ },
591
+ getPlotLinePath: function (a, b, c, d, pos) {
592
+ if (pos) { // crosshairs only
593
+ return this.horiz ?
594
+ ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] :
595
+ ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z'];
596
+ } else {
597
+ return Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
598
+ }
599
+ },
600
+
601
+ update: function (newOptions, redraw) {
602
+ Axis.prototype.update.call(this, newOptions, redraw);
603
+ if (this.legendItem) {
604
+ this.setLegendColor();
605
+ this.chart.legend.colorizeItem(this, true);
606
+ }
607
+ },
608
+
609
+ /**
610
+ * Get the legend item symbols for data classes
611
+ */
612
+ getDataClassLegendSymbols: function () {
613
+ var axis = this,
614
+ chart = this.chart,
615
+ legendItems = [],
616
+ legendOptions = chart.options.legend,
617
+ valueDecimals = legendOptions.valueDecimals,
618
+ valueSuffix = legendOptions.valueSuffix || '',
619
+ name;
620
+
621
+ each(this.dataClasses, function (dataClass, i) {
622
+ var vis = true,
623
+ from = dataClass.from,
624
+ to = dataClass.to;
625
+
626
+ // Assemble the default name. This can be overridden by legend.options.labelFormatter
627
+ name = '';
628
+ if (from === UNDEFINED) {
629
+ name = '< ';
630
+ } else if (to === UNDEFINED) {
631
+ name = '> ';
632
+ }
633
+ if (from !== UNDEFINED) {
634
+ name += numberFormat(from, valueDecimals) + valueSuffix;
635
+ }
636
+ if (from !== UNDEFINED && to !== UNDEFINED) {
637
+ name += ' - ';
638
+ }
639
+ if (to !== UNDEFINED) {
640
+ name += numberFormat(to, valueDecimals) + valueSuffix;
641
+ }
642
+
643
+ // Add a mock object to the legend items
644
+ legendItems.push(H.extend({
645
+ chart: chart,
646
+ name: name,
647
+ options: {},
648
+ drawLegendSymbol: H.LegendSymbolMixin.drawRectangle,
649
+ visible: true,
650
+ setState: noop,
651
+ setVisible: function () {
652
+ vis = this.visible = !vis;
653
+ each(axis.series, function (series) {
654
+ each(series.points, function (point) {
655
+ if (point.dataClass === i) {
656
+ point.setVisible(vis);
657
+ }
658
+ });
659
+ });
660
+
661
+ chart.legend.colorizeItem(this, vis);
662
+ }
663
+ }, dataClass));
664
+ });
665
+ return legendItems;
666
+ }
667
+ });
668
+
669
+ /**
670
+ * Wrap the legend getAllItems method to add the color axis. This also removes the
671
+ * axis' own series to prevent them from showing up individually.
672
+ */
673
+ wrap(Legend.prototype, 'getAllItems', function (proceed) {
674
+ var allItems = [],
675
+ colorAxis = this.chart.colorAxis[0];
676
+
677
+ if (colorAxis) {
678
+
679
+ // Data classes
680
+ if (colorAxis.options.dataClasses) {
681
+ allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
682
+ // Gradient legend
683
+ } else {
684
+ // Add this axis on top
685
+ allItems.push(colorAxis);
686
+ }
687
+
688
+ // Don't add the color axis' series
689
+ each(colorAxis.series, function (series) {
690
+ series.options.showInLegend = false;
691
+ });
692
+ }
693
+
694
+ return allItems.concat(proceed.call(this));
695
+ });
696
+
697
+
307
698
  // Add events to the Chart object itself
308
699
  extend(Chart.prototype, {
309
700
  renderMapNavigation: function () {
@@ -319,7 +710,7 @@
319
710
  this.handler.call(chart);
320
711
  };
321
712
 
322
- if (pick(options.enableButtons, options.enabled)) {
713
+ if (pick(options.enableButtons, options.enabled) && !chart.renderer.forExport) {
323
714
  for (n in buttons) {
324
715
  if (buttons.hasOwnProperty(n)) {
325
716
  buttonOptions = merge(options.buttonOptions, buttons[n]);
@@ -384,11 +775,11 @@
384
775
  /**
385
776
  * Zoom the map in or out by a certain amount. Less than 1 zooms in, greater than 1 zooms out.
386
777
  */
387
- mapZoom: function (howMuch, centerXArg, centerYArg) {
388
-
389
- if (this.isMapZooming) {
778
+ mapZoom: function (howMuch, centerXArg, centerYArg, mouseX, mouseY) {
779
+ /*if (this.isMapZooming) {
780
+ this.mapZoomQueue = arguments;
390
781
  return;
391
- }
782
+ }*/
392
783
 
393
784
  var chart = this,
394
785
  xAxis = chart.xAxis[0],
@@ -399,10 +790,10 @@
399
790
  yRange = yAxis.max - yAxis.min,
400
791
  centerY = pick(centerYArg, yAxis.min + yRange / 2),
401
792
  newYRange = yRange * howMuch,
402
- newXMin = centerX - newXRange / 2,
403
- newYMin = centerY - newYRange / 2,
404
- animation = pick(chart.options.chart.animation, true),
405
- delay,
793
+ fixToX = mouseX ? ((mouseX - xAxis.pos) / xAxis.len) : 0.5,
794
+ fixToY = mouseY ? ((mouseY - yAxis.pos) / yAxis.len) : 0.5,
795
+ newXMin = centerX - newXRange * fixToX,
796
+ newYMin = centerY - newYRange * fixToY,
406
797
  newExt = chart.fitToBox({
407
798
  x: newXMin,
408
799
  y: newYMin,
@@ -415,47 +806,93 @@
415
806
  height: yAxis.dataMax - yAxis.dataMin
416
807
  });
417
808
 
418
- xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false);
419
- yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false);
809
+ // When mousewheel zooming, fix the point under the mouse
810
+ if (mouseX) {
811
+ xAxis.fixTo = [mouseX - xAxis.pos, centerXArg];
812
+ }
813
+ if (mouseY) {
814
+ yAxis.fixTo = [mouseY - yAxis.pos, centerYArg];
815
+ }
816
+
817
+ // Zoom
818
+ if (howMuch !== undefined) {
819
+ xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false);
820
+ yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false);
420
821
 
822
+ // Reset zoom
823
+ } else {
824
+ xAxis.setExtremes(undefined, undefined, false);
825
+ yAxis.setExtremes(undefined, undefined, false);
826
+ }
827
+
421
828
  // Prevent zooming until this one is finished animating
422
- delay = animation ? animation.duration || 500 : 0;
829
+ /*delay = animation ? animation.duration || 500 : 0;
423
830
  if (delay) {
424
831
  chart.isMapZooming = true;
425
832
  setTimeout(function () {
426
833
  chart.isMapZooming = false;
834
+ if (chart.mapZoomQueue) {
835
+ chart.mapZoom.apply(chart, chart.mapZoomQueue);
836
+ }
837
+ chart.mapZoomQueue = null;
427
838
  }, delay);
428
- }
429
-
839
+ }*/
430
840
 
431
841
  chart.redraw();
432
- },
842
+ }
843
+ });
433
844
 
434
- /**
435
- * Zoom the chart to view a specific area point
436
- */
437
- zoomToShape: function (point) {
438
- var series = point.series,
439
- chart = series.chart;
845
+ /**
846
+ * Extend the chart getAxes method to also get the color axis
847
+ */
848
+ wrap(Chart.prototype, 'getAxes', function (proceed) {
440
849
 
441
- series.xAxis.setExtremes(
442
- point._minX,
443
- point._maxX,
444
- false
445
- );
446
- series.yAxis.setExtremes(
447
- point._minY,
448
- point._maxY,
449
- false
450
- );
451
- chart.redraw();
850
+ var options = this.options,
851
+ colorAxisOptions = options.colorAxis;
852
+
853
+ proceed.call(this);
854
+
855
+ this.colorAxis = [];
856
+ if (colorAxisOptions) {
857
+ proceed = new ColorAxis(this, colorAxisOptions); // Fake assignment for jsLint
858
+ }
859
+ });
860
+
861
+ /**
862
+ * Extend the Chart.render method to add zooming and panning
863
+ */
864
+ wrap(Chart.prototype, 'render', function (proceed) {
865
+ var chart = this,
866
+ mapNavigation = chart.options.mapNavigation;
867
+
868
+ proceed.call(chart);
869
+
870
+ // Render the plus and minus buttons
871
+ chart.renderMapNavigation();
872
+
873
+ // Add the double click event
874
+ if (pick(mapNavigation.enableDoubleClickZoom, mapNavigation.enabled) || mapNavigation.enableDoubleClickZoomTo) {
875
+ H.addEvent(chart.container, 'dblclick', function (e) {
876
+ chart.pointer.onContainerDblClick(e);
877
+ });
878
+ }
879
+
880
+ // Add the mousewheel event
881
+ if (pick(mapNavigation.enableMouseWheelZoom, mapNavigation.enabled)) {
882
+ H.addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) {
883
+ chart.pointer.onContainerMouseWheel(e);
884
+ return false;
885
+ });
452
886
  }
453
887
  });
888
+
889
+
454
890
 
455
891
  /**
456
892
  * Extend the default options with map options
457
893
  */
458
894
  plotOptions.map = merge(plotOptions.scatter, {
895
+ allAreas: true,
459
896
  animation: false, // makes the complex shapes slow
460
897
  nullColor: '#F8F8F8',
461
898
  borderColor: 'silver',
@@ -463,20 +900,27 @@
463
900
  marker: null,
464
901
  stickyTracking: false,
465
902
  dataLabels: {
903
+ format: '{point.value}',
466
904
  verticalAlign: 'middle'
467
905
  },
468
906
  turboThreshold: 0,
469
907
  tooltip: {
470
908
  followPointer: true,
471
- pointFormat: '{point.name}: {point.y}<br/>'
909
+ pointFormat: '{point.name}: {point.value}<br/>'
472
910
  },
473
911
  states: {
474
912
  normal: {
475
913
  animation: true
914
+ },
915
+ hover: {
916
+ brightness: 0.2
476
917
  }
477
918
  }
478
919
  });
479
920
 
921
+ /**
922
+ * The MapAreaPoint object
923
+ */
480
924
  var MapAreaPoint = extendClass(Point, {
481
925
  /**
482
926
  * Extend the Point object to split paths
@@ -486,11 +930,13 @@
486
930
  var point = Point.prototype.applyOptions.call(this, options, x),
487
931
  series = this.series,
488
932
  seriesOptions = series.options,
489
- joinBy = seriesOptions.dataJoinBy,
933
+ joinBy = seriesOptions.joinBy,
490
934
  mapPoint;
491
935
 
492
- if (joinBy && seriesOptions.mapData) {
493
- mapPoint = series.getMapData(joinBy, point[joinBy]);
936
+ if (seriesOptions.mapData) {
937
+ mapPoint = joinBy ?
938
+ series.getMapData(joinBy, point[joinBy]) : // Join by a string
939
+ seriesOptions.mapData[point.x]; // Use array position (faster)
494
940
 
495
941
  if (mapPoint) {
496
942
  // This applies only to bubbles
@@ -500,7 +946,7 @@
500
946
  }
501
947
  extend(point, mapPoint); // copy over properties
502
948
  } else {
503
- point.y = point.y || null;
949
+ point.value = point.value || null;
504
950
  }
505
951
  }
506
952
 
@@ -543,7 +989,7 @@
543
989
  animation = point.series.options.states.normal.animation,
544
990
  duration = animation && (animation.duration || 500);
545
991
 
546
- if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4) {
992
+ if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4 && point.state !== 'select') {
547
993
  delete point.pointAttr[''].fill; // avoid resetting it in Point.setState
548
994
 
549
995
  clearTimeout(point.colorInterval);
@@ -562,6 +1008,26 @@
562
1008
  }, 13);
563
1009
  }
564
1010
  Point.prototype.onMouseOut.call(point);
1011
+ },
1012
+
1013
+ /**
1014
+ * Zoom the chart to view a specific area point
1015
+ */
1016
+ zoomTo: function () {
1017
+ var point = this,
1018
+ series = point.series;
1019
+
1020
+ series.xAxis.setExtremes(
1021
+ point._minX,
1022
+ point._maxX,
1023
+ false
1024
+ );
1025
+ series.yAxis.setExtremes(
1026
+ point._minY,
1027
+ point._maxY,
1028
+ false
1029
+ );
1030
+ series.chart.redraw();
565
1031
  }
566
1032
  });
567
1033
 
@@ -573,201 +1039,19 @@
573
1039
  pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
574
1040
  stroke: 'borderColor',
575
1041
  'stroke-width': 'borderWidth',
576
- fill: 'color'
1042
+ fill: 'color',
1043
+ dashstyle: 'dashStyle'
577
1044
  },
578
- colorKey: 'y',
579
1045
  pointClass: MapAreaPoint,
1046
+ pointArrayMap: ['value'],
1047
+ axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
1048
+ optionalAxis: 'colorAxis',
580
1049
  trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
581
1050
  getSymbol: noop,
582
1051
  supportsDrilldown: true,
583
1052
  getExtremesFromAll: true,
584
1053
  useMapGeometry: true, // get axis extremes from paths, not values
585
- init: function (chart) {
586
- var series = this,
587
- legendOptions = chart.options.legend,
588
- valueDecimals = legendOptions.valueDecimals,
589
- valueSuffix = legendOptions.valueSuffix || '',
590
- legendItems = [],
591
- name,
592
- from,
593
- to,
594
- fromLabel,
595
- toLabel,
596
- colorRange,
597
- valueRanges,
598
- gradientColor,
599
- grad,
600
- tmpLabel,
601
- horizontal = chart.options.legend.layout === 'horizontal';
602
-
603
-
604
- H.Series.prototype.init.apply(this, arguments);
605
- colorRange = series.options.colorRange;
606
- valueRanges = series.options.valueRanges;
607
-
608
- if (valueRanges) {
609
- each(valueRanges, function (range, i) {
610
- var vis = true;
611
- from = range.from;
612
- to = range.to;
613
-
614
- // Assemble the default name. This can be overridden by legend.options.labelFormatter
615
- name = '';
616
- if (from === UNDEFINED) {
617
- name = '< ';
618
- } else if (to === UNDEFINED) {
619
- name = '> ';
620
- }
621
- if (from !== UNDEFINED) {
622
- name += numberFormat(from, valueDecimals) + valueSuffix;
623
- }
624
- if (from !== UNDEFINED && to !== UNDEFINED) {
625
- name += ' - ';
626
- }
627
- if (to !== UNDEFINED) {
628
- name += numberFormat(to, valueDecimals) + valueSuffix;
629
- }
630
-
631
- // Add a mock object to the legend items
632
- legendItems.push(H.extend({
633
- chart: series.chart,
634
- name: name,
635
- options: {},
636
- drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol,
637
- visible: true,
638
- setState: noop,
639
- setVisible: function () {
640
- vis = this.visible = !vis;
641
- each(series.points, function (point) {
642
- if (point.valueRange === i) {
643
- point.setVisible(vis);
644
- }
645
- });
646
-
647
- chart.legend.colorizeItem(this, vis);
648
- }
649
- }, range));
650
- });
651
- series.legendItems = legendItems;
652
-
653
- } else if (colorRange) {
654
-
655
- from = colorRange.from;
656
- to = colorRange.to;
657
- fromLabel = colorRange.fromLabel;
658
- toLabel = colorRange.toLabel;
659
-
660
- // Flips linearGradient variables and label text.
661
- grad = horizontal ? [0, 0, 1, 0] : [0, 1, 0, 0];
662
- if (!horizontal) {
663
- tmpLabel = fromLabel;
664
- fromLabel = toLabel;
665
- toLabel = tmpLabel;
666
- }
667
-
668
- // Creates color gradient.
669
- gradientColor = {
670
- linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
671
- stops:
672
- [
673
- [0, from],
674
- [1, to]
675
- ]
676
- };
677
-
678
- // Add a mock object to the legend items.
679
- legendItems = [{
680
- chart: series.chart,
681
- options: {},
682
- fromLabel: fromLabel,
683
- toLabel: toLabel,
684
- color: gradientColor,
685
- drawLegendSymbol: this.drawLegendSymbolGradient,
686
- visible: true,
687
- setState: noop,
688
- setVisible: noop
689
- }];
690
-
691
- series.legendItems = legendItems;
692
- }
693
- },
694
-
695
- /**
696
- * If neither valueRanges nor colorRanges are defined, use basic area symbol.
697
- */
698
- drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol,
699
-
700
- /**
701
- * Gets the series' symbol in the legend and extended legend with more information.
702
- *
703
- * @param {Object} legend The legend object
704
- * @param {Object} item The series (this) or point
705
- */
706
- drawLegendSymbolGradient: function (legend, item) {
707
- var spacing = legend.options.symbolPadding,
708
- padding = pick(legend.options.padding, 8),
709
- positionY,
710
- positionX,
711
- gradientSize = this.chart.renderer.fontMetrics(legend.options.itemStyle.fontSize).h,
712
- horizontal = legend.options.layout === 'horizontal',
713
- box1,
714
- box2,
715
- box3,
716
- rectangleLength = pick(legend.options.rectangleLength, 200);
717
-
718
- // Set local variables based on option.
719
- if (horizontal) {
720
- positionY = -(spacing / 2);
721
- positionX = 0;
722
- } else {
723
- positionY = -rectangleLength + legend.baseline - (spacing / 2);
724
- positionX = padding + gradientSize;
725
- }
726
-
727
- // Creates the from text.
728
- item.fromText = this.chart.renderer.text(
729
- item.fromLabel, // Text.
730
- positionX, // Lower left x.
731
- positionY // Lower left y.
732
- ).attr({
733
- zIndex: 2
734
- }).add(item.legendGroup);
735
- box1 = item.fromText.getBBox();
736
-
737
- // Creates legend symbol.
738
- // Ternary changes variables based on option.
739
- item.legendSymbol = this.chart.renderer.rect(
740
- horizontal ? box1.x + box1.width + spacing : box1.x - gradientSize - spacing, // Upper left x.
741
- box1.y, // Upper left y.
742
- horizontal ? rectangleLength : gradientSize, // Width.
743
- horizontal ? gradientSize : rectangleLength, // Height.
744
- 2 // Corner radius.
745
- ).attr({
746
- zIndex: 1
747
- }).add(item.legendGroup);
748
- box2 = item.legendSymbol.getBBox();
749
-
750
- // Creates the to text.
751
- // Vertical coordinate changed based on option.
752
- item.toText = this.chart.renderer.text(
753
- item.toLabel,
754
- box2.x + box2.width + spacing,
755
- horizontal ? positionY : box2.y + box2.height - spacing
756
- ).attr({
757
- zIndex: 2
758
- }).add(item.legendGroup);
759
- box3 = item.toText.getBBox();
760
-
761
- // Changes legend box settings based on option.
762
- if (horizontal) {
763
- legend.offsetWidth = box1.width + box2.width + box3.width + (spacing * 2) + padding;
764
- legend.itemY = gradientSize + padding;
765
- } else {
766
- legend.offsetWidth = Math.max(box1.width, box3.width) + (spacing) + box2.width + padding;
767
- legend.itemY = box2.height + padding;
768
- legend.itemX = spacing;
769
- }
770
- },
1054
+ parallelArrays: ['x', 'y', 'value'],
771
1055
 
772
1056
  /**
773
1057
  * Get the bounding box of all paths in the map combined.
@@ -810,8 +1094,8 @@
810
1094
  }
811
1095
  }
812
1096
  // Cache point bounding box for use to position data labels, bubbles etc
813
- point._midX = pointMinX + (pointMaxX - pointMinX) * pick(point.middleX, 0.5);
814
- point._midY = pointMinY + (pointMaxY - pointMinY) * pick(point.middleY, 0.5);
1097
+ point._midX = pointMinX + (pointMaxX - pointMinX) * (point.middleX || 0.5); // pick is slower and very marginally needed
1098
+ point._midY = pointMinY + (pointMaxY - pointMinY) * (point.middleY || 0.5);
815
1099
  point._maxX = pointMaxX;
816
1100
  point._minX = pointMinX;
817
1101
  point._maxY = pointMaxY;
@@ -838,6 +1122,18 @@
838
1122
  },
839
1123
 
840
1124
  getExtremes: function () {
1125
+ // Get the actual value extremes for colors
1126
+ Series.prototype.getExtremes.call(this, this.valueData);
1127
+
1128
+ // Recalculate box on updated data
1129
+ if (this.chart.hasRendered && this.isDirtyData) {
1130
+ this.getBox(this.options.data);
1131
+ }
1132
+
1133
+ this.valueMin = this.dataMin;
1134
+ this.valueMax = this.dataMax;
1135
+
1136
+ // Extremes for the mock Y axis
841
1137
  this.dataMin = this.minY;
842
1138
  this.dataMax = this.maxY;
843
1139
  },
@@ -852,26 +1148,31 @@
852
1148
  even = false, // while loop reads from the end
853
1149
  xAxis = series.xAxis,
854
1150
  yAxis = series.yAxis,
855
- i;
1151
+ xMin = xAxis.min,
1152
+ xTransA = xAxis.transA,
1153
+ xMinPixelPadding = xAxis.minPixelPadding,
1154
+ yMin = yAxis.min,
1155
+ yTransA = yAxis.transA,
1156
+ yMinPixelPadding = yAxis.minPixelPadding,
1157
+ i,
1158
+ ret = []; // Preserve the original
856
1159
 
857
- // Preserve the original
858
- path = [].concat(path);
859
-
860
1160
  // Do the translation
861
- i = path.length;
862
- while (i--) {
863
- if (typeof path[i] === 'number') {
864
- if (even) { // even = x
865
- path[i] = xAxis.translate(path[i]);
866
- } else { // odd = Y
867
- path[i] = yAxis.len - yAxis.translate(path[i]);
1161
+ if (path) {
1162
+ i = path.length;
1163
+ while (i--) {
1164
+ if (typeof path[i] === 'number') {
1165
+ ret[i] = even ?
1166
+ (path[i] - xMin) * xTransA + xMinPixelPadding :
1167
+ (path[i] - yMin) * yTransA + yMinPixelPadding;
1168
+ even = !even;
1169
+ } else {
1170
+ ret[i] = path[i];
868
1171
  }
869
- even = !even;
870
1172
  }
871
1173
  }
872
1174
 
873
-
874
- return path;
1175
+ return ret;
875
1176
  },
876
1177
 
877
1178
  /**
@@ -882,7 +1183,7 @@
882
1183
  setData: function (data, redraw) {
883
1184
  var options = this.options,
884
1185
  mapData = options.mapData,
885
- joinBy = options.dataJoinBy,
1186
+ joinBy = options.joinBy,
886
1187
  dataUsed = [];
887
1188
 
888
1189
 
@@ -900,13 +1201,14 @@
900
1201
  }
901
1202
 
902
1203
  // Add those map points that don't correspond to data, which will be drawn as null points
1204
+ dataUsed = '|' + dataUsed.join('|') + '|'; // String search is faster than array.indexOf
903
1205
  each(mapData, function (mapPoint) {
904
- if (!joinBy || inArray(mapPoint[joinBy], dataUsed) === -1) {
905
- data.push(merge(mapPoint, { y: null }));
1206
+ if (!joinBy || dataUsed.indexOf('|' + mapPoint[joinBy] + '|') === -1) {
1207
+ data.push(merge(mapPoint, { value: null }));
906
1208
  }
907
1209
  });
908
1210
  }
909
- H.Series.prototype.setData.call(this, data, redraw);
1211
+ Series.prototype.setData.call(this, data, redraw);
910
1212
  },
911
1213
 
912
1214
  /**
@@ -920,7 +1222,7 @@
920
1222
 
921
1223
  // Create a cache for quicker lookup second time
922
1224
  if (!mapMap) {
923
- mapMap = this.mapMap = [];
1225
+ mapMap = this.mapMap = {};
924
1226
  }
925
1227
  if (mapMap[value] !== undefined) {
926
1228
  return mapData[mapMap[value]];
@@ -935,92 +1237,29 @@
935
1237
  }
936
1238
  },
937
1239
 
938
- /**
939
- * Add the path option for data points. Find the max value for color calculation.
940
- */
941
- translate: function () {
942
- var series = this,
943
- dataMin = Number.MAX_VALUE,
944
- dataMax = Number.MIN_VALUE;
945
-
946
- series.generatePoints();
947
-
948
- each(series.data, function (point) {
949
-
950
- point.shapeType = 'path';
951
- point.shapeArgs = {
952
- d: series.translatePath(point.path)
953
- };
954
-
955
- // TODO: do point colors in drawPoints instead of point.init
956
- if (typeof point.y === 'number') {
957
- if (point.y > dataMax) {
958
- dataMax = point.y;
959
- } else if (point.y < dataMin) {
960
- dataMin = point.y;
961
- }
962
- }
963
- });
964
-
965
- series.translateColors(dataMin, dataMax);
966
- },
967
-
968
1240
  /**
969
1241
  * In choropleth maps, the color is a result of the value, so this needs translation too
970
1242
  */
971
- translateColors: function (dataMin, dataMax) {
972
-
973
- var seriesOptions = this.options,
974
- valueRanges = seriesOptions.valueRanges,
975
- colorRange = seriesOptions.colorRange,
976
- colorKey = this.colorKey,
977
- nullColor = seriesOptions.nullColor,
978
- from,
979
- to;
1243
+ translateColors: function () {
1244
+ var series = this,
1245
+ nullColor = this.options.nullColor,
1246
+ colorAxis = this.colorAxis;
980
1247
 
981
- if (colorRange) {
982
- from = Color(colorRange.from);
983
- to = Color(colorRange.to);
984
- }
985
1248
  each(this.data, function (point) {
986
- var value = point[colorKey],
987
- isNull = value === null,
988
- range,
989
- color,
990
- i,
991
- pos;
992
-
993
- if (valueRanges) {
994
- i = valueRanges.length;
995
- if (isNull) {
996
- color = nullColor;
997
- } else {
998
- while (i--) {
999
- range = valueRanges[i];
1000
- from = range.from;
1001
- to = range.to;
1002
- if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
1003
- color = range.color;
1004
- break;
1005
- }
1006
- }
1007
- point.valueRange = i;
1008
- }
1009
- } else if (colorRange && !isNull) {
1249
+ var value = point.value,
1250
+ color;
1010
1251
 
1011
- pos = 1 - ((dataMax - value) / (dataMax - dataMin));
1012
- color = tweenColors(from, to, pos);
1013
- } else if (isNull) {
1014
- color = nullColor;
1015
- }
1252
+ color = value === null ? nullColor : colorAxis ? colorAxis.toColor(value, point) : (point.color) || series.color;
1016
1253
 
1017
1254
  if (color) {
1018
- point.color = null; // reset from previous drilldowns, use of the same data options
1019
- point.options.color = color;
1255
+ point.color = point.options.color = color;
1020
1256
  }
1021
1257
  });
1022
1258
  },
1023
1259
 
1260
+ /**
1261
+ * No graph for the map series
1262
+ */
1024
1263
  drawGraph: noop,
1025
1264
 
1026
1265
  /**
@@ -1029,46 +1268,186 @@
1029
1268
  */
1030
1269
  drawDataLabels: noop,
1031
1270
 
1032
- /**
1033
- * Use the drawPoints method of column, that is able to handle simple shapeArgs.
1034
- * Extend it by assigning the tooltip position.
1271
+ /**
1272
+ * Add the path option for data points. Find the max value for color calculation.
1035
1273
  */
1036
- drawPoints: function () {
1274
+ translate: function () {
1037
1275
  var series = this,
1038
1276
  xAxis = series.xAxis,
1039
- yAxis = series.yAxis,
1040
- colorKey = series.colorKey;
1277
+ yAxis = series.yAxis;
1278
+
1279
+ series.generatePoints();
1041
1280
 
1042
- // Make points pass test in drawing
1043
1281
  each(series.data, function (point) {
1044
- point.plotY = 1; // pass null test in column.drawPoints
1045
- if (point[colorKey] === null) {
1046
- point[colorKey] = 0;
1047
- point.isNull = true;
1048
- }
1049
- });
1050
1282
 
1051
- // Draw them
1052
- seriesTypes.column.prototype.drawPoints.apply(series);
1053
-
1054
- each(series.data, function (point) {
1055
-
1056
1283
  // Record the middle point (loosely based on centroid), determined
1057
1284
  // by the middleX and middleY options.
1058
1285
  point.plotX = xAxis.toPixels(point._midX, true);
1059
1286
  point.plotY = yAxis.toPixels(point._midY, true);
1060
-
1061
- // Reset escaped null points
1062
- if (point.isNull) {
1063
- point[colorKey] = null;
1287
+
1288
+ if (series.isDirtyData || series.chart.renderer.isVML) {
1289
+
1290
+ point.shapeType = 'path';
1291
+ point.shapeArgs = {
1292
+ //d: display ? series.translatePath(point.path) : ''
1293
+ d: series.translatePath(point.path),
1294
+ 'vector-effect': 'non-scaling-stroke'
1295
+ };
1064
1296
  }
1065
1297
  });
1298
+
1299
+ series.translateColors();
1300
+ },
1301
+
1302
+ /**
1303
+ * Use the drawPoints method of column, that is able to handle simple shapeArgs.
1304
+ * Extend it by assigning the tooltip position.
1305
+ */
1306
+ drawPoints: function () {
1307
+ var series = this,
1308
+ xAxis = series.xAxis,
1309
+ yAxis = series.yAxis,
1310
+ scale,
1311
+ translateX,
1312
+ group = series.group,
1313
+ chart = series.chart,
1314
+ renderer = chart.renderer,
1315
+ translateY,
1316
+ getTranslate = function (axis, mapRatio) {
1317
+ var dataMin = axis.dataMin,
1318
+ dataMax = axis.dataMax,
1319
+ fullDataMin = dataMin - ((dataMax - dataMin) * (mapRatio - 1) / 2),
1320
+ fullMin = axis.min - axis.minPixelPadding / axis.transA,
1321
+ minOffset = fullMin - fullDataMin,
1322
+ centerOffset = (dataMax - dataMin - axis.max + axis.min) * mapRatio,
1323
+ center = minOffset / centerOffset;
1324
+ return (axis.len * (1 - scale)) * center;
1325
+ };
1326
+
1327
+ // Set a group that handles transform during zooming and panning in order to preserve clipping
1328
+ // on series.group
1329
+ if (!series.transformGroup) {
1330
+ series.transformGroup = renderer.g()
1331
+ .attr({
1332
+ scaleX: 1,
1333
+ scaleY: 1
1334
+ })
1335
+ .add(group);
1336
+ }
1337
+
1338
+ // Draw the shapes again
1339
+ if (series.isDirtyData || renderer.isVML) {
1340
+
1341
+ // Draw them in transformGroup
1342
+ series.group = series.transformGroup;
1343
+ seriesTypes.column.prototype.drawPoints.apply(series);
1344
+ series.group = group; // Reset
1345
+
1346
+ // Individual point actions
1347
+ each(series.points, function (point) {
1348
+
1349
+ // Reset color on update/redraw
1350
+ if (chart.hasRendered && point.graphic) {
1351
+ point.graphic.attr('fill', point.options.color);
1352
+ }
1353
+
1354
+ });
1355
+
1356
+ // Set the base for later scale-zooming
1357
+ this.transA = xAxis.transA;
1066
1358
 
1359
+ // Just update the scale and transform for better performance
1360
+ } else {
1361
+ scale = xAxis.transA / this.transA;
1362
+ if (scale > 0.99 && scale < 1.01) { // rounding errors
1363
+ translateX = 0;
1364
+ translateY = 0;
1365
+ scale = 1;
1366
+
1367
+ } else {
1368
+ translateX = getTranslate(xAxis, Math.max(1, series.chart.mapRatio));
1369
+ translateY = getTranslate(yAxis, 1 / Math.min(1, series.chart.mapRatio));
1370
+ }
1371
+
1372
+ this.transformGroup.animate({
1373
+ translateX: translateX,
1374
+ translateY: translateY,
1375
+ scaleX: scale,
1376
+ scaleY: scale
1377
+ });
1378
+
1379
+ }
1380
+
1381
+
1067
1382
  // Now draw the data labels
1068
- H.Series.prototype.drawDataLabels.call(series);
1383
+ Series.prototype.drawDataLabels.call(series);
1069
1384
 
1070
1385
  },
1071
1386
 
1387
+ /**
1388
+ * Override render to throw in an async call in IE8. Otherwise it chokes on the US counties demo.
1389
+ */
1390
+ render: function () {
1391
+ var series = this,
1392
+ render = Series.prototype.render;
1393
+
1394
+ // Give IE8 some time to breathe.
1395
+ if (series.chart.renderer.isVML && series.data.length > 3000) {
1396
+ setTimeout(function () {
1397
+ render.call(series);
1398
+ });
1399
+ } else {
1400
+ render.call(series);
1401
+ }
1402
+ },
1403
+
1404
+ /**
1405
+ * The initial animation for the map series. By default, animation is disabled.
1406
+ * Animation of map shapes is not at all supported in VML browsers.
1407
+ */
1408
+ animate: function (init) {
1409
+ var chart = this.chart,
1410
+ animation = this.options.animation,
1411
+ group = this.group,
1412
+ xAxis = this.xAxis,
1413
+ yAxis = this.yAxis,
1414
+ left = xAxis.pos,
1415
+ top = yAxis.pos;
1416
+
1417
+ if (chart.renderer.isSVG) {
1418
+
1419
+ if (animation === true) {
1420
+ animation = {
1421
+ duration: 1000
1422
+ };
1423
+ }
1424
+
1425
+ // Initialize the animation
1426
+ if (init) {
1427
+
1428
+ // Scale down the group and place it in the center
1429
+ group.attr({
1430
+ translateX: left + xAxis.len / 2,
1431
+ translateY: top + yAxis.len / 2,
1432
+ scaleX: 0.001, // #1499
1433
+ scaleY: 0.001
1434
+ });
1435
+
1436
+ // Run the animation
1437
+ } else {
1438
+ group.animate({
1439
+ translateX: left,
1440
+ translateY: top,
1441
+ scaleX: 1,
1442
+ scaleY: 1
1443
+ }, animation);
1444
+
1445
+ // Delete this function to allow it only once
1446
+ this.animate = null;
1447
+ }
1448
+ }
1449
+ },
1450
+
1072
1451
  /**
1073
1452
  * Animate in the new series from the clicked point in the old series.
1074
1453
  * Depends on the drilldown.js module
@@ -1104,11 +1483,13 @@
1104
1483
 
1105
1484
  });
1106
1485
 
1107
- delete this.animate;
1486
+ this.animate = null;
1108
1487
  }
1109
1488
 
1110
1489
  },
1111
1490
 
1491
+ drawLegendSymbol: H.LegendSymbolMixin.drawRectangle,
1492
+
1112
1493
  /**
1113
1494
  * When drilling up, pull out the individual point graphics from the lower series
1114
1495
  * and animate them into the origin point in the upper series.
@@ -1131,14 +1512,14 @@
1131
1512
  // The mapline series type
1132
1513
  plotOptions.mapline = merge(plotOptions.map, {
1133
1514
  lineWidth: 1,
1134
- backgroundColor: 'none'
1515
+ fillColor: 'none'
1135
1516
  });
1136
1517
  seriesTypes.mapline = extendClass(seriesTypes.map, {
1137
1518
  type: 'mapline',
1138
1519
  pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
1139
1520
  stroke: 'color',
1140
1521
  'stroke-width': 'lineWidth',
1141
- fill: 'backgroundColor'
1522
+ fill: 'fillColor'
1142
1523
  },
1143
1524
  drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol
1144
1525
  });
@@ -1233,15 +1614,14 @@
1233
1614
  var hiddenAxis = {
1234
1615
  endOnTick: false,
1235
1616
  gridLineWidth: 0,
1236
- labels: {
1237
- enabled: false
1238
- },
1239
1617
  lineWidth: 0,
1240
1618
  minPadding: 0,
1241
1619
  maxPadding: 0,
1242
1620
  startOnTick: false,
1243
- tickWidth: 0,
1244
- title: null
1621
+ title: null,
1622
+ tickPositions: []
1623
+ //tickInterval: 500,
1624
+ //gridZIndex: 10
1245
1625
  },
1246
1626
  seriesOptions;
1247
1627
 
@@ -1251,7 +1631,8 @@
1251
1631
 
1252
1632
  options = merge({
1253
1633
  chart: {
1254
- panning: 'xy'
1634
+ panning: 'xy',
1635
+ type: 'map'
1255
1636
  },
1256
1637
  xAxis: hiddenAxis,
1257
1638
  yAxis: merge(hiddenAxis, { reversed: true })
@@ -1260,8 +1641,9 @@
1260
1641
 
1261
1642
  { // forced options
1262
1643
  chart: {
1263
- type: 'map',
1264
- inverted: false
1644
+ inverted: false,
1645
+ alignTicks: false,
1646
+ preserveAspectRatio: true
1265
1647
  }
1266
1648
  });
1267
1649
 
@@ -1271,3 +1653,4 @@
1271
1653
  return new Chart(options, callback);
1272
1654
  };
1273
1655
  }(Highcharts));
1656
+