offline-exporting.src.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /**
  2. * @license Highcharts JS v8.2.0 (2020-08-20)
  3. *
  4. * Client side exporting module
  5. *
  6. * (c) 2015-2019 Torstein Honsi / Oystein Moseng
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/offline-exporting', ['highcharts', 'highcharts/modules/exporting'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Extensions/DownloadURL.js', [_modules['Core/Globals.js']], function (Highcharts) {
  32. /* *
  33. *
  34. * (c) 2015-2020 Oystein Moseng
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * Mixin for downloading content in the browser
  41. *
  42. * */
  43. var win = Highcharts.win,
  44. nav = win.navigator,
  45. doc = win.document,
  46. domurl = win.URL || win.webkitURL || win,
  47. isEdgeBrowser = /Edge\/\d+/.test(nav.userAgent);
  48. /**
  49. * Convert base64 dataURL to Blob if supported, otherwise returns undefined.
  50. * @private
  51. * @function Highcharts.dataURLtoBlob
  52. * @param {string} dataURL
  53. * URL to convert
  54. * @return {string|undefined}
  55. * Blob
  56. */
  57. var dataURLtoBlob = Highcharts.dataURLtoBlob = function (dataURL) {
  58. var parts = dataURL.match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
  59. if (parts &&
  60. parts.length > 3 &&
  61. win.atob &&
  62. win.ArrayBuffer &&
  63. win.Uint8Array &&
  64. win.Blob &&
  65. domurl.createObjectURL) {
  66. // Try to convert data URL to Blob
  67. var binStr = win.atob(parts[3]),
  68. buf = new win.ArrayBuffer(binStr.length),
  69. binary = new win.Uint8Array(buf),
  70. blob;
  71. for (var i = 0; i < binary.length; ++i) {
  72. binary[i] = binStr.charCodeAt(i);
  73. }
  74. blob = new win.Blob([binary], { 'type': parts[1] });
  75. return domurl.createObjectURL(blob);
  76. }
  77. };
  78. /**
  79. * Download a data URL in the browser. Can also take a blob as first param.
  80. *
  81. * @private
  82. * @function Highcharts.downloadURL
  83. * @param {string|global.URL} dataURL
  84. * The dataURL/Blob to download
  85. * @param {string} filename
  86. * The name of the resulting file (w/extension)
  87. * @return {void}
  88. */
  89. var downloadURL = Highcharts.downloadURL = function (dataURL,
  90. filename) {
  91. var a = doc.createElement('a'),
  92. windowRef;
  93. // IE specific blob implementation
  94. // Don't use for normal dataURLs
  95. if (typeof dataURL !== 'string' &&
  96. !(dataURL instanceof String) &&
  97. nav.msSaveOrOpenBlob) {
  98. nav.msSaveOrOpenBlob(dataURL, filename);
  99. return;
  100. }
  101. dataURL = "" + dataURL;
  102. // Some browsers have limitations for data URL lengths. Try to convert to
  103. // Blob or fall back. Edge always needs that blob.
  104. if (isEdgeBrowser || dataURL.length > 2000000) {
  105. dataURL = dataURLtoBlob(dataURL) || '';
  106. if (!dataURL) {
  107. throw new Error('Failed to convert to blob');
  108. }
  109. }
  110. // Try HTML5 download attr if supported
  111. if (typeof a.download !== 'undefined') {
  112. a.href = dataURL;
  113. a.download = filename; // HTML5 download attribute
  114. doc.body.appendChild(a);
  115. a.click();
  116. doc.body.removeChild(a);
  117. }
  118. else {
  119. // No download attr, just opening data URI
  120. try {
  121. windowRef = win.open(dataURL, 'chart');
  122. if (typeof windowRef === 'undefined' || windowRef === null) {
  123. throw new Error('Failed to open window');
  124. }
  125. }
  126. catch (e) {
  127. // window.open failed, trying location.href
  128. win.location.href = dataURL;
  129. }
  130. }
  131. };
  132. var exports = {
  133. dataURLtoBlob: dataURLtoBlob,
  134. downloadURL: downloadURL
  135. };
  136. return exports;
  137. });
  138. _registerModule(_modules, 'Extensions/OfflineExporting.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js'], _modules['Extensions/DownloadURL.js']], function (Chart, H, SVGRenderer, U, DownloadURL) {
  139. /* *
  140. *
  141. * Client side exporting module
  142. *
  143. * (c) 2015 Torstein Honsi / Oystein Moseng
  144. *
  145. * License: www.highcharts.com/license
  146. *
  147. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  148. *
  149. * */
  150. /* global MSBlobBuilder */
  151. var win = H.win,
  152. doc = H.doc;
  153. var addEvent = U.addEvent,
  154. error = U.error,
  155. extend = U.extend,
  156. getOptions = U.getOptions,
  157. merge = U.merge;
  158. var downloadURL = DownloadURL.downloadURL;
  159. var domurl = win.URL || win.webkitURL || win,
  160. nav = win.navigator,
  161. isMSBrowser = /Edge\/|Trident\/|MSIE /.test(nav.userAgent),
  162. // Milliseconds to defer image load event handlers to offset IE bug
  163. loadEventDeferDelay = isMSBrowser ? 150 : 0;
  164. // Dummy object so we can reuse our canvas-tools.js without errors
  165. H.CanVGRenderer = {};
  166. /* eslint-disable valid-jsdoc */
  167. /**
  168. * Downloads a script and executes a callback when done.
  169. *
  170. * @private
  171. * @function getScript
  172. * @param {string} scriptLocation
  173. * @param {Function} callback
  174. * @return {void}
  175. */
  176. function getScript(scriptLocation, callback) {
  177. var head = doc.getElementsByTagName('head')[0], script = doc.createElement('script');
  178. script.type = 'text/javascript';
  179. script.src = scriptLocation;
  180. script.onload = callback;
  181. script.onerror = function () {
  182. error('Error loading script ' + scriptLocation);
  183. };
  184. head.appendChild(script);
  185. }
  186. /**
  187. * Get blob URL from SVG code. Falls back to normal data URI.
  188. *
  189. * @private
  190. * @function Highcharts.svgToDataURL
  191. * @param {string} svg
  192. * @return {string}
  193. */
  194. H.svgToDataUrl = function (svg) {
  195. // Webkit and not chrome
  196. var webKit = (nav.userAgent.indexOf('WebKit') > -1 &&
  197. nav.userAgent.indexOf('Chrome') < 0);
  198. try {
  199. // Safari requires data URI since it doesn't allow navigation to blob
  200. // URLs. Firefox has an issue with Blobs and internal references,
  201. // leading to gradients not working using Blobs (#4550)
  202. if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
  203. return domurl.createObjectURL(new win.Blob([svg], {
  204. type: 'image/svg+xml;charset-utf-16'
  205. }));
  206. }
  207. }
  208. catch (e) {
  209. // Ignore
  210. }
  211. return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
  212. };
  213. /**
  214. * Get data:URL from image URL. Pass in callbacks to handle results.
  215. *
  216. * @private
  217. * @function Highcharts.imageToDataUrl
  218. *
  219. * @param {string} imageURL
  220. *
  221. * @param {string} imageType
  222. *
  223. * @param {*} callbackArgs
  224. * callbackArgs is used only by callbacks.
  225. *
  226. * @param {number} scale
  227. *
  228. * @param {Function} successCallback
  229. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  230. *
  231. * @param {Function} taintedCallback
  232. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  233. *
  234. * @param {Function} noCanvasSupportCallback
  235. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  236. *
  237. * @param {Function} failedLoadCallback
  238. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  239. *
  240. * @param {Function} [finallyCallback]
  241. * finallyCallback is always called at the end of the process. All
  242. * callbacks receive four arguments: imageURL, imageType, callbackArgs,
  243. * and scale.
  244. *
  245. * @return {void}
  246. */
  247. H.imageToDataUrl = function (imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
  248. var img = new win.Image(), taintedHandler, loadHandler = function () {
  249. setTimeout(function () {
  250. var canvas = doc.createElement('canvas'), ctx = canvas.getContext && canvas.getContext('2d'), dataURL;
  251. try {
  252. if (!ctx) {
  253. noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
  254. }
  255. else {
  256. canvas.height = img.height * scale;
  257. canvas.width = img.width * scale;
  258. ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  259. // Now we try to get the contents of the canvas.
  260. try {
  261. dataURL = canvas.toDataURL(imageType);
  262. successCallback(dataURL, imageType, callbackArgs, scale);
  263. }
  264. catch (e) {
  265. taintedHandler(imageURL, imageType, callbackArgs, scale);
  266. }
  267. }
  268. }
  269. finally {
  270. if (finallyCallback) {
  271. finallyCallback(imageURL, imageType, callbackArgs, scale);
  272. }
  273. }
  274. // IE bug where image is not always ready despite calling load
  275. // event.
  276. }, loadEventDeferDelay);
  277. },
  278. // Image load failed (e.g. invalid URL)
  279. errorHandler = function () {
  280. failedLoadCallback(imageURL, imageType, callbackArgs, scale);
  281. if (finallyCallback) {
  282. finallyCallback(imageURL, imageType, callbackArgs, scale);
  283. }
  284. };
  285. // This is called on load if the image drawing to canvas failed with a
  286. // security error. We retry the drawing with crossOrigin set to Anonymous.
  287. taintedHandler = function () {
  288. img = new win.Image();
  289. taintedHandler = taintedCallback;
  290. // Must be set prior to loading image source
  291. img.crossOrigin = 'Anonymous';
  292. img.onload = loadHandler;
  293. img.onerror = errorHandler;
  294. img.src = imageURL;
  295. };
  296. img.onload = loadHandler;
  297. img.onerror = errorHandler;
  298. img.src = imageURL;
  299. };
  300. /* eslint-enable valid-jsdoc */
  301. /**
  302. * Get data URL to an image of an SVG and call download on it options object:
  303. *
  304. * - **filename:** Name of resulting downloaded file without extension. Default
  305. * is `chart`.
  306. *
  307. * - **type:** File type of resulting download. Default is `image/png`.
  308. *
  309. * - **scale:** Scaling factor of downloaded image compared to source. Default
  310. * is `1`.
  311. *
  312. * - **libURL:** URL pointing to location of dependency scripts to download on
  313. * demand. Default is the exporting.libURL option of the global Highcharts
  314. * options pointing to our server.
  315. *
  316. * @function Highcharts.downloadSVGLocal
  317. *
  318. * @param {string} svg
  319. * The generated SVG
  320. *
  321. * @param {Highcharts.ExportingOptions} options
  322. * The exporting options
  323. *
  324. * @param {Function} failCallback
  325. * The callback function in case of errors
  326. *
  327. * @param {Function} [successCallback]
  328. * The callback function in case of success
  329. *
  330. * @return {void}
  331. */
  332. H.downloadSVGLocal = function (svg, options, failCallback, successCallback) {
  333. var svgurl, blob, objectURLRevoke = true, finallyHandler, libURL = (options.libURL || getOptions().exporting.libURL), dummySVGContainer = doc.createElement('div'), imageType = options.type || 'image/png', filename = ((options.filename || 'chart') +
  334. '.' +
  335. (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1])), scale = options.scale || 1;
  336. // Allow libURL to end with or without fordward slash
  337. libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL;
  338. /* eslint-disable valid-jsdoc */
  339. /**
  340. * @private
  341. */
  342. function svgToPdf(svgElement, margin) {
  343. var width = svgElement.width.baseVal.value + 2 * margin,
  344. height = svgElement.height.baseVal.value + 2 * margin,
  345. pdf = new win.jsPDF(// eslint-disable-line new-cap
  346. height > width ? 'p' : 'l', // setting orientation to portrait if height exceeds width
  347. 'pt',
  348. [width,
  349. height]);
  350. // Workaround for #7090, hidden elements were drawn anyway. It comes
  351. // down to https://github.com/yWorks/svg2pdf.js/issues/28. Check this
  352. // later.
  353. [].forEach.call(svgElement.querySelectorAll('*[visibility="hidden"]'), function (node) {
  354. node.parentNode.removeChild(node);
  355. });
  356. win.svg2pdf(svgElement, pdf, { removeInvalid: true });
  357. return pdf.output('datauristring');
  358. }
  359. /**
  360. * @private
  361. * @return {void}
  362. */
  363. function downloadPDF() {
  364. dummySVGContainer.innerHTML = svg;
  365. var textElements = dummySVGContainer.getElementsByTagName('text'),
  366. titleElements,
  367. svgData,
  368. // Copy style property to element from parents if it's not there.
  369. // Searches up hierarchy until it finds prop, or hits the chart
  370. // container.
  371. setStylePropertyFromParents = function (el,
  372. propName) {
  373. var curParent = el;
  374. while (curParent && curParent !== dummySVGContainer) {
  375. if (curParent.style[propName]) {
  376. el.style[propName] =
  377. curParent.style[propName];
  378. break;
  379. }
  380. curParent = curParent.parentNode;
  381. }
  382. };
  383. // Workaround for the text styling. Making sure it does pick up settings
  384. // for parent elements.
  385. [].forEach.call(textElements, function (el) {
  386. // Workaround for the text styling. making sure it does pick up the
  387. // root element
  388. ['font-family', 'font-size'].forEach(function (property) {
  389. setStylePropertyFromParents(el, property);
  390. });
  391. el.style['font-family'] = (el.style['font-family'] &&
  392. el.style['font-family'].split(' ').splice(-1));
  393. // Workaround for plotband with width, removing title from text
  394. // nodes
  395. titleElements = el.getElementsByTagName('title');
  396. [].forEach.call(titleElements, function (titleElement) {
  397. el.removeChild(titleElement);
  398. });
  399. });
  400. svgData = svgToPdf(dummySVGContainer.firstChild, 0);
  401. try {
  402. downloadURL(svgData, filename);
  403. if (successCallback) {
  404. successCallback();
  405. }
  406. }
  407. catch (e) {
  408. failCallback(e);
  409. }
  410. }
  411. /* eslint-enable valid-jsdoc */
  412. // Initiate download depending on file type
  413. if (imageType === 'image/svg+xml') {
  414. // SVG download. In this case, we want to use Microsoft specific Blob if
  415. // available
  416. try {
  417. if (typeof nav.msSaveOrOpenBlob !== 'undefined') {
  418. blob = new MSBlobBuilder();
  419. blob.append(svg);
  420. svgurl = blob.getBlob('image/svg+xml');
  421. }
  422. else {
  423. svgurl = H.svgToDataUrl(svg);
  424. }
  425. downloadURL(svgurl, filename);
  426. if (successCallback) {
  427. successCallback();
  428. }
  429. }
  430. catch (e) {
  431. failCallback(e);
  432. }
  433. }
  434. else if (imageType === 'application/pdf') {
  435. if (win.jsPDF && win.svg2pdf) {
  436. downloadPDF();
  437. }
  438. else {
  439. // Must load pdf libraries first. // Don't destroy the object URL
  440. // yet since we are doing things asynchronously. A cleaner solution
  441. // would be nice, but this will do for now.
  442. objectURLRevoke = true;
  443. getScript(libURL + 'jspdf.js', function () {
  444. getScript(libURL + 'svg2pdf.js', function () {
  445. downloadPDF();
  446. });
  447. });
  448. }
  449. }
  450. else {
  451. // PNG/JPEG download - create bitmap from SVG
  452. svgurl = H.svgToDataUrl(svg);
  453. finallyHandler = function () {
  454. try {
  455. domurl.revokeObjectURL(svgurl);
  456. }
  457. catch (e) {
  458. // Ignore
  459. }
  460. };
  461. // First, try to get PNG by rendering on canvas
  462. H.imageToDataUrl(svgurl, imageType, {}, scale, function (imageURL) {
  463. // Success
  464. try {
  465. downloadURL(imageURL, filename);
  466. if (successCallback) {
  467. successCallback();
  468. }
  469. }
  470. catch (e) {
  471. failCallback(e);
  472. }
  473. }, function () {
  474. // Failed due to tainted canvas
  475. // Create new and untainted canvas
  476. 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 () {
  477. ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
  478. try {
  479. downloadURL(nav.msSaveOrOpenBlob ?
  480. canvas.msToBlob() :
  481. canvas.toDataURL(imageType), filename);
  482. if (successCallback) {
  483. successCallback();
  484. }
  485. }
  486. catch (e) {
  487. failCallback(e);
  488. }
  489. finally {
  490. finallyHandler();
  491. }
  492. };
  493. canvas.width = imageWidth;
  494. canvas.height = imageHeight;
  495. if (win.canvg) {
  496. // Use preloaded canvg
  497. downloadWithCanVG();
  498. }
  499. else {
  500. // Must load canVG first. // Don't destroy the object URL
  501. // yet since we are doing things asynchronously. A cleaner
  502. // solution would be nice, but this will do for now.
  503. objectURLRevoke = true;
  504. // Get RGBColor.js first, then canvg
  505. getScript(libURL + 'rgbcolor.js', function () {
  506. getScript(libURL + 'canvg.js', function () {
  507. downloadWithCanVG();
  508. });
  509. });
  510. }
  511. },
  512. // No canvas support
  513. failCallback,
  514. // Failed to load image
  515. failCallback,
  516. // Finally
  517. function () {
  518. if (objectURLRevoke) {
  519. finallyHandler();
  520. }
  521. });
  522. }
  523. };
  524. /* eslint-disable valid-jsdoc */
  525. /**
  526. * Get SVG of chart prepared for client side export. This converts embedded
  527. * images in the SVG to data URIs. It requires the regular exporting module. The
  528. * options and chartOptions arguments are passed to the getSVGForExport
  529. * function.
  530. *
  531. * @private
  532. * @function Highcharts.Chart#getSVGForLocalExport
  533. * @param {Highcharts.ExportingOptions} options
  534. * @param {Highcharts.Options} chartOptions
  535. * @param {Function} failCallback
  536. * @param {Function} successCallback
  537. * @return {void}
  538. */
  539. Chart.prototype.getSVGForLocalExport = function (options, chartOptions, failCallback, successCallback) {
  540. var chart = this,
  541. images,
  542. imagesEmbedded = 0,
  543. chartCopyContainer,
  544. chartCopyOptions,
  545. el,
  546. i,
  547. l,
  548. href,
  549. // After grabbing the SVG of the chart's copy container we need to do
  550. // sanitation on the SVG
  551. sanitize = function (svg) {
  552. return chart.sanitizeSVG(svg,
  553. chartCopyOptions);
  554. },
  555. // When done with last image we have our SVG
  556. checkDone = function () {
  557. if (imagesEmbedded === images.length) {
  558. successCallback(sanitize(chartCopyContainer.innerHTML));
  559. }
  560. },
  561. // Success handler, we converted image to base64!
  562. embeddedSuccess = function (imageURL, imageType, callbackArgs) {
  563. ++imagesEmbedded;
  564. // Change image href in chart copy
  565. callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
  566. checkDone();
  567. };
  568. // Hook into getSVG to get a copy of the chart copy's container (#8273)
  569. chart.unbindGetSVG = addEvent(chart, 'getSVG', function (e) {
  570. chartCopyOptions = e.chartCopy.options;
  571. chartCopyContainer = e.chartCopy.container.cloneNode(true);
  572. });
  573. // Trigger hook to get chart copy
  574. chart.getSVGForExport(options, chartOptions);
  575. images = chartCopyContainer.getElementsByTagName('image');
  576. try {
  577. // If there are no images to embed, the SVG is okay now.
  578. if (!images.length) {
  579. // Use SVG of chart copy
  580. successCallback(sanitize(chartCopyContainer.innerHTML));
  581. return;
  582. }
  583. // Go through the images we want to embed
  584. for (i = 0, l = images.length; i < l; ++i) {
  585. el = images[i];
  586. href = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
  587. if (href) {
  588. H.imageToDataUrl(href, 'image/png', { imageElement: el }, options.scale, embeddedSuccess,
  589. // Tainted canvas
  590. failCallback,
  591. // No canvas support
  592. failCallback,
  593. // Failed to load source
  594. failCallback);
  595. // Hidden, boosted series have blank href (#10243)
  596. }
  597. else {
  598. ++imagesEmbedded;
  599. el.parentNode.removeChild(el);
  600. checkDone();
  601. }
  602. }
  603. }
  604. catch (e) {
  605. failCallback(e);
  606. }
  607. // Clean up
  608. chart.unbindGetSVG();
  609. };
  610. /* eslint-enable valid-jsdoc */
  611. /**
  612. * Exporting and offline-exporting modules required. Export a chart to an image
  613. * locally in the user's browser.
  614. *
  615. * @function Highcharts.Chart#exportChartLocal
  616. *
  617. * @param {Highcharts.ExportingOptions} [exportingOptions]
  618. * Exporting options, the same as in
  619. * {@link Highcharts.Chart#exportChart}.
  620. *
  621. * @param {Highcharts.Options} [chartOptions]
  622. * Additional chart options for the exported chart. For example a
  623. * different background color can be added here, or `dataLabels`
  624. * for export only.
  625. *
  626. * @return {void}
  627. *
  628. * @requires modules/exporting
  629. */
  630. Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
  631. var chart = this,
  632. options = merge(chart.options.exporting,
  633. exportingOptions),
  634. fallbackToExportServer = function (err) {
  635. if (options.fallbackToExportServer === false) {
  636. if (options.error) {
  637. options.error(options,
  638. err);
  639. }
  640. else {
  641. error(28, true); // Fallback disabled
  642. }
  643. }
  644. else {
  645. chart.exportChart(options);
  646. }
  647. }, svgSuccess = function (svg) {
  648. // If SVG contains foreignObjects all exports except SVG will fail,
  649. // as both CanVG and svg2pdf choke on this. Gracefully fall back.
  650. if (svg.indexOf('<foreignObject') > -1 &&
  651. options.type !== 'image/svg+xml') {
  652. fallbackToExportServer('Image type not supported' +
  653. 'for charts with embedded HTML');
  654. }
  655. else {
  656. H.downloadSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  657. }
  658. },
  659. // Return true if the SVG contains images with external data. With the
  660. // boost module there are `image` elements with encoded PNGs, these are
  661. // supported by svg2pdf and should pass (#10243).
  662. hasExternalImages = function () {
  663. return [].some.call(chart.container.getElementsByTagName('image'), function (image) {
  664. var href = image.getAttribute('href');
  665. return href !== '' && href.indexOf('data:') !== 0;
  666. });
  667. };
  668. // If we are on IE and in styled mode, add a whitelist to the renderer for
  669. // inline styles that we want to pass through. There are so many styles by
  670. // default in IE that we don't want to blacklist them all.
  671. if (isMSBrowser && chart.styledMode) {
  672. SVGRenderer.prototype.inlineWhitelist = [
  673. /^blockSize/,
  674. /^border/,
  675. /^caretColor/,
  676. /^color/,
  677. /^columnRule/,
  678. /^columnRuleColor/,
  679. /^cssFloat/,
  680. /^cursor/,
  681. /^fill$/,
  682. /^fillOpacity/,
  683. /^font/,
  684. /^inlineSize/,
  685. /^length/,
  686. /^lineHeight/,
  687. /^opacity/,
  688. /^outline/,
  689. /^parentRule/,
  690. /^rx$/,
  691. /^ry$/,
  692. /^stroke/,
  693. /^textAlign/,
  694. /^textAnchor/,
  695. /^textDecoration/,
  696. /^transform/,
  697. /^vectorEffect/,
  698. /^visibility/,
  699. /^x$/,
  700. /^y$/
  701. ];
  702. }
  703. // Always fall back on:
  704. // - MS browsers: Embedded images JPEG/PNG, or any PDF
  705. // - Embedded images and PDF
  706. if ((isMSBrowser &&
  707. (options.type === 'application/pdf' ||
  708. chart.container.getElementsByTagName('image').length &&
  709. options.type !== 'image/svg+xml')) || (options.type === 'application/pdf' &&
  710. hasExternalImages())) {
  711. fallbackToExportServer('Image type not supported for this chart/browser.');
  712. return;
  713. }
  714. chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
  715. };
  716. // Extend the default options to use the local exporter logic
  717. merge(true, getOptions().exporting, {
  718. libURL: 'https://code.highcharts.com/8.2.0/lib/',
  719. // When offline-exporting is loaded, redefine the menu item definitions
  720. // related to download.
  721. menuItemDefinitions: {
  722. downloadPNG: {
  723. textKey: 'downloadPNG',
  724. onclick: function () {
  725. this.exportChartLocal();
  726. }
  727. },
  728. downloadJPEG: {
  729. textKey: 'downloadJPEG',
  730. onclick: function () {
  731. this.exportChartLocal({
  732. type: 'image/jpeg'
  733. });
  734. }
  735. },
  736. downloadSVG: {
  737. textKey: 'downloadSVG',
  738. onclick: function () {
  739. this.exportChartLocal({
  740. type: 'image/svg+xml'
  741. });
  742. }
  743. },
  744. downloadPDF: {
  745. textKey: 'downloadPDF',
  746. onclick: function () {
  747. this.exportChartLocal({
  748. type: 'application/pdf'
  749. });
  750. }
  751. }
  752. }
  753. });
  754. });
  755. _registerModule(_modules, 'masters/modules/offline-exporting.src.js', [], function () {
  756. });
  757. }));