function AttachSupport(injector, eventBus, canvas, rules, modeling) {

  CommandInterceptor.call(this, eventBus);

  var movePreview = injector.get('movePreview', false);


  // remove all the attached elements from the shapes to be validated
  // add all the attached shapes to the overall list of moved shapes
  eventBus.on('shape.move.start', HIGH_PRIORITY, function(e) {

    var context = e.context,
        shapes = context.shapes,
        validatedShapes = context.validatedShapes;

    context.shapes = addAttached(shapes);

    context.validatedShapes = removeAttached(validatedShapes);
  });

  // add attachers to the visual's group
  movePreview && eventBus.on('shape.move.start', LOW_PRIORITY, function(e) {

    var context = e.context,
        shapes = context.shapes,
        attachers = getAttachers(shapes);

    forEach(attachers, function(attacher) {
      movePreview.makeDraggable(context, attacher, true);

      forEach(attacher.labels, function(label) {
        movePreview.makeDraggable(context, label, true);
      });
    });
  });

  // add attach-ok marker to current host
  movePreview && eventBus.on('shape.move.start', function(event) {
    var context = event.context,
        shapes = context.shapes;

    if (shapes.length !== 1) {
      return;
    }

    var shape = shapes[0];

    var host = shape.host;

    if (host) {
      canvas.addMarker(host, MARKER_ATTACH);

      eventBus.once([
        'shape.move.out',
        'shape.move.cleanup'
      ], function() {
        canvas.removeMarker(host, MARKER_ATTACH);
      });
    }
  });

  // add all attachers to move closure
  this.preExecuted('elements.move', HIGH_PRIORITY, function(e) {
    var context = e.context,
        closure = context.closure,
        shapes = context.shapes,
        attachers = getAttachers(shapes);

    forEach(attachers, function(attacher) {
      closure.add(attacher, closure.topLevel[attacher.host.id]);
    });
  });

  // perform the attaching after shapes are done moving
  this.postExecuted('elements.move', function(e) {

    var context = e.context,
        shapes = context.shapes,
        newHost = context.newHost,
        attachers;

    // only single elements can be attached
    // multiply elements can be detached
    if (newHost && shapes.length !== 1) {
      return;
    }

    if (newHost) {
      attachers = shapes;
    } else {

      // find attachers moved without host
      attachers = filter(shapes, function(shape) {
        var host = shape.host;

        return isAttacher(shape) && !includes(shapes, host);
      });
    }

    forEach(attachers, function(attacher) {
      modeling.updateAttachment(attacher, newHost);
    });
  });

  // ensure invalid attachment connections are removed
  this.postExecuted('elements.move', function(e) {

    var shapes = e.context.shapes;

    forEach(shapes, function(shape) {

      forEach(shape.attachers, function(attacher) {

        // remove invalid outgoing connections
        forEach(attacher.outgoing.slice(), function(connection) {
          var allowed = rules.allowed('connection.reconnect', {
            connection: connection,
            source: connection.source,
            target: connection.target
          });

          if (!allowed) {
            modeling.removeConnection(connection);
          }
        });

        // remove invalid incoming connections
        forEach(attacher.incoming.slice(), function(connection) {
          var allowed = rules.allowed('connection.reconnect', {
            connection: connection,
            source: connection.source,
            target: connection.target
          });

          if (!allowed) {
            modeling.removeConnection(connection);
          }
        });
      });
    });
  });

  this.postExecute('shape.create', function(e) {
    var context = e.context,
        shape = context.shape,
        host = context.host;

    if (host) {
      modeling.updateAttachment(shape, host);
    }
  });

  // update attachments if the host is replaced
  this.postExecute('shape.replace', function(e) {

    var context = e.context,
        oldShape = context.oldShape,
        newShape = context.newShape;

    // move the attachers to the new host
    saveClear(oldShape.attachers, function(attacher) {
      var allowed = rules.allowed('elements.move', {
        target: newShape,
        shapes: [ attacher ]
      });

      if (allowed === 'attach') {
        modeling.updateAttachment(attacher, newShape);
      } else {
        modeling.removeShape(attacher);
      }
    });

    // move attachers if new host has different size
    if (newShape.attachers.length) {

      forEach(newShape.attachers, function(attacher) {
        var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
        modeling.moveShape(attacher, delta, attacher.parent);
      });
    }

  });

  // move shape on host resize
  this.postExecute('shape.resize', function(event) {
    var context = event.context,
        shape = context.shape,
        oldBounds = context.oldBounds,
        newBounds = context.newBounds,
        attachers = shape.attachers,
        hints = context.hints || {};

    if (hints.attachSupport === false) {
      return;
    }

    forEach(attachers, function(attacher) {
      var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);

      modeling.moveShape(attacher, delta, attacher.parent);

      forEach(attacher.labels, function(label) {
        modeling.moveShape(label, delta, label.parent);
      });
    });
  });

  // remove attachments
  this.preExecute('shape.delete', function(event) {

    var shape = event.context.shape;

    saveClear(shape.attachers, function(attacher) {
      modeling.removeShape(attacher);
    });

    if (shape.host) {
      modeling.updateAttachment(shape, null);
    }
  });
}
Adds the notion of attached elements to the modeler.

Optionally depends on `diagram-js/lib/features/move` to render
the attached elements during move preview.

Optionally depends on `diagram-js/lib/features/label-support`
to render attached labels during move preview.

@param {Injector} injector
@param {EventBus} eventBus
@param {Canvas} canvas
@param {Rules} rules
@param {Modeling} modeling