2019-08-13 21:36:46 +02:00
|
|
|
import log from '../log.js';
|
2023-03-15 17:41:41 +01:00
|
|
|
import { Command, Commands } from '../types/Command';
|
2019-08-13 21:36:46 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The definition of a command
|
|
|
|
|
*
|
|
|
|
|
* @typedef {Object} CommandDefinition
|
|
|
|
|
* @property {Function} commandFn - Command to call
|
|
|
|
|
* @property {Object} options - Object of params to pass action
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The Commands Manager tracks named commands (or functions) that are scoped to
|
|
|
|
|
* a context. When we attempt to run a command with a given name, we look for it
|
|
|
|
|
* in our active contexts. If found, we run the command, passing in any application
|
|
|
|
|
* or call specific data specified in the command's definition.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: A more robust version of the CommandsManager lives in v1. If you're looking
|
|
|
|
|
* to extend this class, please check it's source before adding new methods.
|
|
|
|
|
*/
|
|
|
|
|
export class CommandsManager {
|
2022-07-27 18:39:04 +02:00
|
|
|
constructor({} = {}) {
|
2019-08-13 21:36:46 +02:00
|
|
|
this.contexts = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Allows us to create commands "per context". An example would be the "Cornerstone"
|
|
|
|
|
* context having a `SaveImage` command, and the "VTK" context having a `SaveImage`
|
|
|
|
|
* command. The distinction of a context allows us to call the command in either
|
|
|
|
|
* context, and have faith that the correct command will be run.
|
|
|
|
|
*
|
|
|
|
|
* @method
|
|
|
|
|
* @param {string} contextName - Namespace for commands
|
|
|
|
|
* @returns {undefined}
|
|
|
|
|
*/
|
|
|
|
|
createContext(contextName) {
|
|
|
|
|
if (!contextName) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.contexts[contextName]) {
|
|
|
|
|
return this.clearContext(contextName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.contexts[contextName] = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns all command definitions for a given context
|
|
|
|
|
*
|
|
|
|
|
* @method
|
|
|
|
|
* @param {string} contextName - Namespace for commands
|
2019-12-09 18:47:23 +01:00
|
|
|
* @returns {Object} - the matched context
|
2019-08-13 21:36:46 +02:00
|
|
|
*/
|
|
|
|
|
getContext(contextName) {
|
|
|
|
|
const context = this.contexts[contextName];
|
|
|
|
|
|
|
|
|
|
if (!context) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clears all registered commands for a given context.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} contextName - Namespace for commands
|
|
|
|
|
* @returns {undefined}
|
|
|
|
|
*/
|
|
|
|
|
clearContext(contextName) {
|
|
|
|
|
if (!contextName) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.contexts[contextName] = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Register a new command with the command manager. Scoped to a context, and
|
|
|
|
|
* with a definition to assist command callers w/ providing the necessary params
|
|
|
|
|
*
|
|
|
|
|
* @method
|
|
|
|
|
* @param {string} contextName - Namespace for command; often scoped to the extension that added it
|
|
|
|
|
* @param {string} commandName - Unique name identifying the command
|
|
|
|
|
* @param {CommandDefinition} definition - {@link CommandDefinition}
|
|
|
|
|
*/
|
|
|
|
|
registerCommand(contextName, commandName, definition) {
|
|
|
|
|
if (typeof definition !== 'object') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const context = this.getContext(contextName);
|
|
|
|
|
if (!context) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context[commandName] = definition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Finds a command with the provided name if it exists in the specified context,
|
|
|
|
|
* or a currently active context.
|
|
|
|
|
*
|
|
|
|
|
* @method
|
|
|
|
|
* @param {String} commandName - Command to find
|
|
|
|
|
* @param {String} [contextName] - Specific command to look in. Defaults to current activeContexts
|
|
|
|
|
*/
|
2023-03-22 22:45:28 +01:00
|
|
|
getCommand = (commandName: string, contextName?: string) => {
|
2023-03-15 17:41:41 +01:00
|
|
|
const contexts = [];
|
2019-08-13 21:36:46 +02:00
|
|
|
|
|
|
|
|
if (contextName) {
|
|
|
|
|
const context = this.getContext(contextName);
|
|
|
|
|
if (context) {
|
|
|
|
|
contexts.push(context);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-07-27 18:39:04 +02:00
|
|
|
Object.keys(this.contexts).forEach(contextName => {
|
|
|
|
|
contexts.push(this.getContext(contextName));
|
2019-08-13 21:36:46 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contexts.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let foundCommand;
|
|
|
|
|
contexts.forEach(context => {
|
|
|
|
|
if (context[commandName]) {
|
|
|
|
|
foundCommand = context[commandName];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return foundCommand;
|
2020-05-18 23:21:41 +02:00
|
|
|
};
|
2019-08-13 21:36:46 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @method
|
|
|
|
|
* @param {String} commandName
|
|
|
|
|
* @param {Object} [options={}] - Extra options to pass the command. Like a mousedown event
|
|
|
|
|
* @param {String} [contextName]
|
|
|
|
|
*/
|
2023-03-15 17:41:41 +01:00
|
|
|
public runCommand(commandName: string, options = {}, contextName?: string) {
|
2019-08-13 21:36:46 +02:00
|
|
|
const definition = this.getCommand(commandName, contextName);
|
|
|
|
|
if (!definition) {
|
|
|
|
|
log.warn(`Command "${commandName}" not found in current context`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 03:18:46 +02:00
|
|
|
const { commandFn } = definition;
|
|
|
|
|
const commandParams = Object.assign(
|
2019-08-13 21:36:46 +02:00
|
|
|
{},
|
2020-09-15 03:18:46 +02:00
|
|
|
definition.options, // "Command configuration"
|
2019-08-13 21:36:46 +02:00
|
|
|
options // "Time of call" info
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (typeof commandFn !== 'function') {
|
|
|
|
|
log.warn(`No commandFn was defined for command "${commandName}"`);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
return commandFn(commandParams);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-15 17:41:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run one or more commands with specified extra options.
|
|
|
|
|
* Returns the result of the last command run.
|
|
|
|
|
*
|
|
|
|
|
* @param toRun - A specification of one or more commands
|
|
|
|
|
* @param options - to include in the commands run beyond
|
|
|
|
|
* the commandOptions specified in the base.
|
|
|
|
|
*/
|
|
|
|
|
public run(
|
|
|
|
|
toRun: Command | Commands | Command[] | undefined,
|
|
|
|
|
options?: Record<string, unknown>
|
|
|
|
|
): unknown {
|
|
|
|
|
if (!toRun) return;
|
|
|
|
|
const commands =
|
|
|
|
|
(Array.isArray(toRun) && toRun) ||
|
|
|
|
|
((toRun as Command).commandName && [toRun]) ||
|
|
|
|
|
(Array.isArray((toRun as Commands).commands) &&
|
|
|
|
|
(toRun as Commands).commands);
|
|
|
|
|
if (!commands) {
|
|
|
|
|
console.log("Command isn't runnable", toRun);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let result;
|
|
|
|
|
(commands as Command[]).forEach(
|
|
|
|
|
({ commandName, commandOptions, context }) => {
|
|
|
|
|
if (commandName) {
|
|
|
|
|
result = this.runCommand(
|
|
|
|
|
commandName,
|
|
|
|
|
{
|
|
|
|
|
...commandOptions,
|
|
|
|
|
...options,
|
|
|
|
|
},
|
|
|
|
|
context
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('No command name supplied in', toRun);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-08-13 21:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default CommandsManager;
|