package.modules.offline-exporting.src.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of highcharts Show documentation
Show all versions of highcharts Show documentation
JavaScript charting framework
The newest version!
/**
* @license Highcharts JS v11.4.8 (2024-08-29)
*
* Client side exporting module
*
* (c) 2015-2024 Torstein Honsi / Oystein Moseng
*
* License: www.highcharts.com/license
*/
(function (factory) {
if (typeof module === 'object' && module.exports) {
factory['default'] = factory;
module.exports = factory;
} else if (typeof define === 'function' && define.amd) {
define('highcharts/modules/offline-exporting', ['highcharts', 'highcharts/modules/exporting'], function (Highcharts) {
factory(Highcharts);
factory.Highcharts = Highcharts;
return factory;
});
} else {
factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
}
}(function (Highcharts) {
'use strict';
var _modules = Highcharts ? Highcharts._modules : {};
function _registerModule(obj, path, args, fn) {
if (!obj.hasOwnProperty(path)) {
obj[path] = fn.apply(null, args);
if (typeof CustomEvent === 'function') {
Highcharts.win.dispatchEvent(new CustomEvent(
'HighchartsModuleLoaded',
{ detail: { path: path, module: obj[path] } }
));
}
}
}
_registerModule(_modules, 'Extensions/DownloadURL.js', [_modules['Core/Globals.js']], function (H) {
/* *
*
* (c) 2015-2024 Oystein Moseng
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Mixin for downloading content in the browser
*
* */
/* *
*
* Imports
*
* */
const { isSafari, win, win: { document: doc } } = H;
/* *
*
* Constants
*
* */
const domurl = win.URL || win.webkitURL || win;
/* *
*
* Functions
*
* */
/**
* Convert base64 dataURL to Blob if supported, otherwise returns undefined.
* @private
* @function Highcharts.dataURLtoBlob
* @param {string} dataURL
* URL to convert
* @return {string|undefined}
* Blob
*/
function dataURLtoBlob(dataURL) {
const parts = dataURL
.replace(/filename=.*;/, '')
.match(/data:([^;]*)(;base64)?,([A-Z+\d\/]+)/i);
if (parts &&
parts.length > 3 &&
(win.atob) &&
win.ArrayBuffer &&
win.Uint8Array &&
win.Blob &&
(domurl.createObjectURL)) {
// Try to convert data URL to Blob
const binStr = win.atob(parts[3]), buf = new win.ArrayBuffer(binStr.length), binary = new win.Uint8Array(buf);
for (let i = 0; i < binary.length; ++i) {
binary[i] = binStr.charCodeAt(i);
}
return domurl
.createObjectURL(new win.Blob([binary], { 'type': parts[1] }));
}
}
/**
* Download a data URL in the browser. Can also take a blob as first param.
*
* @private
* @function Highcharts.downloadURL
* @param {string|global.URL} dataURL
* The dataURL/Blob to download
* @param {string} filename
* The name of the resulting file (w/extension)
* @return {void}
*/
function downloadURL(dataURL, filename) {
const nav = win.navigator, a = doc.createElement('a');
// IE specific blob implementation
// Don't use for normal dataURLs
if (typeof dataURL !== 'string' &&
!(dataURL instanceof String) &&
nav.msSaveOrOpenBlob) {
nav.msSaveOrOpenBlob(dataURL, filename);
return;
}
dataURL = '' + dataURL;
if (nav.userAgent.length > 1000 /* RegexLimits.shortLimit */) {
throw new Error('Input too long');
}
const // Some browsers have limitations for data URL lengths. Try to convert
// to Blob or fall back. Edge always needs that blob.
isOldEdgeBrowser = /Edge\/\d+/.test(nav.userAgent),
// Safari on iOS needs Blob in order to download PDF
safariBlob = (isSafari &&
typeof dataURL === 'string' &&
dataURL.indexOf('data:application/pdf') === 0);
if (safariBlob || isOldEdgeBrowser || dataURL.length > 2000000) {
dataURL = dataURLtoBlob(dataURL) || '';
if (!dataURL) {
throw new Error('Failed to convert to blob');
}
}
// Try HTML5 download attr if supported
if (typeof a.download !== 'undefined') {
a.href = dataURL;
a.download = filename; // HTML5 download attribute
doc.body.appendChild(a);
a.click();
doc.body.removeChild(a);
}
else {
// No download attr, just opening data URI
try {
if (!win.open(dataURL, 'chart')) {
throw new Error('Failed to open window');
}
}
catch {
// If window.open failed, try location.href
win.location.href = dataURL;
}
}
}
/* *
*
* Default Export
*
* */
const DownloadURL = {
dataURLtoBlob,
downloadURL
};
return DownloadURL;
});
_registerModule(_modules, 'Extensions/OfflineExporting/OfflineExportingDefaults.js', [], function () {
/* *
*
* (c) 2010-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Declarations
*
* */
const OfflineExportingDefaults = {
libURL: 'https://code.highcharts.com/11.4.8/lib/',
// When offline-exporting is loaded, redefine the menu item definitions
// related to download.
menuItemDefinitions: {
downloadPNG: {
textKey: 'downloadPNG',
onclick: function () {
this.exportChartLocal();
}
},
downloadJPEG: {
textKey: 'downloadJPEG',
onclick: function () {
this.exportChartLocal({
type: 'image/jpeg'
});
}
},
downloadSVG: {
textKey: 'downloadSVG',
onclick: function () {
this.exportChartLocal({
type: 'image/svg+xml'
});
}
},
downloadPDF: {
textKey: 'downloadPDF',
onclick: function () {
this.exportChartLocal({
type: 'application/pdf'
});
}
}
}
};
/* *
*
* Default Export
*
* */
return OfflineExportingDefaults;
});
_registerModule(_modules, 'Extensions/OfflineExporting/OfflineExporting.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Defaults.js'], _modules['Extensions/DownloadURL.js'], _modules['Extensions/Exporting/Exporting.js'], _modules['Core/Globals.js'], _modules['Core/HttpUtilities.js'], _modules['Extensions/OfflineExporting/OfflineExportingDefaults.js'], _modules['Core/Utilities.js']], function (AST, Chart, D, DownloadURL, Exporting, H, HU, OfflineExportingDefaults, U) {
/* *
*
* Client side exporting module
*
* (c) 2015 Torstein Honsi / Oystein Moseng
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { defaultOptions } = D;
const { downloadURL } = DownloadURL;
const { doc, win } = H;
const { ajax } = HU;
const { addEvent, error, extend, fireEvent, merge } = U;
AST.allowedAttributes.push('data-z-index', 'fill-opacity', 'filter', 'rx', 'ry', 'stroke-dasharray', 'stroke-linejoin', 'stroke-opacity', 'text-anchor', 'transform', 'version', 'viewBox', 'visibility', 'xmlns', 'xmlns:xlink');
AST.allowedTags.push('desc', 'clippath', 'g');
/* *
*
* Composition
*
* */
var OfflineExporting;
(function (OfflineExporting) {
/* *
*
* Declarations
*
* */
/* *
*
* Constants
*
* */
// Dummy object so we can reuse our canvas-tools.js without errors
OfflineExporting.CanVGRenderer = {}, OfflineExporting.domurl = win.URL || win.webkitURL || win,
// Milliseconds to defer image load event handlers to offset IE bug
OfflineExporting.loadEventDeferDelay = H.isMS ? 150 : 0;
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* Extends OfflineExporting with Chart.
* @private
*/
function compose(ChartClass) {
const chartProto = ChartClass.prototype;
if (!chartProto.exportChartLocal) {
chartProto.getSVGForLocalExport = getSVGForLocalExport;
chartProto.exportChartLocal = exportChartLocal;
// Extend the default options to use the local exporter logic
merge(true, defaultOptions.exporting, OfflineExportingDefaults);
}
return ChartClass;
}
OfflineExporting.compose = compose;
/**
* Get data URL to an image of an SVG and call download on it options
* object:
* - **filename:** Name of resulting downloaded file without extension.
* Default is `chart`.
*
* - **type:** File type of resulting download. Default is `image/png`.
*
* - **scale:** Scaling factor of downloaded image compared to source.
* Default is `1`.
*
* - **libURL:** URL pointing to location of dependency scripts to download
* on demand. Default is the exporting.libURL option of the global
* Highcharts options pointing to our server.
*
* @function Highcharts.downloadSVGLocal
*
* @param {string} svg
* The generated SVG
*
* @param {Highcharts.ExportingOptions} options
* The exporting options
*
* @param {Function} failCallback
* The callback function in case of errors
*
* @param {Function} [successCallback]
* The callback function in case of success
*
*/
function downloadSVGLocal(svg, options, failCallback, successCallback) {
const 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;
let svgurl, blob, finallyHandler, libURL = (options.libURL || defaultOptions.exporting.libURL), objectURLRevoke = true, pdfFont = options.pdfFont;
// Allow libURL to end with or without fordward slash
libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL;
/*
* Detect if we need to load TTF fonts for the PDF, then load them and
* proceed.
*
* @private
*/
const loadPdfFonts = (svgElement, callback) => {
const hasNonASCII = (s) => (
// eslint-disable-next-line no-control-regex
/[^\u0000-\u007F\u200B]+/.test(s));
// Register an event in order to add the font once jsPDF is
// initialized
const addFont = (variant, base64) => {
win.jspdf.jsPDF.API.events.push([
'initialized',
function () {
this.addFileToVFS(variant, base64);
this.addFont(variant, 'HighchartsFont', variant);
if (!this.getFontList().HighchartsFont) {
this.setFont('HighchartsFont');
}
}
]);
};
// If there are no non-ASCII characters in the SVG, do not use
// bother downloading the font files
if (pdfFont && !hasNonASCII(svgElement.textContent || '')) {
pdfFont = void 0;
}
// Add new font if the URL is declared, #6417.
const variants = ['normal', 'italic', 'bold', 'bolditalic'];
// Shift the first element off the variants and add as a font.
// Then asynchronously trigger the next variant until calling the
// callback when the variants are empty.
let normalBase64;
const shiftAndLoadVariant = () => {
const variant = variants.shift();
// All variants shifted and possibly loaded, proceed
if (!variant) {
return callback();
}
const url = pdfFont && pdfFont[variant];
if (url) {
ajax({
url,
responseType: 'blob',
success: (data, xhr) => {
const reader = new FileReader();
reader.onloadend = function () {
if (typeof this.result === 'string') {
const base64 = this.result.split(',')[1];
addFont(variant, base64);
if (variant === 'normal') {
normalBase64 = base64;
}
}
shiftAndLoadVariant();
};
reader.readAsDataURL(xhr.response);
},
error: shiftAndLoadVariant
});
}
else {
// For other variants, fall back to normal text weight/style
if (normalBase64) {
addFont(variant, normalBase64);
}
shiftAndLoadVariant();
}
};
shiftAndLoadVariant();
};
/*
* @private
*/
const downloadPDF = () => {
AST.setElementHTML(dummySVGContainer, svg);
const textElements = dummySVGContainer.getElementsByTagName('text'),
// Copy style property to element from parents if it's not
// there. Searches up hierarchy until it finds prop, or hits the
// chart container.
setStylePropertyFromParents = function (el, propName) {
let curParent = el;
while (curParent && curParent !== dummySVGContainer) {
if (curParent.style[propName]) {
let value = curParent.style[propName];
if (propName === 'fontSize' && /em$/.test(value)) {
value = Math.round(parseFloat(value) * 16) + 'px';
}
el.style[propName] = value;
break;
}
curParent = curParent.parentNode;
}
};
let titleElements, outlineElements;
// Workaround for the text styling. Making sure it does pick up
// settings for parent elements.
[].forEach.call(textElements, function (el) {
// Workaround for the text styling. making sure it does pick up
// the root element
['fontFamily', 'fontSize']
.forEach((property) => {
setStylePropertyFromParents(el, property);
});
el.style.fontFamily = pdfFont && pdfFont.normal ?
// Custom PDF font
'HighchartsFont' :
// Generic font (serif, sans-serif etc)
String(el.style.fontFamily &&
el.style.fontFamily.split(' ').splice(-1));
// Workaround for plotband with width, removing title from text
// nodes
titleElements = el.getElementsByTagName('title');
[].forEach.call(titleElements, function (titleElement) {
el.removeChild(titleElement);
});
// Remove all .highcharts-text-outline elements, #17170
outlineElements =
el.getElementsByClassName('highcharts-text-outline');
while (outlineElements.length > 0) {
const outline = outlineElements[0];
if (outline.parentNode) {
outline.parentNode.removeChild(outline);
}
}
});
const svgNode = dummySVGContainer.querySelector('svg');
if (svgNode) {
loadPdfFonts(svgNode, () => {
svgToPdf(svgNode, 0, scale, (pdfData) => {
try {
downloadURL(pdfData, filename);
if (successCallback) {
successCallback();
}
}
catch (e) {
failCallback(e);
}
});
});
}
};
// 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 (typeof win.MSBlobBuilder !== 'undefined') {
blob = new win.MSBlobBuilder();
blob.append(svg);
svgurl = blob.getBlob('image/svg+xml');
}
else {
svgurl = svgToDataUrl(svg);
}
downloadURL(svgurl, filename);
if (successCallback) {
successCallback();
}
}
catch (e) {
failCallback(e);
}
}
else if (imageType === 'application/pdf') {
if (win.jspdf && win.jspdf.jsPDF) {
downloadPDF();
}
else {
// Must load pdf libraries first. // 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.
objectURLRevoke = true;
getScript(libURL + 'jspdf.js', function () {
getScript(libURL + 'svg2pdf.js', downloadPDF);
});
}
}
else {
// PNG/JPEG download - create bitmap from SVG
svgurl = svgToDataUrl(svg);
finallyHandler = function () {
try {
OfflineExporting.domurl.revokeObjectURL(svgurl);
}
catch (e) {
// Ignore
}
};
// First, try to get PNG by rendering on canvas
imageToDataUrl(svgurl, imageType, {}, scale, function (imageURL) {
// Success
try {
downloadURL(imageURL, filename);
if (successCallback) {
successCallback();
}
}
catch (e) {
failCallback(e);
}
}, function () {
if (svg.length > 100000000 /* RegexLimits.svgLimit */) {
throw new Error('Input too long');
}
// Failed due to tainted canvas
// Create new and untainted canvas
const canvas = doc.createElement('canvas'), ctx = canvas.getContext('2d'), matchedImageWidth = svg.match(
// eslint-disable-next-line max-len
/^