458 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @license Highcharts JS v5.0.2 (2016-10-26)
 | |
|  * Client side exporting module
 | |
|  *
 | |
|  * (c) 2015 Torstein Honsi / Oystein Moseng
 | |
|  *
 | |
|  * License: www.highcharts.com/license
 | |
|  */
 | |
| (function(factory) {
 | |
|     if (typeof module === 'object' && module.exports) {
 | |
|         module.exports = factory;
 | |
|     } else {
 | |
|         factory(Highcharts);
 | |
|     }
 | |
| }(function(Highcharts) {
 | |
|     (function(Highcharts) {
 | |
|         /**
 | |
|          * Client side exporting module
 | |
|          *
 | |
|          * (c) 2015 Torstein Honsi / Oystein Moseng
 | |
|          *
 | |
|          * License: www.highcharts.com/license
 | |
|          */
 | |
| 
 | |
|         'use strict';
 | |
|         /*global MSBlobBuilder */
 | |
| 
 | |
|         var merge = Highcharts.merge,
 | |
|             win = Highcharts.win,
 | |
|             nav = win.navigator,
 | |
|             doc = win.document,
 | |
|             each = Highcharts.each,
 | |
|             domurl = win.URL || win.webkitURL || win,
 | |
|             isMSBrowser = /Edge\/|Trident\/|MSIE /.test(nav.userAgent),
 | |
|             loadEventDeferDelay = isMSBrowser ? 150 : 0; // Milliseconds to defer image load event handlers to offset IE bug
 | |
| 
 | |
|         // Dummy object so we can reuse our canvas-tools.js without errors
 | |
|         Highcharts.CanVGRenderer = {};
 | |
| 
 | |
| 
 | |
|         /**
 | |
|          * Downloads a script and executes a callback when done.
 | |
|          * @param {String} scriptLocation
 | |
|          * @param {Function} callback
 | |
|          */
 | |
|         function getScript(scriptLocation, callback) {
 | |
|             var head = doc.getElementsByTagName('head')[0],
 | |
|                 script = doc.createElement('script');
 | |
| 
 | |
|             script.type = 'text/javascript';
 | |
|             script.src = scriptLocation;
 | |
|             script.onload = callback;
 | |
|             script.onerror = function() {
 | |
|                 console.error('Error loading script', scriptLocation); // eslint-disable-line no-console
 | |
|             };
 | |
| 
 | |
|             head.appendChild(script);
 | |
|         }
 | |
| 
 | |
|         // Download contents by dataURL/blob
 | |
|         Highcharts.downloadURL = function(dataURL, filename) {
 | |
|             var a = doc.createElement('a'),
 | |
|                 windowRef;
 | |
| 
 | |
|             // IE specific blob implementation
 | |
|             if (nav.msSaveOrOpenBlob) {
 | |
|                 nav.msSaveOrOpenBlob(dataURL, filename);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // Try HTML5 download attr if supported
 | |
|             if (a.download !== undefined) {
 | |
|                 a.href = dataURL;
 | |
|                 a.download = filename; // HTML5 download attribute
 | |
|                 a.target = '_blank';
 | |
|                 doc.body.appendChild(a);
 | |
|                 a.click();
 | |
|                 doc.body.removeChild(a);
 | |
|             } else {
 | |
|                 // No download attr, just opening data URI
 | |
|                 try {
 | |
|                     windowRef = win.open(dataURL, 'chart');
 | |
|                     if (windowRef === undefined || windowRef === null) {
 | |
|                         throw 'Failed to open window';
 | |
|                     }
 | |
|                 } catch (e) {
 | |
|                     // window.open failed, trying location.href
 | |
|                     win.location.href = dataURL;
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         // Get blob URL from SVG code. Falls back to normal data URI.
 | |
|         Highcharts.svgToDataUrl = function(svg) {
 | |
|             var webKit = nav.userAgent.indexOf('WebKit') > -1 && nav.userAgent.indexOf('Chrome') < 0; // Webkit and not chrome
 | |
|             try {
 | |
|                 // Safari requires data URI since it doesn't allow navigation to blob URLs
 | |
|                 // Firefox has an issue with Blobs and internal references, leading to gradients not working using Blobs (#4550)
 | |
|                 if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
 | |
|                     return domurl.createObjectURL(new win.Blob([svg], {
 | |
|                         type: 'image/svg+xml;charset-utf-16'
 | |
|                     }));
 | |
|                 }
 | |
|             } catch (e) {
 | |
|                 // Ignore
 | |
|             }
 | |
|             return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
 | |
|         };
 | |
| 
 | |
|         // 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 four arguments: imageURL, imageType, callbackArgs and scale. callbackArgs is used only by callbacks and can contain whatever.
 | |
|         Highcharts.imageToDataUrl = function(imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
 | |
|             var img = new win.Image(),
 | |
|                 taintedHandler,
 | |
|                 loadHandler = function() {
 | |
|                     setTimeout(function() {
 | |
|                         var canvas = doc.createElement('canvas'),
 | |
|                             ctx = canvas.getContext && canvas.getContext('2d'),
 | |
|                             dataURL;
 | |
|                         try {
 | |
|                             if (!ctx) {
 | |
|                                 noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
 | |
|                             } 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(imageType);
 | |
|                                     successCallback(dataURL, imageType, callbackArgs, scale);
 | |
|                                 } catch (e) {
 | |
|                                     taintedHandler(imageURL, imageType, callbackArgs, scale);
 | |
|                                 }
 | |
|                             }
 | |
|                         } finally {
 | |
|                             if (finallyCallback) {
 | |
|                                 finallyCallback(imageURL, imageType, callbackArgs, scale);
 | |
|                             }
 | |
|                         }
 | |
|                     }, loadEventDeferDelay); // IE bug where image is not always ready despite calling load event.
 | |
|                 },
 | |
|                 // Image load failed (e.g. invalid URL)
 | |
|                 errorHandler = function() {
 | |
|                     failedLoadCallback(imageURL, imageType, callbackArgs, scale);
 | |
|                     if (finallyCallback) {
 | |
|                         finallyCallback(imageURL, imageType, callbackArgs, scale);
 | |
|                     }
 | |
|                 };
 | |
| 
 | |
|             // This is called on load if the image drawing to canvas failed with a security error.
 | |
|             // We retry the drawing with crossOrigin set to Anonymous.
 | |
|             taintedHandler = function() {
 | |
|                 img = new win.Image();
 | |
|                 taintedHandler = taintedCallback;
 | |
|                 img.crossOrigin = 'Anonymous'; // Must be set prior to loading image source
 | |
|                 img.onload = loadHandler;
 | |
|                 img.onerror = errorHandler;
 | |
|                 img.src = imageURL;
 | |
|             };
 | |
| 
 | |
|             img.onload = loadHandler;
 | |
|             img.onerror = errorHandler;
 | |
|             img.src = imageURL;
 | |
|         };
 | |
| 
 | |
|         /**
 | |
|          * Get data URL to an image of an SVG and call download on it
 | |
|          *
 | |
|          * options object:
 | |
|          *		filename: Name of resulting downloaded file without extension
 | |
|          *		type: File type of resulting download
 | |
|          *		scale: Scaling factor of downloaded image compared to source
 | |
|          *      libURL: URL pointing to location of dependency scripts to download on demand
 | |
|          */
 | |
|         Highcharts.downloadSVGLocal = function(svg, options, failCallback, successCallback) {
 | |
|             var svgurl,
 | |
|                 blob,
 | |
|                 objectURLRevoke = true,
 | |
|                 finallyHandler,
 | |
|                 libURL = options.libURL || Highcharts.getOptions().exporting.libURL,
 | |
|                 dummySVGContainer = doc.createElement('div'),
 | |
|                 imageType = options.type || 'image/png',
 | |
|                 filename = (options.filename || 'chart') + '.' + (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1]),
 | |
|                 scale = options.scale || 1;
 | |
| 
 | |
|             libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL; // Allow libURL to end with or without fordward slash
 | |
| 
 | |
|             function svgToPdf(svgElement, margin) {
 | |
|                 var width = svgElement.width.baseVal.value + 2 * margin,
 | |
|                     height = svgElement.height.baseVal.value + 2 * margin,
 | |
|                     pdf = new win.jsPDF('l', 'pt', [width, height]); // eslint-disable-line new-cap
 | |
|                 win.svgElementToPdf(svgElement, pdf, {
 | |
|                     removeInvalid: true
 | |
|                 });
 | |
|                 return pdf.output('datauristring');
 | |
|             }
 | |
| 
 | |
|             function downloadPDF() {
 | |
|                 dummySVGContainer.innerHTML = svg;
 | |
|                 var textElements = dummySVGContainer.getElementsByTagName('text'),
 | |
|                     svgElementStyle = dummySVGContainer.getElementsByTagName('svg')[0].style;
 | |
|                 // Workaround for the text styling. Making sure it does pick up the root element
 | |
|                 each(textElements, function(el) {
 | |
|                     each(['font-family', 'font-size'], function(property) {
 | |
|                         if (!el.style[property] && svgElementStyle[property]) {
 | |
|                             el.style[property] = svgElementStyle[property];
 | |
|                         }
 | |
|                     });
 | |
|                     el.style['font-family'] = el.style['font-family'] && el.style['font-family'].split(' ').splice(-1);
 | |
|                 });
 | |
|                 var svgData = svgToPdf(dummySVGContainer.firstChild, 0);
 | |
|                 Highcharts.downloadURL(svgData, filename);
 | |
|                 if (successCallback) {
 | |
|                     successCallback();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Initiate download depending on file type
 | |
|             if (imageType === 'image/svg+xml') {
 | |
|                 // SVG download. In this case, we want to use Microsoft specific Blob if available
 | |
|                 try {
 | |
|                     if (nav.msSaveOrOpenBlob) {
 | |
|                         blob = new MSBlobBuilder();
 | |
|                         blob.append(svg);
 | |
|                         svgurl = blob.getBlob('image/svg+xml');
 | |
|                     } else {
 | |
|                         svgurl = Highcharts.svgToDataUrl(svg);
 | |
|                     }
 | |
|                     Highcharts.downloadURL(svgurl, filename);
 | |
|                     if (successCallback) {
 | |
|                         successCallback();
 | |
|                     }
 | |
|                 } catch (e) {
 | |
|                     failCallback();
 | |
|                 }
 | |
|             } else if (imageType === 'application/pdf') {
 | |
|                 if (win.jsPDF && win.svgElementToPdf) {
 | |
|                     downloadPDF();
 | |
|                 } else {
 | |
|                     // Must load pdf libraries first
 | |
|                     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.
 | |
|                     getScript(libURL + 'jspdf.js', function() {
 | |
|                         getScript(libURL + 'rgbcolor.js', function() {
 | |
|                             getScript(libURL + 'svg2pdf.js', function() {
 | |
|                                 downloadPDF();
 | |
|                             });
 | |
|                         });
 | |
|                     });
 | |
|                 }
 | |
|             } else {
 | |
|                 // PNG/JPEG download - create bitmap from SVG
 | |
| 
 | |
|                 svgurl = Highcharts.svgToDataUrl(svg);
 | |
|                 finallyHandler = function() {
 | |
|                     try {
 | |
|                         domurl.revokeObjectURL(svgurl);
 | |
|                     } catch (e) {
 | |
|                         // Ignore
 | |
|                     }
 | |
|                 };
 | |
|                 // First, try to get PNG by rendering on canvas
 | |
|                 Highcharts.imageToDataUrl(svgurl, imageType, { /* args */ }, scale, function(imageURL) {
 | |
|                         // Success
 | |
|                         try {
 | |
|                             Highcharts.downloadURL(imageURL, filename);
 | |
|                             if (successCallback) {
 | |
|                                 successCallback();
 | |
|                             }
 | |
|                         } catch (e) {
 | |
|                             failCallback();
 | |
|                         }
 | |
|                     }, function() {
 | |
|                         // Failed due to tainted canvas
 | |
|                         // Create new and untainted canvas
 | |
|                         var canvas = doc.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 {
 | |
|                                     Highcharts.downloadURL(nav.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL(imageType), filename);
 | |
|                                     if (successCallback) {
 | |
|                                         successCallback();
 | |
|                                     }
 | |
|                                 } catch (e) {
 | |
|                                     failCallback();
 | |
|                                 } finally {
 | |
|                                     finallyHandler();
 | |
|                                 }
 | |
|                             };
 | |
| 
 | |
|                         canvas.width = imageWidth;
 | |
|                         canvas.height = imageHeight;
 | |
|                         if (win.canvg) {
 | |
|                             // Use preloaded canvg
 | |
|                             downloadWithCanVG();
 | |
|                         } else {
 | |
|                             // Must load canVG first
 | |
|                             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.
 | |
|                             getScript(libURL + 'rgbcolor.js', function() { // Get RGBColor.js first
 | |
|                                 getScript(libURL + 'canvg.js', function() {
 | |
|                                     downloadWithCanVG();
 | |
|                                 });
 | |
|                             });
 | |
|                         }
 | |
|                     },
 | |
|                     // No canvas support
 | |
|                     failCallback,
 | |
|                     // Failed to load image
 | |
|                     failCallback,
 | |
|                     // Finally
 | |
|                     function() {
 | |
|                         if (objectURLRevoke) {
 | |
|                             finallyHandler();
 | |
|                         }
 | |
|                     });
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         // Get SVG of chart prepared for client side export. This converts embedded images in the SVG to data URIs.
 | |
|         // The options and chartOptions arguments are passed to the getSVGForExport function.
 | |
|         Highcharts.Chart.prototype.getSVGForLocalExport = function(options, chartOptions, failCallback, successCallback) {
 | |
|             var chart = this,
 | |
|                 images,
 | |
|                 imagesEmbedded = 0,
 | |
|                 chartCopyContainer,
 | |
|                 el,
 | |
|                 i,
 | |
|                 l,
 | |
|                 // Success handler, we converted image to base64!
 | |
|                 embeddedSuccess = function(imageURL, imageType, callbackArgs) {
 | |
|                     ++imagesEmbedded;
 | |
| 
 | |
|                     // Change image href in chart copy
 | |
|                     callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
 | |
| 
 | |
|                     // When done with last image we have our SVG
 | |
|                     if (imagesEmbedded === images.length) {
 | |
|                         successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML));
 | |
|                     }
 | |
|                 };
 | |
| 
 | |
|             // 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, the SVG is okay now.
 | |
|                 if (!images.length) {
 | |
|                     successCallback(chart.sanitizeSVG(chartCopyContainer.innerHTML)); // Use SVG of chart copy
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 // Go through the images we want to embed
 | |
|                 for (i = 0, l = images.length; i < l; ++i) {
 | |
|                     el = images[i];
 | |
|                     Highcharts.imageToDataUrl(el.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), 'image/png', {
 | |
|                             imageElement: el
 | |
|                         }, options.scale,
 | |
|                         embeddedSuccess,
 | |
|                         // Tainted canvas
 | |
|                         failCallback,
 | |
|                         // No canvas support
 | |
|                         failCallback,
 | |
|                         // Failed to load source
 | |
|                         failCallback
 | |
|                     );
 | |
|                 }
 | |
|             } catch (e) {
 | |
|                 failCallback();
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         /**
 | |
|          * 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),
 | |
|                 fallbackToExportServer = function() {
 | |
|                     if (options.fallbackToExportServer === false) {
 | |
|                         if (options.error) {
 | |
|                             options.error();
 | |
|                         } else {
 | |
|                             throw 'Fallback to export server disabled';
 | |
|                         }
 | |
|                     } else {
 | |
|                         chart.exportChart(options);
 | |
|                     }
 | |
|                 },
 | |
|                 svgSuccess = function(svg) {
 | |
|                     Highcharts.downloadSVGLocal(svg, options, fallbackToExportServer);
 | |
|                 };
 | |
| 
 | |
|             // If we have embedded images and are exporting to JPEG/PNG, Microsoft browsers won't handle it, so fall back
 | |
|             if ((isMSBrowser && options.imageType !== 'image/svg+xml' || options.imageType !== 'application/pdf') && chart.container.getElementsByTagName('image').length) {
 | |
|                 fallbackToExportServer();
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
 | |
|         };
 | |
| 
 | |
|         // Extend the default options to use the local exporter logic
 | |
|         merge(true, Highcharts.getOptions().exporting, {
 | |
|             libURL: 'http://code.highcharts.com/5.0.2/lib/',
 | |
|             buttons: {
 | |
|                 contextButton: {
 | |
|                     menuItems: [{
 | |
|                         textKey: 'printChart',
 | |
|                         onclick: function() {
 | |
|                             this.print();
 | |
|                         }
 | |
|                     }, {
 | |
|                         separator: true
 | |
|                     }, {
 | |
|                         textKey: 'downloadPNG',
 | |
|                         onclick: function() {
 | |
|                             this.exportChartLocal();
 | |
|                         }
 | |
|                     }, {
 | |
|                         textKey: 'downloadJPEG',
 | |
|                         onclick: function() {
 | |
|                             this.exportChartLocal({
 | |
|                                 type: 'image/jpeg'
 | |
|                             });
 | |
|                         }
 | |
|                     }, {
 | |
|                         textKey: 'downloadSVG',
 | |
|                         onclick: function() {
 | |
|                             this.exportChartLocal({
 | |
|                                 type: 'image/svg+xml'
 | |
|                             });
 | |
|                         }
 | |
|                     }, {
 | |
|                         textKey: 'downloadPDF',
 | |
|                         onclick: function() {
 | |
|                             this.exportChartLocal({
 | |
|                                 type: 'application/pdf'
 | |
|                             });
 | |
|                         }
 | |
|                     }]
 | |
|                 }
 | |
|             }
 | |
|         });
 | |
| 
 | |
|     }(Highcharts));
 | |
| }));
 | 
