UNPKG

@29aries/apexcharts

Version:

Interactive JavaScript Charts built on SVG - forked by 29aries to allow custom labels - forked from 3.20.0

308 lines (261 loc) 8.81 kB
import Data from '../modules/Data' import AxesUtils from '../modules/axes/AxesUtils' import Series from '../modules/Series' import Utils from '../utils/Utils' class Exports { constructor(ctx) { this.ctx = ctx this.w = ctx.w } fixSvgStringForIe11(svgData) { // IE11 generates broken SVG that we have to fix by using regex if (!Utils.isIE11()) { // not IE11 - noop return svgData } // replace second occurence of "xmlns" attribute with "xmlns:xlink" with correct url + add xmlns:svgjs let nXmlnsSeen = 0 let result = svgData.replace( /xmlns="http:\/\/www.w3.org\/2000\/svg"/g, (match) => { nXmlnsSeen++ return nXmlnsSeen === 2 ? 'xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"' : match } ) // remove the invalid empty namespace declarations result = result.replace(/xmlns:NS\d+=""/g, '') // remove these broken namespaces from attributes result = result.replace(/NS\d+:(\w+:\w+=")/g, '$1') return result } getSvgString() { const svgString = this.w.globals.dom.Paper.svg() return this.fixSvgStringForIe11(svgString) } cleanup() { const w = this.w // hide some elements to avoid printing them on exported svg const xcrosshairs = w.globals.dom.baseEl.getElementsByClassName( 'apexcharts-xcrosshairs' ) const ycrosshairs = w.globals.dom.baseEl.getElementsByClassName( 'apexcharts-ycrosshairs' ) const zoomSelectionRects = w.globals.dom.baseEl.querySelectorAll( '.apexcharts-zoom-rect, .apexcharts-selection-rect' ) Array.prototype.forEach.call(zoomSelectionRects, (z) => { z.setAttribute('width', 0) }) if (xcrosshairs && xcrosshairs[0]) { xcrosshairs[0].setAttribute('x', -500) xcrosshairs[0].setAttribute('x1', -500) xcrosshairs[0].setAttribute('x2', -500) } if (ycrosshairs && ycrosshairs[0]) { ycrosshairs[0].setAttribute('y', -100) ycrosshairs[0].setAttribute('y1', -100) ycrosshairs[0].setAttribute('y2', -100) } } svgUrl() { this.cleanup() const svgData = this.getSvgString() const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' }) return URL.createObjectURL(svgBlob) } dataURI() { return new Promise((resolve) => { const w = this.w this.cleanup() const canvas = document.createElement('canvas') canvas.width = w.globals.svgWidth canvas.height = parseInt(w.globals.dom.elWrap.style.height, 10) // because of resizeNonAxisCharts const canvasBg = w.config.chart.background === 'transparent' ? '#fff' : w.config.chart.background let ctx = canvas.getContext('2d') ctx.fillStyle = canvasBg ctx.fillRect(0, 0, canvas.width, canvas.height) const svgData = this.getSvgString() if (window.canvg && Utils.isIE11()) { // use canvg as a polyfill to workaround ie11 considering a canvas with loaded svg 'unsafe' // without ignoreClear we lose our background color; without ignoreDimensions some grid lines become invisible let v = window.canvg.Canvg.fromString(ctx, svgData, { ignoreClear: true, ignoreDimensions: true }) // render the svg to canvas v.start() let blob = canvas.msToBlob() // dispose - missing this will cause a memory leak v.stop() resolve({ blob }) } else { const svgUrl = 'data:image/svg+xml,' + encodeURIComponent(svgData) let img = new Image() img.crossOrigin = 'anonymous' img.onload = () => { ctx.drawImage(img, 0, 0) if (canvas.msToBlob) { // IE and Edge can't navigate to data urls, so we return the blob instead let blob = canvas.msToBlob() resolve({ blob }) } else { let imgURI = canvas.toDataURL('image/png') resolve({ imgURI }) } } img.src = svgUrl } }) } exportToSVG() { this.triggerDownload(this.svgUrl(), null, '.svg') } exportToPng() { this.dataURI().then(({ imgURI, blob }) => { if (blob) { navigator.msSaveOrOpenBlob(blob, this.w.globals.chartID + '.png') } else { this.triggerDownload(imgURI, null, '.png') } }) } exportToCSV({ series, columnDelimiter, lineDelimiter = '\n' }) { const w = this.w let columns = [] let rows = [] let result = 'data:text/csv;charset=utf-8,' const isTimeStamp = (num) => { return w.config.xaxis.type === 'datetime' && String(num).length >= 10 } const dataFormat = new Data(this.ctx) const axesUtils = new AxesUtils(this.ctx) const getCat = (i) => { let cat = '' // pie / donut/ radial if (!w.globals.axisCharts) { cat = w.config.labels[i] } else { // xy charts // non datetime if ( w.config.xaxis.type === 'category' || w.config.xaxis.convertedCatToNumeric ) { if (w.globals.isBarHorizontal) { let lbFormatter = w.globals.yLabelFormatters[0] let sr = new Series(this.ctx) let activeSeries = sr.getActiveConfigSeriesIndex() cat = lbFormatter(w.globals.labels[i], { seriesIndex: activeSeries, dataPointIndex: i, w }) } else { cat = axesUtils.getLabel( w.globals.labels, w.globals.timescaleLabels, 0, i ).text } } // datetime, but labels specified in categories or labels if (w.config.xaxis.type === 'datetime') { if (w.config.xaxis.categories.length) { cat = w.config.xaxis.categories[i] } else if (w.config.labels.length) { cat = w.config.labels[i] } } } return cat } const handleAxisRowsColumns = (s, sI) => { if (columns.length && sI === 0) { rows.push(columns.join(columnDelimiter)) } if (s.data && s.data.length) { for (let i = 0; i < s.data.length; i++) { columns = [] let cat = getCat(i) if (!cat) { if (dataFormat.isFormatXY()) { cat = series[sI].data[i].x } else if (dataFormat.isFormat2DArray()) { cat = series[sI].data[i] ? series[sI].data[i][0] : '' } } if (sI === 0) { columns.push( isTimeStamp(cat) ? w.config.chart.toolbar.export.csv.dateFormatter(cat) : cat ) for (let ci = 0; ci < w.globals.series.length; ci++) { columns.push(w.globals.series[ci][i]) } } if ( w.config.chart.type === 'candlestick' || (s.type && s.type === 'candlestick') ) { columns.pop() columns.push(w.globals.seriesCandleO[sI][i]) columns.push(w.globals.seriesCandleH[sI][i]) columns.push(w.globals.seriesCandleL[sI][i]) columns.push(w.globals.seriesCandleC[sI][i]) } if (w.config.chart.type === 'rangeBar') { columns.pop() columns.push(w.globals.seriesRangeStart[sI][i]) columns.push(w.globals.seriesRangeEnd[sI][i]) } if (columns.length) { rows.push(columns.join(columnDelimiter)) } } } } columns.push(w.config.chart.toolbar.export.csv.headerCategory) series.map((s, sI) => { if (w.globals.axisCharts) { columns.push(s.name ? s.name : `series-${sI}`) } }) if (!w.globals.axisCharts) { columns.push(w.config.chart.toolbar.export.csv.headerValue) rows.push(columns.join(columnDelimiter)) } series.map((s, sI) => { if (w.globals.axisCharts) { handleAxisRowsColumns(s, sI) } else { columns = [] columns.push(w.globals.labels[sI]) columns.push(w.globals.series[sI]) rows.push(columns.join(columnDelimiter)) } }) result += rows.join(lineDelimiter) this.triggerDownload( encodeURI(result), w.config.chart.toolbar.export.csv.filename, '.csv' ) } triggerDownload(href, filename, ext) { const downloadLink = document.createElement('a') downloadLink.href = href downloadLink.download = (filename ? filename : this.w.globals.chartID) + ext document.body.appendChild(downloadLink) downloadLink.click() document.body.removeChild(downloadLink) } } export default Exports