highcharts-rails 4.2.5 → 4.2.6

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.
@@ -2,7 +2,7 @@
2
2
  // @compilation_level SIMPLE_OPTIMIZATIONS
3
3
 
4
4
  /**
5
- * @license Highcharts JS v4.2.5 (2016-05-06)
5
+ * @license Highcharts JS v4.2.6 (2016-08-02)
6
6
  *
7
7
  * (c) 2009-2016 Torstein Honsi
8
8
  *
@@ -364,6 +364,7 @@ var arrayMin = Highcharts.arrayMin,
364
364
  options.innerRadius,
365
365
  pick(options.thickness, 10)
366
366
  ],
367
+ offset = Math.min(this.offset, 0),
367
368
  percentRegex = /%$/,
368
369
  start,
369
370
  end,
@@ -406,6 +407,8 @@ var arrayMin = Highcharts.arrayMin,
406
407
  end = startAngleRad + this.translate(to);
407
408
  }
408
409
 
410
+ radii[0] -= offset; // #5283
411
+ radii[2] -= offset; // #5283
409
412
 
410
413
  ret = this.chart.renderer.symbols.arc(
411
414
  this.left + center[0],
@@ -822,7 +825,15 @@ var arrayMin = Highcharts.arrayMin,
822
825
  if (!this.chart.polar && higherAreaPath[0] === 'M') {
823
826
  higherAreaPath[0] = 'L'; // this probably doesn't work for spline
824
827
  }
828
+
829
+ this.graphPath = linePath;
825
830
  this.areaPath = this.areaPath.concat(lowerPath, higherAreaPath);
831
+
832
+ // Prepare for sideways animation
833
+ linePath.isArea = true;
834
+ linePath.xMap = lowerPath.xMap;
835
+ this.areaPath.xMap = lowerPath.xMap;
836
+
826
837
  return linePath;
827
838
  },
828
839
 
@@ -1120,6 +1131,7 @@ var arrayMin = Highcharts.arrayMin,
1120
1131
  drawGraph: noop,
1121
1132
  fixedBox: true,
1122
1133
  forceDL: true,
1134
+ noSharedTooltip: true,
1123
1135
  trackerGroups: ['group', 'dataLabelsGroup'],
1124
1136
 
1125
1137
  /**
@@ -1802,7 +1814,7 @@ var arrayMin = Highcharts.arrayMin,
1802
1814
  options = series.options,
1803
1815
  stateOptions = options.states,
1804
1816
  upColor = options.upColor || series.color,
1805
- hoverColor = Highcharts.Color(upColor).brighten(0.1).get(),
1817
+ hoverColor = Highcharts.Color(upColor).brighten(options.states.hover.brightness).get(),
1806
1818
  seriesDownPointAttr = merge(series.pointAttr),
1807
1819
  upColorProp = series.upColorProp;
1808
1820
 
@@ -1880,8 +1892,19 @@ var arrayMin = Highcharts.arrayMin,
1880
1892
  */
1881
1893
  defaultPlotOptions.polygon = merge(defaultPlotOptions.scatter, {
1882
1894
  marker: {
1883
- enabled: false
1884
- }
1895
+ enabled: false,
1896
+ states: {
1897
+ hover: {
1898
+ enabled: false
1899
+ }
1900
+ }
1901
+ },
1902
+ stickyTracking: false,
1903
+ tooltip: {
1904
+ followPointer: true,
1905
+ pointFormat: ''
1906
+ },
1907
+ trackByArea: true
1885
1908
  });
1886
1909
 
1887
1910
  /**
@@ -1889,13 +1912,28 @@ var arrayMin = Highcharts.arrayMin,
1889
1912
  */
1890
1913
  seriesTypes.polygon = extendClass(seriesTypes.scatter, {
1891
1914
  type: 'polygon',
1892
- fillGraph: true,
1893
- // Close all segments
1894
- getSegmentPath: function (segment) {
1895
- return Series.prototype.getSegmentPath.call(this, segment).concat('z');
1915
+ getGraphPath: function () {
1916
+
1917
+ var graphPath = Series.prototype.getGraphPath.call(this),
1918
+ i = graphPath.length + 1;
1919
+
1920
+ // Close all segments
1921
+ while (i--) {
1922
+ if (i === graphPath.length || (graphPath[i] === 'M' && i > 0)) {
1923
+ graphPath.splice(i, 0, 'z');
1924
+ }
1925
+ }
1926
+
1927
+ this.areaPath = graphPath;
1928
+ return graphPath;
1929
+ },
1930
+ drawGraph: function () {
1931
+ this.options.fillColor = this.color; // Hack into the fill logic in area.drawGraph
1932
+ seriesTypes.area.prototype.drawGraph.call(this);
1896
1933
  },
1897
- drawGraph: Series.prototype.drawGraph,
1898
- drawLegendSymbol: Highcharts.LegendSymbolMixin.drawRectangle
1934
+ drawLegendSymbol: Highcharts.LegendSymbolMixin.drawRectangle,
1935
+ drawTracker: Series.prototype.drawTracker,
1936
+ setStackedPoints: noop // No stacking points on polygons (#5310)
1899
1937
  });
1900
1938
  /* ****************************************************************************
1901
1939
  * Start Bubble series code *
@@ -2456,15 +2494,24 @@ var arrayMin = Highcharts.arrayMin,
2456
2494
  * line-like series.
2457
2495
  */
2458
2496
  wrap(seriesProto, 'getGraphPath', function (proceed, points) {
2459
- var series = this;
2497
+ var series = this,
2498
+ i,
2499
+ firstValid;
2460
2500
 
2461
2501
  // Connect the path
2462
2502
  if (this.chart.polar) {
2463
2503
  points = points || this.points;
2464
2504
 
2465
- if (this.options.connectEnds !== false && points[0] && points[0].y !== null) {
2505
+ // Append first valid point in order to connect the ends
2506
+ for (i = 0; i < points.length; i++) {
2507
+ if (!points[i].isNull) {
2508
+ firstValid = i;
2509
+ break;
2510
+ }
2511
+ }
2512
+ if (this.options.connectEnds !== false && firstValid !== undefined) {
2466
2513
  this.connectEnds = true; // re-used in splines
2467
- points.splice(points.length, 0, points[0]);
2514
+ points.splice(points.length, 0, points[firstValid]);
2468
2515
  }
2469
2516
 
2470
2517
  // For area charts, pseudo points are added to the graph, now we need to translate these
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Highcharts JS v4.2.5 (2016-05-06)
2
+ * Highcharts JS v4.2.6 (2016-08-02)
3
3
  * Highcharts Broken Axis module
4
4
  *
5
5
  * License: www.highcharts.com/license
@@ -2908,7 +2908,7 @@ if (CanvasRenderingContext2D) {
2908
2908
  });
2909
2909
  }
2910
2910
  }/**
2911
- * @license Highcharts JS v4.2.5 (2016-05-06)
2911
+ * @license Highcharts JS v4.2.6 (2016-08-02)
2912
2912
  * CanVGRenderer Extension module
2913
2913
  *
2914
2914
  * (c) 2011-2016 Torstein Honsi, Erik Olsson
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.5 (2016-05-06)
2
+ * @license Highcharts JS v4.2.6 (2016-08-02)
3
3
  * Data module
4
4
  *
5
5
  * (c) 2012-2016 Torstein Honsi
@@ -327,6 +327,16 @@
327
327
  columns[gc - startColumn][gr - startRow] = cell.content.$t;
328
328
  }
329
329
  }
330
+
331
+ // Insert null for empty spreadsheet cells (#5298)
332
+ each(columns, function (column) {
333
+ for (i = 0; i < column.length; i++) {
334
+ if (column[i] === undefined) {
335
+ column[i] = null;
336
+ }
337
+ }
338
+ });
339
+
330
340
  self.dataFound();
331
341
  }
332
342
  });
@@ -21,6 +21,7 @@
21
21
  each = H.each,
22
22
  extend = H.extend,
23
23
  format = H.format,
24
+ merge = H.merge,
24
25
  pick = H.pick,
25
26
  wrap = H.wrap,
26
27
  Chart = H.Chart,
@@ -146,11 +147,11 @@
146
147
  last = undefined;
147
148
  }
148
149
 
149
-
150
- ddOptions = extend({
151
- color: color,
152
- _ddSeriesId: ddSeriesId++
153
- }, ddOptions);
150
+ if (!ddOptions.color) {
151
+ ddOptions.color = color;
152
+ }
153
+ ddOptions._ddSeriesId = ddSeriesId++;
154
+
154
155
  pointIndex = inArray(point, oldSeries.points);
155
156
 
156
157
  // Record options for all current series
@@ -440,7 +441,7 @@
440
441
  point.graphic
441
442
  .attr(animateFrom)
442
443
  .animate(
443
- extend(point.shapeArgs, { fill: point.color }),
444
+ extend(point.shapeArgs, { fill: point.color || series.color }),
444
445
  animationOptions
445
446
  );
446
447
  }
@@ -687,17 +688,23 @@
687
688
  });
688
689
 
689
690
  wrap(H.Series.prototype, 'drawDataLabels', function (proceed) {
690
- var css = this.chart.options.drilldown.activeDataLabelStyle;
691
+ var series = this,
692
+ css = series.chart.options.drilldown.activeDataLabelStyle,
693
+ renderer = series.chart.renderer;
691
694
 
692
- proceed.call(this);
695
+ proceed.call(series);
693
696
 
694
- each(this.points, function (point) {
697
+ each(series.points, function (point) {
698
+ var pointCss = {};
695
699
  if (point.drilldown && point.dataLabel) {
700
+ if (css.color === 'contrast') {
701
+ pointCss.color = renderer.getContrast(point.color || series.color);
702
+ }
696
703
  point.dataLabel
697
704
  .attr({
698
705
  'class': 'highcharts-drilldown-data-label'
699
706
  })
700
- .css(css);
707
+ .css(merge(css, pointCss));
701
708
  }
702
709
  });
703
710
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.5 (2016-05-06)
2
+ * @license Highcharts JS v4.2.6 (2016-08-02)
3
3
  * Exporting module
4
4
  *
5
5
  * (c) 2010-2016 Torstein Honsi
@@ -102,10 +102,10 @@ defaultOptions.exporting = {
102
102
  //enabled: true,
103
103
  //filename: 'chart',
104
104
  type: 'image/png',
105
- url: 'http://export.highcharts.com/',
105
+ url: 'https://export.highcharts.com/',
106
106
  //width: undefined,
107
107
  printMaxWidth: 780,
108
- //scale: 2
108
+ scale: 2,
109
109
  buttons: {
110
110
  contextButton: {
111
111
  menuClassName: PREFIX + 'contextmenu',
@@ -409,7 +409,7 @@ extend(Chart.prototype, {
409
409
  filename: options.filename || 'chart',
410
410
  type: options.type,
411
411
  width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
412
- scale: options.scale || 2,
412
+ scale: options.scale,
413
413
  svg: svg
414
414
  }, options.formAttributes);
415
415
 
@@ -427,7 +427,6 @@ extend(Chart.prototype, {
427
427
  body = doc.body,
428
428
  childNodes = body.childNodes,
429
429
  printMaxWidth = chart.options.exporting.printMaxWidth,
430
- hasUserSize,
431
430
  resetParams,
432
431
  handleMaxWidth;
433
432
 
@@ -443,9 +442,8 @@ extend(Chart.prototype, {
443
442
  // Handle printMaxWidth
444
443
  handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
445
444
  if (handleMaxWidth) {
446
- hasUserSize = chart.hasUserSize;
447
- resetParams = [chart.chartWidth, chart.chartHeight, false];
448
- chart.setSize(printMaxWidth, chart.chartHeight, false);
445
+ resetParams = [chart.options.chart.width, undefined, false];
446
+ chart.setSize(printMaxWidth, undefined, false);
449
447
  }
450
448
 
451
449
  // hide all body content
@@ -481,7 +479,6 @@ extend(Chart.prototype, {
481
479
  // Reset printMaxWidth
482
480
  if (handleMaxWidth) {
483
481
  chart.setSize.apply(chart, resetParams);
484
- chart.hasUserSize = hasUserSize;
485
482
  }
486
483
 
487
484
  fireEvent(chart, 'afterPrint');
@@ -109,7 +109,7 @@ seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
109
109
  neckWidth + (width - neckWidth) * (1 - (y - top) / (height - neckHeight));
110
110
  };
111
111
  series.getX = function (y, half) {
112
- return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? plotHeight - y : y) / 2) + options.dataLabels.distance);
112
+ return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? 2 * centerY - y : y) / 2) + options.dataLabels.distance);
113
113
  };
114
114
 
115
115
  // Expose
@@ -176,9 +176,9 @@ seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
176
176
  }
177
177
 
178
178
  if (reversed) {
179
- y1 = height - y1;
180
- y3 = height - y3;
181
- y5 = (y5 ? height - y5 : null);
179
+ y1 = 2 * centerY - y1;
180
+ y3 = 2 * centerY - y3;
181
+ y5 = (y5 ? 2 * centerY - y5 : null);
182
182
  }
183
183
  // save the path
184
184
  path = [
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.5 (2016-05-06)
2
+ * @license Highcharts JS v4.2.6 (2016-08-02)
3
3
  *
4
4
  * (c) 2011-2016 Torstein Honsi
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.5 (2016-05-06)
2
+ * @license Highcharts JS v4.2.6 (2016-08-02)
3
3
  * Plugin for displaying a message when there is no data visible in chart.
4
4
  *
5
5
  * (c) 2010-2016 Highsoft AS
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.5 (2016-05-06)
2
+ * @license Highcharts JS v4.2.6 (2016-08-02)
3
3
  * Client side exporting module
4
4
  *
5
5
  * (c) 2015 Torstein Honsi / Oystein Moseng
@@ -18,7 +18,10 @@
18
18
 
19
19
  var win = Highcharts.win,
20
20
  nav = win.navigator,
21
- doc = win.document;
21
+ doc = win.document,
22
+ domurl = win.URL || win.webkitURL || win,
23
+ isMSBrowser = /Edge\/|Trident\/|MSIE /.test(nav.userAgent),
24
+ loadEventDeferDelay = isMSBrowser ? 150 : 0; // Milliseconds to defer image load event handlers to offset IE bug
22
25
 
23
26
  // Dummy object so we can reuse our canvas-tools.js without errors
24
27
  Highcharts.CanVGRenderer = {};
@@ -40,217 +43,216 @@
40
43
  head.appendChild(script);
41
44
  }
42
45
 
43
- /**
44
- * Add a new method to the Chart object to perform a local download
45
- */
46
- Highcharts.Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
47
- var chart = this,
48
- options = Highcharts.merge(chart.options.exporting, exportingOptions),
49
- webKit = nav.userAgent.indexOf('WebKit') > -1 && nav.userAgent.indexOf('Chrome') < 0, // Webkit and not chrome
50
- scale = options.scale || 2,
51
- chartCopyContainer,
52
- domurl = win.URL || win.webkitURL || win,
53
- images,
54
- imagesEmbedded = 0,
55
- el,
56
- i,
57
- l,
58
- fallbackToExportServer = function () {
59
- if (options.fallbackToExportServer === false) {
60
- if (options.error) {
61
- options.error();
62
- } else {
63
- throw 'Fallback to export server disabled';
64
- }
65
- } else {
66
- chart.exportChart(options);
46
+ // Download contents by dataURL/blob
47
+ Highcharts.downloadURL = function (dataURL, filename) {
48
+ var a = doc.createElement('a'),
49
+ windowRef;
50
+
51
+ // IE specific blob implementation
52
+ if (nav.msSaveOrOpenBlob) {
53
+ nav.msSaveOrOpenBlob(dataURL, filename);
54
+ return;
55
+ }
56
+
57
+ // Try HTML5 download attr if supported
58
+ if (a.download !== undefined) {
59
+ a.href = dataURL;
60
+ a.download = filename; // HTML5 download attribute
61
+ a.target = '_blank';
62
+ doc.body.appendChild(a);
63
+ a.click();
64
+ doc.body.removeChild(a);
65
+ } else {
66
+ // No download attr, just opening data URI
67
+ try {
68
+ windowRef = win.open(dataURL, 'chart');
69
+ if (windowRef === undefined || windowRef === null) {
70
+ throw 'Failed to open window';
67
71
  }
68
- },
69
- // Get data:URL from image URL
70
- // Pass in callbacks to handle results. finallyCallback is always called at the end of the process. Supplying this callback is optional.
71
- // All callbacks receive three arguments: imageURL, imageType, and callbackArgs. callbackArgs is used only by callbacks and can contain whatever.
72
- imageToDataUrl = function (imageURL, imageType, callbackArgs, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
73
- var img = new win.Image(),
74
- taintedHandler,
75
- loadHandler = function () {
76
- var canvas = doc.createElement('canvas'),
77
- ctx = canvas.getContext && canvas.getContext('2d'),
78
- dataURL;
79
- try {
80
- if (!ctx) {
81
- noCanvasSupportCallback(imageURL, imageType, callbackArgs);
82
- } else {
83
- canvas.height = img.height * scale;
84
- canvas.width = img.width * scale;
85
- ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
72
+ } catch (e) {
73
+ // window.open failed, trying location.href
74
+ win.location.href = dataURL;
75
+ }
76
+ }
77
+ };
86
78
 
87
- // Now we try to get the contents of the canvas.
88
- try {
89
- dataURL = canvas.toDataURL(imageType);
90
- successCallback(dataURL, imageType, callbackArgs);
91
- } catch (e) {
92
- // Failed - either tainted canvas or something else went horribly wrong
93
- if (e.name === 'SecurityError' || e.name === 'SECURITY_ERR' || e.message === 'SecurityError') {
94
- taintedHandler(imageURL, imageType, callbackArgs);
95
- } else {
96
- throw e;
97
- }
98
- }
99
- }
100
- } finally {
101
- if (finallyCallback) {
102
- finallyCallback(imageURL, imageType, callbackArgs);
79
+ // Get blob URL from SVG code. Falls back to normal data URI.
80
+ Highcharts.svgToDataUrl = function (svg) {
81
+ var webKit = nav.userAgent.indexOf('WebKit') > -1 && nav.userAgent.indexOf('Chrome') < 0; // Webkit and not chrome
82
+ try {
83
+ // Safari requires data URI since it doesn't allow navigation to blob URLs
84
+ // Firefox has an issue with Blobs and internal references, leading to gradients not working using Blobs (#4550)
85
+ if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
86
+ return domurl.createObjectURL(new win.Blob([svg], { type: 'image/svg+xml;charset-utf-16' }));
87
+ }
88
+ } catch (e) {
89
+ // Ignore
90
+ }
91
+ return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
92
+ };
93
+
94
+ // Get data:URL from image URL
95
+ // Pass in callbacks to handle results. finallyCallback is always called at the end of the process. Supplying this callback is optional.
96
+ // All callbacks receive four arguments: imageURL, imageType, callbackArgs and scale. callbackArgs is used only by callbacks and can contain whatever.
97
+ Highcharts.imageToDataUrl = function (imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
98
+ var img = new win.Image(),
99
+ taintedHandler,
100
+ loadHandler = function () {
101
+ setTimeout(function () {
102
+ var canvas = doc.createElement('canvas'),
103
+ ctx = canvas.getContext && canvas.getContext('2d'),
104
+ dataURL;
105
+ try {
106
+ if (!ctx) {
107
+ noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
108
+ } else {
109
+ canvas.height = img.height * scale;
110
+ canvas.width = img.width * scale;
111
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
112
+
113
+ // Now we try to get the contents of the canvas.
114
+ try {
115
+ dataURL = canvas.toDataURL(imageType);
116
+ successCallback(dataURL, imageType, callbackArgs, scale);
117
+ } catch (e) {
118
+ taintedHandler(imageURL, imageType, callbackArgs, scale);
103
119
  }
104
120
  }
105
- },
106
- // Image load failed (e.g. invalid URL)
107
- errorHandler = function () {
108
- failedLoadCallback(imageURL, imageType, callbackArgs);
121
+ } finally {
109
122
  if (finallyCallback) {
110
- finallyCallback(imageURL, imageType, callbackArgs);
123
+ finallyCallback(imageURL, imageType, callbackArgs, scale);
111
124
  }
112
- };
113
-
114
- // This is called on load if the image drawing to canvas failed with a security error.
115
- // We retry the drawing with crossOrigin set to Anonymous.
116
- taintedHandler = function () {
117
- img = new win.Image();
118
- taintedHandler = taintedCallback;
119
- img.crossOrigin = 'Anonymous'; // Must be set prior to loading image source
120
- img.onload = loadHandler;
121
- img.onerror = errorHandler;
122
- img.src = imageURL;
123
- };
124
-
125
- img.onload = loadHandler;
126
- img.onerror = errorHandler;
127
- img.src = imageURL;
128
- },
129
- // Get blob URL from SVG code. Falls back to normal data URI.
130
- svgToDataUrl = function (svg) {
131
- try {
132
- // Safari requires data URI since it doesn't allow navigation to blob URLs
133
- // Firefox has an issue with Blobs and internal references, leading to gradients not working using Blobs (#4550)
134
- if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
135
- return domurl.createObjectURL(new win.Blob([svg], { type: 'image/svg+xml;charset-utf-16' }));
136
125
  }
137
- } catch (e) {
138
- // Ignore
139
- }
140
- return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
126
+ }, loadEventDeferDelay); // IE bug where image is not always ready despite calling load event.
141
127
  },
142
- // Download contents by dataURL/blob
143
- download = function (dataURL, extension) {
144
- var a = doc.createElement('a'),
145
- filename = (options.filename || 'chart') + '.' + extension,
146
- windowRef;
147
-
148
- // IE specific blob implementation
149
- if (nav.msSaveOrOpenBlob) {
150
- nav.msSaveOrOpenBlob(dataURL, filename);
151
- return;
128
+ // Image load failed (e.g. invalid URL)
129
+ errorHandler = function () {
130
+ failedLoadCallback(imageURL, imageType, callbackArgs, scale);
131
+ if (finallyCallback) {
132
+ finallyCallback(imageURL, imageType, callbackArgs, scale);
152
133
  }
134
+ };
135
+
136
+ // This is called on load if the image drawing to canvas failed with a security error.
137
+ // We retry the drawing with crossOrigin set to Anonymous.
138
+ taintedHandler = function () {
139
+ img = new win.Image();
140
+ taintedHandler = taintedCallback;
141
+ img.crossOrigin = 'Anonymous'; // Must be set prior to loading image source
142
+ img.onload = loadHandler;
143
+ img.onerror = errorHandler;
144
+ img.src = imageURL;
145
+ };
146
+
147
+ img.onload = loadHandler;
148
+ img.onerror = errorHandler;
149
+ img.src = imageURL;
150
+ };
153
151
 
154
- // Try HTML5 download attr if supported
155
- if (a.download !== undefined) {
156
- a.href = dataURL;
157
- a.download = filename; // HTML5 download attribute
158
- a.target = '_blank';
159
- doc.body.appendChild(a);
160
- a.click();
161
- doc.body.removeChild(a);
152
+ // Get data URL to an image of an SVG and call download on it
153
+ Highcharts.downloadSVGLocal = function (svg, filename, imageType, scale, failCallback, successCallback) {
154
+ var svgurl,
155
+ blob,
156
+ objectURLRevoke = true,
157
+ finallyHandler;
158
+
159
+ // Initiate download depending on file type
160
+ if (imageType === 'image/svg+xml') {
161
+ // SVG download. In this case, we want to use Microsoft specific Blob if available
162
+ try {
163
+ if (nav.msSaveOrOpenBlob) {
164
+ blob = new MSBlobBuilder();
165
+ blob.append(svg);
166
+ svgurl = blob.getBlob('image/svg+xml');
162
167
  } else {
163
- // No download attr, just opening data URI
164
- try {
165
- windowRef = win.open(dataURL, 'chart');
166
- if (windowRef === undefined || windowRef === null) {
167
- throw 'Failed to open window';
168
- }
169
- } catch (e) {
170
- // window.open failed, trying location.href
171
- win.location.href = dataURL;
172
- }
168
+ svgurl = Highcharts.svgToDataUrl(svg);
173
169
  }
174
- },
175
- // Get data URL to an image of the chart and call download on it
176
- initiateDownload = function () {
177
- var svgurl,
178
- blob,
179
- imageType = options && options.type || 'image/png',
180
- imageExtension = imageType.split('/')[1],
181
- svg = chart.sanitizeSVG(chartCopyContainer.innerHTML); // SVG of chart copy
170
+ Highcharts.downloadURL(svgurl, filename);
171
+ if (successCallback) {
172
+ successCallback();
173
+ }
174
+ } catch (e) {
175
+ failCallback();
176
+ }
177
+ } else {
178
+ // PNG/JPEG download - create bitmap from SVG
182
179
 
183
- // Initiate download depending on file type
184
- if (imageType === 'image/svg+xml') {
185
- // SVG download. In this case, we want to use Microsoft specific Blob if available
186
- try {
187
- if (nav.msSaveOrOpenBlob) {
188
- blob = new MSBlobBuilder();
189
- blob.append(svg);
190
- svgurl = blob.getBlob('image/svg+xml');
191
- } else {
192
- svgurl = svgToDataUrl(svg);
193
- }
194
- download(svgurl, 'svg');
195
- } catch (e) {
196
- fallbackToExportServer();
180
+ svgurl = Highcharts.svgToDataUrl(svg);
181
+ finallyHandler = function () {
182
+ try {
183
+ domurl.revokeObjectURL(svgurl);
184
+ } catch (e) {
185
+ // Ignore
186
+ }
187
+ };
188
+ // First, try to get PNG by rendering on canvas
189
+ Highcharts.imageToDataUrl(svgurl, imageType, { /* args */ }, scale, function (imageURL) {
190
+ // Success
191
+ try {
192
+ Highcharts.downloadURL(imageURL, filename);
193
+ if (successCallback) {
194
+ successCallback();
197
195
  }
198
- } else {
199
- // PNG/JPEG download - create bitmap from SVG
200
-
201
- // First, try to get PNG by rendering on canvas
202
- svgurl = svgToDataUrl(svg);
203
- imageToDataUrl(svgurl, imageType, { /* args */ }, function (imageURL) {
204
- // Success
196
+ } catch (e) {
197
+ failCallback();
198
+ }
199
+ }, function () {
200
+ // Failed due to tainted canvas
201
+ // Create new and untainted canvas
202
+ var canvas = doc.createElement('canvas'),
203
+ ctx = canvas.getContext('2d'),
204
+ imageWidth = svg.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
205
+ imageHeight = svg.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
206
+ downloadWithCanVG = function () {
207
+ ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
205
208
  try {
206
- download(imageURL, imageExtension);
209
+ Highcharts.downloadURL(nav.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL(imageType), filename);
210
+ if (successCallback) {
211
+ successCallback();
212
+ }
207
213
  } catch (e) {
208
- fallbackToExportServer();
214
+ failCallback();
215
+ } finally {
216
+ finallyHandler();
209
217
  }
210
- }, function () {
211
- // Failed due to tainted canvas
212
- // Create new and untainted canvas
213
- var canvas = doc.createElement('canvas'),
214
- ctx = canvas.getContext('2d'),
215
- imageWidth = svg.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
216
- imageHeight = svg.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
217
- downloadWithCanVG = function () {
218
- ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
219
- try {
220
- download(nav.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL(imageType), imageExtension);
221
- } catch (e) {
222
- fallbackToExportServer();
223
- }
224
- };
218
+ };
225
219
 
226
- canvas.width = imageWidth;
227
- canvas.height = imageHeight;
228
- if (win.canvg) {
229
- // Use preloaded canvg
230
- downloadWithCanVG();
231
- } else {
232
- // Must load canVG first
233
- chart.showLoading();
234
- getScript(Highcharts.getOptions().global.canvasToolsURL, function () {
235
- chart.hideLoading();
236
- downloadWithCanVG();
237
- });
238
- }
239
- },
240
- // No canvas support
241
- fallbackToExportServer,
242
- // Failed to load image
243
- fallbackToExportServer,
244
- // Finally
245
- function () {
246
- try {
247
- domurl.revokeObjectURL(svgurl);
248
- } catch (e) {
249
- // Ignore
250
- }
220
+ canvas.width = imageWidth;
221
+ canvas.height = imageHeight;
222
+ if (win.canvg) {
223
+ // Use preloaded canvg
224
+ downloadWithCanVG();
225
+ } else {
226
+ // Must load canVG first
227
+ objectURLRevoke = true; // Don't destroy the object URL yet since we are doing things asynchronously. A cleaner solution would be nice, but this will do for now.
228
+ getScript(Highcharts.getOptions().global.canvasToolsURL, function () {
229
+ downloadWithCanVG();
251
230
  });
252
231
  }
253
232
  },
233
+ // No canvas support
234
+ failCallback,
235
+ // Failed to load image
236
+ failCallback,
237
+ // Finally
238
+ function () {
239
+ if (objectURLRevoke) {
240
+ finallyHandler();
241
+ }
242
+ });
243
+ }
244
+ };
245
+
246
+ // Get SVG of chart prepared for client side export. This converts embedded images in the SVG to data URIs.
247
+ // The options and chartOptions arguments are passed to the getSVGForExport function.
248
+ Highcharts.Chart.prototype.getSVGForLocalExport = function (options, chartOptions, failCallback, successCallback) {
249
+ var chart = this,
250
+ images,
251
+ imagesEmbedded = 0,
252
+ chartCopyContainer,
253
+ el,
254
+ i,
255
+ l,
254
256
  // Success handler, we converted image to base64!
255
257
  embeddedSuccess = function (imageURL, imageType, callbackArgs) {
256
258
  ++imagesEmbedded;
@@ -258,9 +260,9 @@
258
260
  // Change image href in chart copy
259
261
  callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
260
262
 
261
- // Start download when done with the last image
263
+ // When done with last image we have our SVG
262
264
  if (imagesEmbedded === images.length) {
263
- initiateDownload();
265
+ successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML));
264
266
  }
265
267
  };
266
268
 
@@ -275,27 +277,61 @@
275
277
  images = chartCopyContainer.getElementsByTagName('image');
276
278
 
277
279
  try {
278
- // If there are no images to embed, just go ahead and start the download process
280
+ // If there are no images to embed, the SVG is okay now.
279
281
  if (!images.length) {
280
- initiateDownload();
282
+ successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML)); // Use SVG of chart copy
283
+ return;
281
284
  }
282
285
 
283
286
  // Go through the images we want to embed
284
287
  for (i = 0, l = images.length; i < l; ++i) {
285
288
  el = images[i];
286
- imageToDataUrl(el.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), 'image/png', { imageElement: el },
289
+ Highcharts.imageToDataUrl(el.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), 'image/png', { imageElement: el }, options.scale,
287
290
  embeddedSuccess,
288
291
  // Tainted canvas
289
- fallbackToExportServer,
290
- // No canvas support
291
- fallbackToExportServer,
292
+ failCallback,
293
+ // No canvas support
294
+ failCallback,
292
295
  // Failed to load source
293
- fallbackToExportServer
296
+ failCallback
294
297
  );
295
298
  }
296
299
  } catch (e) {
300
+ failCallback();
301
+ }
302
+ };
303
+
304
+ /**
305
+ * Add a new method to the Chart object to perform a local download
306
+ */
307
+ Highcharts.Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
308
+ var chart = this,
309
+ options = Highcharts.merge(chart.options.exporting, exportingOptions),
310
+ imageType = options && options.type || 'image/png',
311
+ fallbackToExportServer = function () {
312
+ if (options.fallbackToExportServer === false) {
313
+ if (options.error) {
314
+ options.error();
315
+ } else {
316
+ throw 'Fallback to export server disabled';
317
+ }
318
+ } else {
319
+ chart.exportChart(options);
320
+ }
321
+ },
322
+ svgSuccess = function (svg) {
323
+ var filename = (options.filename || 'chart') + '.' + (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1]);
324
+ Highcharts.downloadSVGLocal(svg, filename, imageType, options.scale, fallbackToExportServer);
325
+ };
326
+
327
+ // If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back
328
+ // docs
329
+ if (isMSBrowser && imageType !== 'image/svg+xml' && chart.container.getElementsByTagName('image').length) {
297
330
  fallbackToExportServer();
331
+ return;
298
332
  }
333
+
334
+ chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
299
335
  };
300
336
 
301
337
  // Extend the default options to use the local exporter logic