diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a0b43a..94251d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to [bpmn-js](https://github.com/bpmn-io/bpmn-js) are documen ___Note:__ Yet to be released changes appear here._ +## 6.5.0 + +* `FEAT`: prefer straight layout for sub-process connections ([#1309](https://github.com/bpmn-io/bpmn-js/pull/1309)) +* `FEAT`: move common auto-place feature to diagram-js, add BPMN-specific auto-place feature ([#1284](https://github.com/bpmn-io/bpmn-js/pull/1284)) +* `CHORE`: make bpmn-font a development dependency ([`63045bdf`](https://github.com/bpmn-io/bpmn-js/commit/63045bdfa87b9f1989a2a7a509facbeb4616acda)) +* `CHORE`: bump to `diagram-js@6.6.1` + ## 6.4.2 * `CHORE`: bump to `bpmn-moddle@6.0.5` diff --git a/lib/BaseModeler.js b/lib/BaseModeler.js index efa727f6..7c6ac913 100644 --- a/lib/BaseModeler.js +++ b/lib/BaseModeler.js @@ -12,8 +12,8 @@ import BaseViewer from './BaseViewer'; * * @param {Object} [options] configuration options to pass to the viewer * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. - * @param {String|Number} [options.width] the width of the viewer - * @param {String|Number} [options.height] the height of the viewer + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer * @param {Object} [options.moddleExtensions] extension packages to provide * @param {Array} [options.modules] a list of modules to override the default modules * @param {Array} [options.additionalModules] a list of modules to use with the default modules diff --git a/lib/BaseViewer.js b/lib/BaseViewer.js index cc0f3385..4977e528 100644 --- a/lib/BaseViewer.js +++ b/lib/BaseViewer.js @@ -40,8 +40,8 @@ import { * * @param {Object} [options] configuration options to pass to the viewer * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. - * @param {String|Number} [options.width] the width of the viewer - * @param {String|Number} [options.height] the height of the viewer + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer * @param {Object} [options.moddleExtensions] extension packages to provide * @param {Array} [options.modules] a list of modules to override the default modules * @param {Array} [options.additionalModules] a list of modules to use with the default modules @@ -84,8 +84,8 @@ inherits(BaseViewer, Diagram); * * You can use these events to hook into the life-cycle. * - * @param {String} xml the BPMN 2.0 xml - * @param {ModdleElement|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * @param {string} xml the BPMN 2.0 xml + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) * @param {Function} [done] invoked with (err, warnings=[]) */ BaseViewer.prototype.importXML = function(xml, bpmnDiagram, done) { @@ -150,7 +150,7 @@ BaseViewer.prototype.importXML = function(xml, bpmnDiagram, done) { * You can use these events to hook into the life-cycle. * * @param {ModdleElement} definitions parsed BPMN 2.0 definitions - * @param {ModdleElement|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) * @param {Function} [done] invoked with (err, warnings=[]) */ BaseViewer.prototype.importDefinitions = function(definitions, bpmnDiagram, done) { @@ -183,7 +183,7 @@ BaseViewer.prototype.importDefinitions = function(definitions, bpmnDiagram, done * * You can use these events to hook into the life-cycle. * - * @param {String|ModdleElement} [bpmnDiagramOrId] id or the diagram to open + * @param {string|ModdleElement} [bpmnDiagramOrId] id or the diagram to open * @param {Function} [done] invoked with (err, warnings=[]) */ BaseViewer.prototype.open = function(bpmnDiagramOrId, done) { @@ -238,8 +238,8 @@ BaseViewer.prototype.open = function(bpmnDiagramOrId, done) { * You can use these events to hook into the life-cycle. * * @param {Object} [options] export options - * @param {Boolean} [options.format=false] output formatted XML - * @param {Boolean} [options.preamble=true] output preamble + * @param {boolean} [options.format=false] output formatted XML + * @param {boolean} [options.preamble=true] output preamble * * @param {Function} done invoked with (err, xml) */ @@ -350,7 +350,7 @@ BaseViewer.prototype.saveSVG = function(options, done) { * var elementRegistry = viewer.get('elementRegistry'); * var startEventShape = elementRegistry.get('StartEvent_1'); * - * @param {String} name + * @param {string} name * * @return {Object} diagram service instance * @@ -432,8 +432,8 @@ BaseViewer.prototype.destroy = function() { * * Remove a previously added listener via {@link #off(event, callback)}. * - * @param {String} event - * @param {Number} [priority] + * @param {string} event + * @param {number} [priority] * @param {Function} callback * @param {Object} [that] */ @@ -444,7 +444,7 @@ BaseViewer.prototype.on = function(event, priority, callback, target) { /** * De-register an event listener * - * @param {String} event + * @param {string} event * @param {Function} callback */ BaseViewer.prototype.off = function(event, callback) { @@ -524,7 +524,7 @@ BaseViewer.prototype._init = function(container, moddle, options) { /** * Emit an event on the underlying {@link EventBus} * - * @param {String} type + * @param {string} type * @param {Object} event * * @return {Object} event processing result (if any) @@ -593,7 +593,7 @@ function ensureUnit(val) { * Find BPMNDiagram in definitions by ID * * @param {ModdleElement} definitions - * @param {String} diagramId + * @param {string} diagramId * * @return {ModdleElement|null} */ diff --git a/lib/Modeler.js b/lib/Modeler.js index c1b57bd7..94d17e0f 100644 --- a/lib/Modeler.js +++ b/lib/Modeler.js @@ -123,8 +123,8 @@ var initialDiagram = * * @param {Object} [options] configuration options to pass to the viewer * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. - * @param {String|Number} [options.width] the width of the viewer - * @param {String|Number} [options.height] the height of the viewer + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer * @param {Object} [options.moddleExtensions] extension packages to provide * @param {Array} [options.modules] a list of modules to override the default modules * @param {Array} [options.additionalModules] a list of modules to use with the default modules diff --git a/lib/Viewer.js b/lib/Viewer.js index 1f57bac3..217887e4 100644 --- a/lib/Viewer.js +++ b/lib/Viewer.js @@ -49,8 +49,8 @@ import BaseViewer from './BaseViewer'; * * @param {Object} [options] configuration options to pass to the viewer * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. - * @param {String|Number} [options.width] the width of the viewer - * @param {String|Number} [options.height] the height of the viewer + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer * @param {Object} [options.moddleExtensions] extension packages to provide * @param {Array} [options.modules] a list of modules to override the default modules * @param {Array} [options.additionalModules] a list of modules to use with the default modules diff --git a/lib/draw/PathMap.js b/lib/draw/PathMap.js index f5d024b7..cc28a48b 100644 --- a/lib/draw/PathMap.js +++ b/lib/draw/PathMap.js @@ -362,7 +362,7 @@ export default function PathMap() { * Also there are use cases where only some parts of a path should be * scaled.

* - * @param {String} pathId The ID of the path. + * @param {string} pathId The ID of the path. * @param {Object} param

* Example param object scales the path to 60% size of the container (data.width, data.height). *

diff --git a/lib/draw/TextRenderer.js b/lib/draw/TextRenderer.js
index 8d1fe0d3..e70b8096 100644
--- a/lib/draw/TextRenderer.js
+++ b/lib/draw/TextRenderer.js
@@ -32,7 +32,7 @@ export default function TextRenderer(config) {
    * layouted label.
    *
    * @param  {Bounds} bounds
-   * @param  {String} text
+   * @param  {string} text
    *
    * @return {Bounds}
    */
@@ -62,7 +62,7 @@ export default function TextRenderer(config) {
    * Get the new bounds of text annotation.
    *
    * @param  {Bounds} bounds
-   * @param  {String} text
+   * @param  {string} text
    *
    * @return {Bounds}
    */
@@ -86,7 +86,7 @@ export default function TextRenderer(config) {
   /**
    * Create a layouted text element.
    *
-   * @param {String} text
+   * @param {string} text
    * @param {Object} [options]
    *
    * @return {SVGElement} rendered text
diff --git a/lib/features/auto-place/AutoPlace.js b/lib/features/auto-place/AutoPlace.js
deleted file mode 100644
index 9c7349e3..00000000
--- a/lib/features/auto-place/AutoPlace.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import { getNewShapePosition } from './AutoPlaceUtil';
-
-
-/**
- * A service that places elements connected to existing ones
- * to an appropriate position in an _automated_ fashion.
- *
- * @param {EventBus} eventBus
- * @param {Modeling} modeling
- */
-export default function AutoPlace(eventBus, modeling) {
-
-  function emit(event, payload) {
-    return eventBus.fire(event, payload);
-  }
-
-
-  /**
-   * Append shape to source at appropriate position.
-   *
-   * @param {djs.model.Shape} source
-   * @param {djs.model.Shape} shape
-   *
-   * @return {djs.model.Shape} appended shape
-   */
-  this.append = function(source, shape) {
-
-    emit('autoPlace.start', {
-      source: source,
-      shape: shape
-    });
-
-    // allow others to provide the position
-    var position = emit('autoPlace', {
-      source: source,
-      shape: shape
-    });
-
-    if (!position) {
-      position = getNewShapePosition(source, shape);
-    }
-
-    var newShape = modeling.appendShape(source, shape, position, source.parent);
-
-    emit('autoPlace.end', {
-      source: source,
-      shape: newShape
-    });
-
-    return newShape;
-  };
-
-}
-
-AutoPlace.$inject = [
-  'eventBus',
-  'modeling'
-];
\ No newline at end of file
diff --git a/lib/features/auto-place/AutoPlaceSelectionBehavior.js b/lib/features/auto-place/AutoPlaceSelectionBehavior.js
deleted file mode 100644
index 6024694c..00000000
--- a/lib/features/auto-place/AutoPlaceSelectionBehavior.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Select element after auto placement.
- *
- * @param {EventBus} eventBus
- * @param {Selection} selection
- */
-export default function AutoPlaceSelectionBehavior(eventBus, selection) {
-
-  eventBus.on('autoPlace.end', 500, function(e) {
-    selection.select(e.shape);
-  });
-
-}
-
-AutoPlaceSelectionBehavior.$inject = [
-  'eventBus',
-  'selection'
-];
\ No newline at end of file
diff --git a/lib/features/auto-place/AutoPlaceUtil.js b/lib/features/auto-place/AutoPlaceUtil.js
deleted file mode 100644
index afa8797e..00000000
--- a/lib/features/auto-place/AutoPlaceUtil.js
+++ /dev/null
@@ -1,430 +0,0 @@
-import { is } from '../../util/ModelUtil';
-import { isAny } from '../modeling/util/ModelingUtil';
-
-import {
-  getMid,
-  asTRBL,
-  getOrientation
-} from 'diagram-js/lib/layout/LayoutUtil';
-
-import {
-  find,
-  reduce
-} from 'min-dash';
-
-var DEFAULT_HORIZONTAL_DISTANCE = 50;
-
-var MAX_HORIZONTAL_DISTANCE = 250;
-
-// padding to detect element placement
-var PLACEMENT_DETECTION_PAD = 10;
-
-/**
- * Find the new position for the target element to
- * connect to source.
- *
- * @param  {djs.model.Shape} source
- * @param  {djs.model.Shape} element
- *
- * @return {Point}
- */
-export function getNewShapePosition(source, element) {
-
-  if (is(element, 'bpmn:TextAnnotation')) {
-    return getTextAnnotationPosition(source, element);
-  }
-
-  if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
-    return getDataElementPosition(source, element);
-  }
-
-  if (is(element, 'bpmn:FlowNode')) {
-    return getFlowNodePosition(source, element);
-  }
-
-  return getDefaultPosition(source, element);
-}
-
-/**
- * Always try to place element right of source;
- * compute actual distance from previous nodes in flow.
- */
-export function getFlowNodePosition(source, element) {
-
-  var sourceTrbl = asTRBL(source);
-  var sourceMid = getMid(source);
-
-  var horizontalDistance = getFlowNodeDistance(source, element);
-
-  var orientation = 'left',
-      rowSize = 80,
-      margin = 30;
-
-  if (is(source, 'bpmn:BoundaryEvent')) {
-    orientation = getOrientation(source, source.host, -25);
-
-    if (orientation.indexOf('top') !== -1) {
-      margin *= -1;
-    }
-  }
-
-  function getVerticalDistance(orient) {
-    if (orient.indexOf('top') != -1) {
-      return -1 * rowSize;
-    } else if (orient.indexOf('bottom') != -1) {
-      return rowSize;
-    } else {
-      return 0;
-    }
-  }
-
-  var position = {
-    x: sourceTrbl.right + horizontalDistance + element.width / 2,
-    y: sourceMid.y + getVerticalDistance(orientation)
-  };
-
-  var escapeDirection = {
-    y: {
-      margin: margin,
-      rowSize: rowSize
-    }
-  };
-
-  return deconflictPosition(source, element, position, escapeDirection);
-}
-
-
-/**
- * Compute best distance between source and target,
- * based on existing connections to and from source.
- *
- * @param {djs.model.Shape} source
- * @param {djs.model.Shape} element
- *
- * @return {Number} distance
- */
-export function getFlowNodeDistance(source, element) {
-
-  var sourceTrbl = asTRBL(source);
-
-  // is connection a reference to consider?
-  function isReference(c) {
-    return is(c, 'bpmn:SequenceFlow');
-  }
-
-  function toTargetNode(weight) {
-
-    return function(shape) {
-      return {
-        shape: shape,
-        weight: weight,
-        distanceTo: function(shape) {
-          var shapeTrbl = asTRBL(shape);
-
-          return shapeTrbl.left - sourceTrbl.right;
-        }
-      };
-    };
-  }
-
-  function toSourceNode(weight) {
-    return function(shape) {
-      return {
-        shape: shape,
-        weight: weight,
-        distanceTo: function(shape) {
-          var shapeTrbl = asTRBL(shape);
-
-          return sourceTrbl.left - shapeTrbl.right;
-        }
-      };
-    };
-  }
-
-  // we create a list of nodes to take into consideration
-  // for calculating the optimal flow node distance
-  //
-  //   * weight existing target nodes higher than source nodes
-  //   * only take into account individual nodes once
-  //
-  var nodes = reduce([].concat(
-    getTargets(source, isReference).map(toTargetNode(5)),
-    getSources(source, isReference).map(toSourceNode(1))
-  ), function(nodes, node) {
-
-    // filter out shapes connected twice via source or target
-    nodes[node.shape.id + '__weight_' + node.weight] = node;
-
-    return nodes;
-  }, {});
-
-  // compute distances between source and incoming nodes;
-  // group at the same time by distance and expose the
-  // favourite distance as { fav: { count, value } }.
-  var distancesGrouped = reduce(nodes, function(result, node) {
-
-    var shape = node.shape,
-        weight = node.weight,
-        distanceTo = node.distanceTo;
-
-    var fav = result.fav,
-        currentDistance,
-        currentDistanceCount,
-        currentDistanceEntry;
-
-    currentDistance = distanceTo(shape);
-
-    // ignore too far away peers
-    // or non-left to right modeled nodes
-    if (currentDistance < 0 || currentDistance > MAX_HORIZONTAL_DISTANCE) {
-      return result;
-    }
-
-    currentDistanceEntry = result[String(currentDistance)] =
-      result[String(currentDistance)] || {
-        value: currentDistance,
-        count: 0
-      };
-
-    // inc diff count
-    currentDistanceCount = currentDistanceEntry.count += 1 * weight;
-
-    if (!fav || fav.count < currentDistanceCount) {
-      result.fav = currentDistanceEntry;
-    }
-
-    return result;
-  }, { });
-
-
-  if (distancesGrouped.fav) {
-    return distancesGrouped.fav.value;
-  } else {
-    return DEFAULT_HORIZONTAL_DISTANCE;
-  }
-}
-
-
-/**
- * Always try to place text annotations top right of source.
- */
-export function getTextAnnotationPosition(source, element) {
-
-  var sourceTrbl = asTRBL(source);
-
-  var position = {
-    x: sourceTrbl.right + element.width / 2,
-    y: sourceTrbl.top - 50 - element.height / 2
-  };
-
-  var escapeDirection = {
-    y: {
-      margin: -30,
-      rowSize: 20
-    }
-  };
-
-  return deconflictPosition(source, element, position, escapeDirection);
-}
-
-
-/**
- * Always put element bottom right of source.
- */
-export function getDataElementPosition(source, element) {
-
-  var sourceTrbl = asTRBL(source);
-
-  var position = {
-    x: sourceTrbl.right - 10 + element.width / 2,
-    y: sourceTrbl.bottom + 40 + element.width / 2
-  };
-
-  var escapeDirection = {
-    x: {
-      margin: 30,
-      rowSize: 30
-    }
-  };
-
-  return deconflictPosition(source, element, position, escapeDirection);
-}
-
-
-/**
- * Always put element right of source per default.
- */
-export function getDefaultPosition(source, element) {
-
-  var sourceTrbl = asTRBL(source);
-
-  var sourceMid = getMid(source);
-
-  // simply put element right next to source
-  return {
-    x: sourceTrbl.right + DEFAULT_HORIZONTAL_DISTANCE + element.width / 2,
-    y: sourceMid.y
-  };
-}
-
-
-/**
- * Returns all connected elements around the given source.
- *
- * This includes:
- *
- *   - connected elements
- *   - host connected elements
- *   - attachers connected elements
- *
- * @param  {djs.model.Shape} source
- * @param  {djs.model.Shape} element
- *
- * @return {Array}
- */
-function getAutoPlaceClosure(source, element) {
-
-  var allConnected = getConnected(source);
-
-  if (source.host) {
-    allConnected = allConnected.concat(getConnected(source.host));
-  }
-
-  if (source.attachers) {
-    allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) {
-      return shapes.concat(getConnected(attacher));
-    }, []));
-  }
-
-  return allConnected;
-}
-
-/**
- * Return target at given position, if defined.
- *
- * This takes connected elements from host and attachers
- * into account, too.
- */
-export function getConnectedAtPosition(source, position, element) {
-
-  var bounds = {
-    x: position.x - (element.width / 2),
-    y: position.y - (element.height / 2),
-    width: element.width,
-    height: element.height
-  };
-
-  var closure = getAutoPlaceClosure(source, element);
-
-  return find(closure, function(target) {
-
-    if (target === element) {
-      return false;
-    }
-
-    var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
-
-    return orientation === 'intersect';
-  });
-}
-
-
-/**
- * Returns a new, position for the given element
- * based on the given element that is not occupied
- * by some element connected to source.
- *
- * Take into account the escapeDirection (where to move
- * on positioning clashes) in the computation.
- *
- * @param {djs.model.Shape} source
- * @param {djs.model.Shape} element
- * @param {Point} position
- * @param {Object} escapeDelta
- *
- * @return {Point}
- */
-export function deconflictPosition(source, element, position, escapeDelta) {
-
-  function nextPosition(existingElement) {
-
-    var newPosition = {
-      x: position.x,
-      y: position.y
-    };
-
-    [ 'x', 'y' ].forEach(function(axis) {
-
-      var axisDelta = escapeDelta[axis];
-
-      if (!axisDelta) {
-        return;
-      }
-
-      var dimension = axis === 'x' ? 'width' : 'height';
-
-      var margin = axisDelta.margin,
-          rowSize = axisDelta.rowSize;
-
-      if (margin < 0) {
-        newPosition[axis] = Math.min(
-          existingElement[axis] + margin - element[dimension] / 2,
-          position[axis] - rowSize + margin
-        );
-      } else {
-        newPosition[axis] = Math.max(
-          existingTarget[axis] + existingTarget[dimension] + margin + element[dimension] / 2,
-          position[axis] + rowSize + margin
-        );
-      }
-    });
-
-    return newPosition;
-  }
-
-  var existingTarget;
-
-  // deconflict position until free slot is found
-  while ((existingTarget = getConnectedAtPosition(source, position, element))) {
-    position = nextPosition(existingTarget);
-  }
-
-  return position;
-}
-
-
-
-// helpers //////////////////////
-
-function noneFilter() {
-  return true;
-}
-
-function getConnected(element, connectionFilter) {
-  return [].concat(
-    getTargets(element, connectionFilter),
-    getSources(element, connectionFilter)
-  );
-}
-
-function getSources(shape, connectionFilter) {
-
-  if (!connectionFilter) {
-    connectionFilter = noneFilter;
-  }
-
-  return shape.incoming.filter(connectionFilter).map(function(c) {
-    return c.source;
-  });
-}
-
-function getTargets(shape, connectionFilter) {
-
-  if (!connectionFilter) {
-    connectionFilter = noneFilter;
-  }
-
-  return shape.outgoing.filter(connectionFilter).map(function(c) {
-    return c.target;
-  });
-}
\ No newline at end of file
diff --git a/lib/features/auto-place/BpmnAutoPlace.js b/lib/features/auto-place/BpmnAutoPlace.js
new file mode 100644
index 00000000..cd2e1171
--- /dev/null
+++ b/lib/features/auto-place/BpmnAutoPlace.js
@@ -0,0 +1,18 @@
+import { getNewShapePosition } from './BpmnAutoPlaceUtil';
+
+
+/**
+ * BPMN auto-place behavior.
+ *
+ * @param {EventBus} eventBus
+ */
+export default function AutoPlace(eventBus) {
+  eventBus.on('autoPlace', function(context) {
+    var shape = context.shape,
+        source = context.source;
+
+    return getNewShapePosition(source, shape);
+  });
+}
+
+AutoPlace.$inject = [ 'eventBus' ];
\ No newline at end of file
diff --git a/lib/features/auto-place/BpmnAutoPlaceUtil.js b/lib/features/auto-place/BpmnAutoPlaceUtil.js
new file mode 100644
index 00000000..bb7343ec
--- /dev/null
+++ b/lib/features/auto-place/BpmnAutoPlaceUtil.js
@@ -0,0 +1,138 @@
+import { is } from '../../util/ModelUtil';
+import { isAny } from '../modeling/util/ModelingUtil';
+
+import {
+  getMid,
+  asTRBL,
+  getOrientation
+} from 'diagram-js/lib/layout/LayoutUtil';
+
+import {
+  findFreePosition,
+  generateGetNextPosition,
+  getConnectedDistance
+} from 'diagram-js/lib/features/auto-place/AutoPlaceUtil';
+
+
+/**
+ * Find the new position for the target element to
+ * connect to source.
+ *
+ * @param  {djs.model.Shape} source
+ * @param  {djs.model.Shape} element
+ *
+ * @return {Point}
+ */
+export function getNewShapePosition(source, element) {
+
+  if (is(element, 'bpmn:TextAnnotation')) {
+    return getTextAnnotationPosition(source, element);
+  }
+
+  if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
+    return getDataElementPosition(source, element);
+  }
+
+  if (is(element, 'bpmn:FlowNode')) {
+    return getFlowNodePosition(source, element);
+  }
+}
+
+/**
+ * Always try to place element right of source;
+ * compute actual distance from previous nodes in flow.
+ */
+export function getFlowNodePosition(source, element) {
+
+  var sourceTrbl = asTRBL(source);
+  var sourceMid = getMid(source);
+
+  var horizontalDistance = getConnectedDistance(source, {
+    filter: function(connection) {
+      return is(connection, 'bpmn:SequenceFlow');
+    }
+  });
+
+  var margin = 30,
+      minDistance = 80,
+      orientation = 'left';
+
+  if (is(source, 'bpmn:BoundaryEvent')) {
+    orientation = getOrientation(source, source.host, -25);
+
+    if (orientation.indexOf('top') !== -1) {
+      margin *= -1;
+    }
+  }
+
+  var position = {
+    x: sourceTrbl.right + horizontalDistance + element.width / 2,
+    y: sourceMid.y + getVerticalDistance(orientation, minDistance)
+  };
+
+  var nextPositionDirection = {
+    y: {
+      margin: margin,
+      minDistance: minDistance
+    }
+  };
+
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+
+
+function getVerticalDistance(orientation, minDistance) {
+  if (orientation.indexOf('top') != -1) {
+    return -1 * minDistance;
+  } else if (orientation.indexOf('bottom') != -1) {
+    return minDistance;
+  } else {
+    return 0;
+  }
+}
+
+
+/**
+ * Always try to place text annotations top right of source.
+ */
+export function getTextAnnotationPosition(source, element) {
+
+  var sourceTrbl = asTRBL(source);
+
+  var position = {
+    x: sourceTrbl.right + element.width / 2,
+    y: sourceTrbl.top - 50 - element.height / 2
+  };
+
+  var nextPositionDirection = {
+    y: {
+      margin: -30,
+      minDistance: 20
+    }
+  };
+
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
+
+
+/**
+ * Always put element bottom right of source.
+ */
+export function getDataElementPosition(source, element) {
+
+  var sourceTrbl = asTRBL(source);
+
+  var position = {
+    x: sourceTrbl.right - 10 + element.width / 2,
+    y: sourceTrbl.bottom + 40 + element.width / 2
+  };
+
+  var nextPositionDirection = {
+    x: {
+      margin: 30,
+      minDistance: 30
+    }
+  };
+
+  return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
+}
\ No newline at end of file
diff --git a/lib/features/auto-place/index.js b/lib/features/auto-place/index.js
index cf9a7a0b..47516500 100644
--- a/lib/features/auto-place/index.js
+++ b/lib/features/auto-place/index.js
@@ -1,8 +1,9 @@
-import AutoPlace from './AutoPlace';
-import AutoPlaceSelectionBehavior from './AutoPlaceSelectionBehavior';
+import AutoPlaceModule from 'diagram-js/lib/features/auto-place';
+
+import BpmnAutoPlace from './BpmnAutoPlace';
 
 export default {
-  __init__: [ 'autoPlaceSelectionBehavior' ],
-  autoPlace: [ 'type', AutoPlace ],
-  autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ]
+  __depends__: [ AutoPlaceModule ],
+  __init__: [ 'bpmnAutoPlace' ],
+  bpmnAutoPlace: [ 'type', BpmnAutoPlace ]
 };
\ No newline at end of file
diff --git a/lib/features/context-pad/ContextPadProvider.js b/lib/features/context-pad/ContextPadProvider.js
index 564435be..3d7ce3cd 100644
--- a/lib/features/context-pad/ContextPadProvider.js
+++ b/lib/features/context-pad/ContextPadProvider.js
@@ -142,9 +142,9 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
   /**
    * Create an append action
    *
-   * @param {String} type
-   * @param {String} className
-   * @param {String} [title]
+   * @param {string} type
+   * @param {string} className
+   * @param {string} [title]
    * @param {Object} [options]
    *
    * @return {Object} descriptor
diff --git a/lib/features/grid-snapping/behavior/AutoPlaceBehavior.js b/lib/features/grid-snapping/behavior/AutoPlaceBehavior.js
index 820b1a01..63c9e5d8 100644
--- a/lib/features/grid-snapping/behavior/AutoPlaceBehavior.js
+++ b/lib/features/grid-snapping/behavior/AutoPlaceBehavior.js
@@ -1,11 +1,13 @@
-import { getNewShapePosition } from '../../auto-place/AutoPlaceUtil';
+import { getNewShapePosition } from '../../auto-place/BpmnAutoPlaceUtil';
 
 import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
 import { is } from '../../../util/ModelUtil';
 
+var HIGH_PRIORITY = 2000;
+
 
 export default function AutoPlaceBehavior(eventBus, gridSnapping) {
-  eventBus.on('autoPlace', function(context) {
+  eventBus.on('autoPlace', HIGH_PRIORITY, function(context) {
     var source = context.source,
         sourceMid = getMid(source),
         shape = context.shape;
diff --git a/lib/features/keyboard/BpmnKeyboardBindings.js b/lib/features/keyboard/BpmnKeyboardBindings.js
index 3531f377..fc940ff7 100644
--- a/lib/features/keyboard/BpmnKeyboardBindings.js
+++ b/lib/features/keyboard/BpmnKeyboardBindings.js
@@ -34,7 +34,7 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio
    * Add keyboard binding if respective editor action
    * is registered.
    *
-   * @param {String} action name
+   * @param {string} action name
    * @param {Function} fn that implements the key binding
    */
   function addListener(action, fn) {
diff --git a/lib/features/label-editing/cmd/UpdateLabelHandler.js b/lib/features/label-editing/cmd/UpdateLabelHandler.js
index 63678b93..0c4e44ee 100644
--- a/lib/features/label-editing/cmd/UpdateLabelHandler.js
+++ b/lib/features/label-editing/cmd/UpdateLabelHandler.js
@@ -31,7 +31,7 @@ export default function UpdateLabelHandler(modeling, textRenderer) {
    * Element parameter can be label itself or connection (i.e. sequence flow).
    *
    * @param {djs.model.Base} element
-   * @param {String} text
+   * @param {string} text
    */
   function setText(element, text) {
 
diff --git a/lib/features/modeling/BpmnLayouter.js b/lib/features/modeling/BpmnLayouter.js
index c629d0b0..9cbbb78c 100644
--- a/lib/features/modeling/BpmnLayouter.js
+++ b/lib/features/modeling/BpmnLayouter.js
@@ -98,6 +98,8 @@ BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
       manhattanOptions = {
         preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd)
       };
+    } else if (isExpandedSubProcess(source) || isExpandedSubProcess(target)) {
+      manhattanOptions = getSubProcessManhattanOptions(source);
     } else if (is(source, 'bpmn:Gateway')) {
       manhattanOptions = {
         preferredLayouts: [ 'v:h' ]
@@ -177,6 +179,17 @@ function getMessageFlowPreserveDocking(source, target) {
   return null;
 }
 
+function getSubProcessManhattanOptions(source) {
+  return {
+    preferredLayouts: [ 'straight', 'h:h' ],
+    preserveDocking: getSubProcessPreserveDocking(source)
+  };
+}
+
+function getSubProcessPreserveDocking(source) {
+  return isExpandedSubProcess(source) ? 'target' : 'source';
+}
+
 function getConnectionDocking(point, shape) {
   return point ? (point.original || point) : getMid(shape);
 }
diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js
index d40fcaed..24a8f695 100644
--- a/lib/features/modeling/ElementFactory.js
+++ b/lib/features/modeling/ElementFactory.js
@@ -229,7 +229,7 @@ ElementFactory.prototype.createParticipantShape = function(attrs) {
  *
  * @param {Base} element
  * @param {Object} attrs (in/out map of attributes)
- * @param {Array} attributeNames name of attributes to apply
+ * @param {Array} attributeNames name of attributes to apply
  */
 function applyAttributes(element, attrs, attributeNames) {
 
@@ -246,7 +246,7 @@ function applyAttributes(element, attrs, attributeNames) {
  *
  * @param {Base} element
  * @param {Object} attrs (in/out map of attributes)
- * @param {String} attributeName to apply
+ * @param {string} attributeName to apply
  */
 function applyAttribute(element, attrs, attributeName) {
   element[attributeName] = attrs[attributeName];
diff --git a/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js b/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js
index 88df818c..b56820db 100644
--- a/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js
+++ b/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js
@@ -173,7 +173,7 @@ AdaptiveLabelPositioningBehavior.$inject = [
  *
  * @param {Shape} element
  *
- * @return {Array}
+ * @return {Array}
  */
 function getTakenHostAlignments(element) {
 
@@ -204,7 +204,7 @@ function getTakenHostAlignments(element) {
  *
  * @param {Shape} element
  *
- * @return {Array}
+ * @return {Array}
  */
 function getTakenConnectionAlignments(element) {
 
@@ -230,7 +230,7 @@ function getTakenConnectionAlignments(element) {
  *
  * @param  {Shape} element
  *
- * @return {String} positioning identifier
+ * @return {string} positioning identifier
  */
 function getOptimalPosition(element) {
 
diff --git a/lib/features/modeling/behavior/GroupBehavior.js b/lib/features/modeling/behavior/GroupBehavior.js
index 6a50c2f2..2cba2453 100644
--- a/lib/features/modeling/behavior/GroupBehavior.js
+++ b/lib/features/modeling/behavior/GroupBehavior.js
@@ -100,7 +100,7 @@ export default function GroupBehavior(
    *
    * @param {Array} elements
    * @param {ModdleElement} categoryValue
-   * @return {Boolean}
+   * @return {boolean}
    */
   function isReferenced(elements, categoryValue) {
     return elements.some(function(e) {
diff --git a/lib/features/modeling/behavior/ResizeBehavior.js b/lib/features/modeling/behavior/ResizeBehavior.js
index f72d16d2..935752cf 100644
--- a/lib/features/modeling/behavior/ResizeBehavior.js
+++ b/lib/features/modeling/behavior/ResizeBehavior.js
@@ -6,6 +6,8 @@ import { getParticipantResizeConstraints } from './util/ResizeUtil';
 
 var HIGH_PRIORITY = 1500;
 
+export var LANE_MIN_DIMENSIONS = { width: 300, height: 60 };
+
 export var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 };
 
 export var SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 };
diff --git a/lib/features/modeling/behavior/SpaceToolBehavior.js b/lib/features/modeling/behavior/SpaceToolBehavior.js
index 64e19ee3..4949f5bb 100644
--- a/lib/features/modeling/behavior/SpaceToolBehavior.js
+++ b/lib/features/modeling/behavior/SpaceToolBehavior.js
@@ -5,21 +5,38 @@ import { is } from '../../../util/ModelUtil';
 import { isExpanded } from '../../../util/DiUtil';
 
 import {
+  LANE_MIN_DIMENSIONS,
   PARTICIPANT_MIN_DIMENSIONS,
   SUB_PROCESS_MIN_DIMENSIONS,
   TEXT_ANNOTATION_MIN_DIMENSIONS
 } from './ResizeBehavior';
 
+import { getChildLanes } from '../util/LaneUtil';
+
+var max = Math.max;
+
+
 export default function SpaceToolBehavior(eventBus) {
   eventBus.on('spaceTool.getMinDimensions', function(context) {
     var shapes = context.shapes,
+        axis = context.axis,
+        start = context.start,
         minDimensions = {};
 
     forEach(shapes, function(shape) {
       var id = shape.id;
 
       if (is(shape, 'bpmn:Participant')) {
-        minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS;
+
+        if (isHorizontal(axis)) {
+          minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS;
+        } else {
+          minDimensions[ id ] = {
+            width: PARTICIPANT_MIN_DIMENSIONS.width,
+            height: getParticipantMinHeight(shape, start)
+          };
+        }
+
       }
 
       if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
@@ -36,3 +53,72 @@ export default function SpaceToolBehavior(eventBus) {
 }
 
 SpaceToolBehavior.$inject = [ 'eventBus' ];
+
+
+// helpers //////////
+function isHorizontal(axis) {
+  return axis === 'x';
+}
+
+/**
+ * Get minimum height for participant taking lanes into account.
+ *
+ * @param {} participant
+ * @param {number} start
+ *
+ * @returns {Object}
+ */
+function getParticipantMinHeight(participant, start) {
+  var lanesMinHeight;
+
+  if (!hasChildLanes(participant)) {
+    return PARTICIPANT_MIN_DIMENSIONS.height;
+  }
+
+  lanesMinHeight = getLanesMinHeight(participant, start);
+
+  return max(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight);
+}
+
+function hasChildLanes(element) {
+  return !!getChildLanes(element).length;
+}
+
+function getLanesMinHeight(participant, resizeStart) {
+  var lanes = getChildLanes(participant),
+      resizedLane;
+
+  // find the nested lane which is currently resized
+  resizedLane = findResizedLane(lanes, resizeStart);
+
+  // resized lane cannot shrink below the minimum height
+  // but remaining lanes' dimensions are kept intact
+  return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height;
+}
+
+/**
+ * Find nested lane which is currently resized.
+ *
+ * @param {Array} lanes
+ * @param {number} resizeStart
+ */
+function findResizedLane(lanes, resizeStart) {
+  var i, lane, childLanes;
+
+  for (i = 0; i < lanes.length; i++) {
+    lane = lanes[i];
+
+    // resizing current lane or a lane nested
+    if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) {
+      childLanes = getChildLanes(lane);
+
+      // a nested lane is resized
+      if (childLanes.length) {
+        return findResizedLane(childLanes, resizeStart);
+      }
+
+      // current lane is the resized one
+      return lane;
+    }
+  }
+}
diff --git a/lib/features/modeling/behavior/util/LineAttachmentUtil.js b/lib/features/modeling/behavior/util/LineAttachmentUtil.js
index ac6acc69..da90b8fa 100644
--- a/lib/features/modeling/behavior/util/LineAttachmentUtil.js
+++ b/lib/features/modeling/behavior/util/LineAttachmentUtil.js
@@ -6,9 +6,9 @@ var sqrt = Math.sqrt,
 /**
  * Calculate the square (power to two) of a number.
  *
- * @param {Number} n
+ * @param {number} n
  *
- * @return {Number}
+ * @return {number}
  */
 function sq(n) {
   return Math.pow(n, 2);
@@ -20,7 +20,7 @@ function sq(n) {
  * @param {Point} p1
  * @param {Point} p2
  *
- * @return {Number}
+ * @return {number}
  */
 function getDistance(p1, p2) {
   return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
@@ -127,7 +127,7 @@ export function getAttachment(point, line) {
  * @param {Point} s1 segment start
  * @param {Point} s2 segment end
  * @param {Point} cc circle center
- * @param {Number} cr circle radius
+ * @param {number} cr circle radius
  *
  * @return {Array} intersections
  */
diff --git a/lib/features/modeling/behavior/util/ResizeUtil.js b/lib/features/modeling/behavior/util/ResizeUtil.js
index bf0bb2a9..f0bfd86e 100644
--- a/lib/features/modeling/behavior/util/ResizeUtil.js
+++ b/lib/features/modeling/behavior/util/ResizeUtil.js
@@ -9,6 +9,8 @@ import {
   getLanesRoot
 } from '../../../modeling/util/LaneUtil';
 
+import { LANE_MIN_DIMENSIONS } from '../ResizeBehavior';
+
 var abs = Math.abs,
     min = Math.min,
     max = Math.max;
@@ -31,9 +33,7 @@ function addMax(trbl, attr, value) {
   return addToTrbl(trbl, attr, value, max);
 }
 
-var LANE_MIN_HEIGHT = 60,
-    LANE_MIN_WIDTH = 300,
-    LANE_RIGHT_PADDING = 20,
+var LANE_RIGHT_PADDING = 20,
     LANE_LEFT_PADDING = 50,
     LANE_TOP_PADDING = 20,
     LANE_BOTTOM_PADDING = 20;
@@ -54,10 +54,10 @@ export function getParticipantResizeConstraints(laneShape, resizeDirection, bala
       minTrbl = {};
 
   if (/e/.test(resizeDirection)) {
-    minTrbl.right = laneTrbl.left + LANE_MIN_WIDTH;
+    minTrbl.right = laneTrbl.left + LANE_MIN_DIMENSIONS.width;
   } else
   if (/w/.test(resizeDirection)) {
-    minTrbl.left = laneTrbl.right - LANE_MIN_WIDTH;
+    minTrbl.left = laneTrbl.right - LANE_MIN_DIMENSIONS.width;
   }
 
   allLanes.forEach(function(other) {
@@ -72,12 +72,12 @@ export function getParticipantResizeConstraints(laneShape, resizeDirection, bala
 
       // max top size (based on next element)
       if (balanced && abs(laneTrbl.top - otherTrbl.bottom) < 10) {
-        addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_HEIGHT);
+        addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
       }
 
       // min top size (based on self or nested element)
       if (abs(laneTrbl.top - otherTrbl.top) < 5) {
-        addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_HEIGHT);
+        addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
       }
     }
 
@@ -89,12 +89,12 @@ export function getParticipantResizeConstraints(laneShape, resizeDirection, bala
 
       // max bottom size (based on previous element)
       if (balanced && abs(laneTrbl.bottom - otherTrbl.top) < 10) {
-        addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_HEIGHT);
+        addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
       }
 
       // min bottom size (based on self or nested element)
       if (abs(laneTrbl.bottom - otherTrbl.bottom) < 5) {
-        addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_HEIGHT);
+        addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
       }
     }
   });
diff --git a/lib/features/modeling/util/ModelingUtil.js b/lib/features/modeling/util/ModelingUtil.js
index 871c777c..92d14780 100644
--- a/lib/features/modeling/util/ModelingUtil.js
+++ b/lib/features/modeling/util/ModelingUtil.js
@@ -9,9 +9,9 @@ import { is } from '../../../util/ModelUtil';
  * Return true if element has any of the given types.
  *
  * @param {djs.model.Base} element
- * @param {Array} types
+ * @param {Array} types
  *
- * @return {Boolean}
+ * @return {boolean}
  */
 export function isAny(element, types) {
   return some(types, function(t) {
@@ -24,7 +24,7 @@ export function isAny(element, types) {
  * Return the parent of the element with any of the given types.
  *
  * @param {djs.model.Base} element
- * @param {String|Array} anyType
+ * @param {string|Array} anyType
  *
  * @return {djs.model.Base}
  */
diff --git a/lib/features/popup-menu/util/TypeUtil.js b/lib/features/popup-menu/util/TypeUtil.js
index 431558d3..3f65d89e 100644
--- a/lib/features/popup-menu/util/TypeUtil.js
+++ b/lib/features/popup-menu/util/TypeUtil.js
@@ -14,7 +14,7 @@ import {
  *
  * @param {djs.model.Base} element
  *
- * @return {Boolean}
+ * @return {boolean}
  */
 export function isDifferentType(element) {
 
diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js
index bd318c37..bbf222a8 100644
--- a/lib/features/rules/BpmnRules.js
+++ b/lib/features/rules/BpmnRules.js
@@ -201,7 +201,7 @@ BpmnRules.prototype.canCopy = canCopy;
  * Checks if given element can be used for starting connection.
  *
  * @param  {Element} source
- * @return {Boolean}
+ * @return {boolean}
  */
 function canStartConnection(element) {
   if (nonExistingOrLabel(element)) {
@@ -435,7 +435,7 @@ function canConnect(source, target, connection) {
 /**
  * Can an element be dropped into the target element
  *
- * @return {Boolean}
+ * @return {boolean}
  */
 function canDrop(element, target, position) {
 
diff --git a/lib/features/search/BpmnSearchProvider.js b/lib/features/search/BpmnSearchProvider.js
index 0f8f351d..d606f1a2 100644
--- a/lib/features/search/BpmnSearchProvider.js
+++ b/lib/features/search/BpmnSearchProvider.js
@@ -39,10 +39,10 @@ BpmnSearchProvider.$inject = [
  *
  *  :
  *  {
- *    normal|matched: 
+ *    normal|matched: 
  *  }
  *
- * @param  {String} pattern
+ * @param  {string} pattern
  * @return {Array}
  */
 BpmnSearchProvider.prototype.find = function(pattern) {
diff --git a/lib/import/BpmnTreeWalker.js b/lib/import/BpmnTreeWalker.js
index eaee6061..2b2342b3 100644
--- a/lib/import/BpmnTreeWalker.js
+++ b/lib/import/BpmnTreeWalker.js
@@ -19,9 +19,9 @@ var diRefs = new Refs(
  * Returns true if an element has the given meta-model type
  *
  * @param  {ModdleElement}  element
- * @param  {String}         type
+ * @param  {string}         type
  *
- * @return {Boolean}
+ * @return {boolean}
  */
 function is(element, type) {
   return element.$instanceOf(type);
diff --git a/lib/util/LabelUtil.js b/lib/util/LabelUtil.js
index 5c6513bf..e23a633d 100644
--- a/lib/util/LabelUtil.js
+++ b/lib/util/LabelUtil.js
@@ -17,7 +17,7 @@ export var FLOW_LABEL_INDENT = 15;
  * Returns true if the given semantic has an external label
  *
  * @param {BpmnElement} semantic
- * @return {Boolean} true if has label
+ * @return {boolean} true if has label
  */
 export function isLabelExternal(semantic) {
   return is(semantic, 'bpmn:Event') ||
@@ -35,7 +35,7 @@ export function isLabelExternal(semantic) {
  * Returns true if the given element has an external label
  *
  * @param {djs.model.shape} element
- * @return {Boolean} true if has label
+ * @return {boolean} true if has label
  */
 export function hasExternalLabel(element) {
   return isLabel(element.label);
diff --git a/lib/util/ModelUtil.js b/lib/util/ModelUtil.js
index e45af72a..d4506f8a 100644
--- a/lib/util/ModelUtil.js
+++ b/lib/util/ModelUtil.js
@@ -2,9 +2,9 @@
  * Is an element of the given BPMN type?
  *
  * @param  {djs.model.Base|ModdleElement} element
- * @param  {String} type
+ * @param  {string} type
  *
- * @return {Boolean}
+ * @return {boolean}
  */
 export function is(element, type) {
   var bo = getBusinessObject(element);
diff --git a/package-lock.json b/package-lock.json
index 802f1952..14d5f741 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "bpmn-js",
-  "version": "6.4.2",
+  "version": "6.5.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1103,7 +1103,8 @@
     "bpmn-font": {
       "version": "0.9.3",
       "resolved": "https://registry.npmjs.org/bpmn-font/-/bpmn-font-0.9.3.tgz",
-      "integrity": "sha512-kzRGXGLzTROLRNCSskkOyj/+SbtTAn2unKfgB9tNt7RWJFybg/Wbe9YjK2ALotI3b64wwlCTkAalXiTiskP6dg=="
+      "integrity": "sha512-kzRGXGLzTROLRNCSskkOyj/+SbtTAn2unKfgB9tNt7RWJFybg/Wbe9YjK2ALotI3b64wwlCTkAalXiTiskP6dg==",
+      "dev": true
     },
     "bpmn-moddle": {
       "version": "6.0.6",
@@ -2026,9 +2027,9 @@
       "dev": true
     },
     "diagram-js": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-6.4.1.tgz",
-      "integrity": "sha512-VqzydNl6RmnWuHT1B82VId7mH+TG+yZYkgEDp5BizaVDJyhl2F9Z1/q+X9FUQaBq08ZMW1NQP/Bljn56nGHjKw==",
+      "version": "6.6.1",
+      "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-6.6.1.tgz",
+      "integrity": "sha512-3SlXwT2ieXCZkQn8dVZWfNry9+6d4R+0Q57Oz9t/SfIyNIrRPg0c9IlsaTHpGUhPE3fossXPDjmvqjJD0lmBLw==",
       "requires": {
         "css.escape": "^1.5.1",
         "didi": "^4.0.0",
diff --git a/package.json b/package.json
index ec19e219..0f277099 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "bpmn-js",
-  "version": "6.4.2",
+  "version": "6.5.0",
   "description": "A bpmn 2.0 toolkit and web modeler",
   "scripts": {
     "all": "run-s lint test distro test:distro",
@@ -39,6 +39,7 @@
     "*.css"
   ],
   "devDependencies": {
+    "bpmn-font": "^0.9.3",
     "camunda-bpmn-moddle": "^4.0.1",
     "chai": "^4.1.2",
     "chai-match": "^1.1.1",
@@ -79,10 +80,9 @@
     "webpack": "^4.35.3"
   },
   "dependencies": {
-    "bpmn-font": "^0.9.3",
     "bpmn-moddle": "^6.0.6",
     "css.escape": "^1.5.1",
-    "diagram-js": "^6.4.1",
+    "diagram-js": "^6.6.1",
     "diagram-js-direct-editing": "^1.6.1",
     "ids": "^1.0.0",
     "inherits": "^2.0.1",
diff --git a/test/fixtures/bpmn/boundary-events.bpmn b/test/fixtures/bpmn/boundary-events.bpmn
index 3502601f..b97c6598 100644
--- a/test/fixtures/bpmn/boundary-events.bpmn
+++ b/test/fixtures/bpmn/boundary-events.bpmn
@@ -1,5 +1,5 @@
 
-
+
   
     
       
@@ -15,43 +15,32 @@
     
     
     
+    
   
   
     
+      
+        
+      
+      
+        
+        
+        
+          
+        
+      
       
         
         
           
         
       
-      
-        
-      
       
         
       
-      
-        
-        
-        
-          
-        
-      
-      
-        
-        
-          
-        
-      
       
         
       
-      
-        
-        
-          
-        
-      
       
         
       
@@ -61,6 +50,24 @@
           
         
       
+      
+        
+        
+          
+        
+      
+      
+        
+        
+          
+        
+      
+      
+        
+        
+          
+        
+      
     
   
 
diff --git a/test/helper/index.js b/test/helper/index.js
index 58902959..10bfdd8d 100644
--- a/test/helper/index.js
+++ b/test/helper/index.js
@@ -142,7 +142,7 @@ export function bootstrapBpmnJS(BpmnJS, diagram, options, locals) {
  *
  * });
  *
- * @param  {String} xml document to display
+ * @param  {string} xml document to display
  * @param  {Object} (options) optional options to be passed to the diagram upon instantiation
  * @param  {Object|Function} locals  the local overrides to be used by the diagram or a function that produces them
  * @return {Function}         a function to be passed to beforeEach
@@ -170,7 +170,7 @@ export function bootstrapModeler(diagram, options, locals) {
  *
  * });
  *
- * @param  {String} xml document to display
+ * @param  {string} xml document to display
  * @param  {Object} (options) optional options to be passed to the diagram upon instantiation
  * @param  {Object|Function} locals  the local overrides to be used by the diagram or a function that produces them
  * @return {Function}         a function to be passed to beforeEach
diff --git a/test/integration/custom-elements/CustomElementFactory.js b/test/integration/custom-elements/CustomElementFactory.js
index 5d42b9f9..c31ceacf 100644
--- a/test/integration/custom-elements/CustomElementFactory.js
+++ b/test/integration/custom-elements/CustomElementFactory.js
@@ -61,7 +61,7 @@ CustomElementFactory.$inject = [ 'injector' ];
  *   return shapes[type];
  *
  *
- * @param  {String} type
+ * @param  {string} type
  *
  * @return {Bounds} { width, height}
  */
diff --git a/test/spec/ViewerSpec.js b/test/spec/ViewerSpec.js
index 29bc3824..fcf3ac78 100644
--- a/test/spec/ViewerSpec.js
+++ b/test/spec/ViewerSpec.js
@@ -267,6 +267,21 @@ describe('Viewer', function() {
       });
     });
 
+
+    it('should throw error due to missing diagram', function(done) {
+
+      var xml = require('../fixtures/bpmn/empty-definitions.bpmn');
+
+      // when
+      createViewer(xml, function(err, warnings) {
+
+        // then
+        expect(err.message).to.eql('no diagram to display');
+
+        done();
+      });
+    });
+
   });
 
 
@@ -549,25 +564,6 @@ describe('Viewer', function() {
 
     });
 
-
-    it('should throw error due to missing diagram', function(done) {
-
-      var xml = require('../fixtures/bpmn/empty-definitions.bpmn');
-
-      // given
-      viewer = new Viewer({ container: container, additionalModules: testModules });
-
-      // when
-      viewer.importXML(xml, function(err) {
-
-        // then
-        expect(err.message).to.eql('no diagram to display');
-
-        done();
-      });
-
-    });
-
   });
 
 
diff --git a/test/spec/draw/BpmnRendererSpec.js b/test/spec/draw/BpmnRendererSpec.js
index 46d50c25..535fb9e9 100644
--- a/test/spec/draw/BpmnRendererSpec.js
+++ b/test/spec/draw/BpmnRendererSpec.js
@@ -416,8 +416,8 @@ describe('draw - bpmn renderer', function() {
        *
        * @param {djs.model.base} element - Element.
        * @param {SVG} gfx - Graphics of element.
-       * @param {String} fillColor - Fill color to expect.
-       * @param {String} strokeColor - Stroke color to expect.
+       * @param {string} fillColor - Fill color to expect.
+       * @param {string} strokeColor - Stroke color to expect.
        */
       function expectColors(element, gfx, fillColor, strokeColor) {
         var djsVisual = domQuery('.djs-visual', gfx);
diff --git a/test/spec/features/auto-place/AutoPlace.boundary-events.bpmn b/test/spec/features/auto-place/BpmnAutoPlace.boundary-events.bpmn
similarity index 100%
rename from test/spec/features/auto-place/AutoPlace.boundary-events.bpmn
rename to test/spec/features/auto-place/BpmnAutoPlace.boundary-events.bpmn
diff --git a/test/spec/features/auto-place/AutoPlace.bpmn b/test/spec/features/auto-place/BpmnAutoPlace.bpmn
similarity index 100%
rename from test/spec/features/auto-place/AutoPlace.bpmn
rename to test/spec/features/auto-place/BpmnAutoPlace.bpmn
diff --git a/test/spec/features/auto-place/AutoPlace.multi-connection.bpmn b/test/spec/features/auto-place/BpmnAutoPlace.multi-connection.bpmn
similarity index 100%
rename from test/spec/features/auto-place/AutoPlace.multi-connection.bpmn
rename to test/spec/features/auto-place/BpmnAutoPlace.multi-connection.bpmn
diff --git a/test/spec/features/auto-place/AutoPlaceSpec.js b/test/spec/features/auto-place/BpmnAutoPlaceSpec.js
similarity index 72%
rename from test/spec/features/auto-place/AutoPlaceSpec.js
rename to test/spec/features/auto-place/BpmnAutoPlaceSpec.js
index 993b45db..a672802e 100644
--- a/test/spec/features/auto-place/AutoPlaceSpec.js
+++ b/test/spec/features/auto-place/BpmnAutoPlaceSpec.js
@@ -11,14 +11,12 @@ import selectionModule from 'diagram-js/lib/features/selection';
 
 import { getBusinessObject } from '../../../../lib/util/ModelUtil';
 
-import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
-
 
 describe('features/auto-place', function() {
 
   describe('element placement', function() {
 
-    var diagramXML = require('./AutoPlace.bpmn');
+    var diagramXML = require('./BpmnAutoPlace.bpmn');
 
     before(bootstrapModeler(diagramXML, {
       modules: [
@@ -116,7 +114,7 @@ describe('features/auto-place', function() {
 
   describe('integration', function() {
 
-    var diagramXML = require('./AutoPlace.bpmn');
+    var diagramXML = require('./BpmnAutoPlace.bpmn');
 
     before(bootstrapModeler(diagramXML, {
       modules: [
@@ -174,7 +172,7 @@ describe('features/auto-place', function() {
 
   describe('multi connection handling', function() {
 
-    var diagramXML = require('./AutoPlace.multi-connection.bpmn');
+    var diagramXML = require('./BpmnAutoPlace.multi-connection.bpmn');
 
     before(bootstrapModeler(diagramXML, {
       modules: [
@@ -209,7 +207,7 @@ describe('features/auto-place', function() {
 
   describe('boundary event connection handling', function() {
 
-    var diagramXML = require('./AutoPlace.boundary-events.bpmn');
+    var diagramXML = require('./BpmnAutoPlace.boundary-events.bpmn');
 
     before(bootstrapModeler(diagramXML, {
       modules: [
@@ -241,12 +239,14 @@ describe('features/auto-place', function() {
       expectedBounds: { x: 242, y: -27, width: 100, height: 80 }
     }));
 
+
     it('should place top right of BOUNDARY_TOP_RIGHT without infinite loop', autoPlace({
       element: 'bpmn:Task',
       behind: 'BOUNDARY_TOP_RIGHT',
       expectedBounds: { x: 473, y: -27, width: 100, height: 80 }
     }));
 
+
     it('should place top right of BOUNDARY_SUBPROCESS_TOP', autoPlace({
       element: 'bpmn:Task',
       behind: 'BOUNDARY_SUBPROCESS_TOP',
@@ -255,114 +255,10 @@ describe('features/auto-place', function() {
 
   });
 
-
-  describe('eventbus integration', function() {
-
-    var diagramXML = require('./AutoPlace.bpmn');
-
-    beforeEach(bootstrapModeler(diagramXML, {
-      modules: [
-        autoPlaceModule,
-        coreModule,
-        labelEditingModule,
-        modelingModule,
-        selectionModule
-      ]
-    }));
-
-
-    it('', inject(
-      function(autoPlace, elementFactory, elementRegistry, eventBus) {
-
-        // given
-        var element = elementFactory.createShape({ type: 'bpmn:Task' });
-
-        var source = elementRegistry.get('TASK_2');
-
-        var listener = sinon.spy(function(event) {
-
-          // then
-          expect(event.shape).to.equal(element);
-          expect(event.source).to.equal(source);
-        });
-
-        eventBus.on('autoPlace.start', listener);
-
-        // when
-        autoPlace.append(source, element);
-
-        expect(listener).to.have.been.called;
-      }
-    ));
-
-
-    it('', inject(
-      function(autoPlace, elementFactory, elementRegistry, eventBus) {
-
-        // given
-        var element = elementFactory.createShape({ type: 'bpmn:Task' });
-
-        var source = elementRegistry.get('TASK_2');
-
-        var listener = sinon.spy(function(event) {
-
-          // then
-          expect(event.shape).to.equal(element);
-          expect(event.source).to.equal(source);
-
-          return {
-            x: 0,
-            y: 0
-          };
-        });
-
-        eventBus.on('autoPlace', listener);
-
-        // when
-        autoPlace.append(source, element);
-
-        expect(listener).to.have.been.called;
-
-        expect(getMid(element)).to.eql({
-          x: 0,
-          y: 0
-        });
-      }
-    ));
-
-
-    it('', inject(
-      function(autoPlace, elementFactory, elementRegistry, eventBus) {
-
-        // given
-        var element = elementFactory.createShape({ type: 'bpmn:Task' });
-
-        var source = elementRegistry.get('TASK_2');
-
-        var listener = sinon.spy(function(event) {
-
-          // then
-          expect(event.shape).to.equal(element);
-          expect(event.source).to.equal(source);
-        });
-
-        eventBus.on('autoPlace.end', listener);
-
-        // when
-        autoPlace.append(source, element);
-
-        expect(listener).to.have.been.called;
-      }
-    ));
-
-  });
-
 });
 
 
-
-
-// helpers //////////////////////
+// helpers //////////
 
 function autoPlace(cfg) {
 
diff --git a/test/spec/features/copy-paste/BpmnCopyPasteSpec.js b/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
index 0fe2f3fd..2d3e96d1 100644
--- a/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
+++ b/test/spec/features/copy-paste/BpmnCopyPasteSpec.js
@@ -834,7 +834,7 @@ describe('features/copy-paste', function() {
 /**
  * Integration test involving copying, pasting, moving, undoing and redoing.
  *
- * @param {String|Array} elementIds
+ * @param {string|Array} elementIds
  */
 function integrationTest(elementIds) {
   if (!isArray(elementIds)) {
@@ -1000,7 +1000,7 @@ function _findDescriptorsInTree(elements, tree, depth) {
 /**
  * Copy elements.
  *
- * @param {Array
+
+  
+    
+    
+    
+  
+  
+  
+    
+      
+      
+    
+  
+  
+    
+      
+      
+        
+          
+          
+        
+      
+    
+  
+  
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
+  
+
diff --git a/test/spec/features/modeling/behavior/SpaceToolBehaviorSpec.bpmn b/test/spec/features/modeling/behavior/SpaceToolBehaviorSpec.subprocess.bpmn
similarity index 100%
rename from test/spec/features/modeling/behavior/SpaceToolBehaviorSpec.bpmn
rename to test/spec/features/modeling/behavior/SpaceToolBehaviorSpec.subprocess.bpmn
diff --git a/test/spec/features/modeling/lanes/AddLaneSpec.js b/test/spec/features/modeling/lanes/AddLaneSpec.js
index baf480da..6c98dfe6 100644
--- a/test/spec/features/modeling/lanes/AddLaneSpec.js
+++ b/test/spec/features/modeling/lanes/AddLaneSpec.js
@@ -5,6 +5,7 @@ import {
 } from 'test/TestHelper';
 
 import {
+  map,
   pick
 } from 'min-dash';
 
@@ -270,6 +271,45 @@ describe('features/modeling - add Lane', function() {
   });
 
 
+  describe('flow node handling', function() {
+
+    var diagramXML = require('./lanes.bpmn');
+
+    beforeEach(bootstrapModeler(diagramXML, {
+      modules: testModules
+    }));
+
+
+    it('should move flow nodes and sequence flows', inject(function(elementRegistry, modeling) {
+
+      // given
+      var laneShape = elementRegistry.get('Nested_Lane_B'),
+          task_Boundary = elementRegistry.get('Task_Boundary'),
+          boundary = elementRegistry.get('Boundary'),
+          sequenceFlow = elementRegistry.get('SequenceFlow'),
+          sequenceFlow_From_Boundary = elementRegistry.get('SequenceFlow_From_Boundary');
+
+      // when
+      var newLane = modeling.addLane(laneShape, 'top');
+
+      // then
+      expect(task_Boundary).to.have.position({ x: 264, y: -57 });
+      expect(boundary).to.have.position({ x: 311, y: 5 });
+
+      expect(sequenceFlow_From_Boundary).to.have.waypoints([
+        { x: 329, y: 161 - newLane.height },
+        { x: 329, y: 188 - newLane.height },
+        { x: 482, y: 188 - newLane.height },
+        { x: 482, y: 143 - newLane.height }
+      ]);
+
+      expect(sequenceFlow).to.have.waypoints([
+        { x: 364, y: 103 - newLane.height },
+        { x: 432, y: 103 - newLane.height }
+      ]);
+    }));
+  });
+
   describe('flow node handling', function() {
 
     var diagramXML = require('./lanes-flow-nodes.bpmn');
@@ -278,6 +318,7 @@ describe('features/modeling - add Lane', function() {
       modules: testModules
     }));
 
+
     function addLaneAbove(laneId) {
 
       return getBpmnJS().invoke(function(elementRegistry, modeling) {
@@ -289,19 +330,32 @@ describe('features/modeling - add Lane', function() {
       });
     }
 
+    function addLaneBelow(laneId) {
+
+      return getBpmnJS().invoke(function(elementRegistry, modeling) {
+        var existingLane = elementRegistry.get(laneId);
+
+        expect(existingLane).to.exist;
+
+        return modeling.addLane(existingLane, 'bottom');
+      });
+    }
+
 
     it('should move flow nodes', inject(function(elementRegistry, modeling) {
 
       // given
       var task_Boundary = elementRegistry.get('Task_Boundary'),
-          boundary = elementRegistry.get('Boundary');
+          taskPosition = getPosition(task_Boundary),
+          boundary = elementRegistry.get('Boundary'),
+          boundaryPosition = getPosition(boundary);
 
       // when
       addLaneAbove('Nested_Lane_B');
 
       // then
-      expect(task_Boundary).to.have.position({ x: 344, y: -7 });
-      expect(boundary).to.have.position({ x: 391, y: 55 });
+      expect(task_Boundary).to.have.position({ x: taskPosition.x, y: taskPosition.y - 120 });
+      expect(boundary).to.have.position({ x: boundaryPosition.x, y: boundaryPosition.y - 120 });
     }));
 
 
@@ -309,22 +363,54 @@ describe('features/modeling - add Lane', function() {
 
       // given
       var sequenceFlow = elementRegistry.get('SequenceFlow'),
-          sequenceFlow_From_Boundary = elementRegistry.get('SequenceFlow_From_Boundary');
+          sequenceFlowWaypoints = sequenceFlow.waypoints,
+          sequenceFlow_From_Boundary = elementRegistry.get('SequenceFlow_From_Boundary'),
+          sequenceFlow_From_BoundaryWaypoints = sequenceFlow_From_Boundary.waypoints;
 
       // when
       addLaneAbove('Nested_Lane_B');
 
       // then
-      expect(sequenceFlow_From_Boundary).to.have.waypoints([
-        { x: 409, y: 91 },
-        { x: 409, y: 118 },
-        { x: 562, y: 118 },
-        { x: 562, y: 73 }
-      ]);
+      expect(sequenceFlow_From_Boundary).to.have.waypoints(
+        moveWaypoints(sequenceFlow_From_BoundaryWaypoints, 0, -120)
+      );
 
-      expect(sequenceFlow).to.have.waypoints([
-        { x: 444, y: 33 },
-        { x: 512, y: 33 }
+      expect(sequenceFlow).to.have.waypoints(
+        moveWaypoints(sequenceFlowWaypoints, 0, -120)
+      );
+    }));
+
+
+    it('should move message flows when lane added above', inject(function(elementRegistry) {
+
+      // given
+      var messageFlow = elementRegistry.get('MessageFlowAbove'),
+          messageFlowWaypoints = messageFlow.waypoints;
+
+      // when
+      addLaneAbove('Nested_Lane_B');
+
+      // then
+      expect(messageFlow).to.have.waypoints([
+        movePosition(messageFlowWaypoints[0], 0, -120),
+        messageFlowWaypoints[1]
+      ]);
+    }));
+
+
+    it('should move message flows when lane added below', inject(function(elementRegistry) {
+
+      // given
+      var messageFlow = elementRegistry.get('MessageFlowBelow'),
+          messageFlowWaypoints = messageFlow.waypoints;
+
+      // when
+      addLaneBelow('Nested_Lane_B');
+
+      // then
+      expect(messageFlow).to.have.waypoints([
+        messageFlowWaypoints[0],
+        movePosition(messageFlowWaypoints[1], 0, 120)
       ]);
     }));
 
@@ -333,7 +419,8 @@ describe('features/modeling - add Lane', function() {
 
       // given
       var event = elementRegistry.get('Event'),
-          label = event.label;
+          label = event.label,
+          labelPosition = getPosition(label);
 
       // TODO(nikku): consolidate import + editing behavior => not consistent right now
 
@@ -344,7 +431,10 @@ describe('features/modeling - add Lane', function() {
       addLaneAbove('Nested_Lane_B');
 
       // then
-      expect(label.y).to.eql(58);
+      expect(label).to.have.position({
+        x: labelPosition.x,
+        y: labelPosition.y - 120
+      });
     }));
 
   });
@@ -407,4 +497,24 @@ function padEvent(entry) {
       clientY: 100
     };
   });
-}
\ No newline at end of file
+}
+
+function getPosition(element) {
+  return {
+    x: element.x,
+    y: element.y
+  };
+}
+
+function moveWaypoints(waypoints, deltaX, deltaY) {
+  return map(waypoints, function(waypoint) {
+    return movePosition(waypoint, deltaX, deltaY);
+  });
+}
+
+function movePosition(point, deltaX, deltaY) {
+  return {
+    x: point.x + deltaX,
+    y: point.y + deltaY
+  };
+}
diff --git a/test/spec/features/modeling/lanes/lanes-flow-nodes.bpmn b/test/spec/features/modeling/lanes/lanes-flow-nodes.bpmn
index dce2514e..85d74419 100644
--- a/test/spec/features/modeling/lanes/lanes-flow-nodes.bpmn
+++ b/test/spec/features/modeling/lanes/lanes-flow-nodes.bpmn
@@ -1,7 +1,11 @@
 
-
+
   
     
+    
+    
+    
+    
   
   
     
@@ -11,13 +15,13 @@
         Event
         Boundary
         
-          
           
             Task_Boundary
             Task
             Event
             Boundary
           
+          
         
       
     
@@ -32,57 +36,76 @@
     
       SequenceFlow_From_Boundary
     
-    
     
+    
   
+  
+  
   
     
       
-        
+        
       
-      
-        
+      
+        
       
-      
-        
-        
-          
-        
+      
+        
       
-      
-        
+      
+        
       
       
-        
-        
-        
-        
+        
+        
+        
+        
         
           
         
       
       
-        
-        
+        
+        
         
-          
+          
         
       
-      
-        
+      
+        
       
-      
-        
-      
-      
-        
+      
+        
       
       
-        
+        
         
-          
+          
         
       
+      
+        
+        
+          
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+        
+      
+      
+        
+        
+        
+          
+        
+      
     
   
 
diff --git a/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.js b/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.js
index 07fed5c1..c2f4f1e7 100644
--- a/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.js
+++ b/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.js
@@ -552,4 +552,119 @@ describe('features/modeling - layout', function() {
 
   });
 
+
+  describe('subProcess', function() {
+
+    var diagramXML = require('./LayoutSequenceFlowSpec.subProcess.bpmn');
+
+    var testModules = [ coreModule, modelingModule ];
+
+    beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
+
+
+    it('should layout straight between subProcesses (top -> bottom)', function() {
+
+      // when
+      var connection = connect('SubProcess_Center', 'SubProcess_Bottom'),
+          source = connection.source,
+          target = connection.target;
+
+      var expectedX = getMid(target).x;
+
+      // then
+      expect(connection).to.have.waypoints([
+        { x: expectedX, y: source.y + source.height },
+        { x: expectedX, y: target.y }
+      ]);
+    });
+
+
+    it('should layout straight between subProcesses (bottom -> top)', function() {
+
+      // when
+      var connection = connect('SubProcess_Bottom', 'SubProcess_Center'),
+          source = connection.source,
+          target = connection.target;
+
+      var expectedX = getMid(target).x;
+
+      // then
+      expect(connection).to.have.waypoints([
+        { x: expectedX, y: source.y },
+        { x: expectedX, y: target.y + target.height }
+      ]);
+    });
+
+
+    it('should layout straight between subProcess and task next to it (subProcess -> task)',
+      function() {
+
+        // when
+        var connection = connect('SubProcess_Center', 'Task_Right'),
+            source = connection.source,
+            target = connection.target;
+
+        var expectedY = getMid(target).y;
+
+        // then
+        expect(connection).to.have.waypoints([
+          { x: source.x + source.width, y: expectedY },
+          { x: target.x, y: expectedY }
+        ]);
+      }
+    );
+
+
+    it('should layout straight between subProcess and task next to it (task -> subProcess)',
+      function() {
+
+        // when
+        var connection = connect('Task_Right', 'SubProcess_Center'),
+            source = connection.source,
+            target = connection.target;
+
+        var expectedY = getMid(source).y;
+
+        // then
+        expect(connection).to.have.waypoints([
+          { x: source.x, y: expectedY },
+          { x: target.x + target.width, y: expectedY }
+        ]);
+      }
+    );
+
+
+    it('should layout straight between subProcess and task above (subProcess -> task)', function() {
+
+      // when
+      var connection = connect('SubProcess_Center', 'Task_Top'),
+          source = connection.source,
+          target = connection.target;
+
+      var expectedX = getMid(target).x;
+
+      // then
+      expect(connection).to.have.waypoints([
+        { x: expectedX, y: source.y },
+        { x: expectedX, y: target.y + target.height }
+      ]);
+    });
+
+
+    it('should layout straight between subProcess and task above (task -> subProcess)', function() {
+
+      // when
+      var connection = connect('Task_Top', 'SubProcess_Center'),
+          source = connection.source,
+          target = connection.target;
+
+      var expectedX = getMid(source).x;
+
+      // then
+      expect(connection).to.have.waypoints([
+        { x: expectedX, y: source.y + source.height },
+        { x: expectedX, y: target.y }
+      ]);
+    });
+  });
 });
diff --git a/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.subProcess.bpmn b/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.subProcess.bpmn
new file mode 100644
index 00000000..0b1a6edc
--- /dev/null
+++ b/test/spec/features/modeling/layout/LayoutSequenceFlowSpec.subProcess.bpmn
@@ -0,0 +1,25 @@
+
+
+  
+    
+    
+    
+    
+  
+  
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
+  
+
diff --git a/test/util/KeyEvents.js b/test/util/KeyEvents.js
index 3ac7bb69..dfc0ceee 100644
--- a/test/util/KeyEvents.js
+++ b/test/util/KeyEvents.js
@@ -6,7 +6,7 @@ import {
 /**
  * Create a fake key event for testing purposes.
  *
- * @param {String|Number} key the key or keyCode/charCode
+ * @param {string|number} key the key or keyCode/charCode
  * @param {Object} [attrs]
  *
  * @return {Event}