offline-exporting.src.js 31 KB

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