offline-exporting.src.js 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. /**
  2. * @license Highcharts JS v8.2.2 (2020-10-22)
  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
  59. .replace(/filename=.*;/, '')
  60. .match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
  61. if (parts &&
  62. parts.length > 3 &&
  63. win.atob &&
  64. win.ArrayBuffer &&
  65. win.Uint8Array &&
  66. win.Blob &&
  67. domurl.createObjectURL) {
  68. // Try to convert data URL to Blob
  69. var binStr = win.atob(parts[3]),
  70. buf = new win.ArrayBuffer(binStr.length),
  71. binary = new win.Uint8Array(buf);
  72. for (var i = 0; i < binary.length; ++i) {
  73. binary[i] = binStr.charCodeAt(i);
  74. }
  75. var blob = new win.Blob([binary], { 'type': parts[1] });
  76. return domurl.createObjectURL(blob);
  77. }
  78. };
  79. /**
  80. * Download a data URL in the browser. Can also take a blob as first param.
  81. *
  82. * @private
  83. * @function Highcharts.downloadURL
  84. * @param {string|global.URL} dataURL
  85. * The dataURL/Blob to download
  86. * @param {string} filename
  87. * The name of the resulting file (w/extension)
  88. * @return {void}
  89. */
  90. var downloadURL = Highcharts.downloadURL = function (dataURL,
  91. filename) {
  92. var a = doc.createElement('a'),
  93. windowRef;
  94. // IE specific blob implementation
  95. // Don't use for normal dataURLs
  96. if (typeof dataURL !== 'string' &&
  97. !(dataURL instanceof String) &&
  98. nav.msSaveOrOpenBlob) {
  99. nav.msSaveOrOpenBlob(dataURL, filename);
  100. return;
  101. }
  102. dataURL = "" + dataURL;
  103. // Some browsers have limitations for data URL lengths. Try to convert to
  104. // Blob or fall back. Edge always needs that blob.
  105. if (isEdgeBrowser || dataURL.length > 2000000) {
  106. dataURL = dataURLtoBlob(dataURL) || '';
  107. if (!dataURL) {
  108. throw new Error('Failed to convert to blob');
  109. }
  110. }
  111. // Try HTML5 download attr if supported
  112. if (typeof a.download !== 'undefined') {
  113. a.href = dataURL;
  114. a.download = filename; // HTML5 download attribute
  115. doc.body.appendChild(a);
  116. a.click();
  117. doc.body.removeChild(a);
  118. }
  119. else {
  120. // No download attr, just opening data URI
  121. try {
  122. windowRef = win.open(dataURL, 'chart');
  123. if (typeof windowRef === 'undefined' || windowRef === null) {
  124. throw new Error('Failed to open window');
  125. }
  126. }
  127. catch (e) {
  128. // window.open failed, trying location.href
  129. win.location.href = dataURL;
  130. }
  131. }
  132. };
  133. var exports = {
  134. dataURLtoBlob: dataURLtoBlob,
  135. downloadURL: downloadURL
  136. };
  137. return exports;
  138. });
  139. _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) {
  140. /* *
  141. *
  142. * Client side exporting module
  143. *
  144. * (c) 2015 Torstein Honsi / Oystein Moseng
  145. *
  146. * License: www.highcharts.com/license
  147. *
  148. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  149. *
  150. * */
  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. var imageData;
  165. // Dummy object so we can reuse our canvas-tools.js without errors
  166. H.CanVGRenderer = {};
  167. /* eslint-disable valid-jsdoc */
  168. /**
  169. * Downloads a script and executes a callback when done.
  170. *
  171. * @private
  172. * @function getScript
  173. * @param {string} scriptLocation
  174. * @param {Function} callback
  175. * @return {void}
  176. */
  177. function getScript(scriptLocation, callback) {
  178. var head = doc.getElementsByTagName('head')[0], script = doc.createElement('script');
  179. script.type = 'text/javascript';
  180. script.src = scriptLocation;
  181. script.onload = callback;
  182. script.onerror = function () {
  183. error('Error loading script ' + scriptLocation);
  184. };
  185. head.appendChild(script);
  186. }
  187. /**
  188. * Get blob URL from SVG code. Falls back to normal data URI.
  189. *
  190. * @private
  191. * @function Highcharts.svgToDataURL
  192. * @param {string} svg
  193. * @return {string}
  194. */
  195. function svgToDataUrl(svg) {
  196. // Webkit and not chrome
  197. var webKit = (nav.userAgent.indexOf('WebKit') > -1 &&
  198. nav.userAgent.indexOf('Chrome') < 0);
  199. try {
  200. // Safari requires data URI since it doesn't allow navigation to blob
  201. // URLs. Firefox has an issue with Blobs and internal references,
  202. // leading to gradients not working using Blobs (#4550)
  203. if (!webKit && nav.userAgent.toLowerCase().indexOf('firefox') < 0) {
  204. return domurl.createObjectURL(new win.Blob([svg], {
  205. type: 'image/svg+xml;charset-utf-16'
  206. }));
  207. }
  208. }
  209. catch (e) {
  210. // Ignore
  211. }
  212. return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
  213. }
  214. /**
  215. * Get data:URL from image URL. Pass in callbacks to handle results.
  216. *
  217. * @private
  218. * @function Highcharts.imageToDataUrl
  219. *
  220. * @param {string} imageURL
  221. *
  222. * @param {string} imageType
  223. *
  224. * @param {*} callbackArgs
  225. * callbackArgs is used only by callbacks.
  226. *
  227. * @param {number} scale
  228. *
  229. * @param {Function} successCallback
  230. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  231. *
  232. * @param {Function} taintedCallback
  233. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  234. *
  235. * @param {Function} noCanvasSupportCallback
  236. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  237. *
  238. * @param {Function} failedLoadCallback
  239. * Receives four arguments: imageURL, imageType, callbackArgs, and scale.
  240. *
  241. * @param {Function} [finallyCallback]
  242. * finallyCallback is always called at the end of the process. All
  243. * callbacks receive four arguments: imageURL, imageType, callbackArgs,
  244. * and scale.
  245. *
  246. * @return {void}
  247. */
  248. function imageToDataUrl(imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
  249. var img = new win.Image(), taintedHandler, loadHandler = function () {
  250. setTimeout(function () {
  251. var canvas = doc.createElement('canvas'), ctx = canvas.getContext && canvas.getContext('2d'), dataURL;
  252. try {
  253. if (!ctx) {
  254. noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
  255. }
  256. else {
  257. canvas.height = img.height * scale;
  258. canvas.width = img.width * scale;
  259. ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  260. // Now we try to get the contents of the canvas.
  261. try {
  262. dataURL = canvas.toDataURL(imageType);
  263. successCallback(dataURL, imageType, callbackArgs, scale);
  264. }
  265. catch (e) {
  266. taintedHandler(imageURL, imageType, callbackArgs, scale);
  267. }
  268. }
  269. }
  270. finally {
  271. if (finallyCallback) {
  272. finallyCallback(imageURL, imageType, callbackArgs, scale);
  273. }
  274. }
  275. // IE bug where image is not always ready despite calling load
  276. // event.
  277. }, loadEventDeferDelay);
  278. },
  279. // Image load failed (e.g. invalid URL)
  280. errorHandler = function () {
  281. failedLoadCallback(imageURL, imageType, callbackArgs, scale);
  282. if (finallyCallback) {
  283. finallyCallback(imageURL, imageType, callbackArgs, scale);
  284. }
  285. };
  286. // This is called on load if the image drawing to canvas failed with a
  287. // security error. We retry the drawing with crossOrigin set to Anonymous.
  288. taintedHandler = function () {
  289. img = new win.Image();
  290. taintedHandler = taintedCallback;
  291. // Must be set prior to loading image source
  292. img.crossOrigin = 'Anonymous';
  293. img.onload = loadHandler;
  294. img.onerror = errorHandler;
  295. img.src = imageURL;
  296. };
  297. img.onload = loadHandler;
  298. img.onerror = errorHandler;
  299. img.src = imageURL;
  300. }
  301. /* eslint-enable valid-jsdoc */
  302. /**
  303. * Get data URL to an image of an SVG and call download on it options object:
  304. *
  305. * - **filename:** Name of resulting downloaded file without extension. Default
  306. * is `chart`.
  307. *
  308. * - **type:** File type of resulting download. Default is `image/png`.
  309. *
  310. * - **scale:** Scaling factor of downloaded image compared to source. Default
  311. * is `1`.
  312. *
  313. * - **libURL:** URL pointing to location of dependency scripts to download on
  314. * demand. Default is the exporting.libURL option of the global Highcharts
  315. * options pointing to our server.
  316. *
  317. * @function Highcharts.downloadSVGLocal
  318. *
  319. * @param {string} svg
  320. * The generated SVG
  321. *
  322. * @param {Highcharts.ExportingOptions} options
  323. * The exporting options
  324. *
  325. * @param {Function} failCallback
  326. * The callback function in case of errors
  327. *
  328. * @param {Function} [successCallback]
  329. * The callback function in case of success
  330. *
  331. * @return {void}
  332. */
  333. function downloadSVGLocal(svg, options, failCallback, successCallback) {
  334. 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') +
  335. '.' +
  336. (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1])), scale = options.scale || 1;
  337. // Allow libURL to end with or without fordward slash
  338. libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL;
  339. /* eslint-disable valid-jsdoc */
  340. /**
  341. * @private
  342. */
  343. function svgToPdf(svgElement, margin) {
  344. var width = svgElement.width.baseVal.value + 2 * margin,
  345. height = svgElement.height.baseVal.value + 2 * margin,
  346. pdf = new win.jsPDF(// eslint-disable-line new-cap
  347. height > width ? 'p' : 'l', // setting orientation to portrait if height exceeds width
  348. 'pt',
  349. [width,
  350. height]);
  351. // Workaround for #7090, hidden elements were drawn anyway. It comes
  352. // down to https://github.com/yWorks/svg2pdf.js/issues/28. Check this
  353. // later.
  354. [].forEach.call(svgElement.querySelectorAll('*[visibility="hidden"]'), function (node) {
  355. node.parentNode.removeChild(node);
  356. });
  357. win.svg2pdf(svgElement, pdf, { removeInvalid: true });
  358. return pdf.output('datauristring');
  359. }
  360. /**
  361. * @private
  362. * @return {void}
  363. */
  364. function downloadPDF() {
  365. dummySVGContainer.innerHTML = svg;
  366. var textElements = dummySVGContainer.getElementsByTagName('text'),
  367. titleElements,
  368. svgData,
  369. // Copy style property to element from parents if it's not there.
  370. // Searches up hierarchy until it finds prop, or hits the chart
  371. // container.
  372. setStylePropertyFromParents = function (el,
  373. propName) {
  374. var curParent = el;
  375. while (curParent && curParent !== dummySVGContainer) {
  376. if (curParent.style[propName]) {
  377. el.style[propName] =
  378. curParent.style[propName];
  379. break;
  380. }
  381. curParent = curParent.parentNode;
  382. }
  383. };
  384. // Workaround for the text styling. Making sure it does pick up settings
  385. // for parent elements.
  386. [].forEach.call(textElements, function (el) {
  387. // Workaround for the text styling. making sure it does pick up the
  388. // root element
  389. ['font-family', 'font-size'].forEach(function (property) {
  390. setStylePropertyFromParents(el, property);
  391. });
  392. el.style['font-family'] = (el.style['font-family'] &&
  393. el.style['font-family'].split(' ').splice(-1));
  394. // Workaround for plotband with width, removing title from text
  395. // nodes
  396. titleElements = el.getElementsByTagName('title');
  397. [].forEach.call(titleElements, function (titleElement) {
  398. el.removeChild(titleElement);
  399. });
  400. });
  401. svgData = svgToPdf(dummySVGContainer.firstChild, 0);
  402. try {
  403. downloadURL(svgData, filename);
  404. if (successCallback) {
  405. successCallback();
  406. }
  407. }
  408. catch (e) {
  409. failCallback(e);
  410. }
  411. }
  412. /* eslint-enable valid-jsdoc */
  413. // Initiate download depending on file type
  414. if (imageType === 'image/svg+xml') {
  415. // SVG download. In this case, we want to use Microsoft specific Blob if
  416. // available
  417. try {
  418. if (typeof nav.msSaveOrOpenBlob !== 'undefined') {
  419. blob = new MSBlobBuilder();
  420. blob.append(svg);
  421. svgurl = blob.getBlob('image/svg+xml');
  422. }
  423. else {
  424. svgurl = svgToDataUrl(svg);
  425. }
  426. downloadURL(svgurl, filename);
  427. if (successCallback) {
  428. successCallback();
  429. }
  430. }
  431. catch (e) {
  432. failCallback(e);
  433. }
  434. }
  435. else if (imageType === 'application/pdf') {
  436. if (win.jsPDF && win.svg2pdf) {
  437. downloadPDF();
  438. }
  439. else {
  440. // Must load pdf libraries first. // Don't destroy the object URL
  441. // yet since we are doing things asynchronously. A cleaner solution
  442. // would be nice, but this will do for now.
  443. objectURLRevoke = true;
  444. getScript(libURL + 'jspdf.js', function () {
  445. getScript(libURL + 'svg2pdf.js', function () {
  446. downloadPDF();
  447. });
  448. });
  449. }
  450. }
  451. else {
  452. // PNG/JPEG download - create bitmap from SVG
  453. svgurl = svgToDataUrl(svg);
  454. finallyHandler = function () {
  455. try {
  456. domurl.revokeObjectURL(svgurl);
  457. }
  458. catch (e) {
  459. // Ignore
  460. }
  461. };
  462. // First, try to get PNG by rendering on canvas
  463. imageToDataUrl(svgurl, imageType, {}, scale, function (imageURL) {
  464. // Success
  465. try {
  466. downloadURL(imageURL, filename);
  467. if (successCallback) {
  468. successCallback();
  469. }
  470. }
  471. catch (e) {
  472. failCallback(e);
  473. }
  474. }, function () {
  475. // Failed due to tainted canvas
  476. // Create new and untainted canvas
  477. 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 () {
  478. ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
  479. try {
  480. downloadURL(nav.msSaveOrOpenBlob ?
  481. canvas.msToBlob() :
  482. canvas.toDataURL(imageType), filename);
  483. if (successCallback) {
  484. successCallback();
  485. }
  486. }
  487. catch (e) {
  488. failCallback(e);
  489. }
  490. finally {
  491. finallyHandler();
  492. }
  493. };
  494. canvas.width = imageWidth;
  495. canvas.height = imageHeight;
  496. if (win.canvg) {
  497. // Use preloaded canvg
  498. downloadWithCanVG();
  499. }
  500. else {
  501. // Must load canVG first. // Don't destroy the object URL
  502. // yet since we are doing things asynchronously. A cleaner
  503. // solution would be nice, but this will do for now.
  504. objectURLRevoke = true;
  505. // Get RGBColor.js first, then canvg
  506. getScript(libURL + 'rgbcolor.js', function () {
  507. getScript(libURL + 'canvg.js', function () {
  508. downloadWithCanVG();
  509. });
  510. });
  511. }
  512. },
  513. // No canvas support
  514. failCallback,
  515. // Failed to load image
  516. failCallback,
  517. // Finally
  518. function () {
  519. if (objectURLRevoke) {
  520. finallyHandler();
  521. }
  522. });
  523. }
  524. }
  525. function getSVGLocal(svg, options, failCallback, successCallback) {
  526. 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') +
  527. '.' +
  528. (imageType === 'image/svg+xml' ? 'svg' : imageType.split('/')[1])), scale = options.scale || 1;
  529. // Allow libURL to end with or without fordward slash
  530. libURL = libURL.slice(-1) !== '/' ? libURL + '/' : libURL;
  531. /* eslint-disable valid-jsdoc */
  532. /**
  533. * @private
  534. */
  535. function svgToPdf(svgElement, margin) {
  536. var width = svgElement.width.baseVal.value + 2 * margin,
  537. height = svgElement.height.baseVal.value + 2 * margin,
  538. pdf = new win.jsPDF(// eslint-disable-line new-cap
  539. height > width ? 'p' : 'l', // setting orientation to portrait if height exceeds width
  540. 'pt',
  541. [width,
  542. height]);
  543. // Workaround for #7090, hidden elements were drawn anyway. It comes
  544. // down to https://github.com/yWorks/svg2pdf.js/issues/28. Check this
  545. // later.
  546. [].forEach.call(svgElement.querySelectorAll('*[visibility="hidden"]'), function (node) {
  547. node.parentNode.removeChild(node);
  548. });
  549. win.svg2pdf(svgElement, pdf, { removeInvalid: true });
  550. return pdf.output('datauristring');
  551. }
  552. /**
  553. * @private
  554. * @return {void}
  555. */
  556. function downloadPDF() {
  557. dummySVGContainer.innerHTML = svg;
  558. var textElements = dummySVGContainer.getElementsByTagName('text'),
  559. titleElements,
  560. svgData,
  561. // Copy style property to element from parents if it's not there.
  562. // Searches up hierarchy until it finds prop, or hits the chart
  563. // container.
  564. setStylePropertyFromParents = function (el,
  565. propName) {
  566. var curParent = el;
  567. while (curParent && curParent !== dummySVGContainer) {
  568. if (curParent.style[propName]) {
  569. el.style[propName] =
  570. curParent.style[propName];
  571. break;
  572. }
  573. curParent = curParent.parentNode;
  574. }
  575. };
  576. // Workaround for the text styling. Making sure it does pick up settings
  577. // for parent elements.
  578. [].forEach.call(textElements, function (el) {
  579. // Workaround for the text styling. making sure it does pick up the
  580. // root element
  581. ['font-family', 'font-size'].forEach(function (property) {
  582. setStylePropertyFromParents(el, property);
  583. });
  584. el.style['font-family'] = (el.style['font-family'] &&
  585. el.style['font-family'].split(' ').splice(-1));
  586. // Workaround for plotband with width, removing title from text
  587. // nodes
  588. titleElements = el.getElementsByTagName('title');
  589. [].forEach.call(titleElements, function (titleElement) {
  590. el.removeChild(titleElement);
  591. });
  592. });
  593. svgData = svgToPdf(dummySVGContainer.firstChild, 0);
  594. try {
  595. //downloadURL(svgData, filename);
  596. imageData = svgData;
  597. if (successCallback) {
  598. successCallback();
  599. }
  600. }
  601. catch (e) {
  602. failCallback(e);
  603. }
  604. }
  605. /* eslint-enable valid-jsdoc */
  606. // Initiate download depending on file type
  607. if (imageType === 'image/svg+xml') {
  608. // SVG download. In this case, we want to use Microsoft specific Blob if
  609. // available
  610. try {
  611. if (typeof nav.msSaveOrOpenBlob !== 'undefined') {
  612. blob = new MSBlobBuilder();
  613. blob.append(svg);
  614. svgurl = blob.getBlob('image/svg+xml');
  615. }
  616. else {
  617. svgurl = svgToDataUrl(svg);
  618. }
  619. //downloadURL(svgurl, filename);
  620. imageData = svgurl;
  621. if (successCallback) {
  622. successCallback();
  623. }
  624. }
  625. catch (e) {
  626. failCallback(e);
  627. }
  628. }
  629. else if (imageType === 'application/pdf') {
  630. if (win.jsPDF && win.svg2pdf) {
  631. downloadPDF();
  632. }
  633. else {
  634. // Must load pdf libraries first. // Don't destroy the object URL
  635. // yet since we are doing things asynchronously. A cleaner solution
  636. // would be nice, but this will do for now.
  637. objectURLRevoke = true;
  638. getScript(libURL + 'jspdf.js', function () {
  639. getScript(libURL + 'svg2pdf.js', function () {
  640. downloadPDF();
  641. });
  642. });
  643. }
  644. }
  645. else {
  646. // PNG/JPEG download - create bitmap from SVG
  647. debugger;
  648. svgurl = svgToDataUrl(svg);
  649. finallyHandler = function () {
  650. try {
  651. domurl.revokeObjectURL(svgurl);
  652. }
  653. catch (e) {
  654. // Ignore
  655. }
  656. };
  657. // First, try to get PNG by rendering on canvas
  658. imageToDataUrl(svgurl, imageType, {}, scale, function (imageURL) {
  659. // Success
  660. try {
  661. // downloadURL(imageURL, filename);
  662. imageData = imageURL;
  663. if (successCallback) {
  664. successCallback();
  665. }
  666. }
  667. catch (e) {
  668. failCallback(e);
  669. }
  670. }, function () {
  671. // Failed due to tainted canvas
  672. // Create new and untainted canvas
  673. 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 () {
  674. ctx.drawSvg(svg, 0, 0, imageWidth, imageHeight);
  675. try {
  676. //downloadURL(nav.msSaveOrOpenBlob ?
  677. // canvas.msToBlob() :
  678. // canvas.toDataURL(imageType), filename);
  679. imageData = nav.msSaveOrOpenBlob ? canvas.msToBlob() : canvas.toDataURL(imageType);
  680. if (successCallback) {
  681. successCallback();
  682. }
  683. }
  684. catch (e) {
  685. failCallback(e);
  686. }
  687. finally {
  688. finallyHandler();
  689. }
  690. };
  691. canvas.width = imageWidth;
  692. canvas.height = imageHeight;
  693. if (win.canvg) {
  694. // Use preloaded canvg
  695. downloadWithCanVG();
  696. }
  697. else {
  698. // Must load canVG first. // Don't destroy the object URL
  699. // yet since we are doing things asynchronously. A cleaner
  700. // solution would be nice, but this will do for now.
  701. objectURLRevoke = true;
  702. // Get RGBColor.js first, then canvg
  703. getScript(libURL + 'rgbcolor.js', function () {
  704. getScript(libURL + 'canvg.js', function () {
  705. downloadWithCanVG();
  706. });
  707. });
  708. }
  709. },
  710. // No canvas support
  711. failCallback,
  712. // Failed to load image
  713. failCallback,
  714. // Finally
  715. function () {
  716. if (objectURLRevoke) {
  717. finallyHandler();
  718. }
  719. });
  720. }
  721. }
  722. function getImageUrl(svg, options) {
  723. var svgUrl = svgToDataUrl(svg), imageType = options.type || 'image/png', scale = options.scale || 1;
  724. return image2DataUrl(svgUrl, imageType, {}, scale);
  725. }
  726. Chart.prototype.getSvg = function(options, chartOptions) {
  727. var chart = this,svg,
  728. images,
  729. imagesEmbedded = 0,
  730. chartCopyContainer,
  731. chartCopyOptions,
  732. el,
  733. i,
  734. l,
  735. href,
  736. // After grabbing the SVG of the chart's copy container we need to do
  737. // sanitation on the SVG
  738. sanitize = function (svg) {
  739. return chart.sanitizeSVG(svg,
  740. chartCopyOptions);
  741. },
  742. // When done with last image we have our SVG
  743. checkDone = function () {
  744. if (imagesEmbedded === images.length) {
  745. svg = sanitize(chartCopyContainer.innerHTML);
  746. }
  747. },
  748. // Success handler, we converted image to base64!
  749. embeddedSuccess = function (imageURL, imageType, callbackArgs) {
  750. ++imagesEmbedded;
  751. // Change image href in chart copy
  752. callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
  753. checkDone();
  754. };
  755. // Hook into getSVG to get a copy of the chart copy's container (#8273)
  756. chart.unbindGetSVG = addEvent(chart, 'getSVG', function (e) {
  757. chartCopyOptions = e.chartCopy.options;
  758. chartCopyContainer = e.chartCopy.container.cloneNode(true);
  759. });
  760. // Trigger hook to get chart copy
  761. chart.getSVGForExport(options, chartOptions);
  762. images = chartCopyContainer.getElementsByTagName('image');
  763. try {
  764. // If there are no images to embed, the SVG is okay now.
  765. if (!images.length) {
  766. // Use SVG of chart copy
  767. return sanitize(chartCopyContainer.innerHTML);
  768. //return;
  769. }
  770. // Go through the images we want to embed
  771. for (i = 0, l = images.length; i < l; ++i) {
  772. el = images[i];
  773. href = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
  774. if (href) {
  775. imageToDataUrl(href, 'image/png', { imageElement: el }, options.scale, embeddedSuccess,
  776. // Tainted canvas
  777. failCallback,
  778. // No canvas support
  779. failCallback,
  780. // Failed to load source
  781. failCallback);
  782. // Hidden, boosted series have blank href (#10243)
  783. }
  784. else {
  785. ++imagesEmbedded;
  786. el.parentNode.removeChild(el);
  787. checkDone();
  788. }
  789. }
  790. }
  791. catch (e) {
  792. //
  793. }
  794. // Clean up
  795. chart.unbindGetSVG();
  796. return svg;
  797. };
  798. /* eslint-disable valid-jsdoc */
  799. /**
  800. * Get SVG of chart prepared for client side export. This converts embedded
  801. * images in the SVG to data URIs. It requires the regular exporting module. The
  802. * options and chartOptions arguments are passed to the getSVGForExport
  803. * function.
  804. *
  805. * @private
  806. * @function Highcharts.Chart#getSVGForLocalExport
  807. * @param {Highcharts.ExportingOptions} options
  808. * @param {Highcharts.Options} chartOptions
  809. * @param {Function} failCallback
  810. * @param {Function} successCallback
  811. * @return {void}
  812. */
  813. Chart.prototype.getSVGForLocalExport = function (options, chartOptions, failCallback, successCallback) {
  814. var chart = this,
  815. images,
  816. imagesEmbedded = 0,
  817. chartCopyContainer,
  818. chartCopyOptions,
  819. el,
  820. i,
  821. l,
  822. href,
  823. // After grabbing the SVG of the chart's copy container we need to do
  824. // sanitation on the SVG
  825. sanitize = function (svg) {
  826. return chart.sanitizeSVG(svg,
  827. chartCopyOptions);
  828. },
  829. // When done with last image we have our SVG
  830. checkDone = function () {
  831. if (imagesEmbedded === images.length) {
  832. var s = sanitize(chartCopyContainer.innerHTML);
  833. successCallback(sanitize(chartCopyContainer.innerHTML));
  834. }
  835. },
  836. // Success handler, we converted image to base64!
  837. embeddedSuccess = function (imageURL, imageType, callbackArgs) {
  838. ++imagesEmbedded;
  839. // Change image href in chart copy
  840. callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
  841. checkDone();
  842. };
  843. // Hook into getSVG to get a copy of the chart copy's container (#8273)
  844. chart.unbindGetSVG = addEvent(chart, 'getSVG', function (e) {
  845. chartCopyOptions = e.chartCopy.options;
  846. chartCopyContainer = e.chartCopy.container.cloneNode(true);
  847. });
  848. // Trigger hook to get chart copy
  849. chart.getSVGForExport(options, chartOptions);
  850. images = chartCopyContainer.getElementsByTagName('image');
  851. try {
  852. // If there are no images to embed, the SVG is okay now.
  853. if (!images.length) {
  854. // Use SVG of chart copy
  855. successCallback(sanitize(chartCopyContainer.innerHTML));
  856. return;
  857. }
  858. // Go through the images we want to embed
  859. for (i = 0, l = images.length; i < l; ++i) {
  860. el = images[i];
  861. href = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
  862. if (href) {
  863. imageToDataUrl(href, 'image/png', { imageElement: el }, options.scale, embeddedSuccess,
  864. // Tainted canvas
  865. failCallback,
  866. // No canvas support
  867. failCallback,
  868. // Failed to load source
  869. failCallback);
  870. // Hidden, boosted series have blank href (#10243)
  871. }
  872. else {
  873. ++imagesEmbedded;
  874. el.parentNode.removeChild(el);
  875. checkDone();
  876. }
  877. }
  878. }
  879. catch (e) {
  880. failCallback(e);
  881. }
  882. // Clean up
  883. chart.unbindGetSVG();
  884. };
  885. /* eslint-enable valid-jsdoc */
  886. /**
  887. * Exporting and offline-exporting modules required. Export a chart to an image
  888. * locally in the user's browser.
  889. *
  890. * @function Highcharts.Chart#exportChartLocal
  891. *
  892. * @param {Highcharts.ExportingOptions} [exportingOptions]
  893. * Exporting options, the same as in
  894. * {@link Highcharts.Chart#exportChart}.
  895. *
  896. * @param {Highcharts.Options} [chartOptions]
  897. * Additional chart options for the exported chart. For example a
  898. * different background color can be added here, or `dataLabels`
  899. * for export only.
  900. *
  901. * @return {void}
  902. *
  903. * @requires modules/exporting
  904. */
  905. Chart.prototype.exportChartLocal = function (exportingOptions, chartOptions) {
  906. var chart = this,
  907. options = merge(chart.options.exporting,
  908. exportingOptions),
  909. fallbackToExportServer = function (err) {
  910. if (options.fallbackToExportServer === false) {
  911. if (options.error) {
  912. options.error(options,
  913. err);
  914. }
  915. else {
  916. error(28, true); // Fallback disabled
  917. }
  918. }
  919. else {
  920. chart.exportChart(options);
  921. }
  922. }, svgSuccess = function (svg) {
  923. // If SVG contains foreignObjects all exports except SVG will fail,
  924. // as both CanVG and svg2pdf choke on this. Gracefully fall back.
  925. if (svg.indexOf('<foreignObject') > -1 &&
  926. options.type !== 'image/svg+xml') {
  927. fallbackToExportServer('Image type not supported' +
  928. 'for charts with embedded HTML');
  929. }
  930. else {
  931. downloadSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  932. }
  933. },
  934. // Return true if the SVG contains images with external data. With the
  935. // boost module there are `image` elements with encoded PNGs, these are
  936. // supported by svg2pdf and should pass (#10243).
  937. hasExternalImages = function () {
  938. return [].some.call(chart.container.getElementsByTagName('image'), function (image) {
  939. var href = image.getAttribute('href');
  940. return href !== '' && href.indexOf('data:') !== 0;
  941. });
  942. };
  943. // If we are on IE and in styled mode, add a whitelist to the renderer for
  944. // inline styles that we want to pass through. There are so many styles by
  945. // default in IE that we don't want to blacklist them all.
  946. if (isMSBrowser && chart.styledMode) {
  947. SVGRenderer.prototype.inlineWhitelist = [
  948. /^blockSize/,
  949. /^border/,
  950. /^caretColor/,
  951. /^color/,
  952. /^columnRule/,
  953. /^columnRuleColor/,
  954. /^cssFloat/,
  955. /^cursor/,
  956. /^fill$/,
  957. /^fillOpacity/,
  958. /^font/,
  959. /^inlineSize/,
  960. /^length/,
  961. /^lineHeight/,
  962. /^opacity/,
  963. /^outline/,
  964. /^parentRule/,
  965. /^rx$/,
  966. /^ry$/,
  967. /^stroke/,
  968. /^textAlign/,
  969. /^textAnchor/,
  970. /^textDecoration/,
  971. /^transform/,
  972. /^vectorEffect/,
  973. /^visibility/,
  974. /^x$/,
  975. /^y$/
  976. ];
  977. }
  978. // Always fall back on:
  979. // - MS browsers: Embedded images JPEG/PNG, or any PDF
  980. // - Embedded images and PDF
  981. if ((isMSBrowser &&
  982. (options.type === 'application/pdf' ||
  983. chart.container.getElementsByTagName('image').length &&
  984. options.type !== 'image/svg+xml')) || (options.type === 'application/pdf' &&
  985. hasExternalImages())) {
  986. fallbackToExportServer('Image type not supported for this chart/browser.');
  987. return;
  988. }
  989. chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
  990. };
  991. Chart.prototype.getImageData = function ( exportingOptions, chartOptions) {
  992. var chart = this,
  993. options = merge(chart.options.exporting,
  994. exportingOptions),
  995. fallbackToExportServer = function (err) {
  996. if (options.fallbackToExportServer === false) {
  997. if (options.error) {
  998. options.error(options,
  999. err);
  1000. }
  1001. else {
  1002. error(28, true); // Fallback disabled
  1003. }
  1004. }
  1005. else {
  1006. chart.exportChart(options);
  1007. }
  1008. }, svgSuccess = function (svg) {
  1009. // If SVG contains foreignObjects all exports except SVG will fail,
  1010. // as both CanVG and svg2pdf choke on this. Gracefully fall back.
  1011. if (svg.indexOf('<foreignObject') > -1 &&
  1012. options.type !== 'image/svg+xml') {
  1013. fallbackToExportServer('Image type not supported' +
  1014. 'for charts with embedded HTML');
  1015. }
  1016. else {
  1017. //downloadSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  1018. getSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  1019. }
  1020. },
  1021. // Return true if the SVG contains images with external data. With the
  1022. // boost module there are `image` elements with encoded PNGs, these are
  1023. // supported by svg2pdf and should pass (#10243).
  1024. hasExternalImages = function () {
  1025. return [].some.call(chart.container.getElementsByTagName('image'), function (image) {
  1026. var href = image.getAttribute('href');
  1027. return href !== '' && href.indexOf('data:') !== 0;
  1028. });
  1029. };
  1030. // If we are on IE and in styled mode, add a whitelist to the renderer for
  1031. // inline styles that we want to pass through. There are so many styles by
  1032. // default in IE that we don't want to blacklist them all.
  1033. if (isMSBrowser && chart.styledMode) {
  1034. SVGRenderer.prototype.inlineWhitelist = [
  1035. /^blockSize/,
  1036. /^border/,
  1037. /^caretColor/,
  1038. /^color/,
  1039. /^columnRule/,
  1040. /^columnRuleColor/,
  1041. /^cssFloat/,
  1042. /^cursor/,
  1043. /^fill$/,
  1044. /^fillOpacity/,
  1045. /^font/,
  1046. /^inlineSize/,
  1047. /^length/,
  1048. /^lineHeight/,
  1049. /^opacity/,
  1050. /^outline/,
  1051. /^parentRule/,
  1052. /^rx$/,
  1053. /^ry$/,
  1054. /^stroke/,
  1055. /^textAlign/,
  1056. /^textAnchor/,
  1057. /^textDecoration/,
  1058. /^transform/,
  1059. /^vectorEffect/,
  1060. /^visibility/,
  1061. /^x$/,
  1062. /^y$/
  1063. ];
  1064. }
  1065. // Always fall back on:
  1066. // - MS browsers: Embedded images JPEG/PNG, or any PDF
  1067. // - Embedded images and PDF
  1068. if ((isMSBrowser &&
  1069. (options.type === 'application/pdf' ||
  1070. chart.container.getElementsByTagName('image').length &&
  1071. options.type !== 'image/svg+xml')) || (options.type === 'application/pdf' &&
  1072. hasExternalImages())) {
  1073. fallbackToExportServer('Image type not supported for this chart/browser.');
  1074. return imageData;
  1075. }
  1076. chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
  1077. return imageData;
  1078. };
  1079. Chart.prototype.getImage = function ( exportingOptions, chartOptions) {
  1080. var chart = this,
  1081. options = merge(chart.options.exporting,
  1082. exportingOptions),
  1083. fallbackToExportServer = function (err) {
  1084. if (options.fallbackToExportServer === false) {
  1085. if (options.error) {
  1086. options.error(options,
  1087. err);
  1088. }
  1089. else {
  1090. error(28, true); // Fallback disabled
  1091. }
  1092. }
  1093. else {
  1094. chart.exportChart(options);
  1095. }
  1096. }, svgSuccess = function (svg) {
  1097. // If SVG contains foreignObjects all exports except SVG will fail,
  1098. // as both CanVG and svg2pdf choke on this. Gracefully fall back.
  1099. if (svg.indexOf('<foreignObject') > -1 &&
  1100. options.type !== 'image/svg+xml') {
  1101. fallbackToExportServer('Image type not supported' +
  1102. 'for charts with embedded HTML');
  1103. }
  1104. else {
  1105. //downloadSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  1106. return getSVGLocal(svg, extend({ filename: chart.getFilename() }, options), fallbackToExportServer);
  1107. }
  1108. },
  1109. // Return true if the SVG contains images with external data. With the
  1110. // boost module there are `image` elements with encoded PNGs, these are
  1111. // supported by svg2pdf and should pass (#10243).
  1112. hasExternalImages = function () {
  1113. return [].some.call(chart.container.getElementsByTagName('image'), function (image) {
  1114. var href = image.getAttribute('href');
  1115. return href !== '' && href.indexOf('data:') !== 0;
  1116. });
  1117. };
  1118. // If we are on IE and in styled mode, add a whitelist to the renderer for
  1119. // inline styles that we want to pass through. There are so many styles by
  1120. // default in IE that we don't want to blacklist them all.
  1121. if (isMSBrowser && chart.styledMode) {
  1122. SVGRenderer.prototype.inlineWhitelist = [
  1123. /^blockSize/,
  1124. /^border/,
  1125. /^caretColor/,
  1126. /^color/,
  1127. /^columnRule/,
  1128. /^columnRuleColor/,
  1129. /^cssFloat/,
  1130. /^cursor/,
  1131. /^fill$/,
  1132. /^fillOpacity/,
  1133. /^font/,
  1134. /^inlineSize/,
  1135. /^length/,
  1136. /^lineHeight/,
  1137. /^opacity/,
  1138. /^outline/,
  1139. /^parentRule/,
  1140. /^rx$/,
  1141. /^ry$/,
  1142. /^stroke/,
  1143. /^textAlign/,
  1144. /^textAnchor/,
  1145. /^textDecoration/,
  1146. /^transform/,
  1147. /^vectorEffect/,
  1148. /^visibility/,
  1149. /^x$/,
  1150. /^y$/
  1151. ];
  1152. }
  1153. // Always fall back on:
  1154. // - MS browsers: Embedded images JPEG/PNG, or any PDF
  1155. // - Embedded images and PDF
  1156. if ((isMSBrowser &&
  1157. (options.type === 'application/pdf' ||
  1158. chart.container.getElementsByTagName('image').length &&
  1159. options.type !== 'image/svg+xml')) || (options.type === 'application/pdf' &&
  1160. hasExternalImages())) {
  1161. fallbackToExportServer('Image type not supported for this chart/browser.');
  1162. return imageData;
  1163. }
  1164. //chart.getSVGForLocalExport(options, chartOptions, fallbackToExportServer, svgSuccess);
  1165. return chart.getSVGForLocalExport2(options, chartOptions, fallbackToExportServer, svgSuccess);
  1166. };
  1167. Chart.prototype.getSVGForLocalExport2 = function (options, chartOptions, failCallback, successCallback) {
  1168. var chart = this,
  1169. images,
  1170. imagesEmbedded = 0,
  1171. chartCopyContainer,
  1172. chartCopyOptions,
  1173. el,
  1174. i,
  1175. l,
  1176. href,
  1177. // After grabbing the SVG of the chart's copy container we need to do
  1178. // sanitation on the SVG
  1179. sanitize = function (svg) {
  1180. return chart.sanitizeSVG(svg,
  1181. chartCopyOptions);
  1182. },
  1183. // When done with last image we have our SVG
  1184. checkDone = function () {
  1185. if (imagesEmbedded === images.length) {
  1186. return successCallback(sanitize(chartCopyContainer.innerHTML));
  1187. }
  1188. return undefined;
  1189. },
  1190. // Success handler, we converted image to base64!
  1191. embeddedSuccess = function (imageURL, imageType, callbackArgs) {
  1192. ++imagesEmbedded;
  1193. // Change image href in chart copy
  1194. callbackArgs.imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imageURL);
  1195. checkDone();
  1196. };
  1197. // Hook into getSVG to get a copy of the chart copy's container (#8273)
  1198. chart.unbindGetSVG = addEvent(chart, 'getSVG', function (e) {
  1199. chartCopyOptions = e.chartCopy.options;
  1200. chartCopyContainer = e.chartCopy.container.cloneNode(true);
  1201. });
  1202. // Trigger hook to get chart copy
  1203. chart.getSVGForExport(options, chartOptions);
  1204. images = chartCopyContainer.getElementsByTagName('image');
  1205. try {
  1206. // If there are no images to embed, the SVG is okay now.
  1207. if (!images.length) {
  1208. // Use SVG of chart copy
  1209. successCallback(sanitize(chartCopyContainer.innerHTML));
  1210. return;
  1211. }
  1212. // Go through the images we want to embed
  1213. for (i = 0, l = images.length; i < l; ++i) {
  1214. el = images[i];
  1215. href = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
  1216. if (href) {
  1217. imageToDataUrl(href, 'image/png', { imageElement: el }, options.scale, embeddedSuccess,
  1218. // Tainted canvas
  1219. failCallback,
  1220. // No canvas support
  1221. failCallback,
  1222. // Failed to load source
  1223. failCallback);
  1224. // Hidden, boosted series have blank href (#10243)
  1225. }
  1226. else {
  1227. ++imagesEmbedded;
  1228. el.parentNode.removeChild(el);
  1229. checkDone();
  1230. }
  1231. }
  1232. }
  1233. catch (e) {
  1234. failCallback(e);
  1235. }
  1236. // Clean up
  1237. chart.unbindGetSVG();
  1238. };
  1239. function image2DataUrl(imageURL, imageType, callbackArgs, scale, successCallback, taintedCallback, noCanvasSupportCallback, failedLoadCallback, finallyCallback) {
  1240. var img = new win.Image(), taintedHandler, loadHandler = function () {
  1241. setTimeout(function () {
  1242. var canvas = doc.createElement('canvas'), ctx = canvas.getContext && canvas.getContext('2d'), dataURL;
  1243. try {
  1244. if (!ctx) {
  1245. noCanvasSupportCallback(imageURL, imageType, callbackArgs, scale);
  1246. }
  1247. else {
  1248. canvas.height = img.height * scale;
  1249. canvas.width = img.width * scale;
  1250. ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  1251. // Now we try to get the contents of the canvas.
  1252. try {
  1253. dataURL = canvas.toDataURL(imageType);
  1254. successCallback(dataURL, imageType, callbackArgs, scale);
  1255. }
  1256. catch (e) {
  1257. taintedHandler(imageURL, imageType, callbackArgs, scale);
  1258. }
  1259. }
  1260. }
  1261. finally {
  1262. if (finallyCallback) {
  1263. finallyCallback(imageURL, imageType, callbackArgs, scale);
  1264. }
  1265. }
  1266. // IE bug where image is not always ready despite calling load
  1267. // event.
  1268. }, loadEventDeferDelay);
  1269. },
  1270. // Image load failed (e.g. invalid URL)
  1271. errorHandler = function () {
  1272. failedLoadCallback(imageURL, imageType, callbackArgs, scale);
  1273. if (finallyCallback) {
  1274. finallyCallback(imageURL, imageType, callbackArgs, scale);
  1275. }
  1276. };
  1277. // This is called on load if the image drawing to canvas failed with a
  1278. // security error. We retry the drawing with crossOrigin set to Anonymous.
  1279. taintedHandler = function () {
  1280. img = new win.Image();
  1281. taintedHandler = taintedCallback;
  1282. // Must be set prior to loading image source
  1283. img.crossOrigin = 'Anonymous';
  1284. img.onload = loadHandler;
  1285. img.onerror = errorHandler;
  1286. img.src = imageURL;
  1287. };
  1288. img.onload = loadHandler;
  1289. img.onerror = errorHandler;
  1290. img.src = imageURL;
  1291. return img;
  1292. }
  1293. // Extend the default options to use the local exporter logic
  1294. merge(true, getOptions().exporting, {
  1295. libURL: 'https://code.highcharts.com/8.2.2/lib/',
  1296. // When offline-exporting is loaded, redefine the menu item definitions
  1297. // related to download.
  1298. menuItemDefinitions: {
  1299. downloadPNG: {
  1300. textKey: 'downloadPNG',
  1301. onclick: function () {
  1302. this.exportChartLocal();
  1303. }
  1304. },
  1305. downloadJPEG: {
  1306. textKey: 'downloadJPEG',
  1307. onclick: function () {
  1308. this.exportChartLocal({
  1309. type: 'image/jpeg'
  1310. });
  1311. }
  1312. },
  1313. downloadSVG: {
  1314. textKey: 'downloadSVG',
  1315. onclick: function () {
  1316. this.exportChartLocal({
  1317. type: 'image/svg+xml'
  1318. });
  1319. }
  1320. },
  1321. downloadPDF: {
  1322. textKey: 'downloadPDF',
  1323. onclick: function () {
  1324. this.exportChartLocal({
  1325. type: 'application/pdf'
  1326. });
  1327. }
  1328. }
  1329. }
  1330. });
  1331. // Compatibility
  1332. H.downloadSVGLocal = downloadSVGLocal;
  1333. });
  1334. _registerModule(_modules, 'masters/modules/offline-exporting.src.js', [], function () {
  1335. });
  1336. }));