highcharts
Version:
JavaScript charting framework
269 lines (268 loc) • 7.53 kB
JavaScript
/* *
*
* (c) 2009-2026 Highsoft AS
*
* A commercial license may be required depending on use.
* See www.highcharts.com/license
*
*
* Authors:
* - Sophie Bremer
*
* */
;
import DataConnector from './Connectors/DataConnector.js';
import { addEvent, fireEvent, merge } from '../Shared/Utilities.js';
/* *
*
* Class
*
* */
/**
* Data pool to load connectors on-demand.
*
* @class
* @name Data.DataPool
*
* @param {DataPoolOptions} options
* Pool options with all connectors.
*/
class DataPool {
/* *
*
* Constructor
*
* */
constructor(options) {
this.options = merge(DataPool.defaultOptions, options);
this.connectors = {};
this.waiting = {};
}
/* *
*
* Methods
*
* */
/**
* Emits an event on this data pool to all registered callbacks of the given
* event.
*
* @param {DataTableEvent} e
* Event object with event information.
*/
emit(e) {
fireEvent(this, e.type, e);
}
/**
* Loads the connector.
*
* @function Data.DataPool#getConnector
*
* @param {string} connectorId
* ID of the connector.
*
* @return {Promise<Data.DataConnectorType>}
* Returns the connector.
*/
getConnector(connectorId) {
const connector = this.connectors[connectorId];
// Already loaded
if (connector?.loaded) {
return Promise.resolve(connector);
}
let waitingList = this.waiting[connectorId];
// Start loading
if (!waitingList) {
waitingList = this.waiting[connectorId] = [];
const connectorOptions = this.getConnectorOptions(connectorId);
if (!connectorOptions) {
throw new Error(`Connector '${connectorId}' not found.`);
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this
.loadConnector(connectorOptions)
.then((connector) => {
delete this.waiting[connectorId];
for (let i = 0, iEnd = waitingList.length; i < iEnd; ++i) {
waitingList[i][0](connector);
}
})['catch']((error) => {
delete this.waiting[connectorId];
for (let i = 0, iEnd = waitingList.length; i < iEnd; ++i) {
waitingList[i][1](error);
}
});
}
// Add request to waiting list
return new Promise((resolve, reject) => {
waitingList.push([resolve, reject]);
});
}
/**
* Returns the IDs of all connectors.
*
* @private
*
* @return {Array<string>}
* Names of all connectors.
*/
getConnectorIds() {
const connectors = this.options.connectors, connectorIds = [];
for (let i = 0, iEnd = connectors.length; i < iEnd; ++i) {
connectorIds.push(connectors[i].id);
}
return connectorIds;
}
/**
* Loads the options of the connector.
*
* @private
*
* @param {string} connectorId
* ID of the connector.
*
* @return {DataConnectorTypeOptions | undefined}
* Returns the options of the connector, or `undefined` if not found.
*/
getConnectorOptions(connectorId) {
const connectors = this.options.connectors;
for (let i = 0, iEnd = connectors.length; i < iEnd; ++i) {
if (connectors[i].id === connectorId) {
return connectors[i];
}
}
}
/**
* Tests whether the connector has never been requested.
*
* @param {string} connectorId
* Name of the connector.
*
* @return {boolean}
* Returns `true`, if the connector has never been requested, otherwise
* `false`.
*/
isNewConnector(connectorId) {
return !this.connectors[connectorId];
}
/**
* Instantiates the connector class for the given options and loads its
* data.
*
* @private
*
* @param {Data.DataPoolConnectorOptions} options
* Options of connector.
*
* @return {Promise<Data.DataConnectorType>}
* Returns the connector.
*/
loadConnector(options) {
return new Promise((resolve, reject) => {
this.emit({
type: 'load',
options
});
const ConnectorClass = DataConnector.types[options.type];
if (!ConnectorClass) {
throw new Error(`Connector type not found. (${options.type})`);
}
const connector = this.connectors[options.id] =
new ConnectorClass(options);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
connector
.load()
.then(({ converter }) => {
connector.converter = converter;
connector.loaded = true;
this.emit({
type: 'afterLoad',
options
});
resolve(connector);
})['catch'](reject);
});
}
/**
* Cancels all data connectors pending requests.
*/
cancelPendingRequests() {
const { connectors } = this;
for (const connectorKey of Object.keys(connectors)) {
connectors[connectorKey].stopPolling();
}
}
/**
* Registers a callback for a specific event.
*
* @function Highcharts.DataPool#on
*
* @param {string} type
* Event type as a string.
*
* @param {Highcharts.EventCallbackFunction<Highcharts.DataPool>} callback
* Function to register for an event callback.
*
* @return {Function}
* Function to unregister callback from the event.
*/
on(type, callback) {
return addEvent(this, type, callback);
}
/**
* Sets connector options under the specified `options.id`.
*
* @param {object} options
* Connector options to set.
*
* @param {boolean} [update]
* Whether to update the existing connector with the new options and reload
* it (`true`) or replace it with a new connector instance (`false`).
*/
async setConnectorOptions(options, update) {
const connectorsOptions = this.options.connectors;
const connectorsInstances = this.connectors;
this.emit({
type: 'setConnectorOptions',
options
});
for (let i = 0, iEnd = connectorsOptions.length; i < iEnd; ++i) {
if (connectorsOptions[i].id === options.id) {
connectorsOptions.splice(i, 1);
break;
}
}
let existingConnector = connectorsInstances[options.id];
if (existingConnector) {
if (update) {
await existingConnector.update(options, true);
}
else {
existingConnector.stopPolling();
existingConnector = void 0;
delete connectorsInstances[options.id];
}
}
if (!existingConnector) {
connectorsOptions.push(options);
}
this.emit({
type: 'afterSetConnectorOptions',
options
});
}
}
/* *
*
* Static Properties
*
* */
DataPool.defaultOptions = {
connectors: []
};
/* *
*
* Default Export
*
* */
export default DataPool;