highcharts-rails 4.2.7 → 5.0.0

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +34 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +53 -32
  5. data/app/assets/javascripts/highcharts.js +18775 -17176
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +1849 -1563
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +2162 -1988
  8. data/app/assets/javascripts/highcharts/modules/accessibility.js +1005 -0
  9. data/app/assets/javascripts/highcharts/modules/annotations.js +408 -401
  10. data/app/assets/javascripts/highcharts/modules/boost.js +561 -546
  11. data/app/assets/javascripts/highcharts/modules/broken-axis.js +330 -324
  12. data/app/assets/javascripts/highcharts/modules/data.js +973 -965
  13. data/app/assets/javascripts/highcharts/modules/drilldown.js +783 -723
  14. data/app/assets/javascripts/highcharts/modules/exporting.js +864 -785
  15. data/app/assets/javascripts/highcharts/modules/funnel.js +290 -306
  16. data/app/assets/javascripts/highcharts/modules/heatmap.js +701 -645
  17. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +150 -132
  18. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +414 -355
  19. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +164 -0
  20. data/app/assets/javascripts/highcharts/modules/series-label.js +473 -448
  21. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +279 -271
  22. data/app/assets/javascripts/highcharts/modules/treemap.js +921 -886
  23. data/app/assets/javascripts/highcharts/themes/dark-blue.js +307 -244
  24. data/app/assets/javascripts/highcharts/themes/dark-green.js +303 -244
  25. data/app/assets/javascripts/highcharts/themes/dark-unica.js +231 -201
  26. data/app/assets/javascripts/highcharts/themes/gray.js +314 -245
  27. data/app/assets/javascripts/highcharts/themes/grid-light.js +91 -66
  28. data/app/assets/javascripts/highcharts/themes/grid.js +124 -96
  29. data/app/assets/javascripts/highcharts/themes/sand-signika.js +119 -94
  30. data/app/assets/javascripts/highcharts/themes/skies.js +108 -85
  31. data/lib/highcharts/version.rb +1 -1
  32. metadata +13 -14
  33. data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +0 -1
  34. data/app/assets/javascripts/highcharts/modules/canvas-tools.js +0 -3115
  35. data/app/assets/javascripts/highcharts/modules/map.js +0 -2117
@@ -1,2117 +0,0 @@
1
- /**
2
- * @license Highmaps JS v1.0.4 (2014-09-02)
3
- * Highmaps as a plugin for Highcharts 4.0.1 or Highstock 2.0.1
4
- *
5
- * (c) 2011-2014 Torstein Honsi
6
- *
7
- * License: www.highcharts.com/license
8
- */
9
-
10
- /*global HighchartsAdapter*/
11
- (function (Highcharts) {
12
-
13
-
14
- var UNDEFINED,
15
- Axis = Highcharts.Axis,
16
- Chart = Highcharts.Chart,
17
- Color = Highcharts.Color,
18
- Point = Highcharts.Point,
19
- Pointer = Highcharts.Pointer,
20
- Legend = Highcharts.Legend,
21
- LegendSymbolMixin = Highcharts.LegendSymbolMixin,
22
- Renderer = Highcharts.Renderer,
23
- Series = Highcharts.Series,
24
- SVGRenderer = Highcharts.SVGRenderer,
25
- VMLRenderer = Highcharts.VMLRenderer,
26
-
27
- addEvent = Highcharts.addEvent,
28
- each = Highcharts.each,
29
- extend = Highcharts.extend,
30
- extendClass = Highcharts.extendClass,
31
- merge = Highcharts.merge,
32
- pick = Highcharts.pick,
33
- numberFormat = Highcharts.numberFormat,
34
- defaultOptions = Highcharts.getOptions(),
35
- seriesTypes = Highcharts.seriesTypes,
36
- defaultPlotOptions = defaultOptions.plotOptions,
37
- wrap = Highcharts.wrap,
38
- noop = function () {};
39
-
40
- /**
41
- * Override to use the extreme coordinates from the SVG shape, not the
42
- * data values
43
- */
44
- wrap(Axis.prototype, 'getSeriesExtremes', function (proceed) {
45
- var isXAxis = this.isXAxis,
46
- dataMin,
47
- dataMax,
48
- xData = [],
49
- useMapGeometry;
50
-
51
- // Remove the xData array and cache it locally so that the proceed method doesn't use it
52
- if (isXAxis) {
53
- each(this.series, function (series, i) {
54
- if (series.useMapGeometry) {
55
- xData[i] = series.xData;
56
- series.xData = [];
57
- }
58
- });
59
- }
60
-
61
- // Call base to reach normal cartesian series (like mappoint)
62
- proceed.call(this);
63
-
64
- // Run extremes logic for map and mapline
65
- if (isXAxis) {
66
- dataMin = pick(this.dataMin, Number.MAX_VALUE);
67
- dataMax = pick(this.dataMax, Number.MIN_VALUE);
68
- each(this.series, function (series, i) {
69
- if (series.useMapGeometry) {
70
- dataMin = Math.min(dataMin, pick(series.minX, dataMin));
71
- dataMax = Math.max(dataMax, pick(series.maxX, dataMin));
72
- series.xData = xData[i]; // Reset xData array
73
- useMapGeometry = true;
74
- }
75
- });
76
- if (useMapGeometry) {
77
- this.dataMin = dataMin;
78
- this.dataMax = dataMax;
79
- }
80
- }
81
- });
82
-
83
- /**
84
- * Override axis translation to make sure the aspect ratio is always kept
85
- */
86
- wrap(Axis.prototype, 'setAxisTranslation', function (proceed) {
87
- var chart = this.chart,
88
- mapRatio,
89
- plotRatio = chart.plotWidth / chart.plotHeight,
90
- adjustedAxisLength,
91
- xAxis = chart.xAxis[0],
92
- padAxis,
93
- fixTo,
94
- fixDiff;
95
-
96
-
97
- // Run the parent method
98
- proceed.call(this);
99
-
100
- // On Y axis, handle both
101
- if (chart.options.chart.preserveAspectRatio && this.coll === 'yAxis' && xAxis.transA !== UNDEFINED) {
102
-
103
- // Use the same translation for both axes
104
- this.transA = xAxis.transA = Math.min(this.transA, xAxis.transA);
105
-
106
- mapRatio = plotRatio / ((xAxis.max - xAxis.min) / (this.max - this.min));
107
-
108
- // What axis to pad to put the map in the middle
109
- padAxis = mapRatio < 1 ? this : xAxis;
110
-
111
- // Pad it
112
- adjustedAxisLength = (padAxis.max - padAxis.min) * padAxis.transA;
113
- padAxis.pixelPadding = padAxis.len - adjustedAxisLength;
114
- padAxis.minPixelPadding = padAxis.pixelPadding / 2;
115
-
116
- fixTo = padAxis.fixTo;
117
- if (fixTo) {
118
- fixDiff = fixTo[1] - padAxis.toValue(fixTo[0], true);
119
- fixDiff *= padAxis.transA;
120
- if (Math.abs(fixDiff) > padAxis.minPixelPadding || (padAxis.min === padAxis.dataMin && padAxis.max === padAxis.dataMax)) { // zooming out again, keep within restricted area
121
- fixDiff = 0;
122
- }
123
- padAxis.minPixelPadding -= fixDiff;
124
- }
125
- }
126
- });
127
-
128
- /**
129
- * Override Axis.render in order to delete the fixTo prop
130
- */
131
- wrap(Axis.prototype, 'render', function (proceed) {
132
- proceed.call(this);
133
- this.fixTo = null;
134
- });
135
-
136
-
137
- /**
138
- * The ColorAxis object for inclusion in gradient legends
139
- */
140
- var ColorAxis = Highcharts.ColorAxis = function () {
141
- this.isColorAxis = true;
142
- this.init.apply(this, arguments);
143
- };
144
- extend(ColorAxis.prototype, Axis.prototype);
145
- extend(ColorAxis.prototype, {
146
- defaultColorAxisOptions: {
147
- lineWidth: 0,
148
- gridLineWidth: 1,
149
- tickPixelInterval: 72,
150
- startOnTick: true,
151
- endOnTick: true,
152
- offset: 0,
153
- marker: {
154
- animation: {
155
- duration: 50
156
- },
157
- color: 'gray',
158
- width: 0.01
159
- },
160
- labels: {
161
- overflow: 'justify'
162
- },
163
- minColor: '#EFEFFF',
164
- maxColor: '#003875',
165
- tickLength: 5
166
- },
167
- init: function (chart, userOptions) {
168
- var horiz = chart.options.legend.layout !== 'vertical',
169
- options;
170
-
171
- // Build the options
172
- options = merge(this.defaultColorAxisOptions, {
173
- side: horiz ? 2 : 1,
174
- reversed: !horiz
175
- }, userOptions, {
176
- isX: horiz,
177
- opposite: !horiz,
178
- showEmpty: false,
179
- title: null,
180
- isColor: true
181
- });
182
-
183
- Axis.prototype.init.call(this, chart, options);
184
-
185
- // Base init() pushes it to the xAxis array, now pop it again
186
- //chart[this.isXAxis ? 'xAxis' : 'yAxis'].pop();
187
-
188
- // Prepare data classes
189
- if (userOptions.dataClasses) {
190
- this.initDataClasses(userOptions);
191
- }
192
- this.initStops(userOptions);
193
-
194
- // Override original axis properties
195
- this.isXAxis = true;
196
- this.horiz = horiz;
197
- this.zoomEnabled = false;
198
- },
199
-
200
- /*
201
- * Return an intermediate color between two colors, according to pos where 0
202
- * is the from color and 1 is the to color
203
- */
204
- tweenColors: function (from, to, pos) {
205
- // Check for has alpha, because rgba colors perform worse due to lack of
206
- // support in WebKit.
207
- var hasAlpha = (to.rgba[3] !== 1 || from.rgba[3] !== 1);
208
- return (hasAlpha ? 'rgba(' : 'rgb(') +
209
- Math.round(to.rgba[0] + (from.rgba[0] - to.rgba[0]) * (1 - pos)) + ',' +
210
- Math.round(to.rgba[1] + (from.rgba[1] - to.rgba[1]) * (1 - pos)) + ',' +
211
- Math.round(to.rgba[2] + (from.rgba[2] - to.rgba[2]) * (1 - pos)) +
212
- (hasAlpha ? (',' + (to.rgba[3] + (from.rgba[3] - to.rgba[3]) * (1 - pos))) : '') + ')';
213
- },
214
-
215
- initDataClasses: function (userOptions) {
216
- var axis = this,
217
- chart = this.chart,
218
- dataClasses,
219
- colorCounter = 0,
220
- options = this.options,
221
- len = userOptions.dataClasses.length;
222
- this.dataClasses = dataClasses = [];
223
- this.legendItems = [];
224
-
225
- each(userOptions.dataClasses, function (dataClass, i) {
226
- var colors;
227
-
228
- dataClass = merge(dataClass);
229
- dataClasses.push(dataClass);
230
- if (!dataClass.color) {
231
- if (options.dataClassColor === 'category') {
232
- colors = chart.options.colors;
233
- dataClass.color = colors[colorCounter++];
234
- // loop back to zero
235
- if (colorCounter === colors.length) {
236
- colorCounter = 0;
237
- }
238
- } else {
239
- dataClass.color = axis.tweenColors(
240
- Color(options.minColor),
241
- Color(options.maxColor),
242
- len < 2 ? 0.5 : i / (len - 1) // #3219
243
- );
244
- }
245
- }
246
- });
247
- },
248
-
249
- initStops: function (userOptions) {
250
- this.stops = userOptions.stops || [
251
- [0, this.options.minColor],
252
- [1, this.options.maxColor]
253
- ];
254
- each(this.stops, function (stop) {
255
- stop.color = Color(stop[1]);
256
- });
257
- },
258
-
259
- /**
260
- * Extend the setOptions method to process extreme colors and color
261
- * stops.
262
- */
263
- setOptions: function (userOptions) {
264
- Axis.prototype.setOptions.call(this, userOptions);
265
-
266
- this.options.crosshair = this.options.marker;
267
- this.coll = 'colorAxis';
268
- },
269
-
270
- setAxisSize: function () {
271
- var symbol = this.legendSymbol,
272
- chart = this.chart,
273
- x,
274
- y,
275
- width,
276
- height;
277
-
278
- if (symbol) {
279
- this.left = x = symbol.attr('x');
280
- this.top = y = symbol.attr('y');
281
- this.width = width = symbol.attr('width');
282
- this.height = height = symbol.attr('height');
283
- this.right = chart.chartWidth - x - width;
284
- this.bottom = chart.chartHeight - y - height;
285
-
286
- this.len = this.horiz ? width : height;
287
- this.pos = this.horiz ? x : y;
288
- }
289
- },
290
-
291
- /**
292
- * Translate from a value to a color
293
- */
294
- toColor: function (value, point) {
295
- var pos,
296
- stops = this.stops,
297
- from,
298
- to,
299
- color,
300
- dataClasses = this.dataClasses,
301
- dataClass,
302
- i;
303
-
304
- if (dataClasses) {
305
- i = dataClasses.length;
306
- while (i--) {
307
- dataClass = dataClasses[i];
308
- from = dataClass.from;
309
- to = dataClass.to;
310
- if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) {
311
- color = dataClass.color;
312
- if (point) {
313
- point.dataClass = i;
314
- }
315
- break;
316
- }
317
- }
318
-
319
- } else {
320
-
321
- if (this.isLog) {
322
- value = this.val2lin(value);
323
- }
324
- pos = 1 - ((this.max - value) / ((this.max - this.min) || 1));
325
- i = stops.length;
326
- while (i--) {
327
- if (pos > stops[i][0]) {
328
- break;
329
- }
330
- }
331
- from = stops[i] || stops[i + 1];
332
- to = stops[i + 1] || from;
333
-
334
- // The position within the gradient
335
- pos = 1 - (to[0] - pos) / ((to[0] - from[0]) || 1);
336
-
337
- color = this.tweenColors(
338
- from.color,
339
- to.color,
340
- pos
341
- );
342
- }
343
- return color;
344
- },
345
-
346
- getOffset: function () {
347
- var group = this.legendGroup,
348
- sideOffset = this.chart.axisOffset[this.side];
349
-
350
- if (group) {
351
-
352
- Axis.prototype.getOffset.call(this);
353
-
354
- if (!this.axisGroup.parentGroup) {
355
-
356
- // Move the axis elements inside the legend group
357
- this.axisGroup.add(group);
358
- this.gridGroup.add(group);
359
- this.labelGroup.add(group);
360
-
361
- this.added = true;
362
- }
363
- // Reset it to avoid color axis reserving space
364
- this.chart.axisOffset[this.side] = sideOffset;
365
- }
366
- },
367
-
368
- /**
369
- * Create the color gradient
370
- */
371
- setLegendColor: function () {
372
- var grad,
373
- horiz = this.horiz,
374
- options = this.options;
375
-
376
- grad = horiz ? [0, 0, 1, 0] : [0, 0, 0, 1];
377
- this.legendColor = {
378
- linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] },
379
- stops: options.stops || [
380
- [0, options.minColor],
381
- [1, options.maxColor]
382
- ]
383
- };
384
- },
385
-
386
- /**
387
- * The color axis appears inside the legend and has its own legend symbol
388
- */
389
- drawLegendSymbol: function (legend, item) {
390
- var padding = legend.padding,
391
- legendOptions = legend.options,
392
- horiz = this.horiz,
393
- box,
394
- width = pick(legendOptions.symbolWidth, horiz ? 200 : 12),
395
- height = pick(legendOptions.symbolHeight, horiz ? 12 : 200),
396
- labelPadding = pick(legendOptions.labelPadding, horiz ? 16 : 30),
397
- itemDistance = pick(legendOptions.itemDistance, 10);
398
-
399
- this.setLegendColor();
400
-
401
- // Create the gradient
402
- item.legendSymbol = this.chart.renderer.rect(
403
- 0,
404
- legend.baseline - 11,
405
- width,
406
- height
407
- ).attr({
408
- zIndex: 1
409
- }).add(item.legendGroup);
410
- box = item.legendSymbol.getBBox();
411
-
412
- // Set how much space this legend item takes up
413
- this.legendItemWidth = width + padding + (horiz ? itemDistance : labelPadding);
414
- this.legendItemHeight = height + padding + (horiz ? labelPadding : 0);
415
- },
416
- /**
417
- * Fool the legend
418
- */
419
- setState: noop,
420
- visible: true,
421
- setVisible: noop,
422
- getSeriesExtremes: function () {
423
- var series;
424
- if (this.series.length) {
425
- series = this.series[0];
426
- this.dataMin = series.valueMin;
427
- this.dataMax = series.valueMax;
428
- }
429
- },
430
- drawCrosshair: function (e, point) {
431
- var newCross = !this.cross,
432
- plotX = point && point.plotX,
433
- plotY = point && point.plotY,
434
- crossPos,
435
- axisPos = this.pos,
436
- axisLen = this.len;
437
-
438
- if (point) {
439
- crossPos = this.toPixels(point.value);
440
- if (crossPos < axisPos) {
441
- crossPos = axisPos - 2;
442
- } else if (crossPos > axisPos + axisLen) {
443
- crossPos = axisPos + axisLen + 2;
444
- }
445
-
446
- point.plotX = crossPos;
447
- point.plotY = this.len - crossPos;
448
- Axis.prototype.drawCrosshair.call(this, e, point);
449
- point.plotX = plotX;
450
- point.plotY = plotY;
451
-
452
- if (!newCross && this.cross) {
453
- this.cross
454
- .attr({
455
- fill: this.crosshair.color
456
- })
457
- .add(this.labelGroup);
458
- }
459
- }
460
- },
461
- getPlotLinePath: function (a, b, c, d, pos) {
462
- if (pos) { // crosshairs only
463
- return this.horiz ?
464
- ['M', pos - 4, this.top - 6, 'L', pos + 4, this.top - 6, pos, this.top, 'Z'] :
465
- ['M', this.left, pos, 'L', this.left - 6, pos + 6, this.left - 6, pos - 6, 'Z'];
466
- } else {
467
- return Axis.prototype.getPlotLinePath.call(this, a, b, c, d);
468
- }
469
- },
470
-
471
- update: function (newOptions, redraw) {
472
- each(this.series, function (series) {
473
- series.isDirtyData = true; // Needed for Axis.update when choropleth colors change
474
- });
475
- Axis.prototype.update.call(this, newOptions, redraw);
476
- if (this.legendItem) {
477
- this.setLegendColor();
478
- this.chart.legend.colorizeItem(this, true);
479
- }
480
- },
481
-
482
- /**
483
- * Get the legend item symbols for data classes
484
- */
485
- getDataClassLegendSymbols: function () {
486
- var axis = this,
487
- chart = this.chart,
488
- legendItems = this.legendItems,
489
- legendOptions = chart.options.legend,
490
- valueDecimals = legendOptions.valueDecimals,
491
- valueSuffix = legendOptions.valueSuffix || '',
492
- name;
493
-
494
- if (!legendItems.length) {
495
- each(this.dataClasses, function (dataClass, i) {
496
- var vis = true,
497
- from = dataClass.from,
498
- to = dataClass.to;
499
-
500
- // Assemble the default name. This can be overridden by legend.options.labelFormatter
501
- name = '';
502
- if (from === UNDEFINED) {
503
- name = '< ';
504
- } else if (to === UNDEFINED) {
505
- name = '> ';
506
- }
507
- if (from !== UNDEFINED) {
508
- name += numberFormat(from, valueDecimals) + valueSuffix;
509
- }
510
- if (from !== UNDEFINED && to !== UNDEFINED) {
511
- name += ' - ';
512
- }
513
- if (to !== UNDEFINED) {
514
- name += numberFormat(to, valueDecimals) + valueSuffix;
515
- }
516
-
517
- // Add a mock object to the legend items
518
- legendItems.push(extend({
519
- chart: chart,
520
- name: name,
521
- options: {},
522
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
523
- visible: true,
524
- setState: noop,
525
- setVisible: function () {
526
- vis = this.visible = !vis;
527
- each(axis.series, function (series) {
528
- each(series.points, function (point) {
529
- if (point.dataClass === i) {
530
- point.setVisible(vis);
531
- }
532
- });
533
- });
534
-
535
- chart.legend.colorizeItem(this, vis);
536
- }
537
- }, dataClass));
538
- });
539
- }
540
- return legendItems;
541
- },
542
- name: '' // Prevents 'undefined' in legend in IE8
543
- });
544
-
545
- /**
546
- * Handle animation of the color attributes directly
547
- */
548
- each(['fill', 'stroke'], function (prop) {
549
- HighchartsAdapter.addAnimSetter(prop, function (fx) {
550
- fx.elem.attr(prop, ColorAxis.prototype.tweenColors(Color(fx.start), Color(fx.end), fx.pos));
551
- });
552
- });
553
-
554
- /**
555
- * Extend the chart getAxes method to also get the color axis
556
- */
557
- wrap(Chart.prototype, 'getAxes', function (proceed) {
558
-
559
- var options = this.options,
560
- colorAxisOptions = options.colorAxis;
561
-
562
- proceed.call(this);
563
-
564
- this.colorAxis = [];
565
- if (colorAxisOptions) {
566
- proceed = new ColorAxis(this, colorAxisOptions); // Fake assignment for jsLint
567
- }
568
- });
569
-
570
-
571
- /**
572
- * Wrap the legend getAllItems method to add the color axis. This also removes the
573
- * axis' own series to prevent them from showing up individually.
574
- */
575
- wrap(Legend.prototype, 'getAllItems', function (proceed) {
576
- var allItems = [],
577
- colorAxis = this.chart.colorAxis[0];
578
-
579
- if (colorAxis) {
580
-
581
- // Data classes
582
- if (colorAxis.options.dataClasses) {
583
- allItems = allItems.concat(colorAxis.getDataClassLegendSymbols());
584
- // Gradient legend
585
- } else {
586
- // Add this axis on top
587
- allItems.push(colorAxis);
588
- }
589
-
590
- // Don't add the color axis' series
591
- each(colorAxis.series, function (series) {
592
- series.options.showInLegend = false;
593
- });
594
- }
595
-
596
- return allItems.concat(proceed.call(this));
597
- });/**
598
- * Mixin for maps and heatmaps
599
- */
600
- var colorSeriesMixin = {
601
-
602
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
603
- stroke: 'borderColor',
604
- 'stroke-width': 'borderWidth',
605
- fill: 'color',
606
- dashstyle: 'dashStyle'
607
- },
608
- pointArrayMap: ['value'],
609
- axisTypes: ['xAxis', 'yAxis', 'colorAxis'],
610
- optionalAxis: 'colorAxis',
611
- trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
612
- getSymbol: noop,
613
- parallelArrays: ['x', 'y', 'value'],
614
- colorKey: 'value',
615
-
616
- /**
617
- * In choropleth maps, the color is a result of the value, so this needs translation too
618
- */
619
- translateColors: function () {
620
- var series = this,
621
- nullColor = this.options.nullColor,
622
- colorAxis = this.colorAxis,
623
- colorKey = this.colorKey;
624
-
625
- each(this.data, function (point) {
626
- var value = point[colorKey],
627
- color;
628
-
629
- color = value === null ? nullColor : (colorAxis && value !== undefined) ? colorAxis.toColor(value, point) : point.color || series.color;
630
-
631
- if (color) {
632
- point.color = color;
633
- }
634
- });
635
- }
636
- };
637
-
638
-
639
- /**
640
- * Wrap the buildText method and add the hook for add text stroke
641
- */
642
- wrap(SVGRenderer.prototype, 'buildText', function (proceed, wrapper) {
643
-
644
- var textStroke = wrapper.styles && wrapper.styles.HcTextStroke;
645
-
646
- proceed.call(this, wrapper);
647
-
648
- // Apply the text stroke
649
- if (textStroke && wrapper.applyTextStroke) {
650
- wrapper.applyTextStroke(textStroke);
651
- }
652
- });
653
-
654
- /**
655
- * Apply an outside text stroke to data labels, based on the custom CSS property, HcTextStroke.
656
- * Consider moving this to Highcharts core, also makes sense on stacked columns etc.
657
- */
658
- SVGRenderer.prototype.Element.prototype.applyTextStroke = function (textStroke) {
659
- var elem = this.element,
660
- tspans,
661
- firstChild;
662
-
663
- textStroke = textStroke.split(' ');
664
- tspans = elem.getElementsByTagName('tspan');
665
- firstChild = elem.firstChild;
666
-
667
- // In order to get the right y position of the clones,
668
- // copy over the y setter
669
- this.ySetter = this.xSetter;
670
-
671
- each([].slice.call(tspans), function (tspan, y) {
672
- var clone;
673
- if (y === 0) {
674
- tspan.setAttribute('x', elem.getAttribute('x'));
675
- if ((y = elem.getAttribute('y')) !== null) {
676
- tspan.setAttribute('y', y);
677
- }
678
- }
679
- clone = tspan.cloneNode(1);
680
- clone.setAttribute('stroke', textStroke[1]);
681
- clone.setAttribute('stroke-width', textStroke[0]);
682
- clone.setAttribute('stroke-linejoin', 'round');
683
- elem.insertBefore(clone, firstChild);
684
- });
685
- };
686
- // Add events to the Chart object itself
687
- extend(Chart.prototype, {
688
- renderMapNavigation: function () {
689
- var chart = this,
690
- options = this.options.mapNavigation,
691
- buttons = options.buttons,
692
- n,
693
- button,
694
- buttonOptions,
695
- attr,
696
- states,
697
- outerHandler = function () {
698
- this.handler.call(chart);
699
- };
700
-
701
- if (pick(options.enableButtons, options.enabled) && !chart.renderer.forExport) {
702
- for (n in buttons) {
703
- if (buttons.hasOwnProperty(n)) {
704
- buttonOptions = merge(options.buttonOptions, buttons[n]);
705
- attr = buttonOptions.theme;
706
- states = attr.states;
707
- button = chart.renderer.button(
708
- buttonOptions.text,
709
- 0,
710
- 0,
711
- outerHandler,
712
- attr,
713
- states && states.hover,
714
- states && states.select,
715
- 0,
716
- n === 'zoomIn' ? 'topbutton' : 'bottombutton'
717
- )
718
- .attr({
719
- width: buttonOptions.width,
720
- height: buttonOptions.height,
721
- title: chart.options.lang[n],
722
- zIndex: 5
723
- })
724
- .css(buttonOptions.style)
725
- .add();
726
- button.handler = buttonOptions.onclick;
727
- button.align(extend(buttonOptions, { width: button.width, height: 2 * button.height }), null, buttonOptions.alignTo);
728
- }
729
- }
730
- }
731
- },
732
-
733
- /**
734
- * Fit an inner box to an outer. If the inner box overflows left or right, align it to the sides of the
735
- * outer. If it overflows both sides, fit it within the outer. This is a pattern that occurs more places
736
- * in Highcharts, perhaps it should be elevated to a common utility function.
737
- */
738
- fitToBox: function (inner, outer) {
739
- each([['x', 'width'], ['y', 'height']], function (dim) {
740
- var pos = dim[0],
741
- size = dim[1];
742
-
743
- if (inner[pos] + inner[size] > outer[pos] + outer[size]) { // right overflow
744
- if (inner[size] > outer[size]) { // the general size is greater, fit fully to outer
745
- inner[size] = outer[size];
746
- inner[pos] = outer[pos];
747
- } else { // align right
748
- inner[pos] = outer[pos] + outer[size] - inner[size];
749
- }
750
- }
751
- if (inner[size] > outer[size]) {
752
- inner[size] = outer[size];
753
- }
754
- if (inner[pos] < outer[pos]) {
755
- inner[pos] = outer[pos];
756
- }
757
- });
758
-
759
-
760
- return inner;
761
- },
762
-
763
- /**
764
- * Zoom the map in or out by a certain amount. Less than 1 zooms in, greater than 1 zooms out.
765
- */
766
- mapZoom: function (howMuch, centerXArg, centerYArg, mouseX, mouseY) {
767
- /*if (this.isMapZooming) {
768
- this.mapZoomQueue = arguments;
769
- return;
770
- }*/
771
-
772
- var chart = this,
773
- xAxis = chart.xAxis[0],
774
- xRange = xAxis.max - xAxis.min,
775
- centerX = pick(centerXArg, xAxis.min + xRange / 2),
776
- newXRange = xRange * howMuch,
777
- yAxis = chart.yAxis[0],
778
- yRange = yAxis.max - yAxis.min,
779
- centerY = pick(centerYArg, yAxis.min + yRange / 2),
780
- newYRange = yRange * howMuch,
781
- fixToX = mouseX ? ((mouseX - xAxis.pos) / xAxis.len) : 0.5,
782
- fixToY = mouseY ? ((mouseY - yAxis.pos) / yAxis.len) : 0.5,
783
- newXMin = centerX - newXRange * fixToX,
784
- newYMin = centerY - newYRange * fixToY,
785
- newExt = chart.fitToBox({
786
- x: newXMin,
787
- y: newYMin,
788
- width: newXRange,
789
- height: newYRange
790
- }, {
791
- x: xAxis.dataMin,
792
- y: yAxis.dataMin,
793
- width: xAxis.dataMax - xAxis.dataMin,
794
- height: yAxis.dataMax - yAxis.dataMin
795
- });
796
-
797
- // When mousewheel zooming, fix the point under the mouse
798
- if (mouseX) {
799
- xAxis.fixTo = [mouseX - xAxis.pos, centerXArg];
800
- }
801
- if (mouseY) {
802
- yAxis.fixTo = [mouseY - yAxis.pos, centerYArg];
803
- }
804
-
805
- // Zoom
806
- if (howMuch !== undefined) {
807
- xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false);
808
- yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false);
809
-
810
- // Reset zoom
811
- } else {
812
- xAxis.setExtremes(undefined, undefined, false);
813
- yAxis.setExtremes(undefined, undefined, false);
814
- }
815
-
816
- // Prevent zooming until this one is finished animating
817
- /*chart.holdMapZoom = true;
818
- setTimeout(function () {
819
- chart.holdMapZoom = false;
820
- }, 200);*/
821
- /*delay = animation ? animation.duration || 500 : 0;
822
- if (delay) {
823
- chart.isMapZooming = true;
824
- setTimeout(function () {
825
- chart.isMapZooming = false;
826
- if (chart.mapZoomQueue) {
827
- chart.mapZoom.apply(chart, chart.mapZoomQueue);
828
- }
829
- chart.mapZoomQueue = null;
830
- }, delay);
831
- }*/
832
-
833
- chart.redraw();
834
- }
835
- });
836
-
837
- /**
838
- * Extend the Chart.render method to add zooming and panning
839
- */
840
- wrap(Chart.prototype, 'render', function (proceed) {
841
- var chart = this,
842
- mapNavigation = chart.options.mapNavigation;
843
-
844
- // Render the plus and minus buttons. Doing this before the shapes makes getBBox much quicker, at least in Chrome.
845
- chart.renderMapNavigation();
846
-
847
- proceed.call(chart);
848
-
849
- // Add the double click event
850
- if (pick(mapNavigation.enableDoubleClickZoom, mapNavigation.enabled) || mapNavigation.enableDoubleClickZoomTo) {
851
- addEvent(chart.container, 'dblclick', function (e) {
852
- chart.pointer.onContainerDblClick(e);
853
- });
854
- }
855
-
856
- // Add the mousewheel event
857
- if (pick(mapNavigation.enableMouseWheelZoom, mapNavigation.enabled)) {
858
- addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) {
859
- chart.pointer.onContainerMouseWheel(e);
860
- return false;
861
- });
862
- }
863
- });
864
-
865
- // Extend the Pointer
866
- extend(Pointer.prototype, {
867
-
868
- /**
869
- * The event handler for the doubleclick event
870
- */
871
- onContainerDblClick: function (e) {
872
- var chart = this.chart;
873
-
874
- e = this.normalize(e);
875
-
876
- if (chart.options.mapNavigation.enableDoubleClickZoomTo) {
877
- if (chart.pointer.inClass(e.target, 'highcharts-tracker')) {
878
- chart.hoverPoint.zoomTo();
879
- }
880
- } else if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
881
- chart.mapZoom(
882
- 0.5,
883
- chart.xAxis[0].toValue(e.chartX),
884
- chart.yAxis[0].toValue(e.chartY),
885
- e.chartX,
886
- e.chartY
887
- );
888
- }
889
- },
890
-
891
- /**
892
- * The event handler for the mouse scroll event
893
- */
894
- onContainerMouseWheel: function (e) {
895
- var chart = this.chart,
896
- delta;
897
-
898
- e = this.normalize(e);
899
-
900
- // Firefox uses e.detail, WebKit and IE uses wheelDelta
901
- delta = e.detail || -(e.wheelDelta / 120);
902
- if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
903
- chart.mapZoom(
904
- //delta > 0 ? 2 : 0.5,
905
- Math.pow(2, delta),
906
- chart.xAxis[0].toValue(e.chartX),
907
- chart.yAxis[0].toValue(e.chartY),
908
- e.chartX,
909
- e.chartY
910
- );
911
- }
912
- }
913
- });
914
-
915
- // Implement the pinchType option
916
- wrap(Pointer.prototype, 'init', function (proceed, chart, options) {
917
-
918
- proceed.call(this, chart, options);
919
-
920
- // Pinch status
921
- if (pick(options.mapNavigation.enableTouchZoom, options.mapNavigation.enabled)) {
922
- this.pinchX = this.pinchHor = this.pinchY = this.pinchVert = this.hasZoom = true;
923
- }
924
- });
925
-
926
- // Extend the pinchTranslate method to preserve fixed ratio when zooming
927
- wrap(Pointer.prototype, 'pinchTranslate', function (proceed, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
928
- var xBigger;
929
- proceed.call(this, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
930
-
931
- // Keep ratio
932
- if (this.chart.options.chart.type === 'map' && this.hasZoom) {
933
- xBigger = transform.scaleX > transform.scaleY;
934
- this.pinchTranslateDirection(
935
- !xBigger,
936
- pinchDown,
937
- touches,
938
- transform,
939
- selectionMarker,
940
- clip,
941
- lastValidTouch,
942
- xBigger ? transform.scaleX : transform.scaleY
943
- );
944
- }
945
- });
946
-
947
-
948
-
949
- /**
950
- * Extend the default options with map options
951
- */
952
- defaultPlotOptions.map = merge(defaultPlotOptions.scatter, {
953
- allAreas: true,
954
-
955
- animation: false, // makes the complex shapes slow
956
- nullColor: '#F8F8F8',
957
- borderColor: 'silver',
958
- borderWidth: 1,
959
- marker: null,
960
- stickyTracking: false,
961
- dataLabels: {
962
- formatter: function () { // #2945
963
- return this.point.value;
964
- },
965
- verticalAlign: 'middle',
966
- crop: false,
967
- overflow: false,
968
- padding: 0,
969
- style: {
970
- color: 'white',
971
- fontWeight: 'bold',
972
- HcTextStroke: '3px rgba(0,0,0,0.5)'
973
- }
974
- },
975
- turboThreshold: 0,
976
- tooltip: {
977
- followPointer: true,
978
- pointFormat: '{point.name}: {point.value}<br/>'
979
- },
980
- states: {
981
- normal: {
982
- animation: true
983
- },
984
- hover: {
985
- brightness: 0.2,
986
- halo: null
987
- }
988
- }
989
- });
990
-
991
- /**
992
- * The MapAreaPoint object
993
- */
994
- var MapAreaPoint = extendClass(Point, {
995
- /**
996
- * Extend the Point object to split paths
997
- */
998
- applyOptions: function (options, x) {
999
-
1000
- var point = Point.prototype.applyOptions.call(this, options, x),
1001
- series = this.series,
1002
- joinBy = series.joinBy,
1003
- mapPoint;
1004
-
1005
- if (series.mapData) {
1006
- mapPoint = point[joinBy[1]] !== undefined && series.mapMap[point[joinBy[1]]];
1007
- if (mapPoint) {
1008
- // This applies only to bubbles
1009
- if (series.xyFromShape) {
1010
- point.x = mapPoint._midX;
1011
- point.y = mapPoint._midY;
1012
- }
1013
- extend(point, mapPoint); // copy over properties
1014
- } else {
1015
- point.value = point.value || null;
1016
- }
1017
- }
1018
-
1019
- return point;
1020
- },
1021
-
1022
- /**
1023
- * Set the visibility of a single map area
1024
- */
1025
- setVisible: function (vis) {
1026
- var point = this,
1027
- method = vis ? 'show' : 'hide';
1028
-
1029
- // Show and hide associated elements
1030
- each(['graphic', 'dataLabel'], function (key) {
1031
- if (point[key]) {
1032
- point[key][method]();
1033
- }
1034
- });
1035
- },
1036
-
1037
- /**
1038
- * Stop the fade-out
1039
- */
1040
- onMouseOver: function (e) {
1041
- clearTimeout(this.colorInterval);
1042
- if (this.value !== null) {
1043
- Point.prototype.onMouseOver.call(this, e);
1044
- }
1045
- },
1046
- /**
1047
- * Custom animation for tweening out the colors. Animation reduces blinking when hovering
1048
- * over islands and coast lines. We run a custom implementation of animation becuase we
1049
- * need to be able to run this independently from other animations like zoom redraw. Also,
1050
- * adding color animation to the adapters would introduce almost the same amount of code.
1051
- */
1052
- onMouseOut: function () {
1053
- var point = this,
1054
- start = +new Date(),
1055
- normalColor = Color(point.color),
1056
- hoverColor = Color(point.pointAttr.hover.fill),
1057
- animation = point.series.options.states.normal.animation,
1058
- duration = animation && (animation.duration || 500),
1059
- fill;
1060
-
1061
- if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4 && point.state !== 'select') {
1062
- fill = point.pointAttr[''].fill;
1063
- delete point.pointAttr[''].fill; // avoid resetting it in Point.setState
1064
-
1065
- clearTimeout(point.colorInterval);
1066
- point.colorInterval = setInterval(function () {
1067
- var pos = (new Date() - start) / duration,
1068
- graphic = point.graphic;
1069
- if (pos > 1) {
1070
- pos = 1;
1071
- }
1072
- if (graphic) {
1073
- graphic.attr('fill', ColorAxis.prototype.tweenColors.call(0, hoverColor, normalColor, pos));
1074
- }
1075
- if (pos >= 1) {
1076
- clearTimeout(point.colorInterval);
1077
- }
1078
- }, 13);
1079
- }
1080
- Point.prototype.onMouseOut.call(point);
1081
-
1082
- if (fill) {
1083
- point.pointAttr[''].fill = fill;
1084
- }
1085
- },
1086
-
1087
- /**
1088
- * Zoom the chart to view a specific area point
1089
- */
1090
- zoomTo: function () {
1091
- var point = this,
1092
- series = point.series;
1093
-
1094
- series.xAxis.setExtremes(
1095
- point._minX,
1096
- point._maxX,
1097
- false
1098
- );
1099
- series.yAxis.setExtremes(
1100
- point._minY,
1101
- point._maxY,
1102
- false
1103
- );
1104
- series.chart.redraw();
1105
- }
1106
- });
1107
-
1108
- /**
1109
- * Add the series type
1110
- */
1111
- seriesTypes.map = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
1112
- type: 'map',
1113
- pointClass: MapAreaPoint,
1114
- supportsDrilldown: true,
1115
- getExtremesFromAll: true,
1116
- useMapGeometry: true, // get axis extremes from paths, not values
1117
- forceDL: true,
1118
- /**
1119
- * Get the bounding box of all paths in the map combined.
1120
- */
1121
- getBox: function (paths) {
1122
- var MAX_VALUE = Number.MAX_VALUE,
1123
- maxX = -MAX_VALUE,
1124
- minX = MAX_VALUE,
1125
- maxY = -MAX_VALUE,
1126
- minY = MAX_VALUE,
1127
- minRange = MAX_VALUE,
1128
- xAxis = this.xAxis,
1129
- yAxis = this.yAxis,
1130
- hasBox;
1131
-
1132
- // Find the bounding box
1133
- each(paths || [], function (point) {
1134
-
1135
- if (point.path) {
1136
- if (typeof point.path === 'string') {
1137
- point.path = Highcharts.splitPath(point.path);
1138
- }
1139
-
1140
- var path = point.path || [],
1141
- i = path.length,
1142
- even = false, // while loop reads from the end
1143
- pointMaxX = -MAX_VALUE,
1144
- pointMinX = MAX_VALUE,
1145
- pointMaxY = -MAX_VALUE,
1146
- pointMinY = MAX_VALUE,
1147
- properties = point.properties;
1148
-
1149
- // The first time a map point is used, analyze its box
1150
- if (!point._foundBox) {
1151
- while (i--) {
1152
- if (typeof path[i] === 'number' && !isNaN(path[i])) {
1153
- if (even) { // even = x
1154
- pointMaxX = Math.max(pointMaxX, path[i]);
1155
- pointMinX = Math.min(pointMinX, path[i]);
1156
- } else { // odd = Y
1157
- pointMaxY = Math.max(pointMaxY, path[i]);
1158
- pointMinY = Math.min(pointMinY, path[i]);
1159
- }
1160
- even = !even;
1161
- }
1162
- }
1163
- // Cache point bounding box for use to position data labels, bubbles etc
1164
- point._midX = pointMinX + (pointMaxX - pointMinX) *
1165
- (point.middleX || (properties && properties['hc-middle-x']) || 0.5); // pick is slower and very marginally needed
1166
- point._midY = pointMinY + (pointMaxY - pointMinY) *
1167
- (point.middleY || (properties && properties['hc-middle-y']) || 0.5);
1168
- point._maxX = pointMaxX;
1169
- point._minX = pointMinX;
1170
- point._maxY = pointMaxY;
1171
- point._minY = pointMinY;
1172
- point.labelrank = pick(point.labelrank, (pointMaxX - pointMinX) * (pointMaxY - pointMinY));
1173
- point._foundBox = true;
1174
- }
1175
-
1176
- maxX = Math.max(maxX, point._maxX);
1177
- minX = Math.min(minX, point._minX);
1178
- maxY = Math.max(maxY, point._maxY);
1179
- minY = Math.min(minY, point._minY);
1180
- minRange = Math.min(point._maxX - point._minX, point._maxY - point._minY, minRange);
1181
- hasBox = true;
1182
- }
1183
- });
1184
-
1185
- // Set the box for the whole series
1186
- if (hasBox) {
1187
- this.minY = Math.min(minY, pick(this.minY, MAX_VALUE));
1188
- this.maxY = Math.max(maxY, pick(this.maxY, -MAX_VALUE));
1189
- this.minX = Math.min(minX, pick(this.minX, MAX_VALUE));
1190
- this.maxX = Math.max(maxX, pick(this.maxX, -MAX_VALUE));
1191
-
1192
- // If no minRange option is set, set the default minimum zooming range to 5 times the
1193
- // size of the smallest element
1194
- if (xAxis && xAxis.options.minRange === undefined) {
1195
- xAxis.minRange = Math.min(5 * minRange, (this.maxX - this.minX) / 5, xAxis.minRange || MAX_VALUE);
1196
- }
1197
- if (yAxis && yAxis.options.minRange === undefined) {
1198
- yAxis.minRange = Math.min(5 * minRange, (this.maxY - this.minY) / 5, yAxis.minRange || MAX_VALUE);
1199
- }
1200
- }
1201
- },
1202
-
1203
- getExtremes: function () {
1204
- // Get the actual value extremes for colors
1205
- Series.prototype.getExtremes.call(this, this.valueData);
1206
-
1207
- // Recalculate box on updated data
1208
- if (this.chart.hasRendered && this.isDirtyData) {
1209
- this.getBox(this.options.data);
1210
- }
1211
-
1212
- this.valueMin = this.dataMin;
1213
- this.valueMax = this.dataMax;
1214
-
1215
- // Extremes for the mock Y axis
1216
- this.dataMin = this.minY;
1217
- this.dataMax = this.maxY;
1218
- },
1219
-
1220
- /**
1221
- * Translate the path so that it automatically fits into the plot area box
1222
- * @param {Object} path
1223
- */
1224
- translatePath: function (path) {
1225
-
1226
- var series = this,
1227
- even = false, // while loop reads from the end
1228
- xAxis = series.xAxis,
1229
- yAxis = series.yAxis,
1230
- xMin = xAxis.min,
1231
- xTransA = xAxis.transA,
1232
- xMinPixelPadding = xAxis.minPixelPadding,
1233
- yMin = yAxis.min,
1234
- yTransA = yAxis.transA,
1235
- yMinPixelPadding = yAxis.minPixelPadding,
1236
- i,
1237
- ret = []; // Preserve the original
1238
-
1239
- // Do the translation
1240
- if (path) {
1241
- i = path.length;
1242
- while (i--) {
1243
- if (typeof path[i] === 'number') {
1244
- ret[i] = even ?
1245
- (path[i] - xMin) * xTransA + xMinPixelPadding :
1246
- (path[i] - yMin) * yTransA + yMinPixelPadding;
1247
- even = !even;
1248
- } else {
1249
- ret[i] = path[i];
1250
- }
1251
- }
1252
- }
1253
-
1254
- return ret;
1255
- },
1256
-
1257
- /**
1258
- * Extend setData to join in mapData. If the allAreas option is true, all areas
1259
- * from the mapData are used, and those that don't correspond to a data value
1260
- * are given null values.
1261
- */
1262
- setData: function (data, redraw) {
1263
- var options = this.options,
1264
- mapData = options.mapData,
1265
- joinBy = options.joinBy,
1266
- joinByNull = joinBy === null,
1267
- dataUsed = [],
1268
- mapPoint,
1269
- props,
1270
- i;
1271
-
1272
- if (joinByNull) {
1273
- joinBy = '_i';
1274
- }
1275
- joinBy = this.joinBy = Highcharts.splat(joinBy);
1276
- if (!joinBy[1]) {
1277
- joinBy[1] = joinBy[0];
1278
- }
1279
-
1280
- // Pick up numeric values, add index
1281
- if (data) {
1282
- each(data, function (val, i) {
1283
- if (typeof val === 'number') {
1284
- data[i] = {
1285
- value: val
1286
- };
1287
- }
1288
- if (joinByNull) {
1289
- data[i]._i = i;
1290
- }
1291
- });
1292
- }
1293
-
1294
- this.getBox(data);
1295
- if (mapData) {
1296
- if (mapData.type === 'FeatureCollection') {
1297
- mapData = Highcharts.geojson(mapData, this.type, this);
1298
- }
1299
-
1300
- this.getBox(mapData);
1301
- this.mapData = mapData;
1302
- this.mapMap = {};
1303
-
1304
- for (i = 0; i < mapData.length; i++) {
1305
- mapPoint = mapData[i];
1306
- props = mapPoint.properties;
1307
-
1308
- mapPoint._i = i;
1309
- // Copy the property over to root for faster access
1310
- if (joinBy[0] && props && props[joinBy[0]]) {
1311
- mapPoint[joinBy[0]] = props[joinBy[0]];
1312
- }
1313
- this.mapMap[mapPoint[joinBy[0]]] = mapPoint;
1314
- }
1315
-
1316
- if (options.allAreas) {
1317
-
1318
- data = data || [];
1319
-
1320
- // Registered the point codes that actually hold data
1321
- if (joinBy[1]) {
1322
- each(data, function (point) {
1323
- dataUsed.push(point[joinBy[1]]);
1324
- });
1325
- }
1326
-
1327
- // Add those map points that don't correspond to data, which will be drawn as null points
1328
- dataUsed = '|' + dataUsed.join('|') + '|'; // String search is faster than array.indexOf
1329
-
1330
- each(mapData, function (mapPoint) {
1331
- if (!joinBy[0] || dataUsed.indexOf('|' + mapPoint[joinBy[0]] + '|') === -1) {
1332
- data.push(merge(mapPoint, { value: null }));
1333
- }
1334
- });
1335
- }
1336
- }
1337
- Series.prototype.setData.call(this, data, redraw);
1338
- },
1339
-
1340
-
1341
- /**
1342
- * No graph for the map series
1343
- */
1344
- drawGraph: noop,
1345
-
1346
- /**
1347
- * We need the points' bounding boxes in order to draw the data labels, so
1348
- * we skip it now and call it from drawPoints instead.
1349
- */
1350
- drawDataLabels: noop,
1351
-
1352
- /**
1353
- * Allow a quick redraw by just translating the area group. Used for zooming and panning
1354
- * in capable browsers.
1355
- */
1356
- doFullTranslate: function () {
1357
- return this.isDirtyData || this.chart.renderer.isVML || !this.baseTrans;
1358
- },
1359
-
1360
- /**
1361
- * Add the path option for data points. Find the max value for color calculation.
1362
- */
1363
- translate: function () {
1364
- var series = this,
1365
- xAxis = series.xAxis,
1366
- yAxis = series.yAxis,
1367
- doFullTranslate = series.doFullTranslate();
1368
-
1369
- series.generatePoints();
1370
-
1371
- each(series.data, function (point) {
1372
-
1373
- // Record the middle point (loosely based on centroid), determined
1374
- // by the middleX and middleY options.
1375
- point.plotX = xAxis.toPixels(point._midX, true);
1376
- point.plotY = yAxis.toPixels(point._midY, true);
1377
-
1378
- if (doFullTranslate) {
1379
-
1380
- point.shapeType = 'path';
1381
- point.shapeArgs = {
1382
- //d: display ? series.translatePath(point.path) : ''
1383
- d: series.translatePath(point.path),
1384
- 'vector-effect': 'non-scaling-stroke'
1385
- };
1386
- }
1387
- });
1388
-
1389
- series.translateColors();
1390
- },
1391
-
1392
- /**
1393
- * Use the drawPoints method of column, that is able to handle simple shapeArgs.
1394
- * Extend it by assigning the tooltip position.
1395
- */
1396
- drawPoints: function () {
1397
- var series = this,
1398
- xAxis = series.xAxis,
1399
- yAxis = series.yAxis,
1400
- group = series.group,
1401
- chart = series.chart,
1402
- renderer = chart.renderer,
1403
- scaleX,
1404
- scaleY,
1405
- translateX,
1406
- translateY,
1407
- baseTrans = this.baseTrans;
1408
-
1409
- // Set a group that handles transform during zooming and panning in order to preserve clipping
1410
- // on series.group
1411
- if (!series.transformGroup) {
1412
- series.transformGroup = renderer.g()
1413
- .attr({
1414
- scaleX: 1,
1415
- scaleY: 1
1416
- })
1417
- .add(group);
1418
- }
1419
-
1420
- // Draw the shapes again
1421
- if (series.doFullTranslate()) {
1422
-
1423
- // Individual point actions
1424
- if (chart.hasRendered && series.pointAttrToOptions.fill === 'color') {
1425
- each(series.points, function (point) {
1426
-
1427
- // Reset color on update/redraw
1428
- if (point.graphic) {
1429
- point.graphic.attr('fill', point.color);
1430
- }
1431
-
1432
- });
1433
- }
1434
-
1435
- // Draw them in transformGroup
1436
- series.group = series.transformGroup;
1437
- seriesTypes.column.prototype.drawPoints.apply(series);
1438
- series.group = group; // Reset
1439
-
1440
- // Add class names
1441
- each(series.points, function (point) {
1442
- if (point.graphic) {
1443
- if (point.name) {
1444
- point.graphic.addClass('highcharts-name-' + point.name.replace(' ', '-').toLowerCase());
1445
- }
1446
- if (point.properties && point.properties['hc-key']) {
1447
- point.graphic.addClass('highcharts-key-' + point.properties['hc-key'].toLowerCase());
1448
- }
1449
- }
1450
- });
1451
-
1452
- // Set the base for later scale-zooming. The originX and originY properties are the
1453
- // axis values in the plot area's upper left corner.
1454
- this.baseTrans = {
1455
- originX: xAxis.min - xAxis.minPixelPadding / xAxis.transA,
1456
- originY: yAxis.min - yAxis.minPixelPadding / yAxis.transA + (yAxis.reversed ? 0 : yAxis.len / yAxis.transA),
1457
- transAX: xAxis.transA,
1458
- transAY: yAxis.transA
1459
- };
1460
-
1461
- // Just update the scale and transform for better performance
1462
- } else {
1463
- scaleX = xAxis.transA / baseTrans.transAX;
1464
- scaleY = yAxis.transA / baseTrans.transAY;
1465
- if (scaleX > 0.99 && scaleX < 1.01 && scaleY > 0.99 && scaleY < 1.01) { // rounding errors
1466
- translateX = 0;
1467
- translateY = 0;
1468
- scaleX = 1;
1469
- scaleY = 1;
1470
-
1471
- } else {
1472
- translateX = xAxis.toPixels(baseTrans.originX, true);
1473
- translateY = yAxis.toPixels(baseTrans.originY, true);
1474
- }
1475
-
1476
- this.transformGroup.animate({
1477
- translateX: translateX,
1478
- translateY: translateY,
1479
- scaleX: scaleX,
1480
- scaleY: scaleY
1481
- });
1482
-
1483
- }
1484
-
1485
- this.drawMapDataLabels();
1486
-
1487
-
1488
- },
1489
-
1490
- /**
1491
- * Draw the data labels. Special for maps is the time that the data labels are drawn (after points),
1492
- * and the clipping of the dataLabelsGroup.
1493
- */
1494
- drawMapDataLabels: function () {
1495
-
1496
- Series.prototype.drawDataLabels.call(this);
1497
- if (this.dataLabelsGroup) {
1498
- this.dataLabelsGroup.clip(this.chart.clipRect);
1499
- }
1500
-
1501
- this.hideOverlappingDataLabels();
1502
- },
1503
-
1504
- /**
1505
- * Hide overlapping labels. Labels are moved and faded in and out on zoom to provide a smooth
1506
- * visual imression.
1507
- */
1508
- hideOverlappingDataLabels: function () {
1509
-
1510
- var points = this.points,
1511
- len = points.length,
1512
- i,
1513
- j,
1514
- label1,
1515
- label2,
1516
- intersectRect = function (pos1, pos2, size1, size2) {
1517
- return !(
1518
- pos2.x > pos1.x + size1.width ||
1519
- pos2.x + size2.width < pos1.x ||
1520
- pos2.y > pos1.y + size1.height ||
1521
- pos2.y + size2.height < pos1.y
1522
- );
1523
- };
1524
-
1525
- // Mark with initial opacity
1526
- each(points, function (point, label) {
1527
- label = point.dataLabel;
1528
- if (label) {
1529
- label.oldOpacity = label.opacity;
1530
- label.newOpacity = 1;
1531
- }
1532
- });
1533
-
1534
- // Detect overlapping labels
1535
- for (i = 0; i < len - 1; ++i) {
1536
- label1 = points[i].dataLabel;
1537
-
1538
- for (j = i + 1; j < len; ++j) {
1539
- label2 = points[j].dataLabel;
1540
- if (label1 && label2 && label1.newOpacity !== 0 && label2.newOpacity !== 0 &&
1541
- intersectRect(label1.alignAttr, label2.alignAttr, label1, label2)) {
1542
- (points[i].labelrank < points[j].labelrank ? label1 : label2).newOpacity = 0;
1543
- }
1544
- }
1545
- }
1546
-
1547
- // Hide or show
1548
- each(points, function (point, label) {
1549
- label = point.dataLabel;
1550
- if (label) {
1551
- if (label.oldOpacity !== label.newOpacity) {
1552
- label[label.isOld ? 'animate' : 'attr'](extend({ opacity: label.newOpacity }, label.alignAttr));
1553
- }
1554
- label.isOld = true;
1555
- }
1556
- });
1557
- },
1558
-
1559
- /**
1560
- * Override render to throw in an async call in IE8. Otherwise it chokes on the US counties demo.
1561
- */
1562
- render: function () {
1563
- var series = this,
1564
- render = Series.prototype.render;
1565
-
1566
- // Give IE8 some time to breathe.
1567
- if (series.chart.renderer.isVML && series.data.length > 3000) {
1568
- setTimeout(function () {
1569
- render.call(series);
1570
- });
1571
- } else {
1572
- render.call(series);
1573
- }
1574
- },
1575
-
1576
- /**
1577
- * The initial animation for the map series. By default, animation is disabled.
1578
- * Animation of map shapes is not at all supported in VML browsers.
1579
- */
1580
- animate: function (init) {
1581
- var chart = this.chart,
1582
- animation = this.options.animation,
1583
- group = this.group,
1584
- xAxis = this.xAxis,
1585
- yAxis = this.yAxis,
1586
- left = xAxis.pos,
1587
- top = yAxis.pos;
1588
-
1589
- if (chart.renderer.isSVG) {
1590
-
1591
- if (animation === true) {
1592
- animation = {
1593
- duration: 1000
1594
- };
1595
- }
1596
-
1597
- // Initialize the animation
1598
- if (init) {
1599
-
1600
- // Scale down the group and place it in the center
1601
- group.attr({
1602
- translateX: left + xAxis.len / 2,
1603
- translateY: top + yAxis.len / 2,
1604
- scaleX: 0.001, // #1499
1605
- scaleY: 0.001
1606
- });
1607
-
1608
- // Run the animation
1609
- } else {
1610
- group.animate({
1611
- translateX: left,
1612
- translateY: top,
1613
- scaleX: 1,
1614
- scaleY: 1
1615
- }, animation);
1616
-
1617
- // Delete this function to allow it only once
1618
- this.animate = null;
1619
- }
1620
- }
1621
- },
1622
-
1623
- /**
1624
- * Animate in the new series from the clicked point in the old series.
1625
- * Depends on the drilldown.js module
1626
- */
1627
- animateDrilldown: function (init) {
1628
- var toBox = this.chart.plotBox,
1629
- level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1],
1630
- fromBox = level.bBox,
1631
- animationOptions = this.chart.options.drilldown.animation,
1632
- scale;
1633
-
1634
- if (!init) {
1635
-
1636
- scale = Math.min(fromBox.width / toBox.width, fromBox.height / toBox.height);
1637
- level.shapeArgs = {
1638
- scaleX: scale,
1639
- scaleY: scale,
1640
- translateX: fromBox.x,
1641
- translateY: fromBox.y
1642
- };
1643
-
1644
- // TODO: Animate this.group instead
1645
- each(this.points, function (point) {
1646
-
1647
- point.graphic
1648
- .attr(level.shapeArgs)
1649
- .animate({
1650
- scaleX: 1,
1651
- scaleY: 1,
1652
- translateX: 0,
1653
- translateY: 0
1654
- }, animationOptions);
1655
-
1656
- });
1657
-
1658
- this.animate = null;
1659
- }
1660
-
1661
- },
1662
-
1663
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
1664
-
1665
- /**
1666
- * When drilling up, pull out the individual point graphics from the lower series
1667
- * and animate them into the origin point in the upper series.
1668
- */
1669
- animateDrillupFrom: function (level) {
1670
- seriesTypes.column.prototype.animateDrillupFrom.call(this, level);
1671
- },
1672
-
1673
-
1674
- /**
1675
- * When drilling up, keep the upper series invisible until the lower series has
1676
- * moved into place
1677
- */
1678
- animateDrillupTo: function (init) {
1679
- seriesTypes.column.prototype.animateDrillupTo.call(this, init);
1680
- }
1681
- }));
1682
-
1683
-
1684
- // The mapline series type
1685
- defaultPlotOptions.mapline = merge(defaultPlotOptions.map, {
1686
- lineWidth: 1,
1687
- fillColor: 'none'
1688
- });
1689
- seriesTypes.mapline = extendClass(seriesTypes.map, {
1690
- type: 'mapline',
1691
- pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
1692
- stroke: 'color',
1693
- 'stroke-width': 'lineWidth',
1694
- fill: 'fillColor',
1695
- dashstyle: 'dashStyle'
1696
- },
1697
- drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol
1698
- });
1699
-
1700
- // The mappoint series type
1701
- defaultPlotOptions.mappoint = merge(defaultPlotOptions.scatter, {
1702
- dataLabels: {
1703
- enabled: true,
1704
- formatter: function () { // #2945
1705
- return this.point.name;
1706
- },
1707
- color: 'black',
1708
- crop: false,
1709
- defer: false,
1710
- overflow: false,
1711
- style: {
1712
- HcTextStroke: '3px rgba(255,255,255,0.5)'
1713
- }
1714
- }
1715
- });
1716
- seriesTypes.mappoint = extendClass(seriesTypes.scatter, {
1717
- type: 'mappoint',
1718
- forceDL: true
1719
- });
1720
-
1721
- // The mapbubble series type
1722
- if (seriesTypes.bubble) {
1723
-
1724
- defaultPlotOptions.mapbubble = merge(defaultPlotOptions.bubble, {
1725
- animationLimit: 500,
1726
- tooltip: {
1727
- pointFormat: '{point.name}: {point.z}'
1728
- }
1729
- });
1730
- seriesTypes.mapbubble = extendClass(seriesTypes.bubble, {
1731
- pointClass: extendClass(Point, {
1732
- applyOptions: MapAreaPoint.prototype.applyOptions
1733
- }),
1734
- xyFromShape: true,
1735
- type: 'mapbubble',
1736
- pointArrayMap: ['z'], // If one single value is passed, it is interpreted as z
1737
- /**
1738
- * Return the map area identified by the dataJoinBy option
1739
- */
1740
- getMapData: seriesTypes.map.prototype.getMapData,
1741
- getBox: seriesTypes.map.prototype.getBox,
1742
- setData: seriesTypes.map.prototype.setData
1743
- });
1744
- }
1745
-
1746
- /**
1747
- * Extend the default options with map options
1748
- */
1749
- defaultOptions.plotOptions.heatmap = merge(defaultOptions.plotOptions.scatter, {
1750
- animation: false,
1751
- borderWidth: 0,
1752
- nullColor: '#F8F8F8',
1753
- dataLabels: {
1754
- formatter: function () { // #2945
1755
- return this.point.value;
1756
- },
1757
- verticalAlign: 'middle',
1758
- crop: false,
1759
- overflow: false,
1760
- style: {
1761
- color: 'white',
1762
- fontWeight: 'bold',
1763
- HcTextStroke: '1px rgba(0,0,0,0.5)'
1764
- }
1765
- },
1766
- marker: null,
1767
- tooltip: {
1768
- pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
1769
- },
1770
- states: {
1771
- normal: {
1772
- animation: true
1773
- },
1774
- hover: {
1775
- brightness: 0.2
1776
- }
1777
- }
1778
- });
1779
-
1780
- // The Heatmap series type
1781
- seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
1782
- type: 'heatmap',
1783
- pointArrayMap: ['y', 'value'],
1784
- hasPointSpecificOptions: true,
1785
- supportsDrilldown: true,
1786
- getExtremesFromAll: true,
1787
- init: function () {
1788
- seriesTypes.scatter.prototype.init.apply(this, arguments);
1789
- this.pointRange = this.options.colsize || 1;
1790
- this.yAxis.axisPointRange = this.options.rowsize || 1; // general point range
1791
- },
1792
- translate: function () {
1793
- var series = this,
1794
- options = series.options,
1795
- xAxis = series.xAxis,
1796
- yAxis = series.yAxis;
1797
-
1798
- series.generatePoints();
1799
-
1800
- each(series.points, function (point) {
1801
- var xPad = (options.colsize || 1) / 2,
1802
- yPad = (options.rowsize || 1) / 2,
1803
- x1 = Math.round(xAxis.len - xAxis.translate(point.x - xPad, 0, 1, 0, 1)),
1804
- x2 = Math.round(xAxis.len - xAxis.translate(point.x + xPad, 0, 1, 0, 1)),
1805
- y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
1806
- y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1));
1807
-
1808
- // Set plotX and plotY for use in K-D-Tree and more
1809
- point.plotX = (x1 + x2) / 2;
1810
- point.plotY = (y1 + y2) / 2;
1811
-
1812
- point.shapeType = 'rect';
1813
- point.shapeArgs = {
1814
- x: Math.min(x1, x2),
1815
- y: Math.min(y1, y2),
1816
- width: Math.abs(x2 - x1),
1817
- height: Math.abs(y2 - y1)
1818
- };
1819
- });
1820
-
1821
- series.translateColors();
1822
-
1823
- // Make sure colors are updated on colorAxis update (#2893)
1824
- if (this.chart.hasRendered) {
1825
- each(series.points, function (point) {
1826
- point.shapeArgs.fill = point.options.color || point.color; // #3311
1827
- });
1828
- }
1829
- },
1830
- drawPoints: seriesTypes.column.prototype.drawPoints,
1831
- animate: noop,
1832
- getBox: noop,
1833
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
1834
-
1835
- getExtremes: function () {
1836
- // Get the extremes from the value data
1837
- Series.prototype.getExtremes.call(this, this.valueData);
1838
- this.valueMin = this.dataMin;
1839
- this.valueMax = this.dataMax;
1840
-
1841
- // Get the extremes from the y data
1842
- Series.prototype.getExtremes.call(this);
1843
- }
1844
-
1845
- }));
1846
-
1847
-
1848
- /**
1849
- * Convert a geojson object to map data of a given Highcharts type (map, mappoint or mapline).
1850
- */
1851
- Highcharts.geojson = function (geojson, hType, series) {
1852
- var mapData = [],
1853
- path = [],
1854
- polygonToPath = function (polygon) {
1855
- var i = 0,
1856
- len = polygon.length;
1857
- path.push('M');
1858
- for (; i < len; i++) {
1859
- if (i === 1) {
1860
- path.push('L');
1861
- }
1862
- path.push(polygon[i][0], -polygon[i][1]);
1863
- }
1864
- };
1865
-
1866
- hType = hType || 'map';
1867
-
1868
- each(geojson.features, function (feature) {
1869
-
1870
- var geometry = feature.geometry,
1871
- type = geometry.type,
1872
- coordinates = geometry.coordinates,
1873
- properties = feature.properties,
1874
- point;
1875
-
1876
- path = [];
1877
-
1878
- if (hType === 'map' || hType === 'mapbubble') {
1879
- if (type === 'Polygon') {
1880
- each(coordinates, polygonToPath);
1881
- path.push('Z');
1882
-
1883
- } else if (type === 'MultiPolygon') {
1884
- each(coordinates, function (items) {
1885
- each(items, polygonToPath);
1886
- });
1887
- path.push('Z');
1888
- }
1889
-
1890
- if (path.length) {
1891
- point = { path: path };
1892
- }
1893
-
1894
- } else if (hType === 'mapline') {
1895
- if (type === 'LineString') {
1896
- polygonToPath(coordinates);
1897
- } else if (type === 'MultiLineString') {
1898
- each(coordinates, polygonToPath);
1899
- }
1900
-
1901
- if (path.length) {
1902
- point = { path: path };
1903
- }
1904
-
1905
- } else if (hType === 'mappoint') {
1906
- if (type === 'Point') {
1907
- point = {
1908
- x: coordinates[0],
1909
- y: -coordinates[1]
1910
- };
1911
- }
1912
- }
1913
- if (point) {
1914
- mapData.push(extend(point, {
1915
- name: properties.name || properties.NAME,
1916
- properties: properties
1917
- }));
1918
- }
1919
-
1920
- });
1921
-
1922
- // Create a credits text that includes map source, to be picked up in Chart.showCredits
1923
- if (series) {
1924
- series.chart.mapCredits = '<a href="http://www.highcharts.com">Highcharts</a> \u00A9 ' +
1925
- '<a href="' + geojson.copyrightUrl + '">' + geojson.copyrightShort + '</a>';
1926
- }
1927
-
1928
- return mapData;
1929
- };
1930
-
1931
- /**
1932
- * Override showCredits to include map source by default
1933
- */
1934
- wrap(Chart.prototype, 'showCredits', function (proceed, credits) {
1935
-
1936
- if (defaultOptions.credits.text === this.options.credits.text && this.mapCredits) { // default text and mapCredits is set
1937
- credits.text = this.mapCredits;
1938
- credits.href = null;
1939
- }
1940
-
1941
- proceed.call(this, credits);
1942
- });
1943
-
1944
- // Add language
1945
- extend(defaultOptions.lang, {
1946
- zoomIn: 'Zoom in',
1947
- zoomOut: 'Zoom out'
1948
- });
1949
-
1950
-
1951
- // Set the default map navigation options
1952
- defaultOptions.mapNavigation = {
1953
- buttonOptions: {
1954
- alignTo: 'plotBox',
1955
- align: 'left',
1956
- verticalAlign: 'top',
1957
- x: 0,
1958
- width: 18,
1959
- height: 18,
1960
- style: {
1961
- fontSize: '15px',
1962
- fontWeight: 'bold',
1963
- textAlign: 'center'
1964
- },
1965
- theme: {
1966
- 'stroke-width': 1
1967
- }
1968
- },
1969
- buttons: {
1970
- zoomIn: {
1971
- onclick: function () {
1972
- this.mapZoom(0.5);
1973
- },
1974
- text: '+',
1975
- y: 0
1976
- },
1977
- zoomOut: {
1978
- onclick: function () {
1979
- this.mapZoom(2);
1980
- },
1981
- text: '-',
1982
- y: 28
1983
- }
1984
- }
1985
- // enabled: false,
1986
- // enableButtons: null, // inherit from enabled
1987
- // enableTouchZoom: null, // inherit from enabled
1988
- // enableDoubleClickZoom: null, // inherit from enabled
1989
- // enableDoubleClickZoomTo: false
1990
- // enableMouseWheelZoom: null, // inherit from enabled
1991
- };
1992
-
1993
- /**
1994
- * Utility for reading SVG paths directly.
1995
- */
1996
- Highcharts.splitPath = function (path) {
1997
- var i;
1998
-
1999
- // Move letters apart
2000
- path = path.replace(/([A-Za-z])/g, ' $1 ');
2001
- // Trim
2002
- path = path.replace(/^\s*/, "").replace(/\s*$/, "");
2003
-
2004
- // Split on spaces and commas
2005
- path = path.split(/[ ,]+/);
2006
-
2007
- // Parse numbers
2008
- for (i = 0; i < path.length; i++) {
2009
- if (!/[a-zA-Z]/.test(path[i])) {
2010
- path[i] = parseFloat(path[i]);
2011
- }
2012
- }
2013
- return path;
2014
- };
2015
-
2016
- // A placeholder for map definitions
2017
- Highcharts.maps = {};
2018
-
2019
-
2020
-
2021
-
2022
-
2023
- // Create symbols for the zoom buttons
2024
- function selectiveRoundedRect(attr, x, y, w, h, rTopLeft, rTopRight, rBottomRight, rBottomLeft) {
2025
- var normalize = (attr['stroke-width'] % 2 / 2);
2026
-
2027
- x -= normalize;
2028
- y -= normalize;
2029
-
2030
- return ['M', x + rTopLeft, y,
2031
- // top side
2032
- 'L', x + w - rTopRight, y,
2033
- // top right corner
2034
- 'C', x + w - rTopRight / 2, y, x + w, y + rTopRight / 2, x + w, y + rTopRight,
2035
- // right side
2036
- 'L', x + w, y + h - rBottomRight,
2037
- // bottom right corner
2038
- 'C', x + w, y + h - rBottomRight / 2, x + w - rBottomRight / 2, y + h, x + w - rBottomRight, y + h,
2039
- // bottom side
2040
- 'L', x + rBottomLeft, y + h,
2041
- // bottom left corner
2042
- 'C', x + rBottomLeft / 2, y + h, x, y + h - rBottomLeft / 2, x, y + h - rBottomLeft,
2043
- // left side
2044
- 'L', x, y + rTopLeft,
2045
- // top left corner
2046
- 'C', x, y + rTopLeft / 2, x + rTopLeft / 2, y, x + rTopLeft, y,
2047
- 'Z'
2048
- ];
2049
- }
2050
- SVGRenderer.prototype.symbols.topbutton = function (x, y, w, h, attr) {
2051
- return selectiveRoundedRect(attr, x, y, w, h, attr.r, attr.r, 0, 0);
2052
- };
2053
- SVGRenderer.prototype.symbols.bottombutton = function (x, y, w, h, attr) {
2054
- return selectiveRoundedRect(attr, x, y, w, h, 0, 0, attr.r, attr.r);
2055
- };
2056
- // The symbol callbacks are generated on the SVGRenderer object in all browsers. Even
2057
- // VML browsers need this in order to generate shapes in export. Now share
2058
- // them with the VMLRenderer.
2059
- if (Renderer === VMLRenderer) {
2060
- each(['topbutton', 'bottombutton'], function (shape) {
2061
- VMLRenderer.prototype.symbols[shape] = SVGRenderer.prototype.symbols[shape];
2062
- });
2063
- }
2064
-
2065
-
2066
- /**
2067
- * A wrapper for Chart with all the default values for a Map
2068
- */
2069
- Highcharts.Map = function (options, callback) {
2070
-
2071
- var hiddenAxis = {
2072
- endOnTick: false,
2073
- gridLineWidth: 0,
2074
- lineWidth: 0,
2075
- minPadding: 0,
2076
- maxPadding: 0,
2077
- startOnTick: false,
2078
- title: null,
2079
- tickPositions: []
2080
- },
2081
- seriesOptions;
2082
-
2083
- /* For visual testing
2084
- hiddenAxis.gridLineWidth = 1;
2085
- hiddenAxis.gridZIndex = 10;
2086
- hiddenAxis.tickPositions = undefined;
2087
- // */
2088
-
2089
- // Don't merge the data
2090
- seriesOptions = options.series;
2091
- options.series = null;
2092
-
2093
- options = merge({
2094
- chart: {
2095
- panning: 'xy',
2096
- type: 'map'
2097
- },
2098
- xAxis: hiddenAxis,
2099
- yAxis: merge(hiddenAxis, { reversed: true })
2100
- },
2101
- options, // user's options
2102
-
2103
- { // forced options
2104
- chart: {
2105
- inverted: false,
2106
- alignTicks: false,
2107
- preserveAspectRatio: true
2108
- }
2109
- });
2110
-
2111
- options.series = seriesOptions;
2112
-
2113
-
2114
- return new Chart(options, callback);
2115
- };
2116
-
2117
- }(Highcharts));