/**
|
|
* @license Highcharts JS v4.1.8 (2015-08-20)
|
|
* Client side exporting module
|
|
*
|
|
* (c) 2015 Torstein Honsi / Oystein Moseng
|
|
*
|
|
* License: www.highcharts.com/license
|
|
*/
|
|
|
|
// JSLint options:
|
|
/*global Highcharts, HighchartsAdapter, document, window, Blob, MSBlobBuilder */
|
|
|
|
(function (Highcharts) {
|
|
|
|
// Dummy object so we can reuse our canvas-tools.js without errors
|
|
Highcharts.CanVGRenderer = {};
|
|
|
|
/**
|
|
* Add a new method to the Chart object to perform a local download
|
|
*/
|
|
Highcharts.Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
|
|
var chart = this,
|
|
options = Highcharts.merge(chart.options.exporting, exportingOptions),
|
|
webKit = navigator.userAgent.indexOf('WebKit') > -1 && navigator.userAgent.indexOf("Chrome") < 0, // Webkit and not chrome
|
|
scale = options.scale || 2,
|
|
chartCopyContainer,
|
|
domurl = window.URL || window.webkitURL || window,
|
|
images,
|
|
imagesEmbedded = 0,
|
|
el,
|
|
i,
|
|
l,
|
|
fallbackToExportServer = function () {
|
|
if (options.fallbackToExportServer === false) {
|
|
throw 'Fallback to export server disabled';
|
|
}
|
|
chart.exportChart(options);
|
|
},
|
|
// Get data:URL from image URL
|
|
// Pass in callbacks to handle results. finallyCallback is always called at the end of the process. Supplying this callback is optional.
|
|
// All callbacks receive two arguments: imageURL, and callbackArgs. callbackArgs is used only by callbacks and can contain whatever.
|
|
imageToDataUrl = function (imageURL, callbackArgs, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
|
|
var img = new Image();
|
|
if (!webKit) {
|
|
img.crossOrigin = 'Anonymous'; // For some reason Safari chokes on this attribute
|
|
}
|
|
img.onload = function () {
|
|
var canvas = document.createElement('canvas'),
|
|
ctx = canvas.getContext && canvas.getContext('2d'),
|
|
dataURL;
|
|
|
|
if (!ctx) {
|
|
noCanvasSupportCallback(imageURL, callbackArgs);
|
|
} else {
|
|
canvas.height = img.height * scale;
|
|
canvas.width = img.width * scale;
|
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
|
|
// Now we try to get the contents of the canvas.
|
|
try {
|
|
dataURL = canvas.toDataURL();
|
|
successCallback(dataURL, callbackArgs);
|
|
} catch (e) {
|
|
// Failed - either tainted canvas or something else went horribly wrong
|
|
if (e.name === 'SecurityError' || e.name === 'SECURITY_ERR' || e.message === 'SecurityError') {
|
|
taintedCallback(imageURL, callbackArgs);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
if (finallyCallback) {
|
|
finallyCallback(imageURL, callbackArgs);
|
|
}
|
|
};
|
|
img.onerror = function () {
|
|
failedLoadCallback(imageURL, callbackArgs);
|
|
if (finallyCallback) {
|
|
finallyCallback(imageURL, callbackArgs);
|
|
}
|
|
};
|
|
img.src = imageURL;
|
|
},
|
|
// Get blob URL from SVG code. Falls back to normal data URI.
|
|
svgToDataUrl = function (svg) {
|
|
try {
|
|
// Safari requires data URI since it doesn't allow navigation to blob URLs
|
|
if (!webKit) {
|
|
return domurl.createObjectURL(new Blob([svg], { type: 'image/svg+xml;charset-utf-16'}));
|
|
}
|
|
} catch (e) {
|
|
// Ignore
|
|
}
|
|
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
|
|
},
|
|
// Download contents by dataURL/blob
|
|
download = function (dataURL, extension) {
|
|
var a = document.createElement('a'),
|
|
filename = (options.filename || 'chart') + '.' + extension,
|
|
windowRef;
|
|
|
|
// IE specific blob implementation
|
|
if (navigator.msSaveOrOpenBlob) {
|
|
navigator.msSaveOrOpenBlob(dataURL, filename);
|
|
return;
|
|
}
|
|
|
|
// Try HTML5 download attr if supported
|
|
if (typeof a.download !== 'undefined') {
|
|
a.href = dataURL;
|
|
a.download = filename; // HTML5 download attribute
|
|
a.target = '_blank';
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
} else {
|
|
// No download attr, just opening data URI
|
|
try {
|
|
windowRef = window.open(dataURL, 'chart');
|
|
if (typeof windowRef === 'undefined' || windowRef === null) {
|
|
throw 1;
|
|
}
|
|
} catch (e) {
|
|
// window.open failed, trying location.href
|
|
window.location.href = dataURL;
|
|
}
|
|
}
|
|
},
|
|
// Get data URL to an image of the chart and call download on it
|
|
initiateDownload = function () {
|
|
var svgurl,
|
|
blob,
|
|
svg = chart.sanitizeSVG(chartCopyContainer.innerHTML); // SVG of chart copy
|
|
|
|
// Initiate download depending on file type
|
|
if (options && options.type === 'image/svg+xml') {
|
|
// SVG download. In this case, we want to use Microsoft specific Blob if available
|
|
try {
|
|
if (navigator.msSaveOrOpenBlob) {
|
|
blob = new MSBlobBuilder();
|
|
blob.append(svg);
|
|
svgurl = blob.getBlob('image/svg+xml');
|
|
} else {
|
|
svgurl = svgToDataUrl(svg);
|
|
}
|
|
download(svgurl, 'svg');
|
|
} catch (e) {
|
|
fallbackToExportServer();
|
|
}
|
|
} else {
|
|
// PNG download - create bitmap from SVG
|
|
|
|
// First, try to get PNG by rendering on canvas
|
|
svgurl = svgToDataUrl(svg);
|
|
imageToDataUrl(svgurl, { /* args */ }, function (imageURL) {
|
|
// Success
|
|
try {
|
|
download(imageURL, 'png');
|
|
} catch (e) {
|
|
fallbackToExportServer();
|
|
}
|
|
}, function () {
|
|
// Failed due to tainted canvas
|
|
// Create new and untainted canvas
|
|
var canvas = document.createElement('canvas'),
|
|
ctx = canvas.getContext('2d'),
|
|
imageWidth = svg.match(/^<svg[^>]*width\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
|
|
imageHeight = svg.match(/^<svg[^>]*height\s*=\s*\"?(\d+)\"?[^>]*>/)[1] * scale,
|
|
downloadWithCanVG = function () {
|
|
ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
|
|
try {
|
|
download(navigator.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL('image/png'), 'png');
|
|
} catch (e) {
|
|
fallbackToExportServer();
|
|
}
|
|
};
|
|
|
|
canvas.width = imageWidth;
|
|
canvas.height = imageHeight;
|
|
if (window.canvg) {
|
|
// Use preloaded canvg
|
|
downloadWithCanVG();
|
|
} else {
|
|
// Must load canVG first
|
|
chart.showLoading();
|
|
HighchartsAdapter.getScript(Highcharts.getOptions().global.canvasToolsURL, function () {
|
|
chart.hideLoading();
|
|
downloadWithCanVG();
|
|
});
|
|
}
|
|
},
|
|
// No canvas support
|
|
fallbackToExportServer,
|
|
// Failed to load image
|
|
fallbackToExportServer,
|
|
// Finally
|
|
function () {
|
|
try {
|
|
domurl.revokeObjectURL(svgurl);
|
|
} catch (e) {
|
|
// Ignore
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Hook into getSVG to get a copy of the chart copy's container
|
|
Highcharts.wrap(Highcharts.Chart.prototype, 'getChartHTML', function (proceed) {
|
|
chartCopyContainer = this.container.cloneNode(true);
|
|
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
});
|
|
|
|
// Trigger hook to get chart copy
|
|
chart.getSVGForExport(options, chartOptions);
|
|
images = chartCopyContainer.getElementsByTagName('image');
|
|
|
|
try {
|
|
// If there are no images to embed, just go ahead and start the download process
|
|
if (!images.length) {
|
|
initiateDownload();
|
|
}
|
|
|
|
// Success handler, we converted image to base64!
|
|
function embeddedSuccess(imageURL, callbackArgs) {
|
|
++imagesEmbedded;
|
|
|
|
// Change image href in chart copy
|
|
callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
|
|
|
|
// Start download when done with the last image
|
|
if (imagesEmbedded === images.length) {
|
|
initiateDownload();
|
|
}
|
|
}
|
|
|
|
// Go through the images we want to embed
|
|
for (i = 0, l = images.length; i < l; ++i) {
|
|
el = images[i];
|
|
imageToDataUrl(el.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), { imageElement: el },
|
|
embeddedSuccess,
|
|
// Tainted canvas
|
|
fallbackToExportServer,
|
|
// No canvas support
|
|
fallbackToExportServer,
|
|
// Failed to load source
|
|
fallbackToExportServer
|
|
);
|
|
}
|
|
} catch (e) {
|
|
fallbackToExportServer();
|
|
}
|
|
};
|
|
|
|
// Extend the default options to use the local exporter logic
|
|
Highcharts.getOptions().exporting.buttons.contextButton.menuItems = [{
|
|
textKey: 'printChart',
|
|
onclick: function () {
|
|
this.print();
|
|
}
|
|
}, {
|
|
separator: true
|
|
}, {
|
|
textKey: 'downloadPNG',
|
|
onclick: function () {
|
|
this.exportChartLocal();
|
|
}
|
|
}, {
|
|
textKey: 'downloadSVG',
|
|
onclick: function () {
|
|
this.exportChartLocal({
|
|
type: 'image/svg+xml'
|
|
});
|
|
}
|
|
}];
|
|
|
|
}(Highcharts));
|