2015-04-16 07:11:04 +00:00
|
|
|
'use strict';
|
|
|
|
|
2015-09-23 13:00:05 +00:00
|
|
|
var find = require('lodash/collection/find'),
|
2015-07-13 08:37:43 +00:00
|
|
|
any = require('lodash/collection/any'),
|
2016-04-26 13:04:24 +00:00
|
|
|
every = require('lodash/collection/every'),
|
2016-03-23 14:32:10 +00:00
|
|
|
filter = require('lodash/collection/filter'),
|
2015-08-17 14:43:15 +00:00
|
|
|
forEach = require('lodash/collection/forEach'),
|
2015-04-16 07:11:04 +00:00
|
|
|
inherits = require('inherits');
|
|
|
|
|
2016-12-06 13:49:45 +00:00
|
|
|
var is = require('../../util/ModelUtil').is,
|
2015-10-21 14:25:56 +00:00
|
|
|
isAny = require('../modeling/util/ModelingUtil').isAny,
|
2015-08-19 15:16:21 +00:00
|
|
|
getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
|
|
|
|
isExpanded = require('../../util/DiUtil').isExpanded,
|
|
|
|
isEventSubProcess = require('../../util/DiUtil').isEventSubProcess,
|
2016-11-09 10:45:12 +00:00
|
|
|
isInterrupting = require('../../util/DiUtil').isInterrupting,
|
|
|
|
hasErrorEventDefinition = require('../../util/DiUtil').hasErrorEventDefinition,
|
|
|
|
hasEscalationEventDefinition = require('../../util/DiUtil').hasEscalationEventDefinition,
|
|
|
|
hasCompensateEventDefinition = require('../../util/DiUtil').hasCompensateEventDefinition;
|
2015-04-16 07:11:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
|
|
|
|
|
2015-08-19 15:16:21 +00:00
|
|
|
var isBoundaryAttachment = require('../snapping/BpmnSnappingUtil').getBoundaryAttachment;
|
2015-07-13 08:37:43 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
/**
|
|
|
|
* 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,
|
2017-12-20 09:34:45 +00:00
|
|
|
target = context.target,
|
|
|
|
hints = context.hints || {},
|
|
|
|
targetParent = hints.targetParent,
|
|
|
|
targetAttach = hints.targetAttach;
|
|
|
|
|
|
|
|
// don't allow incoming connections on
|
|
|
|
// newly created boundary events
|
|
|
|
// to boundary events
|
|
|
|
if (targetAttach) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// temporarily set target parent for scoping
|
|
|
|
// checks to work
|
|
|
|
if (targetParent) {
|
|
|
|
target.parent = targetParent;
|
|
|
|
}
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
try {
|
|
|
|
return canConnect(source, target);
|
|
|
|
} finally {
|
|
|
|
// unset temporary target parent
|
|
|
|
if (targetParent) {
|
|
|
|
target.parent = null;
|
|
|
|
}
|
|
|
|
}
|
2015-04-16 07:11:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2015-08-13 08:51:52 +00:00
|
|
|
this.addRule('elements.move', function(context) {
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2015-07-03 14:00:41 +00:00
|
|
|
var target = context.target,
|
2015-08-06 07:40:58 +00:00
|
|
|
shapes = context.shapes,
|
|
|
|
position = context.position;
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2015-08-17 14:43:15 +00:00
|
|
|
return canAttach(shapes, target, null, position) ||
|
2015-09-17 07:05:09 +00:00
|
|
|
canReplace(shapes, target, position) ||
|
2017-01-25 16:27:30 +00:00
|
|
|
canMove(shapes, target, position) ||
|
|
|
|
canInsert(shapes, target, position);
|
2015-04-16 07:11:04 +00:00
|
|
|
});
|
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
this.addRule('shape.create', function(context) {
|
|
|
|
return canCreate(
|
|
|
|
context.shape,
|
|
|
|
context.target,
|
|
|
|
context.source,
|
|
|
|
context.position
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.addRule('shape.attach', function(context) {
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
return canAttach(
|
|
|
|
context.shape,
|
|
|
|
context.target,
|
|
|
|
null,
|
|
|
|
context.position
|
|
|
|
);
|
2015-04-16 07:11:04 +00:00
|
|
|
});
|
|
|
|
|
2016-04-20 15:31:42 +00:00
|
|
|
this.addRule('element.copy', function(context) {
|
|
|
|
var collection = context.collection,
|
|
|
|
element = context.element;
|
|
|
|
|
|
|
|
return canCopy(collection, element);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.addRule('element.paste', function(context) {
|
|
|
|
var parent = context.parent,
|
|
|
|
element = context.element,
|
|
|
|
position = context.position,
|
|
|
|
source = context.source,
|
|
|
|
target = context.target;
|
|
|
|
|
|
|
|
if (source || target) {
|
|
|
|
return canConnect(source, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
return canAttach([ element ], parent, null, position) || canCreate(element, parent, null, position);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.addRule('elements.paste', function(context) {
|
2016-04-26 13:04:24 +00:00
|
|
|
var tree = context.tree,
|
|
|
|
target = context.target;
|
2016-04-20 15:31:42 +00:00
|
|
|
|
2016-04-26 13:04:24 +00:00
|
|
|
return canPaste(tree, target);
|
2016-04-20 15:31:42 +00:00
|
|
|
});
|
|
|
|
|
2016-03-23 14:32:10 +00:00
|
|
|
this.addRule([ 'elements.delete' ], function(context) {
|
|
|
|
|
|
|
|
// do not allow deletion of labels
|
|
|
|
return filter(context.elements, function(e) {
|
|
|
|
return !isLabel(e);
|
|
|
|
});
|
|
|
|
});
|
2015-04-16 07:11:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
|
|
|
|
|
|
|
|
BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
|
|
|
|
|
2015-10-06 10:33:21 +00:00
|
|
|
BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
|
|
|
|
|
|
|
|
BpmnRules.prototype.canMove = canMove;
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
BpmnRules.prototype.canAttach = canAttach;
|
|
|
|
|
2015-08-17 14:43:15 +00:00
|
|
|
BpmnRules.prototype.canReplace = canReplace;
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
BpmnRules.prototype.canDrop = canDrop;
|
|
|
|
|
2015-05-21 12:55:10 +00:00
|
|
|
BpmnRules.prototype.canInsert = canInsert;
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
BpmnRules.prototype.canCreate = canCreate;
|
|
|
|
|
|
|
|
BpmnRules.prototype.canConnect = canConnect;
|
|
|
|
|
|
|
|
BpmnRules.prototype.canResize = canResize;
|
|
|
|
|
2016-04-20 15:31:42 +00:00
|
|
|
BpmnRules.prototype.canCopy = canCopy;
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
/**
|
|
|
|
* Utility functions for rule checking
|
|
|
|
*/
|
|
|
|
|
|
|
|
function nonExistantOrLabel(element) {
|
|
|
|
return !element || isLabel(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isSame(a, b) {
|
|
|
|
return a === b;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getOrganizationalParent(element) {
|
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
do {
|
|
|
|
if (is(element, 'bpmn:Process')) {
|
|
|
|
return getBusinessObject(element);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
if (is(element, 'bpmn:Participant')) {
|
|
|
|
return getBusinessObject(element).processRef;
|
|
|
|
}
|
|
|
|
} while ((element = element.parent));
|
2015-04-16 07:11:04 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-01-21 13:19:59 +00:00
|
|
|
function isTextAnnotation(element) {
|
|
|
|
return is(element, 'bpmn:TextAnnotation');
|
|
|
|
}
|
|
|
|
|
|
|
|
function isCompensationBoundary(element) {
|
|
|
|
return is(element, 'bpmn:BoundaryEvent') &&
|
|
|
|
hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
|
|
|
|
}
|
|
|
|
|
2016-01-20 17:30:34 +00:00
|
|
|
function isForCompensation(e) {
|
|
|
|
return getBusinessObject(e).isForCompensation;
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
function isSameOrganization(a, b) {
|
|
|
|
var parentA = getOrganizationalParent(a),
|
|
|
|
parentB = getOrganizationalParent(b);
|
|
|
|
|
|
|
|
return parentA === parentB;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isMessageFlowSource(element) {
|
2018-01-05 13:32:52 +00:00
|
|
|
return (
|
|
|
|
is(element, 'bpmn:InteractionNode') &&
|
|
|
|
!isForCompensation(element) && (
|
|
|
|
!is(element, 'bpmn:Event') || (
|
|
|
|
is(element, 'bpmn:ThrowEvent') &&
|
|
|
|
hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
|
|
|
|
)
|
|
|
|
)
|
2015-04-16 07:11:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isMessageFlowTarget(element) {
|
2018-01-05 13:32:52 +00:00
|
|
|
return (
|
|
|
|
is(element, 'bpmn:InteractionNode') &&
|
|
|
|
!isForCompensation(element) && (
|
|
|
|
!is(element, 'bpmn:Event') || (
|
|
|
|
is(element, 'bpmn:CatchEvent') &&
|
|
|
|
hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
|
|
|
|
)
|
|
|
|
)
|
2015-04-16 07:11:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getScopeParent(element) {
|
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
var parent = element;
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
while ((parent = parent.parent)) {
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
if (is(parent, 'bpmn:FlowElementsContainer')) {
|
|
|
|
return getBusinessObject(parent);
|
|
|
|
}
|
2015-04-16 07:11:04 +00:00
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
if (is(parent, 'bpmn:Participant')) {
|
|
|
|
return getBusinessObject(parent).processRef;
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-20 09:34:45 +00:00
|
|
|
return null;
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isSameScope(a, b) {
|
|
|
|
var scopeParentA = getScopeParent(a),
|
|
|
|
scopeParentB = getScopeParent(b);
|
|
|
|
|
|
|
|
return scopeParentA && (scopeParentA === scopeParentB);
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasEventDefinition(element, eventDefinition) {
|
|
|
|
var bo = getBusinessObject(element);
|
|
|
|
|
2015-05-11 15:55:06 +00:00
|
|
|
return !!find(bo.eventDefinitions || [], function(definition) {
|
|
|
|
return is(definition, eventDefinition);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasEventDefinitionOrNone(element, eventDefinition) {
|
|
|
|
var bo = getBusinessObject(element);
|
|
|
|
|
|
|
|
return (bo.eventDefinitions || []).every(function(definition) {
|
2015-04-16 07:11:04 +00:00
|
|
|
return is(definition, eventDefinition);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function isSequenceFlowSource(element) {
|
2018-01-05 13:32:52 +00:00
|
|
|
return (
|
|
|
|
is(element, 'bpmn:FlowNode') &&
|
|
|
|
!is(element, 'bpmn:EndEvent') &&
|
|
|
|
!isEventSubProcess(element) &&
|
|
|
|
!(is(element, 'bpmn:IntermediateThrowEvent') &&
|
|
|
|
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
|
|
|
|
) &&
|
|
|
|
!isCompensationBoundary(element) &&
|
|
|
|
!isForCompensation(element)
|
|
|
|
);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isSequenceFlowTarget(element) {
|
2018-01-05 13:32:52 +00:00
|
|
|
return (
|
|
|
|
is(element, 'bpmn:FlowNode') &&
|
|
|
|
!is(element, 'bpmn:StartEvent') &&
|
|
|
|
!is(element, 'bpmn:BoundaryEvent') &&
|
|
|
|
!isEventSubProcess(element) &&
|
|
|
|
!(is(element, 'bpmn:IntermediateCatchEvent') &&
|
|
|
|
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
|
|
|
|
) &&
|
|
|
|
!isForCompensation(element)
|
|
|
|
);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isEventBasedTarget(element) {
|
2018-01-05 13:32:52 +00:00
|
|
|
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')
|
|
|
|
)
|
|
|
|
)
|
2015-04-16 07:11:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isLabel(element) {
|
|
|
|
return element.labelTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isConnection(element) {
|
|
|
|
return element.waypoints;
|
|
|
|
}
|
|
|
|
|
2016-12-06 13:49:45 +00:00
|
|
|
function getParents(element) {
|
|
|
|
|
|
|
|
var parents = [];
|
|
|
|
|
|
|
|
while (element) {
|
|
|
|
element = element.parent;
|
|
|
|
|
|
|
|
if (element) {
|
|
|
|
parents.push(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return parents;
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-01-21 13:19:59 +00:00
|
|
|
if (!is(connection, 'bpmn:DataAssociation')) {
|
|
|
|
|
|
|
|
if (canConnectMessageFlow(source, target)) {
|
|
|
|
return { type: 'bpmn:MessageFlow' };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canConnectSequenceFlow(source, target)) {
|
|
|
|
return { type: 'bpmn:SequenceFlow' };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var connectDataAssociation = canConnectDataAssociation(source, target);
|
|
|
|
|
|
|
|
if (connectDataAssociation) {
|
|
|
|
return connectDataAssociation;
|
2015-10-06 10:33:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-21 13:19:59 +00:00
|
|
|
if (isCompensationBoundary(source) && isForCompensation(target)) {
|
|
|
|
return {
|
|
|
|
type: 'bpmn:Association',
|
|
|
|
associationDirection: 'One'
|
|
|
|
};
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2017-04-10 12:21:14 +00:00
|
|
|
if (canConnectAssociation(source, target)) {
|
2016-01-21 13:19:59 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
type: 'bpmn:Association'
|
|
|
|
};
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can an element be dropped into the target element
|
|
|
|
*
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2015-09-17 07:05:09 +00:00
|
|
|
function canDrop(element, target, position) {
|
2015-04-16 07:11:04 +00:00
|
|
|
|
|
|
|
// can move labels everywhere
|
|
|
|
if (isLabel(element) && !isConnection(target)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-08 16:21:41 +00:00
|
|
|
// disallow to create elements on collapsed pools
|
|
|
|
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
// 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');
|
|
|
|
}
|
|
|
|
|
2015-08-11 12:46:17 +00:00
|
|
|
// allow creating lanes on participants and other lanes only
|
|
|
|
if (is(element, 'bpmn:Lane')) {
|
|
|
|
return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane');
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
if (is(element, 'bpmn:BoundaryEvent')) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
// drop flow elements onto flow element containers
|
|
|
|
// and participants
|
2016-12-06 13:49:45 +00:00
|
|
|
if (is(element, 'bpmn:FlowElement')) {
|
2015-04-16 07:11:04 +00:00
|
|
|
if (is(target, 'bpmn:FlowElementsContainer')) {
|
2016-01-27 14:11:27 +00:00
|
|
|
return isExpanded(target);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 14:25:56 +00:00
|
|
|
return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-06 13:49:45 +00:00
|
|
|
// account for the fact that data associations are always
|
|
|
|
// rendered and moved to top (Process or Collaboration level)
|
2016-12-19 14:57:34 +00:00
|
|
|
//
|
|
|
|
// artifacts may be placed wherever, too
|
|
|
|
if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation' ])) {
|
2015-10-21 14:25:56 +00:00
|
|
|
return isAny(target, [
|
2016-06-07 06:46:45 +00:00
|
|
|
'bpmn:Collaboration',
|
|
|
|
'bpmn:Lane',
|
|
|
|
'bpmn:Participant',
|
|
|
|
'bpmn:Process',
|
|
|
|
'bpmn:SubProcess' ]);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-12 16:03:51 +00:00
|
|
|
if (is(element, 'bpmn:MessageFlow')) {
|
2016-09-22 18:41:12 +00:00
|
|
|
return is(target, 'bpmn:Collaboration')
|
|
|
|
|| element.source.parent == target
|
|
|
|
|| element.target.parent == target;
|
2015-05-12 16:03:51 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-04-26 13:04:24 +00:00
|
|
|
function canPaste(tree, target) {
|
2016-05-02 06:57:12 +00:00
|
|
|
var topLevel = tree[0],
|
|
|
|
participants;
|
2016-04-26 13:04:24 +00:00
|
|
|
|
|
|
|
if (is(target, 'bpmn:Collaboration')) {
|
|
|
|
return every(topLevel, function(e) {
|
|
|
|
return e.type === 'bpmn:Participant';
|
|
|
|
});
|
|
|
|
}
|
2016-04-20 15:31:42 +00:00
|
|
|
|
2016-05-02 06:57:12 +00:00
|
|
|
if (is(target, 'bpmn:Process')) {
|
|
|
|
participants = any(topLevel, function(e) {
|
|
|
|
return e.type === 'bpmn:Participant';
|
|
|
|
});
|
|
|
|
|
|
|
|
return !(participants && target.children.length > 0);
|
|
|
|
}
|
|
|
|
|
2016-04-20 15:31:42 +00:00
|
|
|
// disallow to create elements on collapsed pools
|
|
|
|
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(target, 'bpmn:FlowElementsContainer')) {
|
|
|
|
return isExpanded(target);
|
|
|
|
}
|
|
|
|
|
|
|
|
return isAny(target, [
|
2016-06-07 06:46:45 +00:00
|
|
|
'bpmn:Collaboration',
|
|
|
|
'bpmn:Lane',
|
|
|
|
'bpmn:Participant',
|
|
|
|
'bpmn:Process',
|
|
|
|
'bpmn:SubProcess' ]);
|
2016-04-20 15:31:42 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
function isBoundaryEvent(element) {
|
|
|
|
return !isLabel(element) && is(element, 'bpmn:BoundaryEvent');
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:42:09 +00:00
|
|
|
function isLane(element) {
|
|
|
|
return is(element, 'bpmn:Lane');
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
/**
|
|
|
|
* We treat IntermediateThrowEvents as boundary events during create,
|
|
|
|
* this must be reflected in the rules.
|
|
|
|
*/
|
|
|
|
function isBoundaryCandidate(element) {
|
|
|
|
return isBoundaryEvent(element) ||
|
|
|
|
(is(element, 'bpmn:IntermediateThrowEvent') && !element.parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function canAttach(elements, target, source, position) {
|
2016-03-08 16:21:41 +00:00
|
|
|
|
2015-09-17 07:05:09 +00:00
|
|
|
if (!Array.isArray(elements)) {
|
|
|
|
elements = [ elements ];
|
|
|
|
}
|
2015-07-13 08:37:43 +00:00
|
|
|
|
|
|
|
// disallow appending as boundary event
|
|
|
|
if (source) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only (re-)attach one element at a time
|
|
|
|
if (elements.length !== 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var element = elements[0];
|
|
|
|
|
|
|
|
// do not attach labels
|
|
|
|
if (isLabel(element)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only handle boundary events
|
|
|
|
if (!isBoundaryCandidate(element)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow default move operation
|
|
|
|
if (!target) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-04 15:52:43 +00:00
|
|
|
// disallow drop on event sub processes
|
|
|
|
if (isEventSubProcess(target)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-20 17:30:34 +00:00
|
|
|
// only allow drop on non compensation activities
|
|
|
|
if (!is(target, 'bpmn:Activity') || isForCompensation(target)) {
|
2015-07-13 08:37:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only attach to subprocess border
|
2015-08-06 07:40:58 +00:00
|
|
|
if (position && !isBoundaryAttachment(position, target)) {
|
2015-07-13 08:37:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'attach';
|
|
|
|
}
|
|
|
|
|
2015-08-17 14:43:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Defines how to replace elements for a given target.
|
|
|
|
*
|
|
|
|
* Returns an array containing all elements which will be replaced.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* [{ id: 'IntermediateEvent_2',
|
|
|
|
* type: 'bpmn:StartEvent'
|
|
|
|
* },
|
|
|
|
* { id: 'IntermediateEvent_5',
|
|
|
|
* type: 'bpmn:EndEvent'
|
|
|
|
* }]
|
|
|
|
*
|
|
|
|
* @param {Array} elements
|
|
|
|
* @param {Object} target
|
|
|
|
*
|
|
|
|
* @return {Object} an object containing all elements which have to be replaced
|
|
|
|
*/
|
2015-09-17 07:05:09 +00:00
|
|
|
function canReplace(elements, target, position) {
|
2015-08-17 14:43:15 +00:00
|
|
|
|
|
|
|
if (!target) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var canExecute = {
|
2015-09-01 15:57:32 +00:00
|
|
|
replacements: []
|
2015-08-17 14:43:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
forEach(elements, function(element) {
|
|
|
|
|
|
|
|
if (!isEventSubProcess(target)) {
|
|
|
|
|
|
|
|
if (is(element, 'bpmn:StartEvent') &&
|
|
|
|
element.type !== 'label' &&
|
|
|
|
canDrop(element, target)) {
|
|
|
|
|
2016-11-09 10:45:12 +00:00
|
|
|
// replace a non-interrupting start event by a blank interrupting start event
|
|
|
|
// when the target is not an event sub process
|
|
|
|
if (!isInterrupting(element)) {
|
|
|
|
canExecute.replacements.push({
|
|
|
|
oldElementId: element.id,
|
|
|
|
newElementType: 'bpmn:StartEvent'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace an error/escalation/compansate start event by a blank interrupting start event
|
|
|
|
// when the target is not an event sub process
|
|
|
|
if (hasErrorEventDefinition(element) ||
|
|
|
|
hasEscalationEventDefinition(element) ||
|
|
|
|
hasCompensateEventDefinition(element)) {
|
|
|
|
canExecute.replacements.push({
|
|
|
|
oldElementId: element.id,
|
|
|
|
newElementType: 'bpmn:StartEvent'
|
|
|
|
});
|
|
|
|
}
|
2015-08-17 14:43:15 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-17 07:05:09 +00:00
|
|
|
|
|
|
|
if (!is(target, 'bpmn:Transaction')) {
|
|
|
|
if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') &&
|
|
|
|
element.type !== 'label') {
|
|
|
|
|
|
|
|
if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) {
|
|
|
|
canExecute.replacements.push({
|
|
|
|
oldElementId: element.id,
|
|
|
|
newElementType: 'bpmn:EndEvent'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
|
|
|
|
canExecute.replacements.push({
|
|
|
|
oldElementId: element.id,
|
|
|
|
newElementType: 'bpmn:BoundaryEvent'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-17 14:43:15 +00:00
|
|
|
});
|
|
|
|
|
2015-09-01 15:57:32 +00:00
|
|
|
return canExecute.replacements.length ? canExecute : false;
|
2015-08-17 14:43:15 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
function canMove(elements, target) {
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
// do not move selection containing boundary events
|
|
|
|
if (any(elements, isBoundaryEvent)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:42:09 +00:00
|
|
|
// do not move selection containing lanes
|
|
|
|
if (any(elements, isLane)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
// allow default move check to start move operation
|
2015-04-16 07:11:04 +00:00
|
|
|
if (!target) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return elements.every(function(element) {
|
|
|
|
return canDrop(element, target);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:37:43 +00:00
|
|
|
function canCreate(shape, target, source, position) {
|
2015-04-16 07:11:04 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-30 08:48:17 +00:00
|
|
|
return canDrop(shape, target, position) || canInsert(shape, target, position);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function canResize(shape, newBounds) {
|
|
|
|
if (is(shape, 'bpmn:SubProcess')) {
|
2018-01-05 13:32:52 +00:00
|
|
|
return (
|
|
|
|
isExpanded(shape) && (
|
|
|
|
!newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
|
|
|
|
)
|
2015-04-16 07:11:04 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-08-11 12:46:17 +00:00
|
|
|
if (is(shape, 'bpmn:Lane')) {
|
|
|
|
return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
if (is(shape, 'bpmn:Participant')) {
|
2015-08-21 11:58:23 +00:00
|
|
|
return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-01 12:17:44 +00:00
|
|
|
if (isTextAnnotation(shape)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-17 14:37:05 +00:00
|
|
|
/**
|
|
|
|
* Check, whether one side of the relationship
|
|
|
|
* is a text annotation.
|
|
|
|
*/
|
|
|
|
function isOneTextAnnotation(source, target) {
|
|
|
|
|
|
|
|
var sourceTextAnnotation = isTextAnnotation(source),
|
|
|
|
targetTextAnnotation = isTextAnnotation(target);
|
|
|
|
|
|
|
|
return (
|
|
|
|
(sourceTextAnnotation || targetTextAnnotation) &&
|
|
|
|
(sourceTextAnnotation !== targetTextAnnotation)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-16 07:11:04 +00:00
|
|
|
function canConnectAssociation(source, target) {
|
|
|
|
|
|
|
|
// do not connect connections
|
|
|
|
if (isConnection(source) || isConnection(target)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-10 12:21:14 +00:00
|
|
|
// compensation boundary events are exception
|
|
|
|
if (isCompensationBoundary(source) && isForCompensation(target)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-17 14:37:05 +00:00
|
|
|
// don't connect parent <-> child
|
|
|
|
if (isParent(target, source) || isParent(source, target)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allow connection of associations between <!TextAnnotation> and <TextAnnotation>
|
|
|
|
return isOneTextAnnotation(source, target);
|
2015-04-16 07:11:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
2015-05-19 21:58:52 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 14:25:56 +00:00
|
|
|
|
2015-10-06 10:33:21 +00:00
|
|
|
function canConnectDataAssociation(source, target) {
|
2015-10-21 14:25:56 +00:00
|
|
|
|
2016-01-25 20:25:30 +00:00
|
|
|
if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
|
2015-10-21 14:25:56 +00:00
|
|
|
isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
|
2015-10-06 10:33:21 +00:00
|
|
|
return { type: 'bpmn:DataInputAssociation' };
|
|
|
|
}
|
|
|
|
|
2016-01-25 20:25:30 +00:00
|
|
|
if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
|
2015-10-21 14:25:56 +00:00
|
|
|
isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
|
2015-10-06 10:33:21 +00:00
|
|
|
return { type: 'bpmn:DataOutputAssociation' };
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-30 08:48:17 +00:00
|
|
|
function canInsert(shape, flow, position) {
|
2015-05-21 12:55:10 +00:00
|
|
|
|
2017-01-25 16:27:30 +00:00
|
|
|
if (Array.isArray(shape)) {
|
|
|
|
if (shape.length !== 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
shape = shape[0];
|
|
|
|
}
|
|
|
|
|
2015-05-21 12:55:10 +00:00
|
|
|
// return true if we can drop on the
|
|
|
|
// underlying flow parent
|
|
|
|
//
|
|
|
|
// at this point we are not really able to talk
|
|
|
|
// about connection rules (yet)
|
2017-03-10 09:38:29 +00:00
|
|
|
|
2015-05-21 12:55:10 +00:00
|
|
|
return (
|
2015-10-21 14:25:56 +00:00
|
|
|
isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
|
2017-03-10 09:38:29 +00:00
|
|
|
!isLabel(flow) &&
|
2015-10-21 14:25:56 +00:00
|
|
|
is(shape, 'bpmn:FlowNode') &&
|
|
|
|
!is(shape, 'bpmn:BoundaryEvent') &&
|
|
|
|
canDrop(shape, flow.parent, position));
|
2015-07-03 08:48:32 +00:00
|
|
|
}
|
2016-04-20 15:31:42 +00:00
|
|
|
|
|
|
|
function contains(collection, element) {
|
|
|
|
return (collection && element) && collection.indexOf(element) !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function canCopy(collection, element) {
|
|
|
|
if (is(element, 'bpmn:Lane') && !contains(collection, element.parent)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(element, 'bpmn:BoundaryEvent') && !contains(collection, element.host)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|