BpmnRules.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  1. import {
  2. every,
  3. find,
  4. forEach,
  5. some
  6. } from 'min-dash';
  7. import inherits from 'inherits';
  8. import {
  9. is,
  10. getBusinessObject
  11. } from '../../util/ModelUtil';
  12. import {
  13. getParent,
  14. isAny
  15. } from '../modeling/util/ModelingUtil';
  16. import {
  17. isLabel
  18. } from '../../util/LabelUtil';
  19. import {
  20. isExpanded,
  21. isEventSubProcess,
  22. isInterrupting,
  23. hasErrorEventDefinition,
  24. hasEscalationEventDefinition,
  25. hasCompensateEventDefinition
  26. } from '../../util/DiUtil';
  27. import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider';
  28. import {
  29. getBoundaryAttachment as isBoundaryAttachment
  30. } from '../snapping/BpmnSnappingUtil';
  31. /**
  32. * BPMN specific modeling rule
  33. */
  34. export default function BpmnRules(eventBus) {
  35. RuleProvider.call(this, eventBus);
  36. }
  37. inherits(BpmnRules, RuleProvider);
  38. BpmnRules.$inject = [ 'eventBus' ];
  39. BpmnRules.prototype.init = function() {
  40. this.addRule('connection.start', function(context) {
  41. var source = context.source;
  42. return canStartConnection(source);
  43. });
  44. this.addRule('connection.create', function(context) {
  45. var source = context.source,
  46. target = context.target,
  47. hints = context.hints || {},
  48. targetParent = hints.targetParent,
  49. targetAttach = hints.targetAttach;
  50. // don't allow incoming connections on
  51. // newly created boundary events
  52. // to boundary events
  53. if (targetAttach) {
  54. return false;
  55. }
  56. // temporarily set target parent for scoping
  57. // checks to work
  58. if (targetParent) {
  59. target.parent = targetParent;
  60. }
  61. try {
  62. return canConnect(source, target);
  63. } finally {
  64. // unset temporary target parent
  65. if (targetParent) {
  66. target.parent = null;
  67. }
  68. }
  69. });
  70. this.addRule('connection.reconnect', function(context) {
  71. var connection = context.connection,
  72. source = context.source,
  73. target = context.target;
  74. return canConnect(source, target, connection);
  75. });
  76. this.addRule('connection.updateWaypoints', function(context) {
  77. return {
  78. type: context.connection.type
  79. };
  80. });
  81. this.addRule('shape.resize', function(context) {
  82. var shape = context.shape,
  83. newBounds = context.newBounds;
  84. return canResize(shape, newBounds);
  85. });
  86. this.addRule('elements.create', function(context) {
  87. var elements = context.elements,
  88. position = context.position,
  89. target = context.target;
  90. return every(elements, function(element) {
  91. if (isConnection(element)) {
  92. return canConnect(element.source, element.target, element);
  93. }
  94. if (element.host) {
  95. return canAttach(element, element.host, null, position);
  96. }
  97. return canCreate(element, target, null, position);
  98. });
  99. });
  100. this.addRule('elements.move', function(context) {
  101. var target = context.target,
  102. shapes = context.shapes,
  103. position = context.position;
  104. return canAttach(shapes, target, null, position) ||
  105. canReplace(shapes, target, position) ||
  106. canMove(shapes, target, position) ||
  107. canInsert(shapes, target, position);
  108. });
  109. this.addRule('shape.create', function(context) {
  110. return canCreate(
  111. context.shape,
  112. context.target,
  113. context.source,
  114. context.position
  115. );
  116. });
  117. this.addRule('shape.attach', function(context) {
  118. return canAttach(
  119. context.shape,
  120. context.target,
  121. null,
  122. context.position
  123. );
  124. });
  125. this.addRule('element.copy', function(context) {
  126. var element = context.element,
  127. elements = context.elements;
  128. return canCopy(elements, element);
  129. });
  130. };
  131. BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
  132. BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
  133. BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
  134. BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
  135. BpmnRules.prototype.canMove = canMove;
  136. BpmnRules.prototype.canAttach = canAttach;
  137. BpmnRules.prototype.canReplace = canReplace;
  138. BpmnRules.prototype.canDrop = canDrop;
  139. BpmnRules.prototype.canInsert = canInsert;
  140. BpmnRules.prototype.canCreate = canCreate;
  141. BpmnRules.prototype.canConnect = canConnect;
  142. BpmnRules.prototype.canResize = canResize;
  143. BpmnRules.prototype.canCopy = canCopy;
  144. /**
  145. * Utility functions for rule checking
  146. */
  147. /**
  148. * Checks if given element can be used for starting connection.
  149. *
  150. * @param {Element} source
  151. * @return {boolean}
  152. */
  153. function canStartConnection(element) {
  154. if (nonExistingOrLabel(element)) {
  155. return null;
  156. }
  157. return isAny(element, [
  158. 'bpmn:FlowNode',
  159. 'bpmn:InteractionNode',
  160. 'bpmn:DataObjectReference',
  161. 'bpmn:DataStoreReference'
  162. ]);
  163. }
  164. function nonExistingOrLabel(element) {
  165. return !element || isLabel(element);
  166. }
  167. function isSame(a, b) {
  168. return a === b;
  169. }
  170. function getOrganizationalParent(element) {
  171. do {
  172. if (is(element, 'bpmn:Process')) {
  173. return getBusinessObject(element);
  174. }
  175. if (is(element, 'bpmn:Participant')) {
  176. return (
  177. getBusinessObject(element).processRef ||
  178. getBusinessObject(element)
  179. );
  180. }
  181. } while ((element = element.parent));
  182. }
  183. function isTextAnnotation(element) {
  184. return is(element, 'bpmn:TextAnnotation');
  185. }
  186. function isGroup(element) {
  187. return is(element, 'bpmn:Group') && !element.labelTarget;
  188. }
  189. function isCompensationBoundary(element) {
  190. return is(element, 'bpmn:BoundaryEvent') &&
  191. hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
  192. }
  193. function isForCompensation(e) {
  194. return getBusinessObject(e).isForCompensation;
  195. }
  196. function isSameOrganization(a, b) {
  197. var parentA = getOrganizationalParent(a),
  198. parentB = getOrganizationalParent(b);
  199. return parentA === parentB;
  200. }
  201. function isMessageFlowSource(element) {
  202. return (
  203. is(element, 'bpmn:InteractionNode') && (
  204. !is(element, 'bpmn:Event') || (
  205. is(element, 'bpmn:ThrowEvent') &&
  206. hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
  207. )
  208. )
  209. );
  210. }
  211. function isMessageFlowTarget(element) {
  212. return (
  213. is(element, 'bpmn:InteractionNode') &&
  214. !isForCompensation(element) && (
  215. !is(element, 'bpmn:Event') || (
  216. is(element, 'bpmn:CatchEvent') &&
  217. hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
  218. )
  219. )
  220. );
  221. }
  222. function getScopeParent(element) {
  223. var parent = element;
  224. while ((parent = parent.parent)) {
  225. if (is(parent, 'bpmn:FlowElementsContainer')) {
  226. return getBusinessObject(parent);
  227. }
  228. if (is(parent, 'bpmn:Participant')) {
  229. return getBusinessObject(parent).processRef;
  230. }
  231. }
  232. return null;
  233. }
  234. function isSameScope(a, b) {
  235. var scopeParentA = getScopeParent(a),
  236. scopeParentB = getScopeParent(b);
  237. return scopeParentA === scopeParentB;
  238. }
  239. function hasEventDefinition(element, eventDefinition) {
  240. var bo = getBusinessObject(element);
  241. return !!find(bo.eventDefinitions || [], function(definition) {
  242. return is(definition, eventDefinition);
  243. });
  244. }
  245. function hasEventDefinitionOrNone(element, eventDefinition) {
  246. var bo = getBusinessObject(element);
  247. return (bo.eventDefinitions || []).every(function(definition) {
  248. return is(definition, eventDefinition);
  249. });
  250. }
  251. function isSequenceFlowSource(element) {
  252. return (
  253. is(element, 'bpmn:FlowNode') &&
  254. !is(element, 'bpmn:EndEvent') &&
  255. !isEventSubProcess(element) &&
  256. !(is(element, 'bpmn:IntermediateThrowEvent') &&
  257. hasEventDefinition(element, 'bpmn:LinkEventDefinition')
  258. ) &&
  259. !isCompensationBoundary(element) &&
  260. !isForCompensation(element)
  261. );
  262. }
  263. function isSequenceFlowTarget(element) {
  264. return (
  265. is(element, 'bpmn:FlowNode') &&
  266. !is(element, 'bpmn:StartEvent') &&
  267. !is(element, 'bpmn:BoundaryEvent') &&
  268. !isEventSubProcess(element) &&
  269. !(is(element, 'bpmn:IntermediateCatchEvent') &&
  270. hasEventDefinition(element, 'bpmn:LinkEventDefinition')
  271. ) &&
  272. !isForCompensation(element)
  273. );
  274. }
  275. function isEventBasedTarget(element) {
  276. return (
  277. is(element, 'bpmn:ReceiveTask') || (
  278. is(element, 'bpmn:IntermediateCatchEvent') && (
  279. hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
  280. hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
  281. hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
  282. hasEventDefinition(element, 'bpmn:SignalEventDefinition')
  283. )
  284. )
  285. );
  286. }
  287. function isConnection(element) {
  288. return element.waypoints;
  289. }
  290. function getParents(element) {
  291. var parents = [];
  292. while (element) {
  293. element = element.parent;
  294. if (element) {
  295. parents.push(element);
  296. }
  297. }
  298. return parents;
  299. }
  300. function isParent(possibleParent, element) {
  301. var allParents = getParents(element);
  302. return allParents.indexOf(possibleParent) !== -1;
  303. }
  304. function canConnect(source, target, connection) {
  305. if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) {
  306. return null;
  307. }
  308. if (!is(connection, 'bpmn:DataAssociation')) {
  309. if (canConnectMessageFlow(source, target)) {
  310. return { type: 'bpmn:MessageFlow' };
  311. }
  312. if (canConnectSequenceFlow(source, target)) {
  313. return { type: 'bpmn:SequenceFlow' };
  314. }
  315. }
  316. var connectDataAssociation = canConnectDataAssociation(source, target);
  317. if (connectDataAssociation) {
  318. return connectDataAssociation;
  319. }
  320. if (isCompensationBoundary(source) && isForCompensation(target)) {
  321. return {
  322. type: 'bpmn:Association',
  323. associationDirection: 'One'
  324. };
  325. }
  326. if (canConnectAssociation(source, target)) {
  327. return {
  328. type: 'bpmn:Association'
  329. };
  330. }
  331. return false;
  332. }
  333. /**
  334. * Can an element be dropped into the target element
  335. *
  336. * @return {boolean}
  337. */
  338. function canDrop(element, target, position) {
  339. // can move labels and groups everywhere
  340. if (isLabel(element) || isGroup(element)) {
  341. return true;
  342. }
  343. // disallow to create elements on collapsed pools
  344. if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
  345. return false;
  346. }
  347. // allow to create new participants on
  348. // existing collaboration and process diagrams
  349. if (is(element, 'bpmn:Participant')) {
  350. return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration');
  351. }
  352. // allow moving DataInput / DataOutput within its original container only
  353. if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) {
  354. if (element.parent) {
  355. return target === element.parent;
  356. }
  357. }
  358. // allow creating lanes on participants and other lanes only
  359. if (is(element, 'bpmn:Lane')) {
  360. return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane');
  361. }
  362. // disallow dropping boundary events which cannot replace with intermediate event
  363. if (is(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) {
  364. return false;
  365. }
  366. // drop flow elements onto flow element containers
  367. // and participants
  368. if (is(element, 'bpmn:FlowElement') && !is(element, 'bpmn:DataStoreReference')) {
  369. if (is(target, 'bpmn:FlowElementsContainer')) {
  370. return isExpanded(target);
  371. }
  372. return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
  373. }
  374. // account for the fact that data associations are always
  375. // rendered and moved to top (Process or Collaboration level)
  376. //
  377. // artifacts may be placed wherever, too
  378. if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) {
  379. return isAny(target, [
  380. 'bpmn:Collaboration',
  381. 'bpmn:Lane',
  382. 'bpmn:Participant',
  383. 'bpmn:Process',
  384. 'bpmn:SubProcess' ]);
  385. }
  386. if (is(element, 'bpmn:MessageFlow')) {
  387. return is(target, 'bpmn:Collaboration')
  388. || element.source.parent == target
  389. || element.target.parent == target;
  390. }
  391. return false;
  392. }
  393. function isDroppableBoundaryEvent(event) {
  394. return getBusinessObject(event).cancelActivity && (
  395. hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event)
  396. );
  397. }
  398. function isBoundaryEvent(element) {
  399. return !isLabel(element) && is(element, 'bpmn:BoundaryEvent');
  400. }
  401. function isLane(element) {
  402. return is(element, 'bpmn:Lane');
  403. }
  404. /**
  405. * We treat IntermediateThrowEvents as boundary events during create,
  406. * this must be reflected in the rules.
  407. */
  408. function isBoundaryCandidate(element) {
  409. if (isBoundaryEvent(element)) {
  410. return true;
  411. }
  412. if (is(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) {
  413. return true;
  414. }
  415. return (
  416. is(element, 'bpmn:IntermediateCatchEvent') &&
  417. hasCommonBoundaryIntermediateEventDefinition(element)
  418. );
  419. }
  420. function hasNoEventDefinition(element) {
  421. var bo = getBusinessObject(element);
  422. return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
  423. }
  424. function hasCommonBoundaryIntermediateEventDefinition(element) {
  425. return hasOneOfEventDefinitions(element, [
  426. 'bpmn:MessageEventDefinition',
  427. 'bpmn:TimerEventDefinition',
  428. 'bpmn:SignalEventDefinition',
  429. 'bpmn:ConditionalEventDefinition'
  430. ]);
  431. }
  432. function hasOneOfEventDefinitions(element, eventDefinitions) {
  433. return eventDefinitions.some(function(definition) {
  434. return hasEventDefinition(element, definition);
  435. });
  436. }
  437. function isReceiveTaskAfterEventBasedGateway(element) {
  438. return (
  439. is(element, 'bpmn:ReceiveTask') &&
  440. find(element.incoming, function(incoming) {
  441. return is(incoming.source, 'bpmn:EventBasedGateway');
  442. })
  443. );
  444. }
  445. function canAttach(elements, target, source, position) {
  446. if (!Array.isArray(elements)) {
  447. elements = [ elements ];
  448. }
  449. // only (re-)attach one element at a time
  450. if (elements.length !== 1) {
  451. return false;
  452. }
  453. var element = elements[0];
  454. // do not attach labels
  455. if (isLabel(element)) {
  456. return false;
  457. }
  458. // only handle boundary events
  459. if (!isBoundaryCandidate(element)) {
  460. return false;
  461. }
  462. // disallow drop on event sub processes
  463. if (isEventSubProcess(target)) {
  464. return false;
  465. }
  466. // only allow drop on non compensation activities
  467. if (!is(target, 'bpmn:Activity') || isForCompensation(target)) {
  468. return false;
  469. }
  470. // only attach to subprocess border
  471. if (position && !isBoundaryAttachment(position, target)) {
  472. return false;
  473. }
  474. // do not attach on receive tasks after event based gateways
  475. if (isReceiveTaskAfterEventBasedGateway(target)) {
  476. return false;
  477. }
  478. return 'attach';
  479. }
  480. /**
  481. * Defines how to replace elements for a given target.
  482. *
  483. * Returns an array containing all elements which will be replaced.
  484. *
  485. * @example
  486. *
  487. * [{ id: 'IntermediateEvent_2',
  488. * type: 'bpmn:StartEvent'
  489. * },
  490. * { id: 'IntermediateEvent_5',
  491. * type: 'bpmn:EndEvent'
  492. * }]
  493. *
  494. * @param {Array} elements
  495. * @param {Object} target
  496. *
  497. * @return {Object} an object containing all elements which have to be replaced
  498. */
  499. function canReplace(elements, target, position) {
  500. if (!target) {
  501. return false;
  502. }
  503. var canExecute = {
  504. replacements: []
  505. };
  506. forEach(elements, function(element) {
  507. if (!isEventSubProcess(target)) {
  508. if (is(element, 'bpmn:StartEvent') &&
  509. element.type !== 'label' &&
  510. canDrop(element, target)) {
  511. // replace a non-interrupting start event by a blank interrupting start event
  512. // when the target is not an event sub process
  513. if (!isInterrupting(element)) {
  514. canExecute.replacements.push({
  515. oldElementId: element.id,
  516. newElementType: 'bpmn:StartEvent'
  517. });
  518. }
  519. // replace an error/escalation/compensate start event by a blank interrupting start event
  520. // when the target is not an event sub process
  521. if (hasErrorEventDefinition(element) ||
  522. hasEscalationEventDefinition(element) ||
  523. hasCompensateEventDefinition(element)) {
  524. canExecute.replacements.push({
  525. oldElementId: element.id,
  526. newElementType: 'bpmn:StartEvent'
  527. });
  528. }
  529. }
  530. }
  531. if (!is(target, 'bpmn:Transaction')) {
  532. if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') &&
  533. element.type !== 'label') {
  534. if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) {
  535. canExecute.replacements.push({
  536. oldElementId: element.id,
  537. newElementType: 'bpmn:EndEvent'
  538. });
  539. }
  540. if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
  541. canExecute.replacements.push({
  542. oldElementId: element.id,
  543. newElementType: 'bpmn:BoundaryEvent'
  544. });
  545. }
  546. }
  547. }
  548. });
  549. return canExecute.replacements.length ? canExecute : false;
  550. }
  551. function canMove(elements, target) {
  552. // do not move selection containing lanes
  553. if (some(elements, isLane)) {
  554. return false;
  555. }
  556. // allow default move check to start move operation
  557. if (!target) {
  558. return true;
  559. }
  560. return elements.every(function(element) {
  561. return canDrop(element, target);
  562. });
  563. }
  564. function canCreate(shape, target, source, position) {
  565. if (!target) {
  566. return false;
  567. }
  568. if (isLabel(shape) || isGroup(shape)) {
  569. return true;
  570. }
  571. if (isSame(source, target)) {
  572. return false;
  573. }
  574. // ensure we do not drop the element
  575. // into source
  576. if (source && isParent(source, target)) {
  577. return false;
  578. }
  579. return canDrop(shape, target, position) || canInsert(shape, target, position);
  580. }
  581. function canResize(shape, newBounds) {
  582. if (is(shape, 'bpmn:SubProcess')) {
  583. return (
  584. isExpanded(shape) && (
  585. !newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
  586. )
  587. );
  588. }
  589. if (is(shape, 'bpmn:Lane')) {
  590. return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
  591. }
  592. if (is(shape, 'bpmn:Participant')) {
  593. return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
  594. }
  595. if (isTextAnnotation(shape)) {
  596. return true;
  597. }
  598. if (isGroup(shape)) {
  599. return true;
  600. }
  601. return false;
  602. }
  603. /**
  604. * Check, whether one side of the relationship
  605. * is a text annotation.
  606. */
  607. function isOneTextAnnotation(source, target) {
  608. var sourceTextAnnotation = isTextAnnotation(source),
  609. targetTextAnnotation = isTextAnnotation(target);
  610. return (
  611. (sourceTextAnnotation || targetTextAnnotation) &&
  612. (sourceTextAnnotation !== targetTextAnnotation)
  613. );
  614. }
  615. function canConnectAssociation(source, target) {
  616. // do not connect connections
  617. if (isConnection(source) || isConnection(target)) {
  618. return false;
  619. }
  620. // compensation boundary events are exception
  621. if (isCompensationBoundary(source) && isForCompensation(target)) {
  622. return true;
  623. }
  624. // don't connect parent <-> child
  625. if (isParent(target, source) || isParent(source, target)) {
  626. return false;
  627. }
  628. // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
  629. if (isOneTextAnnotation(source, target)) {
  630. return true;
  631. }
  632. // can connect associations where we can connect
  633. // data associations, too (!)
  634. return !!canConnectDataAssociation(source, target);
  635. }
  636. function canConnectMessageFlow(source, target) {
  637. // during connect user might move mouse out of canvas
  638. // https://github.com/bpmn-io/bpmn-js/issues/1033
  639. if (getRootElement(source) && !getRootElement(target)) {
  640. return false;
  641. }
  642. return (
  643. isMessageFlowSource(source) &&
  644. isMessageFlowTarget(target) &&
  645. !isSameOrganization(source, target)
  646. );
  647. }
  648. function canConnectSequenceFlow(source, target) {
  649. if (
  650. isEventBasedTarget(target) &&
  651. target.incoming.length > 0 &&
  652. areOutgoingEventBasedGatewayConnections(target.incoming) &&
  653. !is(source, 'bpmn:EventBasedGateway')
  654. ) {
  655. return false;
  656. }
  657. return isSequenceFlowSource(source) &&
  658. isSequenceFlowTarget(target) &&
  659. isSameScope(source, target) &&
  660. !(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
  661. }
  662. function canConnectDataAssociation(source, target) {
  663. if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
  664. isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
  665. return { type: 'bpmn:DataInputAssociation' };
  666. }
  667. if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
  668. isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
  669. return { type: 'bpmn:DataOutputAssociation' };
  670. }
  671. return false;
  672. }
  673. function canInsert(shape, flow, position) {
  674. if (!flow) {
  675. return false;
  676. }
  677. if (Array.isArray(shape)) {
  678. if (shape.length !== 1) {
  679. return false;
  680. }
  681. shape = shape[0];
  682. }
  683. if (flow.source === shape ||
  684. flow.target === shape) {
  685. return false;
  686. }
  687. // return true if we can drop on the
  688. // underlying flow parent
  689. //
  690. // at this point we are not really able to talk
  691. // about connection rules (yet)
  692. return (
  693. isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
  694. !isLabel(flow) &&
  695. is(shape, 'bpmn:FlowNode') &&
  696. !is(shape, 'bpmn:BoundaryEvent') &&
  697. canDrop(shape, flow.parent, position));
  698. }
  699. function includes(elements, element) {
  700. return (elements && element) && elements.indexOf(element) !== -1;
  701. }
  702. function canCopy(elements, element) {
  703. if (isLabel(element)) {
  704. return true;
  705. }
  706. if (is(element, 'bpmn:Lane') && !includes(elements, element.parent)) {
  707. return false;
  708. }
  709. return true;
  710. }
  711. function isOutgoingEventBasedGatewayConnection(connection) {
  712. if (connection && connection.source) {
  713. return is(connection.source, 'bpmn:EventBasedGateway');
  714. }
  715. }
  716. function areOutgoingEventBasedGatewayConnections(connections) {
  717. connections = connections || [];
  718. return connections.some(isOutgoingEventBasedGatewayConnection);
  719. }
  720. function getRootElement(element) {
  721. return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
  722. }