feat(modeling): add message flow modeling

Related to #199
Closes #201
This commit is contained in:
Nico Rehwaldt 2015-04-16 09:11:04 +02:00 committed by Ricardo Matias
parent 6011de1c4a
commit c14a87e5ad
21 changed files with 1114 additions and 647 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
@font-face {
font-family: 'bpmn';
src: url('../font/bpmn.eot?37618458');
src: url('../font/bpmn.eot?37618458#iefix') format('embedded-opentype'),
url('../font/bpmn.woff?37618458') format('woff'),
url('../font/bpmn.ttf?37618458') format('truetype'),
url('../font/bpmn.svg?37618458#bpmn') format('svg');
src: url('../font/bpmn.eot?84549978');
src: url('../font/bpmn.eot?84549978#iefix') format('embedded-opentype'),
url('../font/bpmn.woff?84549978') format('woff'),
url('../font/bpmn.ttf?84549978') format('truetype'),
url('../font/bpmn.svg?84549978#bpmn') format('svg');
font-weight: normal;
font-style: normal;
}
@ -14,7 +14,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'bpmn';
src: url('../font/bpmn.svg?37618458#bpmn') format('svg');
src: url('../font/bpmn.svg?84549978#bpmn') format('svg');
}
}
*/
@ -149,4 +149,5 @@
.icon-participant:before { content: '\e85c'; } /* '' */
.icon-edit-default:before { content: '\e85d'; } /* '' */
.icon-edit-navigation:before { content: '\e85e'; } /* '' */
.icon-edit-space-tool:before { content: '\e85f'; } /* '' */
.icon-edit-space-tool:before { content: '\e85f'; } /* '' */
.icon-connection-multi:before { content: '\e860'; } /* '' */

Binary file not shown.

View File

@ -102,6 +102,7 @@
<glyph glyph-name="edit-default" unicode="&#xe85d;" d="m14 839l0-1 0-1 0 0 35-600 0 0 0 0 133 170 120-219 0 0 1 0 95 56-123 215 202 18-1 0 3 0-465 362z m1-2l461-359-1 0-460 358 0 1z m259-379l123-213 0-1-124 214 1 0z m196-59l0-139 40 0 0 99 80 0 19 0 0 40-19 0-120 0z m200 0l0-40 120 0 0 40-120 0z m180 0l0-40 100 0 0-100 40 0 0 140-140 0z m-380-220l0-120 40 0 0 120-40 0z m480 0l0-120 40 0 0 120-40 0z m0-180l0-100-100 0 0-40 140 0 0 140-40 0z m-480-1l0-140 140 0 0 40-100 0 0 100-40 0z m200-100l0-40 120 0 0 40-120 0z" horiz-adv-x="1000" />
<glyph glyph-name="edit-navigation" unicode="&#xe85e;" d="m506 826l-223-240 6 0 1 0 193 0 0-219-214 0 0 200-5-4-236-206 241-230 0 200 214 0 0-211-200 0 4-4 220-236 216 235-1 0 0 2 0 3-199 0 0 211 216 0 0-200 240 210-2 2-238 228 0-200-216 0 0 219 200 0-4 4-213 236z" horiz-adv-x="1000" />
<glyph glyph-name="edit-space-tool" unicode="&#xe85f;" d="m366 810l0-920 38 0 0 920-38 0z m233 0l0-920 38 0 0 920-38 0z m-394-297l-164-163 164-163 0 111 93 0 0 105-29 0-64 0 0 110z m595 0l0-108-94-1 0-29 0-77 94 0 0-111 163 163-163 163z" horiz-adv-x="1000" />
<glyph glyph-name="connection-multi" unicode="&#xe860;" d="m916 798l-410-109 64-95-486-327 25-38 486 328 62-92 259 333 0 0z m0-328l-411-109 151-224 260 333 0 0z m-409-245l-63-43 25-37 63 42-25 38z m-126-85l-62-42 25-38 62 43-25 37z m-133-90l-63-43 25-37 63 43-25 37z m-126-85l-38-26 25-37 38 26-25 37z" horiz-adv-x="1000" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Binary file not shown.

View File

@ -116,15 +116,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
'append.gateway': appendAction('bpmn:ExclusiveGateway', 'icon-gateway-xor'),
'append.append-task': appendAction('bpmn:Task', 'icon-task'),
'append.intermediate-event': appendAction('bpmn:IntermediateThrowEvent',
'icon-intermediate-event-none'),
'connect': {
group: 'connect',
className: 'icon-connection',
action: {
click: startConnect,
dragstart: startConnect
}
}
'icon-intermediate-event-none')
});
}
@ -155,10 +147,6 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
});
}
assign(actions, {
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'icon-text-annotation')
});
// Replace menu entry
if (!bpmnElement.$instanceOf('bpmn:SubProcess')) {
@ -176,6 +164,23 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
}
}
if (bpmnElement.$instanceOf('bpmn:FlowNode') ||
bpmnElement.$instanceOf('bpmn:InteractionNode')) {
assign(actions, {
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'icon-text-annotation'),
'connect': {
group: 'connect',
className: 'icon-connection-multi',
action: {
click: startConnect,
dragstart: startConnect
}
}
});
}
// Delete Element Entry
assign(actions, {
'delete': {

View File

@ -15,6 +15,7 @@ BpmnFactory.$inject = [ 'moddle' ];
BpmnFactory.prototype._needsId = function(element) {
return element.$instanceOf('bpmn:RootElement') ||
element.$instanceOf('bpmn:FlowElement') ||
element.$instanceOf('bpmn:MessageFlow') ||
element.$instanceOf('bpmn:Artifact') ||
element.$instanceOf('bpmn:Participant') ||
element.$instanceOf('bpmn:Process') ||

View File

@ -208,6 +208,7 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent)
}
containment = 'flowElements';
} else
if (businessObject.$instanceOf('bpmn:Artifact')) {
@ -226,7 +227,12 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent)
}
containment = 'artifacts';
}
} else
if (businessObject.$instanceOf('bpmn:MessageFlow')) {
containment = 'messageFlows';
} else
if (businessObject.$instanceOf('bpmn:Participant')) {
containment = 'participants';

View File

@ -14,14 +14,17 @@ var UpdatePropertiesHandler = require('./cmd/UpdatePropertiesHandler'),
* @param {EventBus} eventBus
* @param {ElementFactory} elementFactory
* @param {CommandStack} commandStack
* @param {BpmnRules} bpmnRules
*/
function Modeling(eventBus, elementFactory, commandStack) {
function Modeling(eventBus, elementFactory, commandStack, bpmnRules) {
BaseModeling.call(this, eventBus, elementFactory, commandStack);
this._bpmnRules = bpmnRules;
}
inherits(Modeling, BaseModeling);
Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];
Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack', 'bpmnRules' ];
module.exports = Modeling;
@ -44,17 +47,19 @@ Modeling.prototype.updateLabel = function(element, newLabel) {
};
var getSharedParent = require('./ModelingUtil').getSharedParent;
Modeling.prototype.connect = function(source, target, attrs) {
var sourceBo = source.businessObject,
targetBo = target.businessObject;
var bpmnRules = this._bpmnRules;
if (!attrs) {
if (sourceBo.$instanceOf('bpmn:FlowNode') &&
targetBo.$instanceOf('bpmn:FlowNode') &&
!sourceBo.$instanceOf('bpmn:EndEvent') &&
!targetBo.$instanceOf('bpmn:StartEvent')) {
if (bpmnRules.canConnectMessageFlow(source, target)) {
attrs = {
type: 'bpmn:MessageFlow'
};
} else
if (bpmnRules.canConnectSequenceFlow(source, target)) {
attrs = {
type: 'bpmn:SequenceFlow'
};
@ -65,7 +70,7 @@ Modeling.prototype.connect = function(source, target, attrs) {
}
}
return this.createConnection(source, target, attrs, source.parent);
return this.createConnection(source, target, attrs, getSharedParent(source, target));
};

View File

@ -0,0 +1,56 @@
'use strict';
var find = require('lodash/collection/find');
function getParents(element) {
var parents = [];
while (element) {
element = element.parent;
if (element) {
parents.push(element);
}
}
return parents;
}
module.exports.getParents = getParents;
function getSharedParent(a, b) {
var parentsA = getParents(a),
parentsB = getParents(b);
return find(parentsA, function(parent) {
return parentsB.indexOf(parent) !== -1;
});
}
module.exports.getSharedParent = getSharedParent;
/**
* Is an element of the given BPMN type?
*
* @param {djs.model.Base|ModdleElement} element
* @param {String} type
* @return {Boolean}
*/
function is(element, type) {
var bo = getBusinessObject(element);
return bo && bo.$instanceOf(type);
}
module.exports.is = is;
function getBusinessObject(element) {
return (element && element.businessObject) || element;
}
module.exports.getBusinessObject = getBusinessObject;

View File

@ -0,0 +1,374 @@
'use strict';
var groupBy = require('lodash/collection/groupBy'),
size = require('lodash/collection/size'),
find = require('lodash/collection/find'),
inherits = require('inherits');
var getParents = require('../ModelingUtil').getParents,
is = require('../ModelingUtil').is,
getBusinessObject = require('../ModelingUtil').getBusinessObject;
var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
/**
* BPMN specific modeling rule
*/
function BpmnRules(eventBus) {
RuleProvider.call(this, eventBus);
}
inherits(BpmnRules, RuleProvider);
BpmnRules.$inject = [ 'eventBus' ];
module.exports = BpmnRules;
BpmnRules.prototype.init = function() {
this.addRule('connection.create', function(context) {
var source = context.source,
target = context.target;
return canConnect(source, target);
});
this.addRule('connection.reconnectStart', function(context) {
var connection = context.connection,
source = context.hover || context.source,
target = connection.target;
return canConnect(source, target, connection);
});
this.addRule('connection.reconnectEnd', function(context) {
var connection = context.connection,
source = connection.source,
target = context.hover || context.target;
return canConnect(source, target, connection);
});
this.addRule('connection.updateWaypoints', function(context) {
// OK! but visually ignore
return null;
});
this.addRule('shape.resize', function(context) {
var shape = context.shape,
newBounds = context.newBounds;
return canResize(shape, newBounds);
});
this.addRule('shapes.move', function(context) {
var target = context.newParent,
shapes = context.shapes;
return canMove(shapes, target);
});
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
var target = context.parent,
shape = context.shape,
source = context.source;
return canCreate(shape, target, source);
});
};
BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
BpmnRules.prototype.canMove = canMove;
BpmnRules.prototype.canDrop = canDrop;
BpmnRules.prototype.canCreate = canCreate;
BpmnRules.prototype.canConnect = canConnect;
BpmnRules.prototype.canResize = canResize;
/**
* Utility functions for rule checking
*/
function nonExistantOrLabel(element) {
return !element || isLabel(element);
}
function isSame(a, b) {
return a === b;
}
function getOrganizationalParent(element) {
var bo = getBusinessObject(element);
while (bo && !is(bo, 'bpmn:Process')) {
if (is(bo, 'bpmn:Participant')) {
return bo.processRef || bo;
}
bo = bo.$parent;
}
return bo;
}
function isSameOrganization(a, b) {
var parentA = getOrganizationalParent(a),
parentB = getOrganizationalParent(b);
return parentA === parentB;
}
function isMessageFlowSource(element) {
return is(element, 'bpmn:InteractionNode') && (
!is(element, 'bpmn:Event') || is(element, 'bpmn:ThrowEvent')
);
}
function isMessageFlowTarget(element) {
return is(element, 'bpmn:InteractionNode') && (
!is(element, 'bpmn:Event') || is(element, 'bpmn:CatchEvent')
);
}
function getScopeParent(element) {
var bo = getBusinessObject(element);
if (is(bo, 'bpmn:Participant')) {
return null;
}
while (bo) {
bo = bo.$parent;
if (is(bo, 'bpmn:FlowElementsContainer')) {
return bo;
}
}
return bo;
}
function isExpanded(element) {
var bo = getBusinessObject(element);
return bo.di.isExpanded;
}
function isSameScope(a, b) {
var scopeParentA = getScopeParent(a),
scopeParentB = getScopeParent(b);
return scopeParentA && (scopeParentA === scopeParentB);
}
function hasEventDefinition(element, eventDefinition) {
var bo = getBusinessObject(element);
return find(bo.eventDefinitions || [], function(definition) {
return is(definition, eventDefinition);
});
}
function isSequenceFlowSource(element) {
return is(element, 'bpmn:FlowNode') && !is(element, 'bpmn:EndEvent') && !(
is(element, 'bpmn:IntermediateThrowEvent') &&
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
);
}
function isSequenceFlowTarget(element) {
return is(element, 'bpmn:FlowNode') && !is(element, 'bpmn:StartEvent') && !(
is(element, 'bpmn:IntermediateCatchEvent') &&
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
);
}
function isEventBasedTarget(element) {
return is(element, 'bpmn:ReceiveTask') || (
is(element, 'bpmn:IntermediateCatchEvent') && (
hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
hasEventDefinition(element, 'bpmn:SignalEventDefinition')
)
);
}
function isLabel(element) {
return element.labelTarget;
}
function isConnection(element) {
return element.waypoints;
}
function isParent(possibleParent, element) {
var allParents = getParents(element);
return allParents.indexOf(possibleParent) !== -1;
}
function canConnect(source, target, connection) {
if (nonExistantOrLabel(source) || nonExistantOrLabel(target)) {
return null;
}
// See https://github.com/bpmn-io/bpmn-js/issues/178
// as a workround we disallow connections with same
// target and source element.
// This rule must be removed if a auto layout for this
// connections is implemented.
if (isSame(source, target)) {
return false;
}
if (canConnectMessageFlow(source, target) ||
canConnectSequenceFlow(source, target)) {
return true;
}
if (is(connection, 'bpmn:Association')) {
return canConnectAssociation(source, target);
}
return false;
}
/**
* Can an element be dropped into the target element
*
* @return {Boolean}
*/
function canDrop(element, target) {
// can move labels everywhere
if (isLabel(element) && !isConnection(target)) {
return true;
}
// allow to create new participants on
// on existing collaboration and process diagrams
if (is(element, 'bpmn:Participant')) {
return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration');
}
// drop flow elements onto flow element containers
// and participants
if (is(element, 'bpmn:FlowElement')) {
if (is(target, 'bpmn:FlowElementsContainer')) {
return isExpanded(target) !== false;
}
return is(target, 'bpmn:Participant');
}
if (is(element, 'bpmn:Artifact')) {
return is(target, 'bpmn:Collaboration') ||
is(target, 'bpmn:Participant') ||
is(target, 'bpmn:Process');
}
return false;
}
function canMove(elements, target) {
// only move if they have the same parent
var sameParent = size(groupBy(elements, function(s) { return s.parent && s.parent.id; })) === 1;
if (!sameParent) {
return false;
}
if (!target) {
return true;
}
return elements.every(function(element) {
return canDrop(element, target);
});
}
function canCreate(shape, target, source) {
if (!target) {
return false;
}
if (isLabel(target)) {
return null;
}
if (isSame(source, target)) {
return false;
}
// ensure we do not drop the element
// into source
if (source && isParent(source, target)) {
return false;
}
return canDrop(shape, target);
}
function canResize(shape, newBounds) {
if (is(shape, 'bpmn:SubProcess')) {
return isExpanded(shape) && (
!newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
);
}
if (is(shape, 'bpmn:Participant')) {
return !newBounds || (newBounds.width >= 100 && newBounds.height >= 80);
}
if (is(shape, 'bpmn:TextAnnotation')) {
return true;
}
return false;
}
function canConnectAssociation(source, target) {
// do not connect connections
if (isConnection(source) || isConnection(target)) {
return false;
}
// connect if different parent
return !isParent(target, source) &&
!isParent(source, target);
}
function canConnectMessageFlow(source, target) {
return isMessageFlowSource(source) &&
isMessageFlowTarget(target) &&
!isSameOrganization(source, target);
}
function canConnectSequenceFlow(source, target) {
return isSequenceFlowSource(source) &&
isSequenceFlowTarget(target) &&
isSameScope(source, target) &&
!(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
}

View File

@ -1,292 +0,0 @@
'use strict';
var groupBy = require('lodash/collection/groupBy'),
size = require('lodash/collection/size'),
forEach = require('lodash/collection/forEach'),
inherits = require('inherits');
var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
function ModelingRules(eventBus) {
RuleProvider.call(this, eventBus);
}
inherits(ModelingRules, RuleProvider);
ModelingRules.$inject = [ 'eventBus' ];
module.exports = ModelingRules;
ModelingRules.prototype.init = function() {
function nonExistantOrLabel(element) {
return !element || element.labelTarget;
}
function isSame(a, b) {
return a === b;
}
function isEventConnectionInvalid(source, target) {
var sourceBo = source.businessObject,
targetBo = target.businessObject;
// handle start and end event cases
var startEventCheck = targetBo.$instanceOf('bpmn:StartEvent') ||
sourceBo.$instanceOf('bpmn:EndEvent');
// handle event based gateway cases
var eventBasedGatewayCheck = false;
// Ensure target of event based gateway is one of:
// receive task, receiving message, timer, signal, condition event
if (sourceBo.$instanceOf('bpmn:EventBasedGateway')) {
eventBasedGatewayCheck = true;
if (targetBo.$instanceOf('bpmn:ReceiveTask')) {
eventBasedGatewayCheck = false;
} else if (isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:MessageEventDefinition') ||
isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:TimerEventDefinition') ||
isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:ConditionalEventDefinition') ||
isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:SignalEventDefinition')) {
eventBasedGatewayCheck = false;
}
}
// handle link event
var linkEventCheck = false;
if (isEventType(targetBo, 'bpmn:IntermediateCatchEvent', 'bpmn:LinkEventDefinition') ||
isEventType(sourceBo, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition')) {
linkEventCheck = true;
}
return startEventCheck || eventBasedGatewayCheck || linkEventCheck;
}
function isEventType(eventBo, type, definition) {
var isType = eventBo.$instanceOf(type);
var isDefinition = false;
var definitions = eventBo.eventDefinitions || [];
forEach(definitions, function(def) {
if (def.$type === definition) {
isDefinition = true;
}
});
return isType && isDefinition;
}
// rules
function canConnect(source, target, connection) {
if (nonExistantOrLabel(source) || nonExistantOrLabel(target)) {
return null;
}
// See https://github.com/bpmn-io/bpmn-js/issues/178
// as a workround we disallow connections with same
// target and source element.
// This rule must be removed if a auto layout for this
// connections is implemented.
if (isSame(source, target)) {
return false;
}
// allow all associations between elements
if (connection && connection.businessObject.$instanceOf('bpmn:Association')) {
return true;
}
// only move between the same parent
if (!isSame(source.parent, target.parent)) {
return false;
}
var sourceBo = source.businessObject,
targetBo = target.businessObject,
connectionBo = connection && connection.businessObject;
if (connectionBo && connectionBo.$instanceOf('bpmn:SequenceFlow')) {
if (!sourceBo.$instanceOf('bpmn:FlowNode') ||
!targetBo.$instanceOf('bpmn:FlowNode') ||
sourceBo.$instanceOf('bpmn:EndEvent') ||
targetBo.$instanceOf('bpmn:StartEvent')) {
return false;
}
}
// Do not allow incoming connections on StartEvents
// and outgoing connections on EndEvents
if (isEventConnectionInvalid(source, target)) {
return false;
}
return (sourceBo.$instanceOf('bpmn:FlowNode') ||
sourceBo.$instanceOf('bpmn:TextAnnotation')) &&
(targetBo.$instanceOf('bpmn:FlowNode') ||
targetBo.$instanceOf('bpmn:TextAnnotation'));
}
this.addRule('connection.create', function(context) {
var source = context.source,
target = context.target;
return canConnect(source, target);
});
this.addRule('connection.reconnectStart', function(context) {
var connection = context.connection,
source = context.hover || context.source,
target = connection.target;
return canConnect(source, target, connection);
});
this.addRule('connection.reconnectEnd', function(context) {
var connection = context.connection,
source = connection.source,
target = context.hover || context.target;
return canConnect(source, target, connection);
});
this.addRule('connection.updateWaypoints', function(context) {
// OK! but visually ignore
return null;
});
this.addRule('shape.resize', function(context) {
var shape = context.shape,
newBounds = context.newBounds,
bo = shape.businessObject;
if (bo.$instanceOf('bpmn:SubProcess') && bo.di.isExpanded) {
return !newBounds || (newBounds.width >= 100 && newBounds.height >= 80);
}
if (bo.$instanceOf('bpmn:Participant')) {
return !newBounds || (newBounds.width >= 100 && newBounds.height >= 80);
}
return false;
});
/**
* Can an element be dropped into the target element
*
* @return {Boolean}
*/
function canDrop(businessObject, targetBusinessObject, targetDi) {
// allow to create new participants on
// on existing collaboration and process diagrams
if (businessObject.$instanceOf('bpmn:Participant') &&
(targetBusinessObject.$instanceOf('bpmn:Process') ||
targetBusinessObject.$instanceOf('bpmn:Collaboration'))) {
return true;
}
if (businessObject.$instanceOf('bpmn:FlowElement') &&
targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer')) {
// may not drop into collapsed sub processes
if (targetDi.isExpanded === false) {
return false;
}
return true;
}
if (targetBusinessObject.$instanceOf('bpmn:Collaboration') &&
!businessObject.$instanceOf('bpmn:Participant') &&
!targetBusinessObject.participants.length) {
// TODO: transform collaboration into process
return false;
}
if (businessObject.$instanceOf('bpmn:FlowElement') &&
targetBusinessObject.$instanceOf('bpmn:Participant') &&
targetBusinessObject.processRef) {
return true;
}
if (businessObject.$instanceOf('bpmn:TextAnnotation') &&
(targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer') ||
targetBusinessObject.$instanceOf('bpmn:Participant'))) {
return true;
}
if (businessObject.$instanceOf('bpmn:Association') &&
(targetBusinessObject.$instanceOf('bpmn:FlowElementsContainer') ||
targetBusinessObject.$instanceOf('bpmn:Participant'))) {
return true;
}
return false;
}
this.addRule('shapes.move', function(context) {
var target = context.newParent,
shapes = context.shapes;
// only move if they have the same parent
var sameParent = size(groupBy(shapes, function(s) { return s.parent && s.parent.id; })) === 1;
if (!sameParent) {
return false;
}
if (!target) {
return true;
}
var targetBusinessObject = target.businessObject,
targetDi = targetBusinessObject.di;
return shapes.every(function(s) {
return canDrop(s.businessObject, targetBusinessObject, targetDi);
});
});
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
var target = context.parent,
shape = context.shape,
source = context.source;
// ensure we do not drop the element
// into source
var t = target;
while (t) {
if (isSame(t, source)) {
return false;
}
t = t.parent;
}
if (!target) {
return false;
}
if (target.labelTarget) {
return null;
}
return canDrop(shape.businessObject, target.businessObject, target.businessObject.di);
});
};

View File

@ -2,6 +2,6 @@ module.exports = {
__depends__: [
require('diagram-js/lib/features/rules')
],
__init__: [ 'modelingRules' ],
modelingRules: [ 'type', require('./ModelingRules') ]
__init__: [ 'bpmnRules' ],
bpmnRules: [ 'type', require('./BpmnRules') ]
};

View File

@ -209,6 +209,16 @@ module.exports.bootstrapViewer = (window || global).bootstrapViewer = bootstrapV
module.exports.inject = (window || global).inject = inject;
/**
* Returns the current active BpmnJS instance.
*
* @return {BpmnJS}
*/
module.exports.getBpmnJS = function() {
return BPMN_JS;
};
function insertCSS(name, css) {
if (document.querySelector('[data-css-file="' + name + '"]')) {
return;

View File

@ -28,6 +28,12 @@ describe('Modeler', function() {
});
it('should import collaboration', function(done) {
var xml = require('../fixtures/bpmn/collaboration-message-flows.bpmn');
createModeler(xml, done);
});
it('should import empty definitions', function(done) {
var xml = require('../fixtures/bpmn/empty-definitions.bpmn');
createModeler(xml, done);

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_biH3sOTeEeS2YerRfpjPrw" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:collaboration id="Collaboration">
<bpmn2:participant id="Participant" name="Pool" processRef="Process"/>
<bpmn2:participant id="OtherParticipant" name="Pool" processRef="OtherProcess"/>
</bpmn2:collaboration>
<bpmn2:process id="Process" isExecutable="false">
<bpmn2:subProcess id="SubProcess">
<bpmn2:task id="Task_in_SubProcess"/>
<bpmn2:endEvent id="EndEvent_in_SubProcess"/>
</bpmn2:subProcess>
<bpmn2:startEvent id="StartEvent_None"/>
<bpmn2:intermediateThrowEvent id="IntermediateThrowEvent_Message">
<bpmn2:messageEventDefinition id="MessageEventDefinition_1"/>
</bpmn2:intermediateThrowEvent>
<bpmn2:endEvent id="EndEvent_None"/>
<bpmn2:textAnnotation id="TextAnnotation_Global"/>
</bpmn2:process>
<bpmn2:process id="OtherProcess" isExecutable="false">
<bpmn2:task id="Task_in_OtherParticipant"/>
<bpmn2:textAnnotation id="TextAnnotation_OtherParticipant"/>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration">
<bpmndi:BPMNShape id="_BPMNShape_Participant_3" bpmnElement="Participant" isHorizontal="true">
<dc:Bounds height="265.0" width="697.0" x="72.0" y="48.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_3" bpmnElement="SubProcess" isExpanded="true">
<dc:Bounds height="205.0" width="248.0" x="468.0" y="72.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task_in_SubProcess">
<dc:Bounds height="80.0" width="100.0" x="493.0" y="95.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_3" bpmnElement="StartEvent_None">
<dc:Bounds height="36.0" width="36.0" x="144.0" y="157.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="162.0" y="198.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_3" bpmnElement="EndEvent_in_SubProcess">
<dc:Bounds height="36.0" width="36.0" x="660.0" y="117.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="678.0" y="158.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Participant_4" bpmnElement="OtherParticipant" isHorizontal="true">
<dc:Bounds height="157.0" width="697.0" x="72.0" y="432.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_7" bpmnElement="Task_in_OtherParticipant">
<dc:Bounds height="80.0" width="100.0" x="408.0" y="465.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_2" bpmnElement="IntermediateThrowEvent_Message">
<dc:Bounds height="36.0" width="36.0" x="264.0" y="157.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="282.0" y="198.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_4" bpmnElement="EndEvent_None">
<dc:Bounds height="36.0" width="36.0" x="373.0" y="156.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_TextAnnotation_3" bpmnElement="TextAnnotation_OtherParticipant">
<dc:Bounds height="88.0" width="103.0" x="162.0" y="465.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_TextAnnotation_4" bpmnElement="TextAnnotation_Global">
<dc:Bounds height="88.0" width="96.0" x="900.0" y="180.0"/>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_e9eVgOdVEeSyFtFBe7wX5g" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="Process" isExecutable="false">
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent_Message">
<bpmn2:messageEventDefinition id="_MessageEventDefinition_3"/>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateThrowEvent id="IntermediateThrowEvent_Message">
<bpmn2:messageEventDefinition id="_MessageEventDefinition_4"/>
</bpmn2:intermediateThrowEvent>
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent_Signal">
<bpmn2:signalEventDefinition id="_SignalEventDefinition_2"/>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent_Condition">
<bpmn2:conditionalEventDefinition id="_ConditionalEventDefinition_2"/>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent_Timer">
<bpmn2:timerEventDefinition id="_TimerEventDefinition_2"/>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent"/>
<bpmn2:receiveTask id="ReceiveTask"/>
<bpmn2:parallelGateway id="ParallelGateway"/>
<bpmn2:task id="Task_None"/>
<bpmn2:eventBasedGateway id="EventBasedGateway"/>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process">
<bpmndi:BPMNShape id="_BPMNShape_EventBasedGateway_2" bpmnElement="EventBasedGateway">
<dc:Bounds height="50.0" width="50.0" x="269.0" y="204.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="294.0" y="259.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateThrowEvent_3" bpmnElement="IntermediateCatchEvent_Message">
<dc:Bounds height="36.0" width="36.0" x="433.0" y="212.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="451.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateThrowEvent_4" bpmnElement="IntermediateThrowEvent_Message">
<dc:Bounds height="36.0" width="36.0" x="433.0" y="288.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="451.0" y="329.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_10" bpmnElement="ReceiveTask">
<dc:Bounds height="80.0" width="100.0" x="401.0" y="360.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_ParallelGateway_2" bpmnElement="ParallelGateway">
<dc:Bounds height="50.0" width="50.0" x="425.0" y="472.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="450.0" y="527.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_4" bpmnElement="IntermediateCatchEvent_Signal">
<dc:Bounds height="36.0" width="36.0" x="529.0" y="212.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="547.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_5" bpmnElement="IntermediateCatchEvent_Condition">
<dc:Bounds height="36.0" width="36.0" x="624.0" y="212.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="642.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_6" bpmnElement="IntermediateCatchEvent_Timer">
<dc:Bounds height="36.0" width="36.0" x="711.0" y="212.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="729.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_7" bpmnElement="IntermediateCatchEvent">
<dc:Bounds height="36.0" width="36.0" x="804.0" y="212.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="822.0" y="253.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_11" bpmnElement="Task_None">
<dc:Bounds height="80.0" width="100.0" x="547.0" y="360.0"/>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_biH3sOTeEeS2YerRfpjPrw" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="Process" isExecutable="false">
<bpmn2:startEvent id="StartEvent_None"/>
<bpmn2:subProcess id="EventSubProcess" triggeredByEvent="true">
<bpmn2:startEvent id="StartEvent_Message_in_EventSubProcess">
<bpmn2:messageEventDefinition id="MessageEventDefinition_1"/>
</bpmn2:startEvent>
</bpmn2:subProcess>
<bpmn2:task id="Task"/>
<bpmn2:intermediateThrowEvent id="IntermediateThrowEvent_Link">
<bpmn2:linkEventDefinition id="_LinkEventDefinition_2"/>
</bpmn2:intermediateThrowEvent>
<bpmn2:intermediateCatchEvent id="IntermediateCatchEvent_Link">
<bpmn2:linkEventDefinition id="_LinkEventDefinition_3"/>
</bpmn2:intermediateCatchEvent>
<bpmn2:subProcess id="SubProcess">
<bpmn2:endEvent id="EndEvent_in_SubProcess"/>
</bpmn2:subProcess>
<bpmn2:endEvent id="EndEvent_None"/>
<bpmn2:textAnnotation id="TextAnnotation"/>
</bpmn2:process>
<bpmndi:BPMNDiagram id="_BPMNDiagram_2">
<bpmndi:BPMNPlane id="_BPMNPlane_2" bpmnElement="Process">
<bpmndi:BPMNShape id="_BPMNShape_Task_8" bpmnElement="Task">
<dc:Bounds height="80.0" width="100.0" x="172.0" y="147.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_4" bpmnElement="StartEvent_None">
<dc:Bounds height="36.0" width="36.0" x="65.0" y="169.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="83.0" y="210.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_4" bpmnElement="EventSubProcess" isExpanded="true">
<dc:Bounds height="169.0" width="313.0" x="132.0" y="360.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_5" bpmnElement="StartEvent_Message_in_EventSubProcess">
<dc:Bounds height="36.0" width="36.0" x="171.0" y="419.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="189.0" y="460.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateCatchEvent_3" bpmnElement="IntermediateThrowEvent_Link">
<dc:Bounds height="36.0" width="36.0" x="336.0" y="168.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="354.0" y="209.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_IntermediateThrowEvent_2" bpmnElement="IntermediateCatchEvent_Link">
<dc:Bounds height="36.0" width="36.0" x="425.0" y="168.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="443.0" y="209.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_SubProcess_5" bpmnElement="SubProcess" isExpanded="true">
<dc:Bounds height="169.0" width="205.0" x="516.0" y="360.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_TextAnnotation_2" bpmnElement="TextAnnotation">
<dc:Bounds height="85.0" width="85.0" x="576.0" y="36.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_5" bpmnElement="EndEvent_in_SubProcess">
<dc:Bounds height="36.0" width="36.0" x="639.0" y="451.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_6" bpmnElement="EndEvent_None">
<dc:Bounds height="36.0" width="36.0" x="516.0" y="168.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="0.0" width="0.0" x="534.0" y="209.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,384 @@
'use strict';
var TestHelper = require('../../../../TestHelper');
/* global bootstrapModeler, inject */
var rulesModule = require('../../../../../lib/features/modeling/rules'),
coreModule = require('../../../../../lib/core');
function expectCanConnect(source, target, rules) {
var results = {};
TestHelper.getBpmnJS().invoke(function(elementRegistry, bpmnRules) {
source = elementRegistry.get(source);
target = elementRegistry.get(target);
expect(source).toBeDefined();
expect(target).toBeDefined();
if ('sequenceFlow' in rules) {
results.sequenceFlow = bpmnRules.canConnectSequenceFlow(source, target);
}
if ('messageFlow' in rules) {
results.messageFlow = bpmnRules.canConnectMessageFlow(source, target);
}
if ('association' in rules) {
results.association = bpmnRules.canConnectAssociation(source, target);
}
});
expect(results).toEqual(rules);
}
function expectCanDrop(element, target, expectedResult) {
var result;
TestHelper.getBpmnJS().invoke(function(elementRegistry, bpmnRules) {
element = elementRegistry.get(element);
target = elementRegistry.get(target);
expect(element).toBeDefined();
expect(target).toBeDefined();
result = bpmnRules.canDrop(element, target);
});
expect(result).toEqual(expectedResult);
}
describe('features/modeling/rules - BpmnRules', function() {
var testModules = [ coreModule, rulesModule ];
describe('on process diagram', function() {
var testXML = require('./BpmnRules.process.bpmn');
beforeEach(bootstrapModeler(testXML, { modules: testModules }));
it('connect StartEvent_None -> Task', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'Task', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect StartEvent_None -> TextAnnotation', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'TextAnnotation', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect Task -> IntermediateThrowEvent_Link', inject(function(bpmnRules) {
expectCanConnect('Task', 'IntermediateThrowEvent_Link', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect IntermediateThrowEvent_Link -> EndEvent_None', inject(function(bpmnRules) {
expectCanConnect('IntermediateThrowEvent_Link', 'EndEvent_None', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect StartEvent_None -> IntermediateCatchEvent_Link', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'IntermediateCatchEvent_Link', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect IntermediateCatchEvent_Link -> ', inject(function(bpmnRules) {
expectCanConnect('IntermediateCatchEvent_Link', 'Task', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('drop TextAnnotation -> Process', inject(function(bpmnRules) {
expectCanDrop('TextAnnotation', 'Process', true);
}));
});
describe('event based gateway', function() {
var testXML = require('./BpmnRules.eventBasedGateway.bpmn');
beforeEach(bootstrapModeler(testXML, { modules: testModules }));
it('connect EventBasedGateway -> IntermediateCatchEvent_Message', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Message', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateCatchEvent_Message', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Message', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateCatchEvent_Signal', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Signal', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateCatchEvent_Condition', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Condition', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateCatchEvent_Timer', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent_Timer', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateCatchEvent', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateCatchEvent', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> IntermediateThrowEvent_Message', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'IntermediateThrowEvent_Message', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> ReceiveTask', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'ReceiveTask', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> Task_None', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'Task_None', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect EventBasedGateway -> ParallelGateway', inject(function(bpmnRules) {
expectCanConnect('EventBasedGateway', 'ParallelGateway', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
});
describe('on collaboration diagram', function() {
var testXML = require('./BpmnRules.collaboration.bpmn');
beforeEach(bootstrapModeler(testXML, { modules: testModules }));
it('connect StartEvent_None -> IntermediateEvent', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'IntermediateThrowEvent_Message', {
sequenceFlow: true,
messageFlow: false,
association: true
});
}));
it('connect StartEvent_None -> OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'OtherParticipant', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect OtherParticipant -> StartEvent_None', inject(function(bpmnRules) {
expectCanConnect('OtherParticipant', 'StartEvent_None', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect EndEvent_None -> OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('EndEvent_None', 'OtherParticipant', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect OtherParticipant -> EndEvent_None', inject(function(bpmnRules) {
expectCanConnect('OtherParticipant', 'EndEvent_None', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect IntermediateThrowEvent_Message -> OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('IntermediateThrowEvent_Message', 'OtherParticipant', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect OtherParticipant -> IntermediateThrowEvent_Message', inject(function(bpmnRules) {
expectCanConnect('OtherParticipant', 'IntermediateThrowEvent_Message', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect Task_in_SubProcess -> OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('Task_in_SubProcess', 'OtherParticipant', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect OtherParticipant -> Task_in_SubProcess', inject(function(bpmnRules) {
expectCanConnect('OtherParticipant', 'Task_in_SubProcess', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect Participant -> OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('Participant', 'OtherParticipant', {
sequenceFlow: false,
messageFlow: true,
association: true
});
}));
it('connect StartEvent_None -> TextAnnotation_OtherParticipant', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'TextAnnotation_OtherParticipant', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('connect StartEvent_None -> TextAnnotation_Global', inject(function(bpmnRules) {
expectCanConnect('StartEvent_None', 'TextAnnotation_Global', {
sequenceFlow: false,
messageFlow: false,
association: true
});
}));
it('drop TextAnnotation_Global -> Participant', inject(function(bpmnRules) {
expectCanDrop('TextAnnotation_Global', 'Participant', true);
}));
});
});

View File

@ -1,315 +0,0 @@
'use strict';
/* global bootstrapModeler, inject */
var modelingModule = require('../../../../lib/features/modeling'),
rulesModule = require('../../../../lib/features/modeling/rules'),
coreModule = require('../../../../lib/core');
describe('features/ModelingRules', function() {
var sequenceXML = require('../../../fixtures/bpmn/sequence-flows.bpmn');
var eventGatewaysEdgeXML = require('../../../fixtures/bpmn/features/rules/event-based-gateway-outgoing-edge.bpmn');
var linkEventXML = require('../../../fixtures/bpmn/features/rules/link-event.bpmn');
var textAnnotationXML = require('../../../fixtures/bpmn/features/rules/text-annotation-association.bpmn');
var testModules = [ coreModule, modelingModule, rulesModule ];
// See workaround https://github.com/bpmn-io/bpmn-js/issues/176
// The wanted behavior until https://github.com/bpmn-io/bpmn-js/issues/176 is fixed
describe('connect with source == target', function() {
beforeEach(bootstrapModeler(sequenceXML, { modules: testModules }));
it('should not allow connection', inject(function(elementRegistry, modeling, rules) {
// given
var taskShape = elementRegistry.get('Task_1');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: taskShape,
target: taskShape
});
// then
// connection should not be allowed
expect(allowed).toBe(false);
}));
});
describe('eventbased gateway', function() {
beforeEach(bootstrapModeler(eventGatewaysEdgeXML, { modules: testModules }));
it('should allow catching message intermediate event on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
messageEvent = elementRegistry.get('IntermediateCatchEvent_0');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: messageEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
it('should allow catching timer intermediate event on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
timerEvent = elementRegistry.get('IntermediateCatchEvent_1');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: timerEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
it('should allow catching condition intermediate event on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
conditionEvent = elementRegistry.get('IntermediateCatchEvent_2');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: conditionEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
it('should allow catching signal intermediate event on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
signalEvent = elementRegistry.get('IntermediateCatchEvent_3');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: signalEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
it('should allow receive task on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
receiveTask = elementRegistry.get('ReceiveTask_1');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: receiveTask
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
it('should not allow throw event on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
throwEvent = elementRegistry.get('IntermediateThrowEvent_0');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: throwEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(false);
}));
it('should not allow task on outgoing edges',
inject(function(elementRegistry, modeling, rules) {
// given
var eventGateway = elementRegistry.get('EventBasedGateway_1'),
task = elementRegistry.get('Task_1');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: eventGateway,
target: task
});
// then
// connection should not be allowed
expect(allowed).toBe(false);
}));
});
describe('catch link events', function() {
beforeEach(bootstrapModeler(linkEventXML, { modules: testModules }));
it('should not have incoming sequence flows ', inject(function(elementRegistry, modeling, rules) {
// given
var catchEvent = elementRegistry.get('IntermediateCatchEvent'),
incomingTask = elementRegistry.get('Task_incoming');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: incomingTask,
target: catchEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(false);
}));
it('should be allowed to have outgoing sequence flows ',
inject(function(elementRegistry, modeling, rules) {
// given
var catchEvent = elementRegistry.get('IntermediateCatchEvent'),
outgoingTask = elementRegistry.get('Task_outgoing');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: catchEvent,
target: outgoingTask
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
});
describe('throwing link events', function() {
beforeEach(bootstrapModeler(linkEventXML, { modules: testModules }));
it('should not have outgoing sequence flows', inject(function(elementRegistry, modeling, rules) {
// given
var catchEvent = elementRegistry.get('IntermediateThrowEvent'),
outgoingTask = elementRegistry.get('Task_outgoing');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: catchEvent,
target: outgoingTask
});
// then
// connection should not be allowed
expect(allowed).toBe(false);
}));
it('should be allowed to have incoming sequence flows ',
inject(function(elementRegistry, modeling, rules) {
// given
var catchEvent = elementRegistry.get('IntermediateThrowEvent'),
incomingTask = elementRegistry.get('Task_incoming');
// when
var allowed = rules.allowed('connection.create', {
connection: null,
source: incomingTask,
target: catchEvent
});
// then
// connection should not be allowed
expect(allowed).toBe(true);
}));
});
describe('Association', function() {
beforeEach(bootstrapModeler(textAnnotationXML, { modules: testModules }));
it('should allow drop on process', inject(function(elementRegistry, rules) {
// given
var association = elementRegistry.get('Association_1'),
parent = elementRegistry.get('Process_1');
// when
var allowed = rules.allowed('shapes.move', {
connection: null,
shapes: [ association ],
newParent: parent
});
// then
expect(allowed).toBe(true);
}));
});
});