fix(replace): pre-build BPMN elements

This simplifies the ways we work with replacements.

Instead of monkey-patching the original replace functionality we

* create the business object / target object
* pass it to replace

A simple way that works well with our existing infrastructure.

Other changes:

* fixes redo / undo issues.
* splits context-pad + replace

Closes #180
This commit is contained in:
Nico Rehwaldt 2015-03-11 16:17:19 +01:00 committed by Nico Rehwaldt
parent eb63fda6ab
commit 67d81c346c
11 changed files with 679 additions and 612 deletions

View File

@ -67,9 +67,6 @@ Modeler.prototype._modelingModules = [
require('diagram-js/lib/features/bendpoints'),
require('diagram-js/lib/features/resize'),
require('diagram-js/lib/features/lasso-tool'),
require('diagram-js/lib/features/replace'),
require('diagram-js/lib/features/popup-menu'),
require('./features/replace'),
require('./features/modeling'),
require('./features/context-pad'),
require('./features/palette')

View File

@ -4,15 +4,13 @@
var assign = require('lodash/object/assign'),
forEach = require('lodash/collection/forEach');
var getReplacementMenuEntries = require('./ReplaceMenuFactory').getReplacementMenuEntries;
/**
* A provider for BPMN 2.0 elements context pad
*/
function ContextPadProvider(contextPad, modeling, elementFactory,
connect, create, popupMenu,
replace, canvas) {
connect, create, bpmnReplace,
canvas) {
contextPad.registerProvider(this);
@ -23,8 +21,7 @@ function ContextPadProvider(contextPad, modeling, elementFactory,
this._elementFactory = elementFactory;
this._connect = connect;
this._create = create;
this._popupMenu = popupMenu;
this._replace = replace;
this._bpmnReplace = bpmnReplace;
this._canvas = canvas;
}
@ -34,8 +31,7 @@ ContextPadProvider.$inject = [
'elementFactory',
'connect',
'create',
'popupMenu',
'replace',
'bpmnReplace',
'canvas'
];
@ -47,8 +43,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
elementFactory = this._elementFactory,
connect = this._connect,
create = this._create,
popupMenu = this._popupMenu,
replace = this._replace,
bpmnReplace = this._bpmnReplace,
canvas = this._canvas;
var actions = {};
@ -71,11 +66,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
}
}
function replaceElement(element, newType, options) {
replace.replaceElement(element, {type: newType}, options);
}
function getPosition(element) {
function getReplaceMenuPosition(element) {
var Y_OFFSET = 5;
@ -96,7 +87,6 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
}
function appendAction(type, className, options) {
function appendListener(event, element) {
@ -177,17 +167,8 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
group: 'edit',
className: 'icon-screw-wrench',
action: {
click: function(event, element) {
var pos = getPosition(element);
var entries = getReplacementMenuEntries(element, replaceElement);
popupMenu.open(
'replace-menu',
pos,
entries
);
bpmnReplace.openChooser(getReplaceMenuPosition(element), element);
}
}
}

View File

@ -1,395 +0,0 @@
'use strict';
var startEventReplace = [
{
options: {
eventDefinition: ''
},
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
targetType: 'bpmn:StartEvent'
},
{
options: {
eventDefinition: ''
},
label: 'Intermediate Throw Event',
actionName: 'replace-with-intermediate-throwing',
className: 'icon-intermediate-event-none',
targetType: 'bpmn:IntermediateThrowEvent'
},
{
options: {
eventDefinition: ''
},
label: 'End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-none',
targetType: 'bpmn:EndEvent'
},
{
label: 'Message Start Event',
actionName: 'replace-with-message-start',
className: 'icon-start-event-message',
targetType: 'bpmn:StartEvent',
options: {
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Start Event',
actionName: 'replace-with-timer-start',
className: 'icon-start-event-timer',
targetType: 'bpmn:StartEvent',
options: {
eventDefinition: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Conditional Start Event',
actionName: 'replace-with-conditional-start',
className: 'icon-start-event-condition',
targetType: 'bpmn:StartEvent',
options:{
eventDefinition: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Signal Start Event',
actionName: 'replace-with-signal-start',
className: 'icon-start-event-signal',
targetType: 'bpmn:StartEvent',
options: {
eventDefinition: 'bpmn:SignalEventDefinition'
}
}
];
var intermediateEventReplace = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
targetType: 'bpmn:StartEvent',
options: {
eventDefinition: ''
},
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-message-intermediate-throw',
className: 'icon-intermediate-event-none',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: ''
}
},
{
label: 'End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-none',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: ''
}
},
{
label: 'Message Intermediate Catch Event',
actionName: 'replace-with-intermediate-catch',
className: 'icon-intermediate-event-catch-message',
targetType: 'bpmn:IntermediateCatchEvent',
options: {
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Message Intermediate Throw Event',
actionName: 'replace-with-intermediate-throw',
className: 'icon-intermediate-event-throw-message',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Intermediate Catch Event',
actionName: 'replace-with-timer-intermediate-catch',
className: 'icon-intermediate-event-catch-timer',
targetType: 'bpmn:IntermediateCatchEvent',
options: {
eventDefinition: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Escalation Intermediate Catch Event',
actionName: 'replace-with-escalation-catch',
className: 'icon-intermediate-event-catch-escalation',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Conditional Intermediate Catch Event',
actionName: 'replace-with-conditional-intermediate-catch',
className: 'icon-intermediate-event-catch-condition',
targetType: 'bpmn:IntermediateCatchEvent',
options: {
eventDefinition: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Link Intermediate Catch Event',
actionName: 'replace-with-link-intermediate-catch',
className: 'icon-intermediate-event-catch-link',
targetType: 'bpmn:IntermediateCatchEvent',
options: {
eventDefinition: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Link Intermediate Throw Event',
actionName: 'replace-with-link-intermediate-throw',
className: 'icon-intermediate-event-throw-link',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Compensation Intermediate Throw Event',
actionName: 'replace-with-compensation-intermediate-throw',
className: 'icon-intermediate-event-throw-compensation',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal Throw Catch Event',
actionName: 'replace-with-throw-intermediate-catch',
className: 'icon-intermediate-event-catch-signal',
targetType: 'bpmn:IntermediateCatchEvent',
options: {
eventDefinition: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Signal Intermediate Throw Event',
actionName: 'replace-with-signal-intermediate-throw',
className: 'icon-intermediate-event-throw-signal',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: 'bpmn:SignalEventDefinition'
}
}
];
var endEventReplace = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
targetType: 'bpmn:StartEvent',
options: {
eventDefinition: ''
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-message-intermediate-throw',
className: 'icon-intermediate-event-none',
targetType: 'bpmn:IntermediateThrowEvent',
options: {
eventDefinition: ''
},
},
{
label: 'End Event',
actionName: 'replace-with-none-end',
className: 'icon-end-event-none',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: ''
},
},
{
label: 'Message End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-message',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Escalation End Event',
actionName: 'replace-with-escalation-end',
className: 'icon-end-event-escalation',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Error End Event',
actionName: 'replace-with-error-end',
className: 'icon-end-event-error',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Cancel End Event',
actionName: 'replace-with-cancel-end',
className: 'icon-end-event-cancel',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:CancelEventDefinition'
}
},
{
label: 'Compensation End Event',
actionName: 'replace-with-compensation-end',
className: 'icon-end-event-compensation',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal End Event',
actionName: 'replace-with-signal-end',
className: 'icon-end-event-signal',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Terminate End Event',
actionName: 'replace-with-terminate-end',
className: 'icon-end-event-terminate',
targetType: 'bpmn:EndEvent',
options: {
eventDefinition: 'bpmn:TerminateEventDefinition'
}
}
];
var gatewayReplace = [
{
label: 'Exclusive Gateway',
actionName: 'replace-with-exclusive-gateway',
className: 'icon-gateway-xor',
targetType: 'bpmn:ExclusiveGateway'
},
{
label: 'Parallel Gateway',
actionName: 'replace-with-parallel-gateway',
className: 'icon-gateway-parallel',
targetType: 'bpmn:ParallelGateway'
},
{
label: 'Inclusive Gateway',
actionName: 'replace-with-inclusive-gateway',
className: 'icon-gateway-or',
targetType: 'bpmn:InclusiveGateway'
},
{
label: 'Complex Gateway',
actionName: 'replace-with-complex-gateway',
className: 'icon-gateway-complex',
targetType: 'bpmn:ComplexGateway'
},
{
label: 'Event based Gateway',
actionName: 'replace-with-event-based-gateway',
className: 'icon-gateway-eventbased',
targetType: 'bpmn:EventBasedGateway',
options: {
newBusinessAtt: { instantiate: false, eventGatewayType: 'Exclusive' }
}
}
// Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
// {
// label: 'Event based instantiating Gateway',
// actionName: 'replace-with-exclusive-event-based-gateway',
// className: 'icon-exclusive-event-based',
// targetType: 'bpmn:EventBasedGateway',
// options: {
// newBusinessAtt: { instantiate: true, eventGatewayType: 'Exclusive' }
// }
// },
// {
// label: 'Parallel Event based instantiating Gateway',
// actionName: 'replace-with-parallel-event-based-instantiate-gateway',
// className: 'icon-parallel-event-based-instantiate-gateway',
// targetType: 'bpmn:EventBasedGateway',
// options: {
// newBusinessAtt: { instantiate: true, eventGatewayType: 'Parallel' }
// }
// }
];
var taskReplace = [
{
label: 'Task',
actionName: 'replace-with-task',
className: 'icon-task',
targetType: 'bpmn:Task'
},
{
label: 'Send Task',
actionName: 'replace-with-send-task',
className: 'icon-send',
targetType: 'bpmn:SendTask'
},
{
label: 'Receive Task',
actionName: 'replace-with-receive-task',
className: 'icon-receive',
targetType: 'bpmn:ReceiveTask'
},
{
label: 'User Task',
actionName: 'replace-with-user-task',
className: 'icon-user',
targetType: 'bpmn:UserTask'
},
{
label: 'Manual Task',
actionName: 'replace-with-manual-task',
className: 'icon-manual',
targetType: 'bpmn:ManualTask'
},
{
label: 'Business Rule Task',
actionName: 'replace-with-rule-task',
className: 'icon-business-rule',
targetType: 'bpmn:BusinessRuleTask'
},
{
label: 'Service Task',
actionName: 'replace-with-service-task',
className: 'icon-service',
targetType: 'bpmn:ServiceTask'
},
{
label: 'Script Task',
actionName: 'replace-with-script-task',
className: 'icon-script',
targetType: 'bpmn:ScriptTask'
}
];
module.exports.startEventReplace = startEventReplace;
module.exports.intermediateEventReplace = intermediateEventReplace;
module.exports.endEventReplace = endEventReplace;
module.exports.gatewayReplace = gatewayReplace;
module.exports.taskReplace = taskReplace;

View File

@ -1,95 +0,0 @@
'use strict';
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter');
var ReplaceMenuEntries = require ('./ReplaceMenuEntries');
var startEventReplace = ReplaceMenuEntries.startEventReplace,
intermediateEventReplace = ReplaceMenuEntries.intermediateEventReplace,
endEventReplace = ReplaceMenuEntries.endEventReplace,
gatewayReplace = ReplaceMenuEntries.gatewayReplace,
taskReplace = ReplaceMenuEntries.taskReplace;
function getReplacementMenuEntries(element, replaceElement) {
var menuEntries = [];
var bo = element.businessObject;
if (bo.$instanceOf('bpmn:StartEvent')) {
addEntries(startEventReplace, filterEvents);
} else if (bo.$instanceOf('bpmn:IntermediateCatchEvent') ||
bo.$instanceOf('bpmn:IntermediateThrowEvent')) {
addEntries(intermediateEventReplace, filterEvents);
} else if (bo.$instanceOf('bpmn:EndEvent')) {
addEntries(endEventReplace, filterEvents);
} else if (bo.$instanceOf('bpmn:Gateway')) {
addEntries(gatewayReplace, function(entry) {
return entry.targetType !== bo.$type;
});
} else if (bo.$instanceOf('bpmn:FlowNode')) {
addEntries(taskReplace, function(entry) {
return entry.targetType !== bo.$type;
});
}
function filterEvents(entry) {
var eventDefinition = bo.eventDefinitions ? bo.eventDefinitions[0].$type : '';
var isEventDefinitionEqual = entry.options.eventDefinition === eventDefinition;
var isEventTypeEqual = bo.$type === entry.targetType;
return ((!isEventDefinitionEqual && isEventTypeEqual) ||
!isEventTypeEqual) ||
!(isEventDefinitionEqual && isEventTypeEqual);
}
function addEntries(entries, filterFun) {
// Filter selected type from the array
var filteredEntries = filter(entries, filterFun);
// Add entries to replace menu
forEach(filteredEntries, function(definition) {
var entry = addMenuEntry(definition);
menuEntries.push(entry);
});
}
function addMenuEntry(definition) {
var label = definition.label,
newType = definition.targetType,
options = definition.options,
actionName = definition.actionName,
className = definition.className;
function appendListener() {
replaceElement(element, newType, options);
}
return {
label: label,
className: className,
action: {
name: actionName,
handler: appendListener
}
};
}
return menuEntries;
}
module.exports.getReplacementMenuEntries = getReplacementMenuEntries;

View File

@ -5,7 +5,8 @@ module.exports = {
require('diagram-js/lib/features/selection'),
require('diagram-js/lib/features/connect'),
require('diagram-js/lib/features/create'),
require('../modeling')
require('../modeling'),
require('../replace')
],
__init__: [ 'contextPadProvider' ],
contextPadProvider: [ 'type', require('./ContextPadProvider') ]

View File

@ -1,71 +1,170 @@
'use strict';
var _ = require('lodash');
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter');
var LabelUtil = require('../label-editing/LabelUtil'),
BaseReplace = require('diagram-js/lib/features/replace/Replace');
var REPLACE_OPTIONS = require ('./ReplaceOptions');
function BpmnReplace(modeling, eventBus, moddle) {
var startEventReplace = REPLACE_OPTIONS.START_EVENT,
intermediateEventReplace = REPLACE_OPTIONS.INTERMEDIATE_EVENT,
endEventReplace = REPLACE_OPTIONS.END_EVENT,
gatewayReplace = REPLACE_OPTIONS.GATEWAY,
taskReplace = REPLACE_OPTIONS.TASK;
BaseReplace.call(this, modeling);
this._originalReplaceElement = BaseReplace.prototype.replaceElement;
/**
* A replace menu provider that gives users the controls to choose
* and replace BPMN elements with each other.
*
* @param {BpmnFactory} bpmnFactory
* @param {Moddle} moddle
* @param {PopupMenu} popupMenu
* @param {Replace} replace
*/
function BpmnReplace(bpmnFactory, moddle, popupMenu, replace) {
this._modeling = modeling;
/**
* Prepares a new business object for the replacement element
* and triggers the replace operation.
*
* @param {djs.model.Base} element
* @param {Object} target
* @return {djs.model.Base} the newly created element
*/
function replaceElement(element, target) {
eventBus.on([
'commandStack.shape.replace.execute'
], function(event) {
var type = target.type,
oldBusinessObject = element.businessObject,
businessObject = bpmnFactory.create(type);
var context = event.context,
oldShape = context.oldShape,
newShape = context.newShape,
newBusinessAtt = context.options ? context.options.newBusinessAtt || {} : {},
eventDefinitionType = context.options ? context.options.eventDefinition : null;
var newElement = {
type: type,
businessObject: businessObject
};
// Attach eventDefinitions
if (eventDefinitionType) {
var eventDefinitions = newShape.businessObject.get('eventDefinitions'),
newEventDefinition = moddle.create(eventDefinitionType);
// initialize custom BPMN extensions
eventDefinitions.push(newEventDefinition);
if (target.eventDefinition) {
var eventDefinitions = businessObject.get('eventDefinitions'),
eventDefinition = moddle.create(target.eventDefinition);
eventDefinitions.push(eventDefinition);
}
// extend newBusinessAtt
_.assign(newShape.businessObject, newBusinessAtt);
if (target.instantiate !== undefined) {
businessObject.instantiate = target.instantiate;
}
// set label
modeling.updateLabel(newShape, LabelUtil.getLabel(oldShape));
});
if (target.eventGatewayType !== undefined) {
businessObject.eventGatewayType = target.eventGatewayType;
}
// TODO(nre): update bpmn specific properties based on our meta-model
// i.e. we should not only keep the name, but also
// other properties that exist in BOTH the old and new shape
// copy size (for activities only)
if (oldBusinessObject.$instanceOf('bpmn:Activity')) {
// TODO: need also to respect min/max Size
newElement.width = element.width;
newElement.height = element.height;
}
// TODO: copy other elligable properties from old business object
businessObject.name = oldBusinessObject.name;
return replace.replaceElement(element, newElement);
}
function getReplaceOptions(element) {
var menuEntries = [];
var businessObject = element.businessObject;
if (businessObject.$instanceOf('bpmn:StartEvent')) {
addEntries(startEventReplace, filterEvents);
} else
if (businessObject.$instanceOf('bpmn:IntermediateCatchEvent') ||
businessObject.$instanceOf('bpmn:IntermediateThrowEvent')) {
addEntries(intermediateEventReplace, filterEvents);
} else
if (businessObject.$instanceOf('bpmn:EndEvent')) {
addEntries(endEventReplace, filterEvents);
} else
if (businessObject.$instanceOf('bpmn:Gateway')) {
addEntries(gatewayReplace, function(entry) {
return entry.target.type !== businessObject.$type;
});
} else
if (businessObject.$instanceOf('bpmn:FlowNode')) {
addEntries(taskReplace, function(entry) {
return entry.target.type !== businessObject.$type;
});
}
function filterEvents(entry) {
var target = entry.target;
var eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0].$type;
var isEventDefinitionEqual = target.eventDefinition == eventDefinition;
var isEventTypeEqual = businessObject.$type == target.type;
return ((!isEventDefinitionEqual && isEventTypeEqual) ||
!isEventTypeEqual) ||
!(isEventDefinitionEqual && isEventTypeEqual);
}
function addEntries(entries, filterFun) {
// Filter selected type from the array
var filteredEntries = filter(entries, filterFun);
// Add entries to replace menu
forEach(filteredEntries, function(definition) {
var entry = addMenuEntry(definition);
menuEntries.push(entry);
});
}
function addMenuEntry(definition) {
return {
label: definition.label,
className: definition.className,
action: {
name: definition.actionName,
handler: function() {
replaceElement(element, definition.target);
}
}
};
}
return menuEntries;
}
// API
this.openChooser = function(position, element) {
var entries = this.getReplaceOptions(element);
popupMenu.open('replace-menu', position, entries);
};
this.getReplaceOptions = getReplaceOptions;
this.replaceElement = replaceElement;
}
module.exports = BpmnReplace;
BpmnReplace.$inject = [ 'bpmnFactory', 'moddle', 'popupMenu', 'replace' ];
BpmnReplace.$inject = [ 'modeling', 'eventBus', 'moddle' ];
BpmnReplace.prototype = Object.create(BaseReplace.prototype);
BpmnReplace.prototype.replaceElement = function(oldElement, newElementData, options) {
if (oldElement.waypoints) {
throw new Error('connections cannot be replaced (yet)');
}
// use old elements size for activities
// TODO(nre): may also specify do this during the replace suggestions
// already (i.e. replace suggestions = { type, label, newElementData }) (?)
if (oldElement.businessObject.$instanceOf('bpmn:Activity')) {
// TODO need also to respect min/max Size
newElementData.width = oldElement.width;
newElementData.height = oldElement.height;
}
return this._originalReplaceElement(oldElement, newElementData, options || {});
};
module.exports = BpmnReplace;

View File

@ -0,0 +1,407 @@
'use strict';
module.exports.START_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-intermediate-throwing',
className: 'icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message Start Event',
actionName: 'replace-with-message-start',
className: 'icon-start-event-message',
target: {
type: 'bpmn:StartEvent',
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Start Event',
actionName: 'replace-with-timer-start',
className: 'icon-start-event-timer',
target: {
type: 'bpmn:StartEvent',
eventDefinition: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Conditional Start Event',
actionName: 'replace-with-conditional-start',
className: 'icon-start-event-condition',
target: {
type: 'bpmn:StartEvent',
eventDefinition: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Signal Start Event',
actionName: 'replace-with-signal-start',
className: 'icon-start-event-signal',
target: {
type: 'bpmn:StartEvent',
eventDefinition: 'bpmn:SignalEventDefinition'
}
}
];
module.exports.INTERMEDIATE_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-message-intermediate-throw',
className: 'icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message Intermediate Catch Event',
actionName: 'replace-with-intermediate-catch',
className: 'icon-intermediate-event-catch-message',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Message Intermediate Throw Event',
actionName: 'replace-with-intermediate-throw',
className: 'icon-intermediate-event-throw-message',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Intermediate Catch Event',
actionName: 'replace-with-timer-intermediate-catch',
className: 'icon-intermediate-event-catch-timer',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Escalation Intermediate Catch Event',
actionName: 'replace-with-escalation-catch',
className: 'icon-intermediate-event-catch-escalation',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Conditional Intermediate Catch Event',
actionName: 'replace-with-conditional-intermediate-catch',
className: 'icon-intermediate-event-catch-condition',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Link Intermediate Catch Event',
actionName: 'replace-with-link-intermediate-catch',
className: 'icon-intermediate-event-catch-link',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Link Intermediate Throw Event',
actionName: 'replace-with-link-intermediate-throw',
className: 'icon-intermediate-event-throw-link',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinition: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Compensation Intermediate Throw Event',
actionName: 'replace-with-compensation-intermediate-throw',
className: 'icon-intermediate-event-throw-compensation',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinition: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal Throw Catch Event',
actionName: 'replace-with-throw-intermediate-catch',
className: 'icon-intermediate-event-catch-signal',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinition: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Signal Intermediate Throw Event',
actionName: 'replace-with-signal-intermediate-throw',
className: 'icon-intermediate-event-throw-signal',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinition: 'bpmn:SignalEventDefinition'
}
}
];
module.exports.END_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-message-intermediate-throw',
className: 'icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-none-end',
className: 'icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message End Event',
actionName: 'replace-with-message-end',
className: 'icon-end-event-message',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Escalation End Event',
actionName: 'replace-with-escalation-end',
className: 'icon-end-event-escalation',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Error End Event',
actionName: 'replace-with-error-end',
className: 'icon-end-event-error',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Cancel End Event',
actionName: 'replace-with-cancel-end',
className: 'icon-end-event-cancel',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:CancelEventDefinition'
}
},
{
label: 'Compensation End Event',
actionName: 'replace-with-compensation-end',
className: 'icon-end-event-compensation',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal End Event',
actionName: 'replace-with-signal-end',
className: 'icon-end-event-signal',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Terminate End Event',
actionName: 'replace-with-terminate-end',
className: 'icon-end-event-terminate',
target: {
type: 'bpmn:EndEvent',
eventDefinition: 'bpmn:TerminateEventDefinition'
}
}
];
module.exports.GATEWAY = [
{
label: 'Exclusive Gateway',
actionName: 'replace-with-exclusive-gateway',
className: 'icon-gateway-xor',
target: {
type: 'bpmn:ExclusiveGateway'
}
},
{
label: 'Parallel Gateway',
actionName: 'replace-with-parallel-gateway',
className: 'icon-gateway-parallel',
target: {
type: 'bpmn:ParallelGateway'
}
},
{
label: 'Inclusive Gateway',
actionName: 'replace-with-inclusive-gateway',
className: 'icon-gateway-or',
target: {
type: 'bpmn:InclusiveGateway'
}
},
{
label: 'Complex Gateway',
actionName: 'replace-with-complex-gateway',
className: 'icon-gateway-complex',
target: {
type: 'bpmn:ComplexGateway'
}
},
{
label: 'Event based Gateway',
actionName: 'replace-with-event-based-gateway',
className: 'icon-gateway-eventbased',
target: {
type: 'bpmn:EventBasedGateway',
instantiate: false,
eventGatewayType: 'Exclusive'
}
}
// Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
// {
// label: 'Event based instantiating Gateway',
// actionName: 'replace-with-exclusive-event-based-gateway',
// className: 'icon-exclusive-event-based',
// target: {
// type: 'bpmn:EventBasedGateway'
// },
// options: {
// businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
// }
// },
// {
// label: 'Parallel Event based instantiating Gateway',
// actionName: 'replace-with-parallel-event-based-instantiate-gateway',
// className: 'icon-parallel-event-based-instantiate-gateway',
// target: {
// type: 'bpmn:EventBasedGateway'
// },
// options: {
// businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
// }
// }
];
module.exports.TASK = [
{
label: 'Task',
actionName: 'replace-with-task',
className: 'icon-task',
target: {
type: 'bpmn:Task'
}
},
{
label: 'Send Task',
actionName: 'replace-with-send-task',
className: 'icon-send',
target: {
type: 'bpmn:SendTask'
}
},
{
label: 'Receive Task',
actionName: 'replace-with-receive-task',
className: 'icon-receive',
target: {
type: 'bpmn:ReceiveTask'
}
},
{
label: 'User Task',
actionName: 'replace-with-user-task',
className: 'icon-user',
target: {
type: 'bpmn:UserTask'
}
},
{
label: 'Manual Task',
actionName: 'replace-with-manual-task',
className: 'icon-manual',
target: {
type: 'bpmn:ManualTask'
}
},
{
label: 'Business Rule Task',
actionName: 'replace-with-rule-task',
className: 'icon-business-rule',
target: {
type: 'bpmn:BusinessRuleTask'
}
},
{
label: 'Service Task',
actionName: 'replace-with-service-task',
className: 'icon-service',
target: {
type: 'bpmn:ServiceTask'
}
},
{
label: 'Script Task',
actionName: 'replace-with-script-task',
className: 'icon-script',
target: {
type: 'bpmn:ScriptTask'
}
}
];

View File

@ -1,7 +1,8 @@
module.exports = {
__depends__: [
require('diagram-js/lib/features/rules')
require('diagram-js/lib/features/popup-menu'),
require('diagram-js/lib/features/replace'),
require('../modeling')
],
__init__: [ 'replace' ],
replace: [ 'type', require('./BpmnReplace') ],
};
bpmnReplace: [ 'type', require('./BpmnReplace') ]
};

View File

@ -4,8 +4,6 @@ var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
var _ = require('lodash');
var fs = require('fs');
var modelingModule = require('../../../../lib/features/modeling'),
@ -16,7 +14,7 @@ var modelingModule = require('../../../../lib/features/modeling'),
describe('features/replace', function() {
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/bpmn-replace/01_replace.bpmn', 'utf8');
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/replace/01_replace.bpmn', 'utf8');
var testModules = [ coreModule, modelingModule, replaceModule ];
@ -25,7 +23,7 @@ describe('features/replace', function() {
describe('should replace', function() {
it('task', inject(function(elementRegistry, modeling, replace) {
it('task', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -34,7 +32,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
var businessObject = newElement.businessObject;
@ -44,7 +42,7 @@ describe('features/replace', function() {
}));
it('gateway', inject(function(elementRegistry, modeling, replace) {
it('gateway', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var gateway = elementRegistry.get('ExclusiveGateway_1');
@ -53,7 +51,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(gateway, newElementData);
var newElement = bpmnReplace.replaceElement(gateway, newElementData);
// then
@ -68,7 +66,7 @@ describe('features/replace', function() {
describe('position and size', function() {
it('should keep position', inject(function(elementRegistry, replace) {
it('should keep position', inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -77,7 +75,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
expect(newElement.x).toBe(346);
@ -89,7 +87,7 @@ describe('features/replace', function() {
describe('label', function() {
it('should keep copy label', inject(function(elementRegistry, replace) {
it('should keep copy label', inject(function(elementRegistry, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -99,7 +97,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
expect(newElement.businessObject.name).toBe('Task Caption');
@ -107,9 +105,10 @@ describe('features/replace', function() {
});
describe('undo support', function() {
it('should undo replace', inject(function(elementRegistry, modeling, replace, commandStack) {
it('should undo replace', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
@ -117,7 +116,7 @@ describe('features/replace', function() {
type: 'bpmn:UserTask'
};
replace.replaceElement(task, newElementData);
bpmnReplace.replaceElement(task, newElementData);
// when
commandStack.undo();
@ -131,7 +130,7 @@ describe('features/replace', function() {
}));
it('should redo replace', inject(function(elementRegistry, modeling, replace, commandStack, eventBus) {
it('should redo replace', inject(function(elementRegistry, modeling, bpmnReplace, commandStack, eventBus) {
// given
var task = elementRegistry.get('Task_1');
@ -142,8 +141,8 @@ describe('features/replace', function() {
type: 'bpmn:ServiceTask'
};
var usertask = replace.replaceElement(task, newElementData);
var servicetask = replace.replaceElement(usertask, newElementData2);
var usertask = bpmnReplace.replaceElement(task, newElementData);
var servicetask = bpmnReplace.replaceElement(usertask, newElementData2);
commandStack.undo();
commandStack.undo();
@ -164,7 +163,7 @@ describe('features/replace', function() {
describe('connection handling', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, replace) {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var task = elementRegistry.get('Task_1');
@ -173,7 +172,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
var incoming = newElement.incoming[0],
@ -189,7 +188,7 @@ describe('features/replace', function() {
}));
it('should remove invalid incomming connections', inject(function(elementRegistry, modeling, replace) {
it('should remove invalid incomming connections', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var task = elementRegistry.get('StartEvent_1');
@ -198,7 +197,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
var incoming = newElement.incoming[0],
@ -209,7 +208,8 @@ describe('features/replace', function() {
expect(outgoing).toBeUndefined();
}));
it('should remove invalid outgoing connections', inject(function(elementRegistry, modeling, replace) {
it('should remove invalid outgoing connections', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var task = elementRegistry.get('EndEvent_1');
@ -218,7 +218,7 @@ describe('features/replace', function() {
};
// when
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// then
var incoming = newElement.incoming[0],
@ -232,14 +232,15 @@ describe('features/replace', function() {
describe('undo support', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, replace, commandStack) {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
var newElementData = {
type: 'bpmn:UserTask'
};
var newElement = replace.replaceElement(task, newElementData);
bpmnReplace.replaceElement(task, newElementData);
// when
commandStack.undo();
@ -258,15 +259,17 @@ describe('features/replace', function() {
expect(target).toBe(elementRegistry.get('ExclusiveGateway_1'));
}));
it('should remove invalid incoming connections', inject(function(elementRegistry,
modeling, replace, commandStack) {
modeling, bpmnReplace, commandStack) {
// given
var startEvent = elementRegistry.get('StartEvent_1');
var newElementData = {
type: 'bpmn:EndEvent'
};
var newElement = replace.replaceElement(startEvent, newElementData);
bpmnReplace.replaceElement(startEvent, newElementData);
// when
commandStack.undo();
@ -285,14 +288,15 @@ describe('features/replace', function() {
it('should remove invalid outgoing connections', inject(function(elementRegistry,
modeling, replace, commandStack) {
modeling, bpmnReplace, commandStack) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
var newElementData = {
type: 'bpmn:StartEvent'
};
var newElement = replace.replaceElement(endEvent, newElementData);
bpmnReplace.replaceElement(endEvent, newElementData);
// when
commandStack.undo();
@ -311,17 +315,17 @@ describe('features/replace', function() {
});
describe('redo support', function() {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, replace, commandStack) {
it('should reconnect valid connections', inject(function(elementRegistry, modeling, bpmnReplace, commandStack) {
// given
var task = elementRegistry.get('Task_1');
var newElementData = {
type: 'bpmn:UserTask'
};
var newElement = replace.replaceElement(task, newElementData);
var newElement = bpmnReplace.replaceElement(task, newElementData);
// when
commandStack.undo();
@ -342,14 +346,14 @@ describe('features/replace', function() {
it('should remove invalid incoming connections', inject(function(elementRegistry,
modeling, replace, commandStack) {
modeling, bpmnReplace, commandStack) {
// given
var startEvent = elementRegistry.get('StartEvent_1');
var newElementData = {
type: 'bpmn:EndEvent'
};
var newElement = replace.replaceElement(startEvent, newElementData);
var newElement = bpmnReplace.replaceElement(startEvent, newElementData);
// when
commandStack.undo();
@ -366,14 +370,14 @@ describe('features/replace', function() {
it('should remove invalid outgoing connections', inject(function(elementRegistry,
modeling, replace, commandStack) {
modeling, bpmnReplace, commandStack) {
// given
var endEvent = elementRegistry.get('EndEvent_1');
var newElementData = {
type: 'bpmn:StartEvent'
};
var newElement = replace.replaceElement(endEvent, newElementData);
var newElement = bpmnReplace.replaceElement(endEvent, newElementData);
// when
commandStack.undo();
@ -387,6 +391,7 @@ describe('features/replace', function() {
expect(incoming).toBeUndefined();
expect(outgoing).toBeUndefined();
}));
});
});

View File

@ -0,0 +1,66 @@
'use strict';
var TestHelper = require('../../../TestHelper');
/* global bootstrapModeler, inject */
var fs = require('fs');
var modelingModule = require('../../../../lib/features/modeling'),
replaceModule = require('../../../../lib/features/replace'),
coreModule = require('../../../../lib/core');
describe('features/replace - chooser', function() {
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/replace/01_replace.bpmn', 'utf8');
var testModules = [ coreModule, modelingModule, replaceModule ];
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
describe('should show chooser', function() {
it('for task', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('Task_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).toBeDefined();
}));
it('for event event', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('StartEvent_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).toBeDefined();
}));
it('for gateway event', inject(function(elementRegistry, modeling, bpmnReplace) {
// given
var element = elementRegistry.get('ExclusiveGateway_1');
// when
bpmnReplace.openChooser({ x: 100, y: 100 }, element);
// then
expect(null).toBeDefined();
}));
});
});