2023-02-10 14:30:56 -05:00
import MODULE_TYPES from './MODULE_TYPES' ;
import log from '../log' ;
2024-05-08 12:12:49 -04:00
import { PubSubService , ServiceProvidersManager } from '../services' ;
2023-02-10 14:30:56 -05:00
import { HotkeysManager , CommandsManager } from '../classes' ;
2024-07-05 12:33:07 -04:00
import type { DataSourceDefinition } from '../types' ;
import type AppTypes from '../types/AppTypes' ;
2023-02-10 14:30:56 -05:00
/**
* This is the arguments given to create the extension.
*/
export interface ExtensionConstructor {
2024-05-08 12:12:49 -04:00
servicesManager : AppTypes.ServicesManager ;
2024-04-02 09:35:30 -04:00
serviceProvidersManager : ServiceProvidersManager ;
2023-02-10 14:30:56 -05:00
commandsManager : CommandsManager ;
hotkeysManager : HotkeysManager ;
2024-05-24 12:21:09 -04:00
appConfig : AppTypes.Config ;
2023-02-10 14:30:56 -05:00
}
/**
* The configuration of an extension.
* This uses type as the extension manager only knows that the configuration
* is an object of some sort, and doesn't know anything else about it.
*/
export type ExtensionConfiguration = Record < string , unknown >;
/**
* The parameters passed to the extension.
*/
export interface ExtensionParams extends ExtensionConstructor {
extensionManager : ExtensionManager ;
2024-05-08 12:12:49 -04:00
servicesManager : AppTypes.ServicesManager ;
2024-04-02 09:35:30 -04:00
serviceProvidersManager : ServiceProvidersManager ;
2023-02-10 14:30:56 -05:00
configuration? : ExtensionConfiguration ;
2024-07-05 13:19:42 -04:00
peerImport : ( moduleId : string ) => Promise < any >;
2023-02-10 14:30:56 -05:00
}
/**
* The type of an actual extension instance.
* This is an interface as it declares possible calls, but extensions can
* have more values than this.
*/
export interface Extension {
2023-02-17 15:04:07 -05:00
id : string ;
2023-03-22 17:45:28 -04:00
preRegistration ?: ( p : ExtensionParams ) => Promise < void > | void ;
getHangingProtocolModule ?: ( p : ExtensionParams ) => unknown ;
getCommandsModule ?: ( p : ExtensionParams ) => CommandsModule ;
2023-04-18 11:17:50 -04:00
getViewportModule ?: ( p : ExtensionParams ) => unknown ;
getUtilityModule ?: ( p : ExtensionParams ) => unknown ;
2023-04-26 12:21:08 -04:00
getCustomizationModule ?: ( p : ExtensionParams ) => unknown ;
2023-08-08 14:39:31 -04:00
getSopClassHandlerModule ?: ( p : ExtensionParams ) => unknown ;
2024-03-27 16:01:32 -04:00
getToolbarModule ?: ( p : ExtensionParams ) => unknown ;
2024-04-10 00:57:34 -03:00
getPanelModule ?: ( p : ExtensionParams ) => unknown ;
2024-07-05 12:33:07 -04:00
onModeEnter ?: ( p : AppTypes ) => void ;
2024-11-06 07:15:27 -05:00
onModeExit ?: ( p : AppTypes ) => void ;
2023-02-10 14:30:56 -05:00
}
export type ExtensionRegister = {
id : string ;
create : ( p : ExtensionParams ) => Extension ;
};
2019-08-13 15:36:46 -04:00
2023-03-22 17:45:28 -04:00
export type CommandsModule = {
actions : Record < string , unknown >;
definitions : Record < string , unknown >;
defaultContext? : string ;
};
2023-07-20 11:43:05 -04:00
export default class ExtensionManager extends PubSubService {
public static readonly EVENTS = {
ACTIVE_DATA_SOURCE_CHANGED : 'event::activedatasourcechanged' ,
};
2024-03-27 16:01:32 -04:00
public static readonly MODULE_TYPES = MODULE_TYPES ;
2023-02-10 14:30:56 -05:00
private _commandsManager : CommandsManager ;
2024-05-08 12:12:49 -04:00
private _servicesManager : AppTypes.ServicesManager ;
2023-02-10 14:30:56 -05:00
private _hotkeysManager : HotkeysManager ;
2024-04-02 09:35:30 -04:00
private _serviceProvidersManager : ServiceProvidersManager ;
2024-03-27 16:01:32 -04:00
private modulesMap : Record < string , unknown >;
private modules : Record < string , any [] >;
private registeredExtensionIds : string [];
private moduleTypeNames : string [];
private _appConfig : any ;
private _extensionLifeCycleHooks : {
onModeEnter : Record < string , any >;
onModeExit : Record < string , any >;
};
private dataSourceMap : Record < string , any >;
private dataSourceDefs : Record < string , any >;
2025-04-11 15:57:29 -04:00
private activeDataSourceName : string ;
2023-02-10 14:30:56 -05:00
2021-05-28 14:32:48 -04:00
constructor ({
commandsManager ,
servicesManager ,
2024-04-02 09:35:30 -04:00
serviceProvidersManager ,
2021-05-28 14:32:48 -04:00
hotkeysManager ,
appConfig = {},
2023-02-10 14:30:56 -05:00
} : ExtensionConstructor ) {
2023-07-20 11:43:05 -04:00
super ( ExtensionManager . EVENTS );
2019-08-13 15:36:46 -04:00
this . modules = {};
this . registeredExtensionIds = [];
this . moduleTypeNames = Object . values ( MODULE_TYPES );
//
this . _commandsManager = commandsManager ;
2019-11-13 17:02:14 -03:00
this . _servicesManager = servicesManager ;
2024-04-02 09:35:30 -04:00
this . _serviceProvidersManager = serviceProvidersManager ;
2020-08-19 16:02:59 -03:00
this . _hotkeysManager = hotkeysManager ;
2019-12-16 16:07:53 -03:00
this . _appConfig = appConfig ;
2019-08-13 15:36:46 -04:00
2020-05-07 14:04:33 -04:00
this . modulesMap = {};
2019-08-13 15:36:46 -04:00
this . moduleTypeNames . forEach ( moduleType => {
this . modules [ moduleType ] = [];
});
2020-08-04 05:11:23 -04:00
this . _extensionLifeCycleHooks = { onModeEnter : {}, onModeExit : {} };
2020-05-07 14:04:33 -04:00
this . dataSourceMap = {};
2022-12-29 10:13:06 -05:00
this . dataSourceDefs = {};
2020-06-05 17:36:11 +02:00
this . defaultDataSourceName = appConfig . defaultDataSourceName ;
2025-04-11 15:57:29 -04:00
this . activeDataSourceName = appConfig . defaultDataSourceName ;
2024-07-05 13:19:42 -04:00
this . peerImport = appConfig . peerImport ;
2020-06-05 17:36:11 +02:00
}
2020-05-13 12:06:13 +02:00
2023-07-20 11:43:05 -04:00
public setActiveDataSource ( dataSource : string ) : void {
2025-04-11 15:57:29 -04:00
if ( this . activeDataSourceName === dataSource ) {
2023-07-20 11:43:05 -04:00
return ;
}
2025-04-11 15:57:29 -04:00
this . activeDataSourceName = dataSource ;
2023-07-20 11:43:05 -04:00
this . _broadcastEvent (
ExtensionManager . EVENTS . ACTIVE_DATA_SOURCE_CHANGED ,
2025-04-11 15:57:29 -04:00
this . dataSourceDefs [ this . activeDataSourceName ]
2023-07-20 11:43:05 -04:00
);
2019-08-13 15:36:46 -04:00
}
2024-06-19 16:31:59 -04:00
public getRegisteredExtensionIds() {
return [... this . registeredExtensionIds ];
}
2024-06-28 09:58:39 -03:00
private getUniqueServicesList ( servicesManager : AppTypes.ServicesManager ) {
// Make sure only one service instance is returned because almost all services are
// registered with different keys (eg: StudyPrefetcherService and studyPrefetcherService)
return Array . from ( new Set ( Object . values ( servicesManager . services )));
}
2023-02-10 14:30:56 -05:00
/**
* Calls all the services and extension on mode enters.
* The service onModeEnter is called first
* Then registered extensions onModeEnter is called
* This is supposed to setup the extension for a standard entry.
*/
public onModeEnter () : void {
2020-07-03 19:18:59 +02:00
const {
registeredExtensionIds ,
_servicesManager ,
_commandsManager ,
2020-08-19 16:02:59 -03:00
_hotkeysManager ,
2020-07-03 19:18:59 +02:00
_extensionLifeCycleHooks ,
} = this ;
2024-06-28 09:58:39 -03:00
const services = this . getUniqueServicesList ( _servicesManager );
2020-07-03 19:18:59 +02:00
2022-12-09 10:58:01 -05:00
// The onModeEnter of the service must occur BEFORE the extension
// onModeEnter in order to reset the state to a standard state
// before the extension restores and cached data.
2024-06-28 09:58:39 -03:00
for ( const service of services ) {
2022-11-24 16:28:10 -05:00
service ? . onModeEnter ? .();
}
2020-08-04 05:11:23 -04:00
2020-07-03 19:18:59 +02:00
registeredExtensionIds . forEach ( extensionId => {
const onModeEnter = _extensionLifeCycleHooks . onModeEnter [ extensionId ];
if ( typeof onModeEnter === 'function' ) {
onModeEnter ({
servicesManager : _servicesManager ,
commandsManager : _commandsManager ,
2021-05-28 14:32:48 -04:00
hotkeysManager : _hotkeysManager ,
2024-12-05 15:29:59 -03:00
extensionManager : this ,
2020-07-03 19:18:59 +02:00
});
}
});
}
2023-02-10 14:30:56 -05:00
public onModeExit () : void {
2023-09-01 16:19:39 -04:00
const { registeredExtensionIds , _servicesManager , _commandsManager , _extensionLifeCycleHooks } =
this ;
2024-06-28 09:58:39 -03:00
const services = this . getUniqueServicesList ( _servicesManager );
2020-08-04 05:11:23 -04:00
registeredExtensionIds . forEach ( extensionId => {
const onModeExit = _extensionLifeCycleHooks . onModeExit [ extensionId ];
if ( typeof onModeExit === 'function' ) {
onModeExit ({
servicesManager : _servicesManager ,
commandsManager : _commandsManager ,
});
}
});
2022-12-09 10:58:01 -05:00
// The service onModeExit calls must occur after the extension ones
// so that extension ones can store/restore data.
2024-06-28 09:58:39 -03:00
for ( const service of services ) {
2022-12-09 10:58:01 -05:00
try {
service ? . onModeExit ? .();
} catch ( e ) {
console . warn ( 'onModeExit caught' , e );
}
}
2020-08-04 05:11:23 -04:00
}
2019-08-13 15:36:46 -04:00
/**
* An array of extensions, or an array of arrays that contains extension
* configuration pairs.
*
* @param {Object[]} extensions - Array of extensions
*/
2023-02-10 14:30:56 -05:00
public registerExtensions = async (
2023-09-01 16:19:39 -04:00
extensions : ( ExtensionRegister | [ ExtensionRegister , ExtensionConfiguration ])[],
2023-02-10 14:30:56 -05:00
dataSources : unknown [] = []
) : Promise < void > => {
2022-07-27 16:39:04 +00:00
// Todo: we ideally should be able to run registrations in parallel
// but currently since some extensions need to be registered before
// others, we need to run them sequentially. We need a postInit hook
// to avoid this sequential async registration
2023-02-17 15:04:07 -05:00
for ( let i = 0 ; i < extensions . length ; i ++ ) {
const extension = extensions [ i ];
2019-08-13 15:36:46 -04:00
const hasConfiguration = Array . isArray ( extension );
2022-07-27 16:39:04 +00:00
try {
if ( hasConfiguration ) {
2023-02-17 15:04:07 -05:00
// Important: for some reason in the line below the type
// of extension is not recognized as [ExtensionRegister,
// ExtensionConfiguration] by babel DON"T CHANGE IT
// Same for the for loop above don't use
// for (const extension of extensions)
const ohifExtension = extension [ 0 ];
const configuration = extension [ 1 ];
2023-09-01 16:19:39 -04:00
await this . registerExtension ( ohifExtension , configuration , dataSources );
2022-07-27 16:39:04 +00:00
} else {
await this . registerExtension ( extension , {}, dataSources );
}
} catch ( error ) {
console . error ( error );
2019-08-13 15:36:46 -04:00
}
2022-07-27 16:39:04 +00:00
}
2020-05-09 20:23:43 +02:00
};
2019-08-13 15:36:46 -04:00
/**
*
* TODO: Id Management: SopClassHandlers currently refer to viewport module by id; setting the extension id as viewport module id is a workaround for now
* @param {Object} extension
* @param {Object} configuration
*/
2023-02-10 14:30:56 -05:00
public registerExtension = async (
extension : ExtensionRegister ,
2022-07-27 16:39:04 +00:00
configuration = {},
dataSources = []
2023-02-10 14:30:56 -05:00
) : Promise < void > => {
2019-08-13 15:36:46 -04:00
if ( ! extension ) {
2021-05-28 14:32:48 -04:00
throw new Error ( 'Attempting to register a null/undefined extension.' );
2019-08-13 15:36:46 -04:00
}
2023-02-10 14:30:56 -05:00
const extensionId = extension . id ;
2019-08-13 15:36:46 -04:00
if ( ! extensionId ) {
2020-05-13 12:06:13 +02:00
// Note: Mode framework cannot function without IDs.
log . warn ( extension );
throw new Error ( `Extension ID not set` );
2019-08-13 15:36:46 -04:00
}
if ( this . registeredExtensionIds . includes ( extensionId )) {
log . warn (
`Extension ID ${ extensionId } has already been registered. Exiting before duplicating modules.`
);
return ;
}
// preRegistrationHook
if ( extension . preRegistration ) {
2022-07-27 16:39:04 +00:00
await extension . preRegistration ({
2019-11-14 11:50:37 -03:00
servicesManager : this._servicesManager ,
2024-04-02 09:35:30 -04:00
serviceProvidersManager : this._serviceProvidersManager ,
2019-12-02 13:29:04 -03:00
commandsManager : this._commandsManager ,
2020-08-19 16:02:59 -03:00
hotkeysManager : this._hotkeysManager ,
2022-07-27 16:39:04 +00:00
extensionManager : this ,
2019-12-16 16:07:53 -03:00
appConfig : this._appConfig ,
2019-11-13 17:02:14 -03:00
configuration ,
});
2019-08-13 15:36:46 -04:00
}
2020-07-03 19:18:59 +02:00
if ( extension . onModeEnter ) {
2023-09-01 16:19:39 -04:00
this . _extensionLifeCycleHooks . onModeEnter [ extensionId ] = extension . onModeEnter ;
2020-07-03 19:18:59 +02:00
}
2020-08-04 05:11:23 -04:00
if ( extension . onModeExit ) {
2023-09-01 16:19:39 -04:00
this . _extensionLifeCycleHooks . onModeExit [ extensionId ] = extension . onModeExit ;
2020-08-04 05:11:23 -04:00
}
2019-08-13 15:36:46 -04:00
// Register Modules
this . moduleTypeNames . forEach ( moduleType => {
const extensionModule = this . _getExtensionModule (
moduleType ,
extension ,
2019-12-16 16:07:53 -03:00
extensionId ,
configuration
2019-08-13 15:36:46 -04:00
);
2024-03-27 16:01:32 -04:00
if ( ! extensionModule ) {
return ;
}
2019-08-13 15:36:46 -04:00
2024-03-27 16:01:32 -04:00
switch ( moduleType ) {
case MODULE_TYPES.COMMANDS :
this._initCommandsModule ( extensionModule );
break ;
case MODULE_TYPES.DATA_SOURCE :
this._initDataSourcesModule ( extensionModule , extensionId , dataSources );
break ;
case MODULE_TYPES.HANGING_PROTOCOL :
this._initHangingProtocolsModule ( extensionModule , extensionId );
break ;
case MODULE_TYPES.PANEL :
this._initPanelModule ( extensionModule , extensionId );
break ;
case MODULE_TYPES.TOOLBAR :
this._initToolbarModule ( extensionModule , extensionId );
break ;
case MODULE_TYPES.VIEWPORT :
case MODULE_TYPES.SOP_CLASS_HANDLER :
case MODULE_TYPES.CONTEXT :
case MODULE_TYPES.LAYOUT_TEMPLATE :
case MODULE_TYPES.CUSTOMIZATION :
case MODULE_TYPES.STATE_SYNC :
case MODULE_TYPES.UTILITY :
this.processExtensionModule ( extensionModule , extensionId , moduleType );
break ;
default :
throw new Error ( `Module type invalid: ${ moduleType } ` );
2019-08-13 15:36:46 -04:00
}
2024-03-27 16:01:32 -04:00
this . modules [ moduleType ]. push ({
extensionId ,
module : extensionModule ,
});
2019-08-13 15:36:46 -04:00
});
// Track extension registration
this . registeredExtensionIds . push ( extensionId );
2020-05-09 20:23:43 +02:00
};
2019-08-13 15:36:46 -04:00
2024-03-27 16:01:32 -04:00
/**
* Retrieves the module entry associated with the given string entry
* @param stringEntry - The string entry to retrieve the module entry for which is
* in the format of `${extensionId}.${moduleType}.${moduleName}`
* @returns The module entry associated with the given string entry.
*/
2020-05-09 20:23:43 +02:00
getModuleEntry = stringEntry => {
2020-05-07 14:04:33 -04:00
return this . modulesMap [ stringEntry ];
2020-05-09 20:23:43 +02:00
};
2020-05-07 14:04:33 -04:00
2024-03-27 16:01:32 -04:00
/**
* Retrieves all modules of a given type for all registered extensions.
*
* @param moduleType - The type of modules to retrieve.
* @returns An array of modules of the specified type.
*/
getModulesByType = ( moduleType : string ) => {
return this . modules [ moduleType ];
};
2020-05-11 18:56:46 +02:00
getDataSources = dataSourceName => {
2020-06-05 17:36:11 +02:00
if ( dataSourceName === undefined ) {
2020-06-26 11:01:13 +02:00
// Default to the activeDataSource
2025-04-11 15:57:29 -04:00
dataSourceName = this . activeDataSourceName ;
2020-06-05 17:36:11 +02:00
}
2020-05-09 10:57:52 +02:00
// Note: this currently uses the data source name, which feels weird...
2020-05-11 18:56:46 +02:00
return this . dataSourceMap [ dataSourceName ];
2020-05-09 20:23:43 +02:00
};
2020-05-07 14:04:33 -04:00
2025-02-28 15:17:55 -05:00
getDataSourceInstance = dataSourceName => {
return this . dataSourceMap [ dataSourceName ][ 0 ];
};
2020-06-26 11:01:13 +02:00
getActiveDataSource = () => {
2025-04-11 15:57:29 -04:00
return this . dataSourceMap [ this . activeDataSourceName ];
2020-06-26 11:01:13 +02:00
};
2023-07-20 11:43:05 -04:00
/**
* Gets the data source definition for the given data source name.
* If no data source name is provided, the active data source definition is
* returned.
* @param dataSourceName the data source name
* @returns the data source definition
*/
2023-08-25 14:19:17 -04:00
getDataSourceDefinition = dataSourceName => {
2023-07-20 11:43:05 -04:00
if ( dataSourceName === undefined ) {
// Default to the activeDataSource
2025-04-11 15:57:29 -04:00
dataSourceName = this . activeDataSourceName ;
2023-07-20 11:43:05 -04:00
}
return this . dataSourceDefs [ dataSourceName ];
2021-05-28 14:32:48 -04:00
};
2023-08-25 14:19:17 -04:00
/**
* Gets the data source definition for the active data source.
*/
getActiveDataSourceDefinition = () => {
2025-04-11 15:57:29 -04:00
return this . getDataSourceDefinition ( this . activeDataSourceName );
2023-08-25 14:19:17 -04:00
};
2025-02-28 15:17:55 -05:00
/**
* Gets a formatted list of data sources suitable for UI display/selection.
* Only returns data sources that support STOW or have a WADO root.
* @returns Array of data source options with value, label, and placeholder
*/
getDataSourcesForUI = () => {
// If multi-select export is not allowed, return empty list
if ( this . _appConfig ? . allowMultiSelectExport === false ) {
return [];
}
2025-04-11 15:57:29 -04:00
const inactiveDataSourceNames = Object . keys ( this . dataSourceMap ). filter ( ds => {
const configuration = this . dataSourceDefs [ ds ] ? . configuration ;
const isNotActiveDataSource =
this . dataSourceDefs [ ds ]. sourceName !== this . activeDataSourceName ;
const supportsStowOrWado = configuration ? . supportsStow ?? configuration ? . wadoRoot ;
return supportsStowOrWado && isNotActiveDataSource ;
});
const allDatasourcesForUI = [ this . activeDataSourceName , ... inactiveDataSourceNames ]. map ( ds => ({
value : ds ,
label : ds ,
placeHolder : ds ,
}));
return allDatasourcesForUI ;
2025-02-28 15:17:55 -05:00
};
2019-08-13 15:36:46 -04:00
/**
* @private
* @param {string} moduleType
* @param {Object} extension
* @param {string} extensionId - Used for logging warnings
*/
2020-05-09 20:23:43 +02:00
_getExtensionModule = ( moduleType , extension , extensionId , configuration ) => {
2019-08-13 15:36:46 -04:00
const getModuleFnName = 'get' + _capitalizeFirstCharacter ( moduleType );
const getModuleFn = extension [ getModuleFnName ];
if ( ! getModuleFn ) {
return ;
}
try {
2023-03-22 17:45:28 -04:00
const extensionModule = extension [ getModuleFnName ]({
2020-06-14 21:18:29 -04:00
appConfig : this._appConfig ,
2019-12-02 13:29:04 -03:00
commandsManager : this._commandsManager ,
2020-06-14 21:18:29 -04:00
servicesManager : this._servicesManager ,
2020-08-19 16:02:59 -03:00
hotkeysManager : this._hotkeysManager ,
extensionManager : this ,
2019-12-16 16:07:53 -03:00
configuration ,
2019-11-14 19:29:40 -03:00
});
2019-08-13 15:36:46 -04:00
if ( ! extensionModule ) {
log . warn (
`Null or undefined returned when registering the ${ getModuleFnName } module for the ${ extensionId } extension`
);
}
return extensionModule ;
} catch ( ex ) {
2024-11-06 07:15:27 -05:00
console . error ( ex );
2021-03-01 16:52:54 +01:00
throw new Error (
2019-08-13 15:36:46 -04:00
`Exception thrown while trying to call ${ getModuleFnName } for the ${ extensionId } extension`
);
}
2020-05-09 20:23:43 +02:00
};
2019-08-13 15:36:46 -04:00
2022-09-20 23:40:26 -04:00
_initHangingProtocolsModule = ( extensionModule , extensionId ) => {
2023-02-10 16:46:09 -05:00
const { hangingProtocolService } = this . _servicesManager . services ;
2023-03-31 11:58:48 -04:00
extensionModule . forEach (({ name , protocol }) => {
2022-10-05 08:32:54 -04:00
if ( protocol ) {
// Only auto-register if protocol specified, otherwise let mode register
2023-03-31 11:58:48 -04:00
hangingProtocolService . addProtocol ( name , protocol );
2022-10-05 08:32:54 -04:00
}
2022-09-20 23:40:26 -04:00
});
};
2024-03-27 16:01:32 -04:00
_initPanelModule = ( extensionModule , extensionId ) => {
this . processExtensionModule ( extensionModule , extensionId , MODULE_TYPES . PANEL );
};
_initToolbarModule = ( extensionModule , extensionId ) => {
// check if the toolbar module has a handler function for evaluation of
// the toolbar button state
const { toolbarService } = this . _servicesManager . services ;
extensionModule . forEach ( toolbarButton => {
if ( toolbarButton . evaluate ) {
toolbarService . registerEvaluateFunction ( toolbarButton . name , toolbarButton . evaluate );
}
});
};
/**
* Processes an extension module.
* @param extensionModule - The extension module to process.
* @param extensionId - The ID of the extension.
* @param moduleType - The type of the module.
*/
private processExtensionModule ( extensionModule , extensionId : string , moduleType : string ) {
extensionModule . forEach ( element => {
if ( ! element . name ) {
throw new Error ( `Extension ID ${ extensionId } module ${ moduleType } element has no name` );
}
const id = ` ${ extensionId } . ${ moduleType } . ${ element . name } ` ;
element . id = id ;
this . modulesMap [ id ] = element ;
});
}
2023-07-20 11:43:05 -04:00
/**
* Adds the given data source and optionally sets it as the active data source.
* The method does this by first creating the data source.
* @param dataSourceDef the data source definition to be added
* @param activate flag to indicate if the added data source should be set to the active data source
*/
2023-09-01 16:19:39 -04:00
addDataSource ( dataSourceDef : DataSourceDefinition , options = { activate : false }) {
2023-07-20 11:43:05 -04:00
const existingDataSource = this . getDataSources ( dataSourceDef . sourceName );
if ( existingDataSource ? .[ 0 ]) {
// The data source already exists and cannot be added.
return ;
}
this . _createDataSourceInstance ( dataSourceDef );
if ( options . activate ) {
this . setActiveDataSource ( dataSourceDef . sourceName );
}
}
/**
* Updates the configuration of the given data source name. It first creates a new data source with
* the existing definition and the new configuration passed in.
* @param dataSourceName the name of the data source to update
* @param dataSourceConfiguration the new configuration to update the data source with
*/
2023-09-01 16:19:39 -04:00
updateDataSourceConfiguration ( dataSourceName : string , dataSourceConfiguration : any ) {
2023-07-20 11:43:05 -04:00
const existingDataSource = this . getDataSources ( dataSourceName );
if ( ! existingDataSource ? .[ 0 ]) {
// Cannot update a non existent data source.
return ;
}
const dataSourceDef = this . dataSourceDefs [ dataSourceName ];
// Update the configuration.
dataSourceDef . configuration = dataSourceConfiguration ;
this . _createDataSourceInstance ( dataSourceDef );
2025-04-11 15:57:29 -04:00
if ( this . activeDataSourceName === dataSourceName ) {
2023-07-20 11:43:05 -04:00
// When the active data source is changed/set, fire an event to indicate that its configuration has changed.
2023-09-01 16:19:39 -04:00
this . _broadcastEvent ( ExtensionManager . EVENTS . ACTIVE_DATA_SOURCE_CHANGED , dataSourceDef );
2023-07-20 11:43:05 -04:00
}
}
/**
* Creates a data source instance from the given definition. The definition is
* added to dataSourceDefs and the created instance is added to dataSourceMap.
* @param dataSourceDef
* @returns
*/
_createDataSourceInstance ( dataSourceDef : DataSourceDefinition ) {
const module = this . getModuleEntry ( dataSourceDef . namespace );
if ( ! module ) {
return ;
}
this . dataSourceDefs [ dataSourceDef . sourceName ] = dataSourceDef ;
const dataSourceInstance = module . createDataSource (
dataSourceDef . configuration ,
2023-12-13 16:21:46 -03:00
this . _servicesManager ,
2023-11-24 10:46:54 -03:00
this
2023-07-20 11:43:05 -04:00
);
this . dataSourceMap [ dataSourceDef . sourceName ] = [ dataSourceInstance ];
}
_initDataSourcesModule (
extensionModule ,
extensionId ,
dataSources : Array < DataSourceDefinition > = []
) : void {
extensionModule . forEach ( element => {
2023-09-01 16:19:39 -04:00
this . modulesMap [ ` ${ extensionId } . ${ MODULE_TYPES . DATA_SOURCE } . ${ element . name } ` ] = element ;
2022-12-29 10:13:06 -05:00
});
2021-06-23 20:41:27 +02:00
2020-05-13 12:06:13 +02:00
extensionModule . forEach ( element => {
const namespace = ` ${ extensionId } . ${ MODULE_TYPES . DATA_SOURCE } . ${ element . name } ` ;
dataSources . forEach ( dataSource => {
if ( dataSource . namespace === namespace ) {
2023-07-20 11:43:05 -04:00
this . addDataSource ( dataSource );
2019-08-13 15:36:46 -04:00
}
2020-05-13 12:06:13 +02:00
});
});
}
2019-08-13 15:36:46 -04:00
/**
*
* @private
* @param {Object[]} commandDefinitions
*/
2020-05-13 12:06:13 +02:00
_initCommandsModule = extensionModule => {
let { definitions , defaultContext } = extensionModule ;
if ( ! definitions || Object . keys ( definitions ). length === 0 ) {
log . warn ( 'Commands Module contains no command definitions' );
return ;
}
defaultContext = defaultContext || 'VIEWER' ;
2019-08-13 15:36:46 -04:00
if ( ! this . _commandsManager . getContext ( defaultContext )) {
this . _commandsManager . createContext ( defaultContext );
}
2020-05-13 12:06:13 +02:00
Object . keys ( definitions ). forEach ( commandName => {
2025-01-09 22:01:42 -05:00
let commandDefinition = definitions [ commandName ];
if ( typeof commandDefinition === 'function' ) {
commandDefinition = { commandFn : commandDefinition };
}
2019-08-13 15:36:46 -04:00
const commandHasContextThatDoesNotExist =
2023-09-01 16:19:39 -04:00
commandDefinition . context && ! this . _commandsManager . getContext ( commandDefinition . context );
2019-08-13 15:36:46 -04:00
if ( commandHasContextThatDoesNotExist ) {
this . _commandsManager . createContext ( commandDefinition . context );
}
this . _commandsManager . registerCommand (
commandDefinition . context || defaultContext ,
commandName ,
commandDefinition
);
});
2020-05-09 20:23:43 +02:00
};
2024-07-05 13:19:42 -04:00
public get appConfig() {
return this . _appConfig ;
}
2019-08-13 15:36:46 -04:00
}
/**
* @private
* @param {string} lower
*/
function _capitalizeFirstCharacter ( lower ) {
2022-11-11 02:29:55 +08:00
return lower . charAt ( 0 ). toUpperCase () + lower . substring ( 1 );
2019-08-13 15:36:46 -04:00
}