diagram-js
Version:
A toolbox for displaying and modifying diagrams on the web
251 lines (219 loc) • 6.75 kB
JavaScript
import {
forEach,
isFunction,
isArray,
isNumber,
isObject
} from 'min-dash';
/**
* @typedef {import('../core/Types').ElementLike} ElementLike
* @typedef {import('../core/EventBus').default} EventBus
* @typedef {import('./CommandStack').CommandContext} CommandContext
*
* @typedef {string|string[]} Events
* @typedef { (context: CommandContext) => ElementLike[] | void } HandlerFunction
* @typedef { (context: CommandContext) => void } ComposeHandlerFunction
*/
var DEFAULT_PRIORITY = 1000;
/**
* A utility that can be used to plug into the command execution for
* extension and/or validation.
*
* @class
* @constructor
*
* @example
*
* ```javascript
* import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
*
* class CommandLogger extends CommandInterceptor {
* constructor(eventBus) {
* super(eventBus);
*
* this.preExecute('shape.create', (event) => {
* console.log('commandStack.shape-create.preExecute', event);
* });
* }
* ```
*
* @param {EventBus} eventBus
*/
export default function CommandInterceptor(eventBus) {
/**
* @type {EventBus}
*/
this._eventBus = eventBus;
}
CommandInterceptor.$inject = [ 'eventBus' ];
function unwrapEvent(fn, that) {
return function(event) {
return fn.call(that || null, event.context, event.command, event);
};
}
/**
* Intercept a command during one of the phases.
*
* @param {Events} [events] command(s) to intercept
* @param {string} [hook] phase to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) {
if (isFunction(hook) || isNumber(hook)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = hook;
hook = null;
}
if (isFunction(priority)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = DEFAULT_PRIORITY;
}
if (isObject(unwrap)) {
that = unwrap;
unwrap = false;
}
if (!isFunction(handlerFn)) {
throw new Error('handlerFn must be a function');
}
if (!isArray(events)) {
events = [ events ];
}
var eventBus = this._eventBus;
forEach(events, function(event) {
// concat commandStack(.event)?(.hook)?
var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.');
eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that);
});
};
/**
* Add a <canExecute> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.canExecute = createHook('canExecute');
/**
* Add a <preExecute> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.preExecute = createHook('preExecute');
/**
* Add a <preExecuted> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.preExecuted = createHook('preExecuted');
/**
* Add a <execute> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.execute = createHook('execute');
/**
* Add a <executed> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.executed = createHook('executed');
/**
* Add a <postExecute> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.postExecute = createHook('postExecute');
/**
* Add a <postExecuted> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.postExecuted = createHook('postExecuted');
/**
* Add a <revert> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.revert = createHook('revert');
/**
* Add a <reverted> phase of command interceptor.
*
* @param {Events} [events] command(s) to intercept
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap] whether the event should be unwrapped
* @param {any} [that]
*/
CommandInterceptor.prototype.reverted = createHook('reverted');
/*
* Add prototype methods for each phase of command execution (e.g. execute,
* revert).
*
* @param {string} hook
*
* @return { (
* events?: Events,
* priority?: number,
* handlerFn: ComposeHandlerFunction|HandlerFunction,
* unwrap?: boolean
* ) => any }
*/
function createHook(hook) {
/**
* @this {CommandInterceptor}
*
* @param {Events} [events]
* @param {number} [priority]
* @param {ComposeHandlerFunction|HandlerFunction} handlerFn
* @param {boolean} [unwrap]
* @param {any} [that]
*/
const hookFn = function(events, priority, handlerFn, unwrap, that) {
if (isFunction(events) || isNumber(events)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = events;
events = null;
}
this.on(events, hook, priority, handlerFn, unwrap, that);
};
return hookFn;
}