offline-exporting.src.js 34 KB

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