ReplaceConnectionBehavior.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {
  2. forEach,
  3. find,
  4. matchPattern
  5. } from 'min-dash';
  6. import inherits from 'inherits';
  7. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  8. import { is } from '../../../util/ModelUtil';
  9. export default function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) {
  10. CommandInterceptor.call(this, eventBus);
  11. var dragging = injector.get('dragging', false);
  12. function fixConnection(connection) {
  13. var source = connection.source,
  14. target = connection.target,
  15. parent = connection.parent;
  16. // do not do anything if connection
  17. // is already deleted (may happen due to other
  18. // behaviors plugged-in before)
  19. if (!parent) {
  20. return;
  21. }
  22. var replacementType,
  23. remove;
  24. /**
  25. * Check if incoming or outgoing connections
  26. * can stay or could be substituted with an
  27. * appropriate replacement.
  28. *
  29. * This holds true for SequenceFlow <> MessageFlow.
  30. */
  31. if (is(connection, 'bpmn:SequenceFlow')) {
  32. if (!bpmnRules.canConnectSequenceFlow(source, target)) {
  33. remove = true;
  34. }
  35. if (bpmnRules.canConnectMessageFlow(source, target)) {
  36. replacementType = 'bpmn:MessageFlow';
  37. }
  38. }
  39. // transform message flows into sequence flows, if possible
  40. if (is(connection, 'bpmn:MessageFlow')) {
  41. if (!bpmnRules.canConnectMessageFlow(source, target)) {
  42. remove = true;
  43. }
  44. if (bpmnRules.canConnectSequenceFlow(source, target)) {
  45. replacementType = 'bpmn:SequenceFlow';
  46. }
  47. }
  48. if (is(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
  49. remove = true;
  50. }
  51. // remove invalid connection,
  52. // unless it has been removed already
  53. if (remove) {
  54. modeling.removeConnection(connection);
  55. }
  56. // replace SequenceFlow <> MessageFlow
  57. if (replacementType) {
  58. modeling.connect(source, target, {
  59. type: replacementType,
  60. waypoints: connection.waypoints.slice()
  61. });
  62. }
  63. }
  64. function replaceReconnectedConnection(event) {
  65. var context = event.context,
  66. connection = context.connection,
  67. source = context.newSource || connection.source,
  68. target = context.newTarget || connection.target,
  69. allowed,
  70. replacement;
  71. allowed = bpmnRules.canConnect(source, target);
  72. if (!allowed || allowed.type === connection.type) {
  73. return;
  74. }
  75. replacement = modeling.connect(source, target, {
  76. type: allowed.type,
  77. waypoints: connection.waypoints.slice()
  78. });
  79. // remove old connection
  80. modeling.removeConnection(connection);
  81. // replace connection in context to reconnect end/start
  82. context.connection = replacement;
  83. if (dragging) {
  84. cleanDraggingSelection(connection, replacement);
  85. }
  86. }
  87. // monkey-patch selection saved in dragging in order to re-select it when operation is finished
  88. function cleanDraggingSelection(oldConnection, newConnection) {
  89. var context = dragging.context(),
  90. previousSelection = context && context.payload.previousSelection,
  91. index;
  92. // do nothing if not dragging or no selection was present
  93. if (!previousSelection || !previousSelection.length) {
  94. return;
  95. }
  96. index = previousSelection.indexOf(oldConnection);
  97. if (index === -1) {
  98. return;
  99. }
  100. previousSelection.splice(index, 1, newConnection);
  101. }
  102. // lifecycle hooks
  103. this.postExecuted('elements.move', function(context) {
  104. var closure = context.closure,
  105. allConnections = closure.allConnections;
  106. forEach(allConnections, fixConnection);
  107. }, true);
  108. this.preExecute('connection.reconnect', replaceReconnectedConnection);
  109. this.postExecuted('element.updateProperties', function(event) {
  110. var context = event.context,
  111. properties = context.properties,
  112. element = context.element,
  113. businessObject = element.businessObject,
  114. connection;
  115. // remove condition on change to default
  116. if (properties.default) {
  117. connection = find(
  118. element.outgoing,
  119. matchPattern({ id: element.businessObject.default.id })
  120. );
  121. if (connection) {
  122. modeling.updateProperties(connection, { conditionExpression: undefined });
  123. }
  124. }
  125. // remove default from source on change to conditional
  126. if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
  127. modeling.updateProperties(element.source, { default: undefined });
  128. }
  129. });
  130. }
  131. inherits(ReplaceConnectionBehavior, CommandInterceptor);
  132. ReplaceConnectionBehavior.$inject = [
  133. 'eventBus',
  134. 'modeling',
  135. 'bpmnRules',
  136. 'injector'
  137. ];