'use strict' ; import type DataEventEmitter from '../DataEventEmitter' ; import type JSON from '../../Core/JSON' ; import CSVParser from '../Parsers/CSVParser.js' ; import DataStore from './DataStore.js' ; import DataTable from '../DataTable.js' ; import HU from '../../Core/HttpUtilities.js' ; const { ajax } = HU ; import U from '../../Core/Utilities.js' ; const { merge , objectEach } = U ; private class CSVStore extends DataStore < CSVStore . Event > { protected static readonly defaultOptions : CSVStore . Options = { csv : '' , csvURL : '' , enablePolling : false , dataRefreshRate : 1 } ; static readonly defaultExportOptions : CSVStore . ExportOptions = { decimalPoint : null , itemDelimiter : null , lineDelimiter : '\n' } ; param { DataTable } table param { CSVStore . OptionsType } options param { DataParser } parser public constructor ( table = new DataTable ( ) , options = { } , parser ? ) { super ( table ) ; const { csv , csvURL , enablePolling , dataRefreshRate , ... parserOptions } = options ; this . parserOptions = parserOptions ; this . options = merge ( CSVStore . defaultOptions , { csv , csvURL , enablePolling , dataRefreshRate } ) ; this . parser = parser || new CSVParser ( parserOptions ) ; } /**
     * Options related to the handling of the CSV datastore,
     * i.e. source, fetching, polling
     */ public readonly options : CSVStore . Options ; /**
     * The attached parser, which can be replaced in the constructor
     */ public readonly parser : CSVParser ; /**
     * The options that were passed to the parser.
     */ private parserOptions : CSVParser . OptionsType ; /**
     * The URL to fetch if the source is external
     */ private liveDataURL ? : string ; /**
     * The current timeout ID if polling is enabled
     */ private liveDataTimeout ? : number ; /**
     * Handles polling of live data
     */ private poll ( ) { const { dataRefreshRate , enablePolling , csvURL } = this . options ; const updateIntervalMs = ( dataRefreshRate > 1 ? dataRefreshRate : 1 ) * 1000 ; if ( enablePolling && csvURL === this . liveDataURL ) { this . liveDataTimeout = setTimeout ( ( ) : void => { this . fetchCSV ( ) ; } , updateIntervalMs ) ; } } param { boolean } initialFetch param { DataEventEmitter . EventDetail } eventDetail emits emits emits private fetchCSV ( initialFetch ? , eventDetail ? ) { const store = this , maxRetries = 3 , { csvURL } = store . options ; let currentRetries ; store . table . deleteColumns ( ) ; if ( initialFetch ) { clearTimeout ( store . liveDataTimeout ) ; store . liveDataURL = csvURL ; } store . emit ( { type : 'load' , detail : eventDetail , table : store . table } ) ; ajax ( { url : store . liveDataURL , dataType : 'text' , success : function ( csv ) : void { store . parser . parse ( { csv } ) ; store . table . setColumns ( store . parser . getTable ( ) . getColumns ( ) ) ; if ( store . liveDataURL ) { store . poll ( ) ; } store . emit ( { type : 'afterLoad' , csv , detail : eventDetail , table : store . table } ) ; } , error : function ( xhr , error ) : void { if ( ++ currentRetries < maxRetries ) { store . poll ( ) ; } store . emit ( { type : 'loadError' , detail : eventDetail , error , table : store . table , xhr } ) ; } } ) ; } param { DataEventEmitter . EventDetail } eventDetail emits emits public load ( eventDetail ? ) { const store = this , parser = store . parser , table = store . table , { csv , csvURL } = store . options ; if ( csv ) { table . deleteRows ( ) ; store . emit ( { type : 'load' , csv , detail : eventDetail , table } ) ; parser . parse ( { csv } ) ; table . setColumns ( parser . getTable ( ) . getColumns ( ) ) ; store . emit ( { type : 'afterLoad' , csv , detail : eventDetail , table } ) ; } else if ( csvURL ) { store . fetchCSV ( true , eventDetail ) ; } else { store . emit ( { type : 'loadError' , detail : eventDetail , error : 'Unable to load: no CSV string or URL was provided' , table } ) ; } } param { CSVStore . ExportOptions } exportOptions return { string } public getCSVForExport ( exportOptions ) { const { useLocalDecimalPoint , lineDelimiter } = exportOptions , exportNames = ( this . parserOptions . firstRowAsNames !== false ) ; let { decimalPoint , itemDelimiter } = exportOptions ; if ( ! decimalPoint ) { decimalPoint = itemDelimiter !== ',' && useLocalDecimalPoint ? ( 1.1 ) . toLocaleString ( ) [ 1 ] : '.' ; } if ( ! itemDelimiter ) { itemDelimiter = decimalPoint === ',' ? ';' : ',' ; } const { columnNames , columnValues } = this . getColumnsForExport ( exportOptions . usePresentationOrder ) ; const csvRows = [ ] , columnsCount = columnNames . length ; const rowArray = [ ] ; if ( exportNames ) { csvRows . push ( columnNames . map ( ( columnName ) : string => `"${ columnName }"` ) . join ( itemDelimiter ) ) ; } for ( let columnIndex = 0 ; columnIndex < columnsCount ; columnIndex ++ ) { const columnName = columnNames [ columnIndex ] , column = columnValues [ columnIndex ] , columnLength = column . length ; const columnMeta = this . whatIs ( columnName ) ; let columnDataType ; if ( columnMeta ) { columnDataType = columnMeta . dataType ; } for ( let rowIndex = 0 ; rowIndex < columnLength ; rowIndex ++ ) { let cellValue = column [ rowIndex ] ; if ( ! rowArray [ rowIndex ] ) { rowArray [ rowIndex ] = [ ] ; } if ( columnDataType === 'string' ) { cellValue = `"${ cellValue }"` ; } else if ( typeof cellValue === 'number' ) { cellValue = String ( cellValue ) . replace ( '.' , decimalPoint ) ; } else if ( typeof cellValue === 'string' ) { cellValue = `"${ cellValue }"` ; } rowArray [ rowIndex ] [ columnIndex ] = cellValue ; if ( columnIndex === columnsCount - 1 ) { let i = columnIndex ; while ( rowArray [ rowIndex ] . length > 2 ) { const cellVal = rowArray [ rowIndex ] [ i ] ; if ( cellVal !== void 0 ) { break ; } rowArray [ rowIndex ] . pop ( ) ; i -- ; } csvRows . push ( rowArray [ rowIndex ] . join ( itemDelimiter ) ) ; } } } return csvRows . join ( lineDelimiter ) ; } param { CSVStore . ExportOptions } csvExportOptions return { string } public save ( csvExportOptions ? ) { const exportOptions = CSVStore . defaultExportOptions ; objectEach ( this . parserOptions , function ( value , key ) : void { if ( key in exportOptions ) { exportOptions [ key ] = value ; } } ) ; return this . getCSVForExport ( merge ( exportOptions , csvExportOptions ) ) ; } } /**
 *
 *  Namespace
 *
 */ /**
 * Types for class-specific options and events
 */ namespace CSVStore { /**
     * Event objects fired from CSVDataStore events
     */ export type Event = ( ErrorEvent | LoadEvent ) ; /**
     * Options for the CSVDataStore class constructor
     */ export type OptionsType = Partial < ( CSVStore . Options & CSVParser . OptionsType ) > ; todo export interface DataBeforeParseCallbackFunction { ( csv ) : string ; } /**
     * The event object that is provided on errors within CSVDataStore
     */ export interface ErrorEvent extends DataStore . Event { type : ( 'loadError' ) ; error : ( string | Error ) ; xhr ? : XMLHttpRequest ; } /**
     * The event object that is provided on load events within CSVDataStore
     */ export interface LoadEvent extends DataStore . Event { type : ( 'load' | 'afterLoad' ) ; csv ? : string ; } /**
     * Internal options for CSVDataStore
     */ export interface Options extends JSON . Object { csv : string ; csvURL : string ; enablePolling : boolean ; dataRefreshRate : number ; } /**
     * The available options when exporting the table as CSV.
     */ export interface ExportOptions extends JSON . Object { decimalPoint : string | null ; itemDelimiter : string | null ; lineDelimiter : string ; useLocalDecimalPoint ? : boolean ; usePresentationOrder ? : boolean ; } } DataStore . addStore ( CSVStore ) ; declare module './StoreType' { interface StoreTypeRegistry { CSVStore : typeof CSVStore ; } } export default CSVStore ;