BpmnReplace.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import {
  2. pick,
  3. assign,
  4. filter,
  5. forEach,
  6. isArray,
  7. isUndefined,
  8. has
  9. } from 'min-dash';
  10. import {
  11. is,
  12. getBusinessObject
  13. } from '../../util/ModelUtil';
  14. import {
  15. isAny
  16. } from '../modeling/util/ModelingUtil';
  17. import {
  18. isExpanded,
  19. isEventSubProcess
  20. } from '../../util/DiUtil';
  21. import { getPropertyNames } from '../copy-paste/ModdleCopy';
  22. function copyProperties(source, target, properties) {
  23. if (!isArray(properties)) {
  24. properties = [ properties ];
  25. }
  26. forEach(properties, function(property) {
  27. if (!isUndefined(source[property])) {
  28. target[property] = source[property];
  29. }
  30. });
  31. }
  32. var CUSTOM_PROPERTIES = [
  33. 'cancelActivity',
  34. 'instantiate',
  35. 'eventGatewayType',
  36. 'triggeredByEvent',
  37. 'isInterrupting'
  38. ];
  39. function toggeling(element, target) {
  40. var oldCollapsed = (
  41. element && has(element, 'collapsed') ? element.collapsed : !isExpanded(element)
  42. );
  43. var targetCollapsed;
  44. if (target && (has(target, 'collapsed') || has(target, 'isExpanded'))) {
  45. // property is explicitly set so use it
  46. targetCollapsed = (
  47. has(target, 'collapsed') ? target.collapsed : !target.isExpanded
  48. );
  49. } else {
  50. // keep old state
  51. targetCollapsed = oldCollapsed;
  52. }
  53. if (oldCollapsed !== targetCollapsed) {
  54. element.collapsed = oldCollapsed;
  55. return true;
  56. }
  57. return false;
  58. }
  59. /**
  60. * This module takes care of replacing BPMN elements
  61. */
  62. export default function BpmnReplace(
  63. bpmnFactory,
  64. elementFactory,
  65. moddleCopy,
  66. modeling,
  67. replace,
  68. selection
  69. ) {
  70. /**
  71. * Prepares a new business object for the replacement element
  72. * and triggers the replace operation.
  73. *
  74. * @param {djs.model.Base} element
  75. * @param {Object} target
  76. * @param {Object} [hints]
  77. *
  78. * @return {djs.model.Base} the newly created element
  79. */
  80. function replaceElement(element, target, hints) {
  81. hints = hints || {};
  82. var type = target.type,
  83. oldBusinessObject = element.businessObject;
  84. if (isSubProcess(oldBusinessObject)) {
  85. if (type === 'bpmn:SubProcess') {
  86. if (toggeling(element, target)) {
  87. // expanding or collapsing process
  88. modeling.toggleCollapse(element);
  89. return element;
  90. }
  91. }
  92. }
  93. var newBusinessObject = bpmnFactory.create(type);
  94. var newElement = {
  95. type: type,
  96. businessObject: newBusinessObject
  97. };
  98. var elementProps = getPropertyNames(oldBusinessObject.$descriptor),
  99. newElementProps = getPropertyNames(newBusinessObject.$descriptor, true),
  100. copyProps = intersection(elementProps, newElementProps);
  101. // initialize special properties defined in target definition
  102. assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));
  103. var properties = filter(copyProps, function(propertyName) {
  104. // copying event definitions, unless we replace
  105. if (propertyName === 'eventDefinitions') {
  106. return hasEventDefinition(element, target.eventDefinitionType);
  107. }
  108. // retain loop characteristics if the target element
  109. // is not an event sub process
  110. if (propertyName === 'loopCharacteristics') {
  111. return !isEventSubProcess(newBusinessObject);
  112. }
  113. // so the applied properties from 'target' don't get lost
  114. if (newBusinessObject.hasOwnProperty(propertyName)) {
  115. return false;
  116. }
  117. if (propertyName === 'processRef' && target.isExpanded === false) {
  118. return false;
  119. }
  120. if (propertyName === 'triggeredByEvent') {
  121. return false;
  122. }
  123. return true;
  124. });
  125. newBusinessObject = moddleCopy.copyElement(
  126. oldBusinessObject,
  127. newBusinessObject,
  128. properties
  129. );
  130. // initialize custom BPMN extensions
  131. if (target.eventDefinitionType) {
  132. // only initialize with new eventDefinition
  133. // if we did not set an event definition yet,
  134. // i.e. because we copied it
  135. if (!hasEventDefinition(newBusinessObject, target.eventDefinitionType)) {
  136. newElement.eventDefinitionType = target.eventDefinitionType;
  137. newElement.eventDefinitionAttrs = target.eventDefinitionAttrs;
  138. }
  139. }
  140. if (is(oldBusinessObject, 'bpmn:Activity')) {
  141. if (isSubProcess(oldBusinessObject)) {
  142. // no toggeling, so keep old state
  143. newElement.isExpanded = isExpanded(oldBusinessObject);
  144. }
  145. // else if property is explicitly set, use it
  146. else if (target && has(target, 'isExpanded')) {
  147. newElement.isExpanded = target.isExpanded;
  148. }
  149. // TODO: need also to respect min/max Size
  150. // copy size, from an expanded subprocess to an expanded alternative subprocess
  151. // except bpmn:Task, because Task is always expanded
  152. if ((isExpanded(oldBusinessObject) && !is(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) {
  153. newElement.width = element.width;
  154. newElement.height = element.height;
  155. }
  156. }
  157. // remove children if not expanding sub process
  158. if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) {
  159. hints.moveChildren = false;
  160. }
  161. // transform collapsed/expanded pools
  162. if (is(oldBusinessObject, 'bpmn:Participant')) {
  163. // create expanded pool
  164. if (target.isExpanded === true) {
  165. newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
  166. } else {
  167. // remove children when transforming to collapsed pool
  168. hints.moveChildren = false;
  169. }
  170. // apply same width and default height
  171. newElement.width = element.width;
  172. newElement.height = elementFactory._getDefaultSize(newBusinessObject).height;
  173. }
  174. newBusinessObject.name = oldBusinessObject.name;
  175. // retain default flow's reference between inclusive <-> exclusive gateways and activities
  176. if (
  177. isAny(oldBusinessObject, [
  178. 'bpmn:ExclusiveGateway',
  179. 'bpmn:InclusiveGateway',
  180. 'bpmn:Activity'
  181. ]) &&
  182. isAny(newBusinessObject, [
  183. 'bpmn:ExclusiveGateway',
  184. 'bpmn:InclusiveGateway',
  185. 'bpmn:Activity'
  186. ])
  187. ) {
  188. newBusinessObject.default = oldBusinessObject.default;
  189. }
  190. if (
  191. target.host &&
  192. !is(oldBusinessObject, 'bpmn:BoundaryEvent') &&
  193. is(newBusinessObject, 'bpmn:BoundaryEvent')
  194. ) {
  195. newElement.host = target.host;
  196. }
  197. newElement.di = {};
  198. // fill and stroke will be set to DI
  199. copyProperties(oldBusinessObject.di, newElement.di, [
  200. 'fill',
  201. 'stroke'
  202. ]);
  203. newElement = replace.replaceElement(element, newElement, hints);
  204. if (hints.select !== false) {
  205. selection.select(newElement);
  206. }
  207. return newElement;
  208. }
  209. this.replaceElement = replaceElement;
  210. }
  211. BpmnReplace.$inject = [
  212. 'bpmnFactory',
  213. 'elementFactory',
  214. 'moddleCopy',
  215. 'modeling',
  216. 'replace',
  217. 'selection'
  218. ];
  219. function isSubProcess(bo) {
  220. return is(bo, 'bpmn:SubProcess');
  221. }
  222. function hasEventDefinition(element, type) {
  223. var bo = getBusinessObject(element);
  224. return type && bo.get('eventDefinitions').some(function(definition) {
  225. return is(definition, type);
  226. });
  227. }
  228. /**
  229. * Compute intersection between two arrays.
  230. */
  231. function intersection(a1, a2) {
  232. return a1.filter(function(el) {
  233. return a2.indexOf(el) !== -1;
  234. });
  235. }