bpmn-js 0.20.7 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1551,7 +1551,7 @@ function BpmnRenderer(eventBus, styles, pathMap, canvas, priority) {
1551
1551
  });
1552
1552
 
1553
1553
  var text = getSemantic(element).text || '';
1554
- renderLabel(parentGfx, text, { box: element, align: 'left-middle', padding: 5 });
1554
+ renderLabel(parentGfx, text, { box: element, align: 'left-top', padding: 5 });
1555
1555
 
1556
1556
  return textElement;
1557
1557
  },
@@ -37,17 +37,9 @@ function BpmnCopyPaste(
37
37
  var helper = new ModelCloneHelper(eventBus);
38
38
 
39
39
  copyPaste.registerDescriptor(function(element, descriptor) {
40
- var businessObject = getBusinessObject(element),
41
- newBusinessObject = bpmnFactory.create(businessObject.$type);
40
+ var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);
42
41
 
43
- var properties = getProperties(businessObject.$descriptor),
44
- colors = {};
45
-
46
- properties = filter(properties, function(property) {
47
- return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
48
- });
49
-
50
- descriptor.businessObject = helper.clone(businessObject, newBusinessObject, properties);
42
+ var colors = {};
51
43
 
52
44
  descriptor.type = element.type;
53
45
 
@@ -79,11 +71,22 @@ function BpmnCopyPaste(
79
71
  createdElements = context.createdElements,
80
72
  parent = descriptor.parent,
81
73
  rootElement = canvas.getRootElement(),
82
- businessObject,
74
+ oldBusinessObject = descriptor.oldBusinessObject,
75
+ newBusinessObject,
83
76
  source,
84
77
  target,
85
78
  canConnect;
86
79
 
80
+ newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);
81
+
82
+ var properties = getProperties(oldBusinessObject.$descriptor);
83
+
84
+ properties = filter(properties, function(property) {
85
+ return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
86
+ });
87
+
88
+ descriptor.businessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);
89
+
87
90
  if (descriptor.type === 'label') {
88
91
  return;
89
92
  }
@@ -119,19 +122,17 @@ function BpmnCopyPaste(
119
122
  }
120
123
  }
121
124
 
122
- businessObject = descriptor.businessObject;
123
-
124
125
  // remove the id or else we cannot paste multiple times
125
- delete businessObject.id;
126
+ delete newBusinessObject.id;
126
127
 
127
128
  // assign an ID
128
- bpmnFactory._ensureId(businessObject);
129
+ bpmnFactory._ensureId(newBusinessObject);
129
130
 
130
131
  if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
131
- descriptor.processRef = businessObject.processRef = bpmnFactory.create('bpmn:Process');
132
+ descriptor.processRef = newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
132
133
  }
133
134
 
134
- setProperties(businessObject, descriptor, [
135
+ setProperties(newBusinessObject, descriptor, [
135
136
  'isExpanded',
136
137
  'triggeredByEvent'
137
138
  ]);
@@ -141,13 +142,6 @@ function BpmnCopyPaste(
141
142
  ]);
142
143
  });
143
144
 
144
-
145
- eventBus.on('commandStack.elements.paste.postExecuted', function() {
146
- // temporarily disable multi paste until #686
147
- // is addressed
148
- clipboard.clear();
149
- });
150
-
151
145
  }
152
146
 
153
147
 
@@ -0,0 +1,122 @@
1
+ var svgAppend = require('tiny-svg/lib/append'),
2
+ svgAttr = require('tiny-svg/lib/attr'),
3
+ svgCreate = require('tiny-svg/lib/create'),
4
+ svgRemove = require('tiny-svg/lib/remove');
5
+
6
+ var getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
7
+ is = require('../../util/ModelUtil').is;
8
+
9
+ var translate = require('diagram-js/lib/util/SvgTransformUtil').translate;
10
+
11
+ var MARKER_HIDDEN = 'djs-element-hidden',
12
+ MARKER_LABEL_HIDDEN = 'djs-label-hidden';
13
+
14
+ function getStrokeColor(element, defaultColor) {
15
+ var bo = getBusinessObject(element);
16
+
17
+ return bo.di.get('stroke') || defaultColor || 'black';
18
+ }
19
+
20
+ function LabelEditingPreview(eventBus, canvas, elementRegistry, pathMap) {
21
+ var self = this;
22
+
23
+ var defaultLayer = canvas.getDefaultLayer();
24
+
25
+ var element, absoluteElementBBox, gfx;
26
+
27
+ eventBus.on('directEditing.activate', function(context) {
28
+ var activeProvider = context.active;
29
+
30
+ element = activeProvider.element.label || activeProvider.element;
31
+
32
+ // text annotation
33
+ if (is(element, 'bpmn:TextAnnotation')) {
34
+ absoluteElementBBox = canvas.getAbsoluteBBox(element);
35
+
36
+ gfx = svgCreate('g');
37
+
38
+ var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
39
+ xScaleFactor: 1,
40
+ yScaleFactor: 1,
41
+ containerWidth: element.width,
42
+ containerHeight: element.height,
43
+ position: {
44
+ mx: 0.0,
45
+ my: 0.0
46
+ }
47
+ });
48
+
49
+ var path = self.path = svgCreate('path');
50
+
51
+ svgAttr(path, {
52
+ d: textPathData,
53
+ strokeWidth: 2,
54
+ stroke: getStrokeColor(element)
55
+ });
56
+
57
+ svgAppend(gfx, path);
58
+
59
+ svgAppend(defaultLayer, gfx);
60
+
61
+ translate(gfx, element.x, element.y);
62
+ }
63
+
64
+ if (is(element, 'bpmn:TextAnnotation') ||
65
+ element.labelTarget) {
66
+ canvas.addMarker(element, MARKER_HIDDEN);
67
+ } else if (is(element, 'bpmn:Task') ||
68
+ is(element, 'bpmn:CallActivity') ||
69
+ is(element, 'bpmn:SubProcess') ||
70
+ is(element, 'bpmn:Participant')) {
71
+ canvas.addMarker(element, MARKER_LABEL_HIDDEN);
72
+ }
73
+ });
74
+
75
+ eventBus.on('directEditing.resize', function(context) {
76
+
77
+ // text annotation
78
+ if (is(element, 'bpmn:TextAnnotation')) {
79
+ var height = context.height,
80
+ dy = context.dy;
81
+
82
+ var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0);
83
+
84
+ var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
85
+ xScaleFactor: 1,
86
+ yScaleFactor: 1,
87
+ containerWidth: element.width,
88
+ containerHeight: newElementHeight,
89
+ position: {
90
+ mx: 0.0,
91
+ my: 0.0
92
+ }
93
+ });
94
+
95
+ svgAttr(self.path, {
96
+ d: textPathData
97
+ });
98
+ }
99
+ });
100
+
101
+ eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) {
102
+ var activeProvider = context.active;
103
+
104
+ if (activeProvider) {
105
+ canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN);
106
+ canvas.removeMarker(element, MARKER_LABEL_HIDDEN);
107
+ }
108
+
109
+ element = undefined;
110
+ absoluteElementBBox = undefined;
111
+
112
+ if (gfx) {
113
+ svgRemove(gfx);
114
+
115
+ gfx = undefined;
116
+ }
117
+ });
118
+ }
119
+
120
+ LabelEditingPreview.$inject = [ 'eventBus', 'canvas', 'elementRegistry', 'pathMap' ];
121
+
122
+ module.exports = LabelEditingPreview;
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var assign = require('lodash/object/assign');
4
+
3
5
  var UpdateLabelHandler = require('./cmd/UpdateLabelHandler');
4
6
 
5
7
  var LabelUtil = require('./LabelUtil');
@@ -7,10 +9,12 @@ var LabelUtil = require('./LabelUtil');
7
9
  var is = require('../../util/ModelUtil').is,
8
10
  isExpanded = require('../../util/DiUtil').isExpanded;
9
11
 
10
- var LINE_HEIGHT = 14,
11
- PADDING = 6;
12
+ var SMALL_FONT_SIZE = 11,
13
+ SMALL_LINE_HEIGHT = 13,
14
+ MEDIUM_FONT_SIZE = 12,
15
+ MEDIUM_LINE_HEIGHT = 14;
12
16
 
13
- function LabelEditingProvider(eventBus, canvas, directEditing, commandStack) {
17
+ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, resizeHandles) {
14
18
 
15
19
  this._canvas = canvas;
16
20
  this._commandStack = commandStack;
@@ -22,19 +26,20 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack) {
22
26
  // listen to dblclick on non-root elements
23
27
  eventBus.on('element.dblclick', function(event) {
24
28
  directEditing.activate(event.element);
29
+
30
+ resizeHandles.removeResizers();
25
31
  });
26
32
 
27
33
  // complete on followup canvas operation
28
- eventBus.on([ 'element.mousedown', 'drag.init', 'canvas.viewbox.changed' ], function(event) {
34
+ eventBus.on([ 'element.mousedown', 'drag.init', 'canvas.viewbox.changing' ], function(event) {
29
35
  directEditing.complete();
30
36
  });
31
37
 
32
38
  // cancel on command stack changes
33
- eventBus.on([ 'commandStack.changed' ], function() {
39
+ eventBus.on([ 'commandStack.changed' ], function(e) {
34
40
  directEditing.cancel();
35
41
  });
36
42
 
37
-
38
43
  if ('ontouchstart' in document.documentElement) {
39
44
  // we deactivate automatic label editing on mobile devices
40
45
  // as it breaks the user interaction workflow
@@ -42,10 +47,9 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack) {
42
47
  // TODO(nre): we should temporarily focus the edited element here
43
48
  // and release the focused viewport after the direct edit operation is finished
44
49
  } else {
45
- eventBus.on('create.end', 500, function(e) {
46
-
47
- var element = e.shape,
48
- canExecute = e.context.canExecute;
50
+ eventBus.on('create.end', 500, function(event) {
51
+ var element = event.shape,
52
+ canExecute = event.context.canExecute;
49
53
 
50
54
  if (!canExecute) {
51
55
  return;
@@ -53,14 +57,15 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack) {
53
57
 
54
58
  if (is(element, 'bpmn:Task') || is(element, 'bpmn:TextAnnotation') ||
55
59
  (is(element, 'bpmn:SubProcess') && !isExpanded(element))) {
56
-
57
60
  directEditing.activate(element);
61
+
62
+ resizeHandles.removeResizers();
58
63
  }
59
64
  });
60
65
  }
61
66
  }
62
67
 
63
- LabelEditingProvider.$inject = [ 'eventBus', 'canvas', 'directEditing', 'commandStack' ];
68
+ LabelEditingProvider.$inject = [ 'eventBus', 'canvas', 'directEditing', 'commandStack', 'resizeHandles' ];
64
69
 
65
70
  module.exports = LabelEditingProvider;
66
71
 
@@ -70,21 +75,62 @@ module.exports = LabelEditingProvider;
70
75
  *
71
76
  * @param {djs.model.Base} element
72
77
  *
73
- * @return {Object} an object with properties bounds (position and size) and text
78
+ * @return {Object} an object with properties bounds (position and size), text and options
74
79
  */
75
80
  LabelEditingProvider.prototype.activate = function(element) {
76
81
 
82
+ // text
77
83
  var text = LabelUtil.getLabel(element);
78
84
 
79
85
  if (text === undefined) {
80
86
  return;
81
87
  }
82
88
 
83
- var properties = this.getEditingBBox(element);
89
+ var context = {
90
+ text: text
91
+ };
92
+
93
+ // bounds
94
+ var bounds = this.getEditingBBox(element);
95
+
96
+ assign(context, bounds);
97
+
98
+ // options
99
+ var target = element.label || element;
100
+
101
+ var options = {};
84
102
 
85
- properties.text = text;
103
+ // tasks
104
+ if (is(element, 'bpmn:Task') ||
105
+ is(element, 'bpmn:Participant') ||
106
+ is(element, 'bpmn:Lane') ||
107
+ (is(element, 'bpmn:CallActivity') && !isExpanded(element)) ||
108
+ (is(element, 'bpmn:SubProcess') && !isExpanded(element))) {
109
+ assign(options, {
110
+ centerVertically: true
111
+ });
112
+ }
86
113
 
87
- return properties;
114
+ // external labels
115
+ if (target.labelTarget) {
116
+ assign(options, {
117
+ autoResize: true
118
+ });
119
+ }
120
+
121
+ // text annotations
122
+ if (is(element, 'bpmn:TextAnnotation')) {
123
+ assign(options, {
124
+ resizable: true,
125
+ autoResize: true
126
+ });
127
+ }
128
+
129
+ assign(context, {
130
+ options: options
131
+ });
132
+
133
+ return context;
88
134
  };
89
135
 
90
136
 
@@ -110,17 +156,36 @@ LabelEditingProvider.prototype.getEditingBBox = function(element) {
110
156
  // default position
111
157
  var bounds = { x: bbox.x, y: bbox.y };
112
158
 
113
- var style = {},
114
- zoom;
159
+ var zoom = canvas.zoom();
160
+
161
+ // take zoom into account
162
+ var smallFontSize = SMALL_FONT_SIZE * zoom,
163
+ smallLineHeight = SMALL_LINE_HEIGHT * zoom,
164
+ mediumFontSize = MEDIUM_FONT_SIZE * zoom,
165
+ mediumLineHeight = MEDIUM_LINE_HEIGHT * zoom;
166
+
167
+ var style = {};
115
168
 
116
169
  // adjust for expanded pools AND lanes
117
- if ((is(element, 'bpmn:Participant') && isExpanded(element)) || is(element, 'bpmn:Lane')) {
170
+ if ((is(element, 'bpmn:Participant') && isExpanded(element))
171
+ || is(element, 'bpmn:Lane')) {
172
+
173
+ assign(bounds, {
174
+ width: bbox.height,
175
+ height: 30 * zoom,
176
+ x: bbox.x - bbox.height / 2 + (15 * zoom),
177
+ y: mid.y - (30 * zoom) / 2
178
+ });
118
179
 
119
- bounds.width = 150;
120
- bounds.minHeight = LINE_HEIGHT + PADDING;
121
- bounds.maxHeight = LINE_HEIGHT * 2 + PADDING;
122
- bounds.x = bbox.x - bounds.width / 2;
123
- bounds.y = mid.y - bounds.minHeight / 2;
180
+ assign(style, {
181
+ fontSize: mediumFontSize + 'px',
182
+ lineHeight: mediumLineHeight + 'px',
183
+ paddingTop: (7 * zoom) + 'px',
184
+ paddingBottom: (7 * zoom) + 'px',
185
+ paddingLeft: (5 * zoom) + 'px',
186
+ paddingRight: (5 * zoom) + 'px',
187
+ transform: 'rotate(-90deg)'
188
+ });
124
189
  }
125
190
 
126
191
 
@@ -131,63 +196,97 @@ LabelEditingProvider.prototype.getEditingBBox = function(element) {
131
196
  (is(element, 'bpmn:SubProcess') && !isExpanded(element)) ||
132
197
  (is(element, 'bpmn:Participant') && !isExpanded(element))
133
198
  ) {
199
+ assign(bounds, {
200
+ width: bbox.width,
201
+ height: bbox.height
202
+ });
134
203
 
135
- zoom = canvas.zoom();
136
-
137
- // fixed size for internal labels:
138
- // on high zoom levels: text box size === bbox size
139
- // on low zoom levels: text box size === bbox size at 100% zoom
140
- // This ensures minimum bounds at low zoom levels
141
- if (zoom > 1) {
142
- bounds.width = bbox.width;
143
- bounds.height = bbox.height;
144
- } else {
145
- bounds.width = bbox.width / zoom;
146
- bounds.height = bbox.height / zoom;
147
- }
148
-
149
- // centering overlapping text box size at low zoom levels
150
- if (zoom < 1) {
151
- bounds.x = bbox.x - (bounds.width / 2 - bbox.width / 2);
152
- bounds.y = bbox.y - (bounds.height / 2 - bbox.height / 2);
153
- }
154
-
204
+ assign(style, {
205
+ fontSize: mediumFontSize + 'px',
206
+ lineHeight: mediumLineHeight + 'px',
207
+ paddingTop: (7 * zoom) + 'px',
208
+ paddingBottom: (7 * zoom) + 'px',
209
+ paddingLeft: (5 * zoom) + 'px',
210
+ paddingRight: (5 * zoom) + 'px'
211
+ });
155
212
  }
156
213
 
157
214
 
158
215
  // internal labels for expanded sub processes
159
216
  if (is(element, 'bpmn:SubProcess') && isExpanded(element)) {
217
+ assign(bounds, {
218
+ width: bbox.width,
219
+ x: bbox.x
220
+ });
160
221
 
161
- bounds.width = element.width;
162
- bounds.maxHeight = 3 * LINE_HEIGHT + PADDING; // maximum 3 lines
163
- bounds.x = mid.x - element.width / 2;
222
+ assign(style, {
223
+ fontSize: mediumFontSize + 'px',
224
+ lineHeight: mediumLineHeight + 'px',
225
+ paddingTop: (7 * zoom) + 'px',
226
+ paddingBottom: (7 * zoom) + 'px',
227
+ paddingLeft: (5 * zoom) + 'px',
228
+ paddingRight: (5 * zoom) + 'px'
229
+ });
164
230
  }
165
231
 
166
232
 
167
233
  // external labels for events, data elements, gateways and connections
168
234
  if (target.labelTarget) {
235
+ var width = 90 * zoom,
236
+ paddingTop = 7 * zoom,
237
+ paddingBottom = 4 * zoom;
238
+
239
+ assign(bounds, {
240
+ width: width,
241
+ height: bbox.height + paddingTop + paddingBottom,
242
+ x: mid.x - width / 2,
243
+ y: bbox.y - paddingTop
244
+ });
169
245
 
170
- bounds.width = 150;
171
- bounds.minHeight = LINE_HEIGHT + PADDING; // 1 line
172
- bounds.x = mid.x - bounds.width / 2;
246
+ assign(style, {
247
+ fontSize: smallFontSize + 'px',
248
+ lineHeight: smallLineHeight + 'px',
249
+ paddingTop: paddingTop + 'px',
250
+ paddingBottom: paddingBottom + 'px'
251
+ });
173
252
  }
174
253
 
175
254
 
176
255
  // text annotations
177
256
  if (is(element, 'bpmn:TextAnnotation')) {
178
- bounds.minWidth = 100;
179
- bounds.height = element.height;
257
+ assign(bounds, {
258
+ width: bbox.width,
259
+ height: bbox.height,
260
+ minWidth: 30 * zoom,
261
+ minHeight: 10 * zoom
262
+ });
180
263
 
181
- style.textAlign = 'left';
264
+ assign(style, {
265
+ textAlign: 'left',
266
+ paddingTop: (7 * zoom) + 'px',
267
+ paddingBottom: (7 * zoom) + 'px',
268
+ paddingLeft: (5 * zoom) + 'px',
269
+ paddingRight: (5 * zoom) + 'px',
270
+ fontSize: mediumFontSize + 'px',
271
+ lineHeight: mediumLineHeight + 'px'
272
+ });
182
273
  }
183
274
 
184
275
  return { bounds: bounds, style: style };
185
276
  };
186
277
 
187
278
 
188
- LabelEditingProvider.prototype.update = function(element, newLabel) {
279
+ LabelEditingProvider.prototype.update = function(element, newLabel, activeContextText, bounds) {
280
+ var absoluteElementBBox = this._canvas.getAbsoluteBBox(element);
281
+
189
282
  this._commandStack.execute('element.updateLabel', {
190
283
  element: element,
191
- newLabel: newLabel
284
+ newLabel: newLabel,
285
+ bounds: {
286
+ x: element.x,
287
+ y: element.y,
288
+ width: element.width / absoluteElementBBox.width * bounds.width,
289
+ height: element.height / absoluteElementBBox.height * bounds.height
290
+ }
192
291
  });
193
292
  };
@@ -6,7 +6,8 @@ var TextUtil = require('diagram-js/lib/util/Text');
6
6
 
7
7
  var hasExternalLabel = require('../../../util/LabelUtil').hasExternalLabel;
8
8
 
9
- var getBusinessObject = require('../../../util/ModelUtil').getBusinessObject;
9
+ var getBusinessObject = require('../../../util/ModelUtil').getBusinessObject,
10
+ is = require('../../../util/ModelUtil').is;
10
11
 
11
12
  var NULL_DIMENSIONS = {
12
13
  width: 0,
@@ -52,14 +53,17 @@ function UpdateLabelHandler(modeling) {
52
53
 
53
54
  function postExecute(ctx) {
54
55
  var element = ctx.element,
55
- label = element.label || element;
56
+ label = element.label || element,
57
+ bounds = ctx.bounds;
56
58
 
57
- // ignore internal labels
58
- if (!hasExternalLabel(element)) {
59
+ // ignore internal labels for elements except text annotations
60
+ if (!hasExternalLabel(element) && !is(element, 'bpmn:TextAnnotation')) {
59
61
  return;
60
62
  }
61
63
 
62
- var text = getBusinessObject(label).name;
64
+ var bo = getBusinessObject(label);
65
+
66
+ var text = bo.name || bo.text;
63
67
 
64
68
  if (!text) {
65
69
  return;
@@ -67,7 +71,7 @@ function UpdateLabelHandler(modeling) {
67
71
 
68
72
  // get layouted text bounds and resize external
69
73
  // external label accordingly
70
- var newBounds = getLayoutedBounds(label, text, textUtil);
74
+ var newBounds = is(element, 'bpmn:TextAnnotation') ? bounds : getLayoutedBounds(label, text, textUtil);
71
75
 
72
76
  modeling.resizeShape(label, newBounds, NULL_DIMENSIONS);
73
77
  }
@@ -2,8 +2,13 @@ module.exports = {
2
2
  __depends__: [
3
3
  require('diagram-js/lib/command'),
4
4
  require('diagram-js/lib/features/change-support'),
5
+ require('diagram-js/lib/features/resize'),
5
6
  require('diagram-js-direct-editing')
6
7
  ],
7
- __init__: [ 'labelEditingProvider' ],
8
- labelEditingProvider: [ 'type', require('./LabelEditingProvider') ]
9
- };
8
+ __init__: [
9
+ 'labelEditingProvider',
10
+ 'labelEditingPreview'
11
+ ],
12
+ labelEditingProvider: [ 'type', require('./LabelEditingProvider') ],
13
+ labelEditingPreview: [ 'type', require('./LabelEditingPreview') ]
14
+ };
@@ -2,12 +2,23 @@
2
2
 
3
3
  var inherits = require('inherits');
4
4
 
5
- var assign = require('lodash/object/assign');
5
+ var assign = require('lodash/object/assign'),
6
+ find = require('lodash/collection/find');
6
7
 
7
8
  var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
8
9
 
9
10
  var getApproxIntersection = require('diagram-js/lib/util/LineIntersection').getApproxIntersection;
10
11
 
12
+ function isPointInsideBBox(bbox, point) {
13
+ var x = point.x,
14
+ y = point.y;
15
+
16
+ return x >= bbox.x &&
17
+ x <= bbox.x + bbox.width &&
18
+ y >= bbox.y &&
19
+ y <= bbox.y + bbox.height;
20
+ }
21
+
11
22
  function copy(obj) {
12
23
  return assign({}, obj);
13
24
  }
@@ -41,8 +52,15 @@ function DropOnFlow(eventBus, bpmnRules, modeling) {
41
52
 
42
53
  dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : position;
43
54
 
44
- waypointsBefore.push(copy(dockingPoint));
45
- waypointsAfter.unshift(copy(dockingPoint));
55
+ // if last waypointBefore is inside shape's bounds, ignore docking point
56
+ if (!isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) {
57
+ waypointsBefore.push(copy(dockingPoint));
58
+ }
59
+
60
+ // if first waypointAfter is inside shape's bounds, ignore docking point
61
+ if (!isPointInsideBBox(shape, waypointsAfter[0])) {
62
+ waypointsAfter.unshift(copy(dockingPoint));
63
+ }
46
64
  }
47
65
 
48
66
  source = targetFlow.source;
@@ -68,24 +86,40 @@ function DropOnFlow(eventBus, bpmnRules, modeling) {
68
86
 
69
87
  this.preExecute('elements.move', function(context) {
70
88
 
71
- var parent = context.newParent,
89
+ var newParent = context.newParent,
72
90
  shapes = context.shapes,
73
- shape,
74
- shapeMid,
75
- delta = context.delta;
91
+ delta = context.delta,
92
+ shape = shapes[0];
76
93
 
77
- if (bpmnRules.canInsert(shapes, parent)) {
94
+ if (!shape || !newParent) {
95
+ return;
96
+ }
78
97
 
79
- shape = shapes[0];
80
- shapeMid = getMid(shape);
98
+ // if the new parent is a connection,
99
+ // change it to the new parent's parent
100
+ if (newParent && newParent.waypoints) {
101
+ context.newParent = newParent = newParent.parent;
102
+ }
81
103
 
82
- context.targetFlow = parent;
83
- context.newParent = parent.parent;
84
- context.position = {
85
- x: shapeMid.x + delta.x,
86
- y: shapeMid.y + delta.y
87
- };
104
+ var shapeMid = getMid(shape);
105
+ var newShapeMid = {
106
+ x: shapeMid.x + delta.x,
107
+ y: shapeMid.y + delta.y
108
+ };
109
+
110
+ // find a connection which intersects with the
111
+ // element's mid point
112
+ var connection = find(newParent.children, function(element) {
113
+ var canInsert = bpmnRules.canInsert(shapes, element);
114
+
115
+ return canInsert && getApproxIntersection(element.waypoints, newShapeMid);
116
+ });
117
+
118
+ if (connection) {
119
+ context.targetFlow = connection;
120
+ context.position = newShapeMid;
88
121
  }
122
+
89
123
  }, true);
90
124
 
91
125
  this.postExecuted('elements.move', function(context) {