BpmnUpdater.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. import {
  2. assign,
  3. forEach
  4. } from 'min-dash';
  5. import inherits from 'inherits-browser';
  6. import {
  7. add as collectionAdd,
  8. remove as collectionRemove
  9. } from 'diagram-js/lib/util/Collections';
  10. import {
  11. getBusinessObject,
  12. getDi,
  13. is
  14. } from '../../util/ModelUtil';
  15. import { isAny } from './util/ModelingUtil';
  16. import {
  17. getLabel,
  18. isLabel,
  19. isLabelExternal
  20. } from '../../util/LabelUtil';
  21. import { delta } from 'diagram-js/lib/util/PositionUtil';
  22. import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
  23. /**
  24. * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
  25. * @typedef {import('./BpmnFactory').default} BpmnFactory
  26. * @typedef {import('diagram-js/lib/layout/CroppingConnectionDocking').default} CroppingConnectionDocking
  27. * @typedef {import('diagram-js/lib/i18n/translate/translate').default} Translate
  28. *
  29. * @typedef {import('../../model/Types').Connection} Connection
  30. * @typedef {import('../../model/Types').Element} Element
  31. * @typedef {import('../../model/Types').Shape} Shape
  32. * @typedef {import('../../model/Types').Parent} Parent
  33. * @typedef {import('../../model/Types').ModdleElement} ModdleElement
  34. */
  35. /**
  36. * A handler responsible for updating the underlying BPMN 2.0 XML & DI
  37. * once changes on the diagram happen.
  38. *
  39. * @param {EventBus} eventBus
  40. * @param {BpmnFactory} bpmnFactory
  41. * @param {CroppingConnectionDocking} connectionDocking
  42. * @param {Translate} translate
  43. */
  44. export default function BpmnUpdater(
  45. eventBus,
  46. bpmnFactory,
  47. connectionDocking,
  48. translate
  49. ) {
  50. CommandInterceptor.call(this, eventBus);
  51. this._bpmnFactory = bpmnFactory;
  52. this._translate = translate;
  53. var self = this;
  54. // connection cropping //////////////////////
  55. // crop connection ends during create/update
  56. function cropConnection(e) {
  57. var context = e.context,
  58. hints = context.hints || {},
  59. connection;
  60. if (!context.cropped && hints.createElementsBehavior !== false) {
  61. connection = context.connection;
  62. connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
  63. context.cropped = true;
  64. }
  65. }
  66. this.executed([
  67. 'connection.layout',
  68. 'connection.create'
  69. ], cropConnection);
  70. this.reverted([ 'connection.layout' ], function(e) {
  71. delete e.context.cropped;
  72. });
  73. // BPMN + DI update //////////////////////
  74. // update parent
  75. function updateParent(e) {
  76. var context = e.context;
  77. self.updateParent(context.shape || context.connection, context.oldParent);
  78. }
  79. function reverseUpdateParent(e) {
  80. var context = e.context;
  81. var element = context.shape || context.connection,
  82. // oldParent is the (old) new parent, because we are undoing
  83. oldParent = context.parent || context.newParent;
  84. self.updateParent(element, oldParent);
  85. }
  86. this.executed([
  87. 'shape.move',
  88. 'shape.create',
  89. 'shape.delete',
  90. 'connection.create',
  91. 'connection.move',
  92. 'connection.delete'
  93. ], ifBpmn(updateParent));
  94. this.reverted([
  95. 'shape.move',
  96. 'shape.create',
  97. 'shape.delete',
  98. 'connection.create',
  99. 'connection.move',
  100. 'connection.delete'
  101. ], ifBpmn(reverseUpdateParent));
  102. /*
  103. * ## Updating Parent
  104. *
  105. * When morphing a Process into a Collaboration or vice-versa,
  106. * make sure that both the *semantic* and *di* parent of each element
  107. * is updated.
  108. *
  109. */
  110. function updateRoot(event) {
  111. var context = event.context,
  112. oldRoot = context.oldRoot,
  113. children = oldRoot.children;
  114. forEach(children, function(child) {
  115. if (is(child, 'bpmn:BaseElement')) {
  116. self.updateParent(child);
  117. }
  118. });
  119. }
  120. this.executed([ 'canvas.updateRoot' ], updateRoot);
  121. this.reverted([ 'canvas.updateRoot' ], updateRoot);
  122. // update bounds
  123. function updateBounds(e) {
  124. var shape = e.context.shape;
  125. if (!is(shape, 'bpmn:BaseElement')) {
  126. return;
  127. }
  128. self.updateBounds(shape);
  129. }
  130. this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
  131. // exclude labels because they're handled separately during shape.changed
  132. if (event.context.shape.type === 'label') {
  133. return;
  134. }
  135. updateBounds(event);
  136. }));
  137. this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
  138. // exclude labels because they're handled separately during shape.changed
  139. if (event.context.shape.type === 'label') {
  140. return;
  141. }
  142. updateBounds(event);
  143. }));
  144. // Handle labels separately. This is necessary, because the label bounds have to be updated
  145. // every time its shape changes, not only on move, create and resize.
  146. eventBus.on('shape.changed', function(event) {
  147. if (event.element.type === 'label') {
  148. updateBounds({ context: { shape: event.element } });
  149. }
  150. });
  151. // attach / detach connection
  152. function updateConnection(e) {
  153. self.updateConnection(e.context);
  154. }
  155. this.executed([
  156. 'connection.create',
  157. 'connection.move',
  158. 'connection.delete',
  159. 'connection.reconnect'
  160. ], ifBpmn(updateConnection));
  161. this.reverted([
  162. 'connection.create',
  163. 'connection.move',
  164. 'connection.delete',
  165. 'connection.reconnect'
  166. ], ifBpmn(updateConnection));
  167. // update waypoints
  168. function updateConnectionWaypoints(e) {
  169. self.updateConnectionWaypoints(e.context.connection);
  170. }
  171. this.executed([
  172. 'connection.layout',
  173. 'connection.move',
  174. 'connection.updateWaypoints',
  175. ], ifBpmn(updateConnectionWaypoints));
  176. this.reverted([
  177. 'connection.layout',
  178. 'connection.move',
  179. 'connection.updateWaypoints',
  180. ], ifBpmn(updateConnectionWaypoints));
  181. // update conditional/default flows
  182. this.executed('connection.reconnect', ifBpmn(function(event) {
  183. var context = event.context,
  184. connection = context.connection,
  185. oldSource = context.oldSource,
  186. newSource = context.newSource,
  187. connectionBo = getBusinessObject(connection),
  188. oldSourceBo = getBusinessObject(oldSource),
  189. newSourceBo = getBusinessObject(newSource);
  190. // remove condition from connection on reconnect to new source
  191. // if new source can NOT have condional sequence flow
  192. if (connectionBo.conditionExpression && !isAny(newSourceBo, [
  193. 'bpmn:Activity',
  194. 'bpmn:ExclusiveGateway',
  195. 'bpmn:InclusiveGateway'
  196. ])) {
  197. context.oldConditionExpression = connectionBo.conditionExpression;
  198. delete connectionBo.conditionExpression;
  199. }
  200. // remove default from old source flow on reconnect to new source
  201. // if source changed
  202. if (oldSource !== newSource && oldSourceBo.default === connectionBo) {
  203. context.oldDefault = oldSourceBo.default;
  204. delete oldSourceBo.default;
  205. }
  206. }));
  207. this.reverted('connection.reconnect', ifBpmn(function(event) {
  208. var context = event.context,
  209. connection = context.connection,
  210. oldSource = context.oldSource,
  211. newSource = context.newSource,
  212. connectionBo = getBusinessObject(connection),
  213. oldSourceBo = getBusinessObject(oldSource),
  214. newSourceBo = getBusinessObject(newSource);
  215. // add condition to connection on revert reconnect to new source
  216. if (context.oldConditionExpression) {
  217. connectionBo.conditionExpression = context.oldConditionExpression;
  218. }
  219. // add default to old source on revert reconnect to new source
  220. if (context.oldDefault) {
  221. oldSourceBo.default = context.oldDefault;
  222. delete newSourceBo.default;
  223. }
  224. }));
  225. // update attachments
  226. function updateAttachment(e) {
  227. self.updateAttachment(e.context);
  228. }
  229. this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
  230. this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
  231. // update BPMNLabel
  232. this.executed('element.updateLabel', ifBpmn(updateBPMNLabel));
  233. this.reverted('element.updateLabel', ifBpmn(updateBPMNLabel));
  234. function updateBPMNLabel(event) {
  235. const { element } = event.context,
  236. label = getLabel(element);
  237. const di = getDi(element),
  238. diLabel = di && di.get('label');
  239. if (isLabelExternal(element)) {
  240. return;
  241. }
  242. if (label && !diLabel) {
  243. di.set('label', bpmnFactory.create('bpmndi:BPMNLabel'));
  244. } else if (!label && diLabel) {
  245. di.set('label', undefined);
  246. }
  247. }
  248. }
  249. inherits(BpmnUpdater, CommandInterceptor);
  250. BpmnUpdater.$inject = [
  251. 'eventBus',
  252. 'bpmnFactory',
  253. 'connectionDocking',
  254. 'translate'
  255. ];
  256. // implementation //////////////////////
  257. /**
  258. * @param { {
  259. * shape: Shape;
  260. * host: Shape;
  261. * } } context
  262. */
  263. BpmnUpdater.prototype.updateAttachment = function(context) {
  264. var shape = context.shape,
  265. businessObject = shape.businessObject,
  266. host = shape.host;
  267. businessObject.attachedToRef = host && host.businessObject;
  268. };
  269. /**
  270. * @param {Element} element
  271. * @param {Parent} oldParent
  272. */
  273. BpmnUpdater.prototype.updateParent = function(element, oldParent) {
  274. // do not update BPMN 2.0 label parent
  275. if (isLabel(element)) {
  276. return;
  277. }
  278. // data stores in collaborations are handled separately by DataStoreBehavior
  279. if (is(element, 'bpmn:DataStoreReference') &&
  280. element.parent &&
  281. is(element.parent, 'bpmn:Collaboration')) {
  282. return;
  283. }
  284. var parentShape = element.parent;
  285. var businessObject = element.businessObject,
  286. di = getDi(element),
  287. parentBusinessObject = parentShape && parentShape.businessObject,
  288. parentDi = getDi(parentShape);
  289. if (is(element, 'bpmn:FlowNode')) {
  290. this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject);
  291. }
  292. if (is(element, 'bpmn:DataOutputAssociation')) {
  293. if (element.source) {
  294. parentBusinessObject = element.source.businessObject;
  295. } else {
  296. parentBusinessObject = null;
  297. }
  298. }
  299. if (is(element, 'bpmn:DataInputAssociation')) {
  300. if (element.target) {
  301. parentBusinessObject = element.target.businessObject;
  302. } else {
  303. parentBusinessObject = null;
  304. }
  305. }
  306. this.updateSemanticParent(businessObject, parentBusinessObject);
  307. if (is(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) {
  308. this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject);
  309. }
  310. this.updateDiParent(di, parentDi);
  311. };
  312. /**
  313. * @param {Shape} shape
  314. */
  315. BpmnUpdater.prototype.updateBounds = function(shape) {
  316. var di = getDi(shape),
  317. embeddedLabelBounds = getEmbeddedLabelBounds(shape);
  318. // update embedded label bounds if possible
  319. if (embeddedLabelBounds) {
  320. var embeddedLabelBoundsDelta = delta(embeddedLabelBounds, di.get('bounds'));
  321. assign(embeddedLabelBounds, {
  322. x: shape.x + embeddedLabelBoundsDelta.x,
  323. y: shape.y + embeddedLabelBoundsDelta.y
  324. });
  325. }
  326. var target = isLabel(shape) ? this._getLabel(di) : di;
  327. var bounds = target.bounds;
  328. if (!bounds) {
  329. bounds = this._bpmnFactory.createDiBounds();
  330. target.set('bounds', bounds);
  331. }
  332. assign(bounds, {
  333. x: shape.x,
  334. y: shape.y,
  335. width: shape.width,
  336. height: shape.height
  337. });
  338. };
  339. /**
  340. * @param {ModdleElement} businessObject
  341. * @param {ModdleElement} newContainment
  342. * @param {ModdleElement} oldContainment
  343. */
  344. BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) {
  345. if (oldContainment === newContainment) {
  346. return;
  347. }
  348. var oldRefs, newRefs;
  349. if (is (oldContainment, 'bpmn:Lane')) {
  350. oldRefs = oldContainment.get('flowNodeRef');
  351. collectionRemove(oldRefs, businessObject);
  352. }
  353. if (is(newContainment, 'bpmn:Lane')) {
  354. newRefs = newContainment.get('flowNodeRef');
  355. collectionAdd(newRefs, businessObject);
  356. }
  357. };
  358. /**
  359. * @param {Connection} connection
  360. * @param {Element} newSource
  361. * @param {Element} newTarget
  362. */
  363. BpmnUpdater.prototype.updateDiConnection = function(connection, newSource, newTarget) {
  364. var connectionDi = getDi(connection),
  365. newSourceDi = getDi(newSource),
  366. newTargetDi = getDi(newTarget);
  367. if (connectionDi.sourceElement && connectionDi.sourceElement.bpmnElement !== getBusinessObject(newSource)) {
  368. connectionDi.sourceElement = newSource && newSourceDi;
  369. }
  370. if (connectionDi.targetElement && connectionDi.targetElement.bpmnElement !== getBusinessObject(newTarget)) {
  371. connectionDi.targetElement = newTarget && newTargetDi;
  372. }
  373. };
  374. /**
  375. * @param {ModdleElement} di
  376. * @param {ModdleElement} parentDi
  377. */
  378. BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {
  379. if (parentDi && !is(parentDi, 'bpmndi:BPMNPlane')) {
  380. parentDi = parentDi.$parent;
  381. }
  382. if (di.$parent === parentDi) {
  383. return;
  384. }
  385. var planeElements = (parentDi || di.$parent).get('planeElement');
  386. if (parentDi) {
  387. planeElements.push(di);
  388. di.$parent = parentDi;
  389. } else {
  390. collectionRemove(planeElements, di);
  391. di.$parent = null;
  392. }
  393. };
  394. /**
  395. * @param {ModdleElement} element
  396. *
  397. * @return {ModdleElement}
  398. */
  399. function getDefinitions(element) {
  400. while (element && !is(element, 'bpmn:Definitions')) {
  401. element = element.$parent;
  402. }
  403. return element;
  404. }
  405. /**
  406. * @param {ModdleElement} container
  407. *
  408. * @return {ModdleElement}
  409. */
  410. BpmnUpdater.prototype.getLaneSet = function(container) {
  411. var laneSet, laneSets;
  412. // bpmn:Lane
  413. if (is(container, 'bpmn:Lane')) {
  414. laneSet = container.childLaneSet;
  415. if (!laneSet) {
  416. laneSet = this._bpmnFactory.create('bpmn:LaneSet');
  417. container.childLaneSet = laneSet;
  418. laneSet.$parent = container;
  419. }
  420. return laneSet;
  421. }
  422. // bpmn:Participant
  423. if (is(container, 'bpmn:Participant')) {
  424. container = container.processRef;
  425. }
  426. // bpmn:FlowElementsContainer
  427. laneSets = container.get('laneSets');
  428. laneSet = laneSets[0];
  429. if (!laneSet) {
  430. laneSet = this._bpmnFactory.create('bpmn:LaneSet');
  431. laneSet.$parent = container;
  432. laneSets.push(laneSet);
  433. }
  434. return laneSet;
  435. };
  436. /**
  437. * @param {ModdleElement} businessObject
  438. * @param {ModdleElement} newParent
  439. * @param {ModdleElement} visualParent
  440. */
  441. BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) {
  442. var containment,
  443. translate = this._translate;
  444. if (businessObject.$parent === newParent) {
  445. return;
  446. }
  447. if (is(businessObject, 'bpmn:DataInput') || is(businessObject, 'bpmn:DataOutput')) {
  448. if (is(newParent, 'bpmn:Participant') && 'processRef' in newParent) {
  449. newParent = newParent.processRef;
  450. }
  451. // already in correct ioSpecification
  452. if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) {
  453. return;
  454. }
  455. }
  456. if (is(businessObject, 'bpmn:Lane')) {
  457. if (newParent) {
  458. newParent = this.getLaneSet(newParent);
  459. }
  460. containment = 'lanes';
  461. } else
  462. if (is(businessObject, 'bpmn:FlowElement')) {
  463. if (newParent) {
  464. if (is(newParent, 'bpmn:Participant')) {
  465. newParent = newParent.processRef;
  466. } else
  467. if (is(newParent, 'bpmn:Lane')) {
  468. do {
  469. // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
  470. newParent = newParent.$parent.$parent;
  471. } while (is(newParent, 'bpmn:Lane'));
  472. }
  473. }
  474. containment = 'flowElements';
  475. } else
  476. if (is(businessObject, 'bpmn:Artifact')) {
  477. while (newParent &&
  478. !is(newParent, 'bpmn:Process') &&
  479. !is(newParent, 'bpmn:SubProcess') &&
  480. !is(newParent, 'bpmn:Collaboration')) {
  481. if (is(newParent, 'bpmn:Participant')) {
  482. newParent = newParent.processRef;
  483. break;
  484. } else {
  485. newParent = newParent.$parent;
  486. }
  487. }
  488. containment = 'artifacts';
  489. } else
  490. if (is(businessObject, 'bpmn:MessageFlow')) {
  491. containment = 'messageFlows';
  492. } else
  493. if (is(businessObject, 'bpmn:Participant')) {
  494. containment = 'participants';
  495. // make sure the participants process is properly attached / detached
  496. // from the XML document
  497. var process = businessObject.processRef,
  498. definitions;
  499. if (process) {
  500. definitions = getDefinitions(businessObject.$parent || newParent);
  501. if (businessObject.$parent) {
  502. collectionRemove(definitions.get('rootElements'), process);
  503. process.$parent = null;
  504. }
  505. if (newParent) {
  506. collectionAdd(definitions.get('rootElements'), process);
  507. process.$parent = definitions;
  508. }
  509. }
  510. } else
  511. if (is(businessObject, 'bpmn:DataOutputAssociation')) {
  512. containment = 'dataOutputAssociations';
  513. } else
  514. if (is(businessObject, 'bpmn:DataInputAssociation')) {
  515. containment = 'dataInputAssociations';
  516. }
  517. if (!containment) {
  518. throw new Error(translate(
  519. 'no parent for {element} in {parent}',
  520. {
  521. element: businessObject.id,
  522. parent: newParent.id
  523. }
  524. ));
  525. }
  526. var children;
  527. if (businessObject.$parent) {
  528. // remove from old parent
  529. children = businessObject.$parent.get(containment);
  530. collectionRemove(children, businessObject);
  531. }
  532. if (!newParent) {
  533. businessObject.$parent = null;
  534. } else {
  535. // add to new parent
  536. children = newParent.get(containment);
  537. children.push(businessObject);
  538. businessObject.$parent = newParent;
  539. }
  540. if (visualParent) {
  541. var diChildren = visualParent.get(containment);
  542. collectionRemove(children, businessObject);
  543. if (newParent) {
  544. if (!diChildren) {
  545. diChildren = [];
  546. newParent.set(containment, diChildren);
  547. }
  548. diChildren.push(businessObject);
  549. }
  550. }
  551. };
  552. /**
  553. * @param {Connection} connection
  554. */
  555. BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
  556. var di = getDi(connection);
  557. di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints));
  558. };
  559. /**
  560. * @param { {
  561. * connection: Connection;
  562. * parent: Parent;
  563. * newParent: Parent;
  564. * } } context
  565. */
  566. BpmnUpdater.prototype.updateConnection = function(context) {
  567. var connection = context.connection,
  568. businessObject = getBusinessObject(connection),
  569. newSource = connection.source,
  570. newSourceBo = getBusinessObject(newSource),
  571. newTarget = connection.target,
  572. newTargetBo = getBusinessObject(connection.target),
  573. visualParent;
  574. if (!is(businessObject, 'bpmn:DataAssociation')) {
  575. var inverseSet = is(businessObject, 'bpmn:SequenceFlow');
  576. if (businessObject.sourceRef !== newSourceBo) {
  577. if (inverseSet) {
  578. collectionRemove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);
  579. if (newSourceBo && newSourceBo.get('outgoing')) {
  580. newSourceBo.get('outgoing').push(businessObject);
  581. }
  582. }
  583. businessObject.sourceRef = newSourceBo;
  584. }
  585. if (businessObject.targetRef !== newTargetBo) {
  586. if (inverseSet) {
  587. collectionRemove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);
  588. if (newTargetBo && newTargetBo.get('incoming')) {
  589. newTargetBo.get('incoming').push(businessObject);
  590. }
  591. }
  592. businessObject.targetRef = newTargetBo;
  593. }
  594. } else
  595. if (is(businessObject, 'bpmn:DataInputAssociation')) {
  596. // handle obnoxious isMsome sourceRef
  597. businessObject.get('sourceRef')[0] = newSourceBo;
  598. visualParent = context.parent || context.newParent || newTargetBo;
  599. this.updateSemanticParent(businessObject, newTargetBo, visualParent);
  600. } else
  601. if (is(businessObject, 'bpmn:DataOutputAssociation')) {
  602. visualParent = context.parent || context.newParent || newSourceBo;
  603. this.updateSemanticParent(businessObject, newSourceBo, visualParent);
  604. // targetRef = new target
  605. businessObject.targetRef = newTargetBo;
  606. }
  607. this.updateConnectionWaypoints(connection);
  608. this.updateDiConnection(connection, newSource, newTarget);
  609. };
  610. // helpers //////////////////////
  611. BpmnUpdater.prototype._getLabel = function(di) {
  612. if (!di.label) {
  613. di.label = this._bpmnFactory.createDiLabel();
  614. }
  615. return di.label;
  616. };
  617. /**
  618. * Call function if shape or connection is BPMN element.
  619. *
  620. * @param {Function} fn
  621. *
  622. * @return {Function}
  623. */
  624. function ifBpmn(fn) {
  625. return function(event) {
  626. var context = event.context,
  627. element = context.shape || context.connection || context.element;
  628. if (is(element, 'bpmn:BaseElement')) {
  629. fn(event);
  630. }
  631. };
  632. }
  633. /**
  634. * Return dc:Bounds of bpmndi:BPMNLabel if exists.
  635. *
  636. * @param {Shape} shape
  637. *
  638. * @return {ModdleElement|undefined}
  639. */
  640. function getEmbeddedLabelBounds(shape) {
  641. if (!is(shape, 'bpmn:Activity')) {
  642. return;
  643. }
  644. var di = getDi(shape);
  645. if (!di) {
  646. return;
  647. }
  648. var label = di.get('label');
  649. if (!label) {
  650. return;
  651. }
  652. return label.get('bounds');
  653. }