highcharts-rails 4.2.5 → 4.2.6

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