highcharts-rails 3.0.7 → 3.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+