You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
8.2 KiB
331 lines
8.2 KiB
import { |
|
pick, |
|
assign, |
|
filter, |
|
forEach, |
|
isArray, |
|
isUndefined, |
|
has |
|
} from 'min-dash'; |
|
|
|
import { |
|
is, |
|
getBusinessObject |
|
} from '../../util/ModelUtil'; |
|
|
|
import { |
|
isAny |
|
} from '../modeling/util/ModelingUtil'; |
|
|
|
import { |
|
isExpanded, |
|
isEventSubProcess |
|
} from '../../util/DiUtil'; |
|
|
|
import { getPropertyNames } from '../copy-paste/ModdleCopy'; |
|
|
|
function copyProperties(source, target, properties) { |
|
if (!isArray(properties)) { |
|
properties = [ properties ]; |
|
} |
|
|
|
forEach(properties, function(property) { |
|
if (!isUndefined(source[property])) { |
|
target[property] = source[property]; |
|
} |
|
}); |
|
} |
|
|
|
|
|
var CUSTOM_PROPERTIES = [ |
|
'cancelActivity', |
|
'instantiate', |
|
'eventGatewayType', |
|
'triggeredByEvent', |
|
'isInterrupting' |
|
]; |
|
|
|
/** |
|
* Check if element should be collapsed or expanded. |
|
*/ |
|
function shouldToggleCollapsed(element, targetElement) { |
|
|
|
var oldCollapsed = ( |
|
element && has(element, 'collapsed') ? element.collapsed : !isExpanded(element) |
|
); |
|
|
|
var targetCollapsed; |
|
|
|
if (targetElement && (has(targetElement, 'collapsed') || has(targetElement, 'isExpanded'))) { |
|
|
|
// property is explicitly set so use it |
|
targetCollapsed = ( |
|
has(targetElement, 'collapsed') ? targetElement.collapsed : !targetElement.isExpanded |
|
); |
|
} else { |
|
|
|
// keep old state |
|
targetCollapsed = oldCollapsed; |
|
} |
|
|
|
if (oldCollapsed !== targetCollapsed) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
/** |
|
* This module takes care of replacing BPMN elements |
|
*/ |
|
export default function BpmnReplace( |
|
bpmnFactory, |
|
elementFactory, |
|
moddleCopy, |
|
modeling, |
|
replace, |
|
rules, |
|
selection |
|
) { |
|
|
|
/** |
|
* Prepares a new business object for the replacement element |
|
* and triggers the replace operation. |
|
* |
|
* @param {djs.model.Base} element |
|
* @param {Object} target |
|
* @param {Object} [hints] |
|
* |
|
* @return {djs.model.Base} the newly created element |
|
*/ |
|
function replaceElement(element, target, hints) { |
|
|
|
hints = hints || {}; |
|
|
|
var type = target.type, |
|
oldBusinessObject = element.businessObject; |
|
|
|
if (isSubProcess(oldBusinessObject) && type === 'bpmn:SubProcess') { |
|
if (shouldToggleCollapsed(element, target)) { |
|
|
|
// expanding or collapsing process |
|
modeling.toggleCollapse(element); |
|
|
|
return element; |
|
} |
|
} |
|
|
|
var newBusinessObject = bpmnFactory.create(type); |
|
|
|
var newElement = { |
|
type: type, |
|
businessObject: newBusinessObject, |
|
}; |
|
|
|
newElement.di = {}; |
|
|
|
// colors will be set to DI |
|
copyProperties(element.di, newElement.di, [ |
|
'fill', |
|
'stroke', |
|
'background-color', |
|
'border-color', |
|
'color' |
|
]); |
|
|
|
var elementProps = getPropertyNames(oldBusinessObject.$descriptor), |
|
newElementProps = getPropertyNames(newBusinessObject.$descriptor, true), |
|
copyProps = intersection(elementProps, newElementProps); |
|
|
|
// initialize special properties defined in target definition |
|
assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES)); |
|
|
|
var properties = filter(copyProps, function(propertyName) { |
|
|
|
// copying event definitions, unless we replace |
|
if (propertyName === 'eventDefinitions') { |
|
return hasEventDefinition(element, target.eventDefinitionType); |
|
} |
|
|
|
// retain loop characteristics if the target element |
|
// is not an event sub process |
|
if (propertyName === 'loopCharacteristics') { |
|
return !isEventSubProcess(newBusinessObject); |
|
} |
|
|
|
// so the applied properties from 'target' don't get lost |
|
if (has(newBusinessObject, propertyName)) { |
|
return false; |
|
} |
|
|
|
if (propertyName === 'processRef' && target.isExpanded === false) { |
|
return false; |
|
} |
|
|
|
if (propertyName === 'triggeredByEvent') { |
|
return false; |
|
} |
|
|
|
return true; |
|
}); |
|
|
|
newBusinessObject = moddleCopy.copyElement( |
|
oldBusinessObject, |
|
newBusinessObject, |
|
properties |
|
); |
|
|
|
// initialize custom BPMN extensions |
|
if (target.eventDefinitionType) { |
|
|
|
// only initialize with new eventDefinition |
|
// if we did not set an event definition yet, |
|
// i.e. because we copied it |
|
if (!hasEventDefinition(newBusinessObject, target.eventDefinitionType)) { |
|
newElement.eventDefinitionType = target.eventDefinitionType; |
|
newElement.eventDefinitionAttrs = target.eventDefinitionAttrs; |
|
} |
|
} |
|
|
|
if (is(oldBusinessObject, 'bpmn:Activity')) { |
|
|
|
if (isSubProcess(oldBusinessObject)) { |
|
|
|
// no toggeling, so keep old state |
|
newElement.isExpanded = isExpanded(element); |
|
} |
|
|
|
// else if property is explicitly set, use it |
|
else if (target && has(target, 'isExpanded')) { |
|
newElement.isExpanded = target.isExpanded; |
|
|
|
// assign default size of new expanded element |
|
var defaultSize = elementFactory.getDefaultSize(newBusinessObject, { |
|
isExpanded: newElement.isExpanded |
|
}); |
|
|
|
newElement.width = defaultSize.width; |
|
newElement.height = defaultSize.height; |
|
|
|
// keep element centered |
|
newElement.x = element.x - (newElement.width - element.width) / 2; |
|
newElement.y = element.y - (newElement.height - element.height) / 2; |
|
} |
|
|
|
// TODO: need also to respect min/max Size |
|
// copy size, from an expanded subprocess to an expanded alternative subprocess |
|
// except bpmn:Task, because Task is always expanded |
|
if ((isExpanded(element) && !is(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) { |
|
newElement.width = element.width; |
|
newElement.height = element.height; |
|
} |
|
} |
|
|
|
// remove children if not expanding sub process |
|
if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) { |
|
hints.moveChildren = false; |
|
} |
|
|
|
// transform collapsed/expanded pools |
|
if (is(oldBusinessObject, 'bpmn:Participant')) { |
|
|
|
// create expanded pool |
|
if (target.isExpanded === true) { |
|
newBusinessObject.processRef = bpmnFactory.create('bpmn:Process'); |
|
} else { |
|
|
|
// remove children when transforming to collapsed pool |
|
hints.moveChildren = false; |
|
} |
|
|
|
// apply same width and default height |
|
newElement.width = element.width; |
|
newElement.height = elementFactory.getDefaultSize(newElement).height; |
|
} |
|
|
|
if (!rules.allowed('shape.resize', { shape: newBusinessObject })) { |
|
newElement.height = elementFactory.getDefaultSize(newElement).height; |
|
newElement.width = elementFactory.getDefaultSize(newElement).width; |
|
} |
|
|
|
newBusinessObject.name = oldBusinessObject.name; |
|
|
|
// retain default flow's reference between inclusive <-> exclusive gateways and activities |
|
if ( |
|
isAny(oldBusinessObject, [ |
|
'bpmn:ExclusiveGateway', |
|
'bpmn:InclusiveGateway', |
|
'bpmn:Activity' |
|
]) && |
|
isAny(newBusinessObject, [ |
|
'bpmn:ExclusiveGateway', |
|
'bpmn:InclusiveGateway', |
|
'bpmn:Activity' |
|
]) |
|
) { |
|
newBusinessObject.default = oldBusinessObject.default; |
|
} |
|
|
|
if ( |
|
target.host && |
|
!is(oldBusinessObject, 'bpmn:BoundaryEvent') && |
|
is(newBusinessObject, 'bpmn:BoundaryEvent') |
|
) { |
|
newElement.host = target.host; |
|
} |
|
|
|
// The DataStoreReference element is 14px wider than the DataObjectReference element |
|
// This ensures that they stay centered on the x axis when replaced |
|
if ( |
|
newElement.type === 'bpmn:DataStoreReference' || |
|
newElement.type === 'bpmn:DataObjectReference' |
|
) { |
|
newElement.x = element.x + (element.width - newElement.width) / 2; |
|
} |
|
|
|
|
|
newElement = replace.replaceElement(element, newElement, hints); |
|
|
|
if (hints.select !== false) { |
|
selection.select(newElement); |
|
} |
|
|
|
return newElement; |
|
} |
|
|
|
this.replaceElement = replaceElement; |
|
} |
|
|
|
BpmnReplace.$inject = [ |
|
'bpmnFactory', |
|
'elementFactory', |
|
'moddleCopy', |
|
'modeling', |
|
'replace', |
|
'rules', |
|
'selection' |
|
]; |
|
|
|
|
|
function isSubProcess(bo) { |
|
return is(bo, 'bpmn:SubProcess'); |
|
} |
|
|
|
function hasEventDefinition(element, type) { |
|
|
|
var bo = getBusinessObject(element); |
|
|
|
return type && bo.get('eventDefinitions').some(function(definition) { |
|
return is(definition, type); |
|
}); |
|
} |
|
|
|
/** |
|
* Compute intersection between two arrays. |
|
*/ |
|
function intersection(a1, a2) { |
|
return a1.filter(function(el) { |
|
return a2.indexOf(el) !== -1; |
|
}); |
|
}
|
|
|