offline-exporting.src.js 31 KB

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