export-data.src.js 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199
  1. /**
  2. * @license Highcharts JS v9.1.1 (2021-06-04)
  3. *
  4. * Exporting module
  5. *
  6. * (c) 2010-2021 Torstein Honsi
  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/export-data', ['highcharts', 'highcharts/modules/exporting'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Extensions/DownloadURL.js', [_modules['Core/Globals.js']], function (Highcharts) {
  32. /* *
  33. *
  34. * (c) 2015-2021 Oystein Moseng
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * Mixin for downloading content in the browser
  41. *
  42. * */
  43. var isSafari = Highcharts.isSafari;
  44. var win = Highcharts.win,
  45. doc = win.document,
  46. domurl = win.URL || win.webkitURL || win;
  47. /**
  48. * Convert base64 dataURL to Blob if supported, otherwise returns undefined.
  49. * @private
  50. * @function Highcharts.dataURLtoBlob
  51. * @param {string} dataURL
  52. * URL to convert
  53. * @return {string|undefined}
  54. * Blob
  55. */
  56. var dataURLtoBlob = Highcharts.dataURLtoBlob = function (dataURL) {
  57. var parts = dataURL
  58. .replace(/filename=.*;/, '')
  59. .match(/data:([^;]*)(;base64)?,([0-9A-Za-z+/]+)/);
  60. if (parts &&
  61. parts.length > 3 &&
  62. win.atob &&
  63. win.ArrayBuffer &&
  64. win.Uint8Array &&
  65. win.Blob &&
  66. domurl.createObjectURL) {
  67. // Try to convert data URL to Blob
  68. var binStr = win.atob(parts[3]),
  69. buf = new win.ArrayBuffer(binStr.length),
  70. binary = new win.Uint8Array(buf);
  71. for (var i = 0; i < binary.length; ++i) {
  72. binary[i] = binStr.charCodeAt(i);
  73. }
  74. var blob = new win.Blob([binary], { 'type': parts[1] });
  75. return domurl.createObjectURL(blob);
  76. }
  77. };
  78. /**
  79. * Download a data URL in the browser. Can also take a blob as first param.
  80. *
  81. * @private
  82. * @function Highcharts.downloadURL
  83. * @param {string|global.URL} dataURL
  84. * The dataURL/Blob to download
  85. * @param {string} filename
  86. * The name of the resulting file (w/extension)
  87. * @return {void}
  88. */
  89. var downloadURL = Highcharts.downloadURL = function (dataURL,
  90. filename) {
  91. var nav = win.navigator,
  92. a = doc.createElement('a');
  93. // IE specific blob implementation
  94. // Don't use for normal dataURLs
  95. if (typeof dataURL !== 'string' &&
  96. !(dataURL instanceof String) &&
  97. nav.msSaveOrOpenBlob) {
  98. nav.msSaveOrOpenBlob(dataURL, filename);
  99. return;
  100. }
  101. dataURL = "" + dataURL;
  102. // Some browsers have limitations for data URL lengths. Try to convert to
  103. // Blob or fall back. Edge always needs that blob.
  104. var isOldEdgeBrowser = /Edge\/\d+/.test(nav.userAgent);
  105. // Safari on iOS needs Blob in order to download PDF
  106. var safariBlob = (isSafari &&
  107. typeof dataURL === 'string' &&
  108. dataURL.indexOf('data:application/pdf') === 0);
  109. if (safariBlob || isOldEdgeBrowser || dataURL.length > 2000000) {
  110. dataURL = dataURLtoBlob(dataURL) || '';
  111. if (!dataURL) {
  112. throw new Error('Failed to convert to blob');
  113. }
  114. }
  115. // Try HTML5 download attr if supported
  116. if (typeof a.download !== 'undefined') {
  117. a.href = dataURL;
  118. a.download = filename; // HTML5 download attribute
  119. doc.body.appendChild(a);
  120. a.click();
  121. doc.body.removeChild(a);
  122. }
  123. else {
  124. // No download attr, just opening data URI
  125. try {
  126. var windowRef = win.open(dataURL, 'chart');
  127. if (typeof windowRef === 'undefined' || windowRef === null) {
  128. throw new Error('Failed to open window');
  129. }
  130. }
  131. catch (e) {
  132. // window.open failed, trying location.href
  133. win.location.href = dataURL;
  134. }
  135. }
  136. };
  137. var exports = {
  138. dataURLtoBlob: dataURLtoBlob,
  139. downloadURL: downloadURL
  140. };
  141. return exports;
  142. });
  143. _registerModule(_modules, 'Extensions/ExportData.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Renderer/HTML/AST.js'], _modules['Core/Globals.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Utilities.js'], _modules['Extensions/DownloadURL.js']], function (Axis, Chart, AST, H, D, U, DownloadURL) {
  144. /* *
  145. *
  146. * Experimental data export module for Highcharts
  147. *
  148. * (c) 2010-2021 Torstein Honsi
  149. *
  150. * License: www.highcharts.com/license
  151. *
  152. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  153. *
  154. * */
  155. // @todo
  156. // - Set up systematic tests for all series types, paired with tests of the data
  157. // module importing the same data.
  158. var doc = H.doc,
  159. seriesTypes = H.seriesTypes,
  160. win = H.win;
  161. var getOptions = D.getOptions,
  162. setOptions = D.setOptions;
  163. var addEvent = U.addEvent,
  164. defined = U.defined,
  165. extend = U.extend,
  166. find = U.find,
  167. fireEvent = U.fireEvent,
  168. isNumber = U.isNumber,
  169. pick = U.pick;
  170. /**
  171. * Function callback to execute while data rows are processed for exporting.
  172. * This allows the modification of data rows before processed into the final
  173. * format.
  174. *
  175. * @callback Highcharts.ExportDataCallbackFunction
  176. * @extends Highcharts.EventCallbackFunction<Highcharts.Chart>
  177. *
  178. * @param {Highcharts.Chart} this
  179. * Chart context where the event occured.
  180. *
  181. * @param {Highcharts.ExportDataEventObject} event
  182. * Event object with data rows that can be modified.
  183. */
  184. /**
  185. * Contains information about the export data event.
  186. *
  187. * @interface Highcharts.ExportDataEventObject
  188. */ /**
  189. * Contains the data rows for the current export task and can be modified.
  190. * @name Highcharts.ExportDataEventObject#dataRows
  191. * @type {Array<Array<string>>}
  192. */
  193. var downloadURL = DownloadURL.downloadURL;
  194. setOptions({
  195. /**
  196. * Callback that fires while exporting data. This allows the modification of
  197. * data rows before processed into the final format.
  198. *
  199. * @type {Highcharts.ExportDataCallbackFunction}
  200. * @context Highcharts.Chart
  201. * @requires modules/export-data
  202. * @apioption chart.events.exportData
  203. */
  204. /**
  205. * When set to `false` will prevent the series data from being included in
  206. * any form of data export.
  207. *
  208. * Since version 6.0.0 until 7.1.0 the option was existing undocumented
  209. * as `includeInCSVExport`.
  210. *
  211. * @type {boolean}
  212. * @since 7.1.0
  213. * @requires modules/export-data
  214. * @apioption plotOptions.series.includeInDataExport
  215. */
  216. /**
  217. * @optionparent exporting
  218. * @private
  219. */
  220. exporting: {
  221. /**
  222. * Caption for the data table. Same as chart title by default. Set to
  223. * `false` to disable.
  224. *
  225. * @sample highcharts/export-data/multilevel-table
  226. * Multiple table headers
  227. *
  228. * @type {boolean|string}
  229. * @since 6.0.4
  230. * @requires modules/export-data
  231. * @apioption exporting.tableCaption
  232. */
  233. /**
  234. * Options for exporting data to CSV or ExCel, or displaying the data
  235. * in a HTML table or a JavaScript structure.
  236. *
  237. * This module adds data export options to the export menu and provides
  238. * functions like `Chart.getCSV`, `Chart.getTable`, `Chart.getDataRows`
  239. * and `Chart.viewData`.
  240. *
  241. * The XLS converter is limited and only creates a HTML string that is
  242. * passed for download, which works but creates a warning before
  243. * opening. The workaround for this is to use a third party XLSX
  244. * converter, as demonstrated in the sample below.
  245. *
  246. * @sample highcharts/export-data/categorized/ Categorized data
  247. * @sample highcharts/export-data/stock-timeaxis/ Highcharts Stock time axis
  248. * @sample highcharts/export-data/xlsx/
  249. * Using a third party XLSX converter
  250. *
  251. * @since 6.0.0
  252. * @requires modules/export-data
  253. */
  254. csv: {
  255. /**
  256. *
  257. * Options for annotations in the export-data table.
  258. *
  259. * @since 8.2.0
  260. * @requires modules/export-data
  261. * @requires modules/annotations
  262. *
  263. *
  264. */
  265. annotations: {
  266. /**
  267. * The way to mark the separator for annotations
  268. * combined in one export-data table cell.
  269. *
  270. * @since 8.2.0
  271. * @requires modules/annotations
  272. */
  273. itemDelimiter: '; ',
  274. /**
  275. * When several labels are assigned to a specific point,
  276. * they will be displayed in one field in the table.
  277. *
  278. * @sample highcharts/export-data/join-annotations/
  279. * Concatenate point annotations with itemDelimiter set.
  280. *
  281. * @since 8.2.0
  282. * @requires modules/annotations
  283. */
  284. join: false
  285. },
  286. /**
  287. * Formatter callback for the column headers. Parameters are:
  288. * - `item` - The series or axis object)
  289. * - `key` - The point key, for example y or z
  290. * - `keyLength` - The amount of value keys for this item, for
  291. * example a range series has the keys `low` and `high` so the
  292. * key length is 2.
  293. *
  294. * If [useMultiLevelHeaders](#exporting.useMultiLevelHeaders) is
  295. * true, columnHeaderFormatter by default returns an object with
  296. * columnTitle and topLevelColumnTitle for each key. Columns with
  297. * the same topLevelColumnTitle have their titles merged into a
  298. * single cell with colspan for table/Excel export.
  299. *
  300. * If `useMultiLevelHeaders` is false, or for CSV export, it returns
  301. * the series name, followed by the key if there is more than one
  302. * key.
  303. *
  304. * For the axis it returns the axis title or "Category" or
  305. * "DateTime" by default.
  306. *
  307. * Return `false` to use Highcharts' proposed header.
  308. *
  309. * @sample highcharts/export-data/multilevel-table
  310. * Multiple table headers
  311. *
  312. * @type {Function|null}
  313. */
  314. columnHeaderFormatter: null,
  315. /**
  316. * Which date format to use for exported dates on a datetime X axis.
  317. * See `Highcharts.dateFormat`.
  318. */
  319. dateFormat: '%Y-%m-%d %H:%M:%S',
  320. /**
  321. * Which decimal point to use for exported CSV. Defaults to the same
  322. * as the browser locale, typically `.` (English) or `,` (German,
  323. * French etc).
  324. *
  325. * @type {string|null}
  326. * @since 6.0.4
  327. */
  328. decimalPoint: null,
  329. /**
  330. * The item delimiter in the exported data. Use `;` for direct
  331. * exporting to Excel. Defaults to a best guess based on the browser
  332. * locale. If the locale _decimal point_ is `,`, the `itemDelimiter`
  333. * defaults to `;`, otherwise the `itemDelimiter` defaults to `,`.
  334. *
  335. * @type {string|null}
  336. */
  337. itemDelimiter: null,
  338. /**
  339. * The line delimiter in the exported data, defaults to a newline.
  340. */
  341. lineDelimiter: '\n'
  342. },
  343. /**
  344. * Show a HTML table below the chart with the chart's current data.
  345. *
  346. * @sample highcharts/export-data/showtable/
  347. * Show the table
  348. * @sample highcharts/studies/exporting-table-html
  349. * Experiment with putting the table inside the subtitle to
  350. * allow exporting it.
  351. *
  352. * @since 6.0.0
  353. * @requires modules/export-data
  354. */
  355. showTable: false,
  356. /**
  357. * Use multi level headers in data table. If [csv.columnHeaderFormatter
  358. * ](#exporting.csv.columnHeaderFormatter) is defined, it has to return
  359. * objects in order for multi level headers to work.
  360. *
  361. * @sample highcharts/export-data/multilevel-table
  362. * Multiple table headers
  363. *
  364. * @since 6.0.4
  365. * @requires modules/export-data
  366. */
  367. useMultiLevelHeaders: true,
  368. /**
  369. * If using multi level table headers, use rowspans for headers that
  370. * have only one level.
  371. *
  372. * @sample highcharts/export-data/multilevel-table
  373. * Multiple table headers
  374. *
  375. * @since 6.0.4
  376. * @requires modules/export-data
  377. */
  378. useRowspanHeaders: true
  379. },
  380. /**
  381. * @optionparent lang
  382. *
  383. * @private
  384. */
  385. lang: {
  386. /**
  387. * The text for the menu item.
  388. *
  389. * @since 6.0.0
  390. * @requires modules/export-data
  391. */
  392. downloadCSV: 'Download CSV',
  393. /**
  394. * The text for the menu item.
  395. *
  396. * @since 6.0.0
  397. * @requires modules/export-data
  398. */
  399. downloadXLS: 'Download XLS',
  400. /**
  401. * The text for exported table.
  402. *
  403. * @since 8.1.0
  404. * @requires modules/export-data
  405. */
  406. exportData: {
  407. /**
  408. * The annotation column title.
  409. */
  410. annotationHeader: 'Annotations',
  411. /**
  412. * The category column title.
  413. */
  414. categoryHeader: 'Category',
  415. /**
  416. * The category column title when axis type set to "datetime".
  417. */
  418. categoryDatetimeHeader: 'DateTime'
  419. },
  420. /**
  421. * The text for the menu item.
  422. *
  423. * @since 6.0.0
  424. * @requires modules/export-data
  425. */
  426. viewData: 'View data table',
  427. /**
  428. * The text for the menu item.
  429. *
  430. * @since 8.2.0
  431. * @requires modules/export-data
  432. */
  433. hideData: 'Hide data table'
  434. }
  435. });
  436. /* eslint-disable no-invalid-this */
  437. // Add an event listener to handle the showTable option
  438. addEvent(Chart, 'render', function () {
  439. if (this.options &&
  440. this.options.exporting &&
  441. this.options.exporting.showTable &&
  442. !this.options.chart.forExport &&
  443. !this.dataTableDiv) {
  444. this.viewData();
  445. }
  446. });
  447. /* eslint-enable no-invalid-this */
  448. /**
  449. * Set up key-to-axis bindings. This is used when the Y axis is datetime or
  450. * categorized. For example in an arearange series, the low and high values
  451. * should be formatted according to the Y axis type, and in order to link them
  452. * we need this map.
  453. *
  454. * @private
  455. * @function Highcharts.Chart#setUpKeyToAxis
  456. */
  457. Chart.prototype.setUpKeyToAxis = function () {
  458. if (seriesTypes.arearange) {
  459. seriesTypes.arearange.prototype.keyToAxis = {
  460. low: 'y',
  461. high: 'y'
  462. };
  463. }
  464. if (seriesTypes.gantt) {
  465. seriesTypes.gantt.prototype.keyToAxis = {
  466. start: 'x',
  467. end: 'x'
  468. };
  469. }
  470. };
  471. /**
  472. * Export-data module required. Returns a two-dimensional array containing the
  473. * current chart data.
  474. *
  475. * @function Highcharts.Chart#getDataRows
  476. *
  477. * @param {boolean} [multiLevelHeaders]
  478. * Use multilevel headers for the rows by default. Adds an extra row with
  479. * top level headers. If a custom columnHeaderFormatter is defined, this
  480. * can override the behavior.
  481. *
  482. * @return {Array<Array<(number|string)>>}
  483. * The current chart data
  484. *
  485. * @fires Highcharts.Chart#event:exportData
  486. */
  487. Chart.prototype.getDataRows = function (multiLevelHeaders) {
  488. var hasParallelCoords = this.hasParallelCoordinates,
  489. time = this.time,
  490. csvOptions = ((this.options.exporting && this.options.exporting.csv) || {}),
  491. xAxis,
  492. xAxes = this.xAxis,
  493. rows = {},
  494. rowArr = [],
  495. dataRows,
  496. topLevelColumnTitles = [],
  497. columnTitles = [],
  498. columnTitleObj,
  499. i,
  500. x,
  501. xTitle,
  502. langOptions = this.options.lang,
  503. exportDataOptions = langOptions.exportData,
  504. categoryHeader = exportDataOptions.categoryHeader,
  505. categoryDatetimeHeader = exportDataOptions.categoryDatetimeHeader,
  506. // Options
  507. columnHeaderFormatter = function (item,
  508. key,
  509. keyLength) {
  510. if (csvOptions.columnHeaderFormatter) {
  511. var s = csvOptions.columnHeaderFormatter(item,
  512. key,
  513. keyLength);
  514. if (s !== false) {
  515. return s;
  516. }
  517. }
  518. if (!item) {
  519. return categoryHeader;
  520. }
  521. if (item instanceof Axis) {
  522. return (item.options.title && item.options.title.text) ||
  523. (item.dateTime ? categoryDatetimeHeader : categoryHeader);
  524. }
  525. if (multiLevelHeaders) {
  526. return {
  527. columnTitle: keyLength > 1 ?
  528. key :
  529. item.name,
  530. topLevelColumnTitle: item.name
  531. };
  532. }
  533. return item.name + (keyLength > 1 ? ' (' + key + ')' : '');
  534. },
  535. // Map the categories for value axes
  536. getCategoryAndDateTimeMap = function (series, pointArrayMap, pIdx) {
  537. var categoryMap = {},
  538. dateTimeValueAxisMap = {};
  539. pointArrayMap.forEach(function (prop) {
  540. var axisName = ((series.keyToAxis && series.keyToAxis[prop]) ||
  541. prop) + 'Axis',
  542. // Points in parallel coordinates refers to all yAxis
  543. // not only `series.yAxis`
  544. axis = isNumber(pIdx) ?
  545. series.chart[axisName][pIdx] :
  546. series[axisName];
  547. categoryMap[prop] = (axis && axis.categories) || [];
  548. dateTimeValueAxisMap[prop] = (axis && axis.dateTime);
  549. });
  550. return {
  551. categoryMap: categoryMap,
  552. dateTimeValueAxisMap: dateTimeValueAxisMap
  553. };
  554. },
  555. // Create point array depends if xAxis is category
  556. // or point.name is defined #13293
  557. getPointArray = function (series, xAxis) {
  558. var namedPoints = series.data.filter(function (d) {
  559. return (typeof d.y !== 'undefined') && d.name;
  560. });
  561. if (namedPoints.length &&
  562. xAxis &&
  563. !xAxis.categories &&
  564. !series.keyToAxis) {
  565. if (series.pointArrayMap) {
  566. var pointArrayMapCheck = series.pointArrayMap.filter(function (p) { return p === 'x'; });
  567. if (pointArrayMapCheck.length) {
  568. series.pointArrayMap.unshift('x');
  569. return series.pointArrayMap;
  570. }
  571. }
  572. return ['x', 'y'];
  573. }
  574. return series.pointArrayMap || ['y'];
  575. }, xAxisIndices = [];
  576. // Loop the series and index values
  577. i = 0;
  578. this.setUpKeyToAxis();
  579. this.series.forEach(function (series) {
  580. var keys = series.options.keys,
  581. xAxis = series.xAxis,
  582. pointArrayMap = keys || getPointArray(series,
  583. xAxis),
  584. valueCount = pointArrayMap.length,
  585. xTaken = !series.requireSorting && {},
  586. xAxisIndex = xAxes.indexOf(xAxis);
  587. var categoryAndDatetimeMap = getCategoryAndDateTimeMap(series,
  588. pointArrayMap),
  589. mockSeries,
  590. j;
  591. if (series.options.includeInDataExport !== false &&
  592. !series.options.isInternal &&
  593. series.visible !== false // #55
  594. ) {
  595. // Build a lookup for X axis index and the position of the first
  596. // series that belongs to that X axis. Includes -1 for non-axis
  597. // series types like pies.
  598. if (!find(xAxisIndices, function (index) {
  599. return index[0] === xAxisIndex;
  600. })) {
  601. xAxisIndices.push([xAxisIndex, i]);
  602. }
  603. // Compute the column headers and top level headers, usually the
  604. // same as series names
  605. j = 0;
  606. while (j < valueCount) {
  607. columnTitleObj = columnHeaderFormatter(series, pointArrayMap[j], pointArrayMap.length);
  608. columnTitles.push(columnTitleObj.columnTitle || columnTitleObj);
  609. if (multiLevelHeaders) {
  610. topLevelColumnTitles.push(columnTitleObj.topLevelColumnTitle ||
  611. columnTitleObj);
  612. }
  613. j++;
  614. }
  615. mockSeries = {
  616. chart: series.chart,
  617. autoIncrement: series.autoIncrement,
  618. options: series.options,
  619. pointArrayMap: series.pointArrayMap
  620. };
  621. // Export directly from options.data because we need the uncropped
  622. // data (#7913), and we need to support Boost (#7026).
  623. series.options.data.forEach(function eachData(options, pIdx) {
  624. var key,
  625. prop,
  626. val,
  627. name,
  628. point;
  629. // In parallel coordinates chart, each data point is connected
  630. // to a separate yAxis, conform this
  631. if (hasParallelCoords) {
  632. categoryAndDatetimeMap = getCategoryAndDateTimeMap(series, pointArrayMap, pIdx);
  633. }
  634. point = { series: mockSeries };
  635. series.pointClass.prototype.applyOptions.apply(point, [options]);
  636. key = point.x;
  637. name = series.data[pIdx] && series.data[pIdx].name;
  638. j = 0;
  639. // Pies, funnels, geo maps etc. use point name in X row
  640. if (!xAxis ||
  641. series.exportKey === 'name' ||
  642. (!hasParallelCoords && xAxis && xAxis.hasNames) && name) {
  643. key = name;
  644. }
  645. if (xTaken) {
  646. if (xTaken[key]) {
  647. key += '|' + pIdx;
  648. }
  649. xTaken[key] = true;
  650. }
  651. if (!rows[key]) {
  652. // Generate the row
  653. rows[key] = [];
  654. // Contain the X values from one or more X axes
  655. rows[key].xValues = [];
  656. }
  657. rows[key].x = point.x;
  658. rows[key].name = name;
  659. rows[key].xValues[xAxisIndex] = point.x;
  660. while (j < valueCount) {
  661. prop = pointArrayMap[j]; // y, z etc
  662. val = point[prop];
  663. rows[key][i + j] = pick(
  664. // Y axis category if present
  665. categoryAndDatetimeMap.categoryMap[prop][val],
  666. // datetime yAxis
  667. categoryAndDatetimeMap.dateTimeValueAxisMap[prop] ?
  668. time.dateFormat(csvOptions.dateFormat, val) :
  669. null,
  670. // linear/log yAxis
  671. val);
  672. j++;
  673. }
  674. });
  675. i = i + j;
  676. }
  677. });
  678. // Make a sortable array
  679. for (x in rows) {
  680. if (Object.hasOwnProperty.call(rows, x)) {
  681. rowArr.push(rows[x]);
  682. }
  683. }
  684. var xAxisIndex,
  685. column;
  686. // Add computed column headers and top level headers to final row set
  687. dataRows = multiLevelHeaders ? [topLevelColumnTitles, columnTitles] :
  688. [columnTitles];
  689. i = xAxisIndices.length;
  690. while (i--) { // Start from end to splice in
  691. xAxisIndex = xAxisIndices[i][0];
  692. column = xAxisIndices[i][1];
  693. xAxis = xAxes[xAxisIndex];
  694. // Sort it by X values
  695. rowArr.sort(function (// eslint-disable-line no-loop-func
  696. a, b) {
  697. return a.xValues[xAxisIndex] - b.xValues[xAxisIndex];
  698. });
  699. // Add header row
  700. xTitle = columnHeaderFormatter(xAxis);
  701. dataRows[0].splice(column, 0, xTitle);
  702. if (multiLevelHeaders && dataRows[1]) {
  703. // If using multi level headers, we just added top level header.
  704. // Also add for sub level
  705. dataRows[1].splice(column, 0, xTitle);
  706. }
  707. // Add the category column
  708. rowArr.forEach(function (// eslint-disable-line no-loop-func
  709. row) {
  710. var category = row.name;
  711. if (xAxis && !defined(category)) {
  712. if (xAxis.dateTime) {
  713. if (row.x instanceof Date) {
  714. row.x = row.x.getTime();
  715. }
  716. category = time.dateFormat(csvOptions.dateFormat, row.x);
  717. }
  718. else if (xAxis.categories) {
  719. category = pick(xAxis.names[row.x], xAxis.categories[row.x], row.x);
  720. }
  721. else {
  722. category = row.x;
  723. }
  724. }
  725. // Add the X/date/category
  726. row.splice(column, 0, category);
  727. });
  728. }
  729. dataRows = dataRows.concat(rowArr);
  730. fireEvent(this, 'exportData', { dataRows: dataRows });
  731. return dataRows;
  732. };
  733. /**
  734. * Export-data module required. Returns the current chart data as a CSV string.
  735. *
  736. * @function Highcharts.Chart#getCSV
  737. *
  738. * @param {boolean} [useLocalDecimalPoint]
  739. * Whether to use the local decimal point as detected from the browser.
  740. * This makes it easier to export data to Excel in the same locale as the
  741. * user is.
  742. *
  743. * @return {string}
  744. * CSV representation of the data
  745. */
  746. Chart.prototype.getCSV = function (useLocalDecimalPoint) {
  747. var csv = '';
  748. var rows = this.getDataRows(), csvOptions = this.options.exporting.csv, decimalPoint = pick(csvOptions.decimalPoint, csvOptions.itemDelimiter !== ',' && useLocalDecimalPoint ?
  749. (1.1).toLocaleString()[1] :
  750. '.'),
  751. // use ';' for direct to Excel
  752. itemDelimiter = pick(csvOptions.itemDelimiter, decimalPoint === ',' ? ';' : ','),
  753. // '\n' isn't working with the js csv data extraction
  754. lineDelimiter = csvOptions.lineDelimiter;
  755. // Transform the rows to CSV
  756. rows.forEach(function (row, i) {
  757. var val = '',
  758. j = row.length;
  759. while (j--) {
  760. val = row[j];
  761. if (typeof val === 'string') {
  762. val = '"' + val + '"';
  763. }
  764. if (typeof val === 'number') {
  765. if (decimalPoint !== '.') {
  766. val = val.toString().replace('.', decimalPoint);
  767. }
  768. }
  769. row[j] = val;
  770. }
  771. // Add the values
  772. csv += row.join(itemDelimiter);
  773. // Add the line delimiter
  774. if (i < rows.length - 1) {
  775. csv += lineDelimiter;
  776. }
  777. });
  778. return csv;
  779. };
  780. /**
  781. * Export-data module required. Build a HTML table with the chart's current
  782. * data.
  783. *
  784. * @sample highcharts/export-data/viewdata/
  785. * View the data from the export menu
  786. *
  787. * @function Highcharts.Chart#getTable
  788. *
  789. * @param {boolean} [useLocalDecimalPoint]
  790. * Whether to use the local decimal point as detected from the browser.
  791. * This makes it easier to export data to Excel in the same locale as the
  792. * user is.
  793. *
  794. * @return {string}
  795. * HTML representation of the data.
  796. *
  797. * @fires Highcharts.Chart#event:afterGetTable
  798. */
  799. Chart.prototype.getTable = function (useLocalDecimalPoint) {
  800. var serialize = function (node) {
  801. if (!node.tagName || node.tagName === '#text') {
  802. // Text node
  803. return node.textContent || '';
  804. }
  805. var attributes = node.attributes;
  806. var html = "<" + node.tagName;
  807. if (attributes) {
  808. Object.keys(attributes).forEach(function (key) {
  809. var value = attributes[key];
  810. html += " " + key + "=\"" + value + "\"";
  811. });
  812. }
  813. html += '>';
  814. html += node.textContent || '';
  815. (node.children || []).forEach(function (child) {
  816. html += serialize(child);
  817. });
  818. html += "</" + node.tagName + ">";
  819. return html;
  820. };
  821. var tree = this.getTableAST(useLocalDecimalPoint);
  822. return serialize(tree);
  823. };
  824. /**
  825. * Get the AST of a HTML table representing the chart data.
  826. *
  827. * @private
  828. *
  829. * @function Highcharts.Chart#getTableAST
  830. *
  831. * @param {boolean} [useLocalDecimalPoint]
  832. * Whether to use the local decimal point as detected from the browser.
  833. * This makes it easier to export data to Excel in the same locale as the
  834. * user is.
  835. *
  836. * @return {Highcharts.ASTNode}
  837. * The abstract syntax tree
  838. */
  839. Chart.prototype.getTableAST = function (useLocalDecimalPoint) {
  840. var rowLength = 0;
  841. var treeChildren = [];
  842. var options = this.options,
  843. decimalPoint = useLocalDecimalPoint ? (1.1).toLocaleString()[1] : '.',
  844. useMultiLevelHeaders = pick(options.exporting.useMultiLevelHeaders,
  845. true),
  846. rows = this.getDataRows(useMultiLevelHeaders),
  847. topHeaders = useMultiLevelHeaders ? rows.shift() : null,
  848. subHeaders = rows.shift(),
  849. // Compare two rows for equality
  850. isRowEqual = function (row1,
  851. row2) {
  852. var i = row1.length;
  853. if (row2.length === i) {
  854. while (i--) {
  855. if (row1[i] !== row2[i]) {
  856. return false;
  857. }
  858. }
  859. }
  860. else {
  861. return false;
  862. }
  863. return true;
  864. },
  865. // Get table cell HTML from value
  866. getCellHTMLFromValue = function (tagName, classes, attributes, value) {
  867. var textContent = pick(value, ''), className = 'text' + (classes ? ' ' + classes : '');
  868. // Convert to string if number
  869. if (typeof textContent === 'number') {
  870. textContent = textContent.toString();
  871. if (decimalPoint === ',') {
  872. textContent = textContent.replace('.', decimalPoint);
  873. }
  874. className = 'number';
  875. }
  876. else if (!value) {
  877. className = 'empty';
  878. }
  879. attributes = extend({ 'class': className }, attributes);
  880. return {
  881. tagName: tagName,
  882. attributes: attributes,
  883. textContent: textContent
  884. };
  885. },
  886. // Get table header markup from row data
  887. getTableHeaderHTML = function (topheaders, subheaders, rowLength) {
  888. var theadChildren = [];
  889. var i = 0,
  890. len = rowLength || subheaders && subheaders.length,
  891. next,
  892. cur,
  893. curColspan = 0,
  894. rowspan;
  895. // Clean up multiple table headers. Chart.getDataRows() returns two
  896. // levels of headers when using multilevel, not merged. We need to
  897. // merge identical headers, remove redundant headers, and keep it
  898. // all marked up nicely.
  899. if (useMultiLevelHeaders &&
  900. topheaders &&
  901. subheaders &&
  902. !isRowEqual(topheaders, subheaders)) {
  903. var trChildren = [];
  904. for (; i < len; ++i) {
  905. cur = topheaders[i];
  906. next = topheaders[i + 1];
  907. if (cur === next) {
  908. ++curColspan;
  909. }
  910. else if (curColspan) {
  911. // Ended colspan
  912. // Add cur to HTML with colspan.
  913. trChildren.push(getCellHTMLFromValue('th', 'highcharts-table-topheading', {
  914. scope: 'col',
  915. colspan: curColspan + 1
  916. }, cur));
  917. curColspan = 0;
  918. }
  919. else {
  920. // Cur is standalone. If it is same as sublevel,
  921. // remove sublevel and add just toplevel.
  922. if (cur === subheaders[i]) {
  923. if (options.exporting.useRowspanHeaders) {
  924. rowspan = 2;
  925. delete subheaders[i];
  926. }
  927. else {
  928. rowspan = 1;
  929. subheaders[i] = '';
  930. }
  931. }
  932. else {
  933. rowspan = 1;
  934. }
  935. var cell = getCellHTMLFromValue('th', 'highcharts-table-topheading', { scope: 'col' }, cur);
  936. if (rowspan > 1 && cell.attributes) {
  937. cell.attributes.valign = 'top';
  938. cell.attributes.rowspan = rowspan;
  939. }
  940. trChildren.push(cell);
  941. }
  942. }
  943. theadChildren.push({
  944. tagName: 'tr',
  945. children: trChildren
  946. });
  947. }
  948. // Add the subheaders (the only headers if not using multilevels)
  949. if (subheaders) {
  950. var trChildren = [];
  951. for (i = 0, len = subheaders.length; i < len; ++i) {
  952. if (typeof subheaders[i] !== 'undefined') {
  953. trChildren.push(getCellHTMLFromValue('th', null, { scope: 'col' }, subheaders[i]));
  954. }
  955. }
  956. theadChildren.push({
  957. tagName: 'tr',
  958. children: trChildren
  959. });
  960. }
  961. return {
  962. tagName: 'thead',
  963. children: theadChildren
  964. };
  965. };
  966. // Add table caption
  967. if (options.exporting.tableCaption !== false) {
  968. treeChildren.push({
  969. tagName: 'caption',
  970. attributes: {
  971. 'class': 'highcharts-table-caption'
  972. },
  973. textContent: pick(options.exporting.tableCaption, (options.title.text ?
  974. options.title.text :
  975. 'Chart'))
  976. });
  977. }
  978. // Find longest row
  979. for (var i = 0, len = rows.length; i < len; ++i) {
  980. if (rows[i].length > rowLength) {
  981. rowLength = rows[i].length;
  982. }
  983. }
  984. // Add header
  985. treeChildren.push(getTableHeaderHTML(topHeaders, subHeaders, Math.max(rowLength, subHeaders.length)));
  986. // Transform the rows to HTML
  987. var trs = [];
  988. rows.forEach(function (row) {
  989. var trChildren = [];
  990. for (var j = 0; j < rowLength; j++) {
  991. // Make first column a header too. Especially important for
  992. // category axes, but also might make sense for datetime? Should
  993. // await user feedback on this.
  994. trChildren.push(getCellHTMLFromValue(j ? 'td' : 'th', null, j ? {} : { scope: 'row' }, row[j]));
  995. }
  996. trs.push({
  997. tagName: 'tr',
  998. children: trChildren
  999. });
  1000. });
  1001. treeChildren.push({
  1002. tagName: 'tbody',
  1003. children: trs
  1004. });
  1005. var e = {
  1006. tree: {
  1007. tagName: 'table',
  1008. id: "highcharts-data-table-" + this.index,
  1009. children: treeChildren
  1010. }
  1011. };
  1012. fireEvent(this, 'aftergetTableAST', e);
  1013. return e.tree;
  1014. };
  1015. /**
  1016. * Get a blob object from content, if blob is supported
  1017. *
  1018. * @private
  1019. * @param {string} content
  1020. * The content to create the blob from.
  1021. * @param {string} type
  1022. * The type of the content.
  1023. * @return {string|undefined}
  1024. * The blob object, or undefined if not supported.
  1025. */
  1026. function getBlobFromContent(content, type) {
  1027. var nav = win.navigator,
  1028. webKit = (nav.userAgent.indexOf('WebKit') > -1 &&
  1029. nav.userAgent.indexOf('Chrome') < 0),
  1030. domurl = win.URL || win.webkitURL || win;
  1031. try {
  1032. // MS specific
  1033. if (nav.msSaveOrOpenBlob && win.MSBlobBuilder) {
  1034. var blob = new win.MSBlobBuilder();
  1035. blob.append(content);
  1036. return blob.getBlob('image/svg+xml');
  1037. }
  1038. // Safari requires data URI since it doesn't allow navigation to blob
  1039. // URLs.
  1040. if (!webKit) {
  1041. return domurl.createObjectURL(new win.Blob(['\uFEFF' + content], // #7084
  1042. { type: type }));
  1043. }
  1044. }
  1045. catch (e) {
  1046. // Ignore
  1047. }
  1048. }
  1049. /**
  1050. * Generates a data URL of CSV for local download in the browser. This is the
  1051. * default action for a click on the 'Download CSV' button.
  1052. *
  1053. * See {@link Highcharts.Chart#getCSV} to get the CSV data itself.
  1054. *
  1055. * @function Highcharts.Chart#downloadCSV
  1056. *
  1057. * @requires modules/exporting
  1058. */
  1059. Chart.prototype.downloadCSV = function () {
  1060. var csv = this.getCSV(true);
  1061. downloadURL(getBlobFromContent(csv, 'text/csv') ||
  1062. 'data:text/csv,\uFEFF' + encodeURIComponent(csv), this.getFilename() + '.csv');
  1063. };
  1064. /**
  1065. * Generates a data URL of an XLS document for local download in the browser.
  1066. * This is the default action for a click on the 'Download XLS' button.
  1067. *
  1068. * See {@link Highcharts.Chart#getTable} to get the table data itself.
  1069. *
  1070. * @function Highcharts.Chart#downloadXLS
  1071. *
  1072. * @requires modules/exporting
  1073. */
  1074. Chart.prototype.downloadXLS = function () {
  1075. var uri = 'data:application/vnd.ms-excel;base64,', template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" ' +
  1076. 'xmlns:x="urn:schemas-microsoft-com:office:excel" ' +
  1077. 'xmlns="http://www.w3.org/TR/REC-html40">' +
  1078. '<head><!--[if gte mso 9]><xml><x:ExcelWorkbook>' +
  1079. '<x:ExcelWorksheets><x:ExcelWorksheet>' +
  1080. '<x:Name>Ark1</x:Name>' +
  1081. '<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>' +
  1082. '</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook>' +
  1083. '</xml><![endif]-->' +
  1084. '<style>td{border:none;font-family: Calibri, sans-serif;} ' +
  1085. '.number{mso-number-format:"0.00";} ' +
  1086. '.text{ mso-number-format:"\@";}</style>' +
  1087. '<meta name=ProgId content=Excel.Sheet>' +
  1088. '<meta charset=UTF-8>' +
  1089. '</head><body>' +
  1090. this.getTable(true) +
  1091. '</body></html>', base64 = function (s) {
  1092. return win.btoa(unescape(encodeURIComponent(s))); // #50
  1093. };
  1094. downloadURL(getBlobFromContent(template, 'application/vnd.ms-excel') ||
  1095. uri + base64(template), this.getFilename() + '.xls');
  1096. };
  1097. /**
  1098. * Export-data module required. View the data in a table below the chart.
  1099. *
  1100. * @function Highcharts.Chart#viewData
  1101. *
  1102. * @fires Highcharts.Chart#event:afterViewData
  1103. */
  1104. Chart.prototype.viewData = function () {
  1105. this.toggleDataTable(true);
  1106. };
  1107. /**
  1108. * Export-data module required. Hide the data table when visible.
  1109. *
  1110. * @function Highcharts.Chart#hideData
  1111. */
  1112. Chart.prototype.hideData = function () {
  1113. this.toggleDataTable(false);
  1114. };
  1115. Chart.prototype.toggleDataTable = function (show) {
  1116. show = pick(show, !this.isDataTableVisible);
  1117. // Create the div
  1118. if (show && !this.dataTableDiv) {
  1119. this.dataTableDiv = doc.createElement('div');
  1120. this.dataTableDiv.className = 'highcharts-data-table';
  1121. // Insert after the chart container
  1122. this.renderTo.parentNode.insertBefore(this.dataTableDiv, this.renderTo.nextSibling);
  1123. }
  1124. // Toggle the visibility
  1125. if (this.dataTableDiv) {
  1126. this.dataTableDiv.style.display = show ? 'block' : 'none';
  1127. // Generate the data table
  1128. if (show) {
  1129. this.dataTableDiv.innerHTML = '';
  1130. var ast = new AST([this.getTableAST()]);
  1131. ast.addToDOM(this.dataTableDiv);
  1132. fireEvent(this, 'afterViewData', this.dataTableDiv);
  1133. }
  1134. }
  1135. // Set the flag
  1136. this.isDataTableVisible = show;
  1137. // Change the menu item text
  1138. var exportDivElements = this.exportDivElements,
  1139. options = this.options.exporting,
  1140. menuItems = options &&
  1141. options.buttons &&
  1142. options.buttons.contextButton.menuItems,
  1143. lang = this.options.lang;
  1144. if (exportingOptions &&
  1145. exportingOptions.menuItemDefinitions &&
  1146. lang &&
  1147. lang.viewData &&
  1148. lang.hideData &&
  1149. menuItems &&
  1150. exportDivElements &&
  1151. exportDivElements.length) {
  1152. AST.setElementHTML(exportDivElements[menuItems.indexOf('viewData')], this.isDataTableVisible ? lang.hideData : lang.viewData);
  1153. }
  1154. };
  1155. // Add "Download CSV" to the exporting menu.
  1156. var exportingOptions = getOptions().exporting;
  1157. if (exportingOptions) {
  1158. extend(exportingOptions.menuItemDefinitions, {
  1159. downloadCSV: {
  1160. textKey: 'downloadCSV',
  1161. onclick: function () {
  1162. this.downloadCSV();
  1163. }
  1164. },
  1165. downloadXLS: {
  1166. textKey: 'downloadXLS',
  1167. onclick: function () {
  1168. this.downloadXLS();
  1169. }
  1170. },
  1171. viewData: {
  1172. textKey: 'viewData',
  1173. onclick: function () {
  1174. this.toggleDataTable();
  1175. }
  1176. }
  1177. });
  1178. if (exportingOptions.buttons) {
  1179. exportingOptions.buttons.contextButton.menuItems.push('separator', 'downloadCSV', 'downloadXLS', 'viewData');
  1180. }
  1181. }
  1182. // Series specific
  1183. if (seriesTypes.map) {
  1184. seriesTypes.map.prototype.exportKey = 'name';
  1185. }
  1186. if (seriesTypes.mapbubble) {
  1187. seriesTypes.mapbubble.prototype.exportKey = 'name';
  1188. }
  1189. if (seriesTypes.treemap) {
  1190. seriesTypes.treemap.prototype.exportKey = 'name';
  1191. }
  1192. });
  1193. _registerModule(_modules, 'masters/modules/export-data.src.js', [], function () {
  1194. });
  1195. }));