function Create(
    canvas,
    dragging,
    eventBus,
    modeling,
    rules
) {

  // rules //////////

  /**
   * Check wether elements can be created.
   *
   * @param {Element[]} elements
   * @param {Shape} target
   * @param {Point} position
   * @param {Element} [source]
   *
   * @return {boolean|null|Object}
   */
  function canCreate(elements, target, position, source, hints) {
    if (!target) {
      return false;
    }

    // ignore child elements and external labels
    elements = filter(elements, function(element) {
      var labelTarget = element.labelTarget;

      return !element.parent && !(isLabel(element) && elements.indexOf(labelTarget) !== -1);
    });

    var shape = find(elements, function(element) {
      return !isConnection(element);
    });

    var attach = false,
        connect = false,
        create = false;

    // (1) attaching single shapes
    if (isSingleShape(elements)) {
      attach = rules.allowed('shape.attach', {
        position: position,
        shape: shape,
        target: target
      });
    }

    if (!attach) {

      // (2) creating elements
      if (isSingleShape(elements)) {
        create = rules.allowed('shape.create', {
          position: position,
          shape: shape,
          source: source,
          target: target
        });
      } else {
        create = rules.allowed('elements.create', {
          elements: elements,
          position: position,
          target: target
        });
      }

    }

    var connectionTarget = hints.connectionTarget;

    // (3) appending single shapes
    if (create || attach) {
      if (shape && source) {
        connect = rules.allowed('connection.create', {
          source: connectionTarget === source ? shape : source,
          target: connectionTarget === source ? source : shape,
          hints: {
            targetParent: target,
            targetAttach: attach
          }
        });
      }

      return {
        attach: attach,
        connect: connect
      };
    }

    // ignore wether or not elements can be created
    if (create === null || attach === null) {
      return null;
    }

    return false;
  }

  function setMarker(element, marker) {
    [ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {

      if (m === marker) {
        canvas.addMarker(element, m);
      } else {
        canvas.removeMarker(element, m);
      }
    });
  }

  // event handling //////////

  eventBus.on([ 'create.move', 'create.hover' ], function(event) {
    var context = event.context,
        elements = context.elements,
        hover = event.hover,
        source = context.source,
        hints = context.hints || {};

    if (!hover) {
      context.canExecute = false;
      context.target = null;

      return;
    }

    ensureConstraints(event);

    var position = {
      x: event.x,
      y: event.y
    };

    var canExecute = context.canExecute = hover && canCreate(elements, hover, position, source, hints);

    if (hover && canExecute !== null) {
      context.target = hover;

      if (canExecute && canExecute.attach) {
        setMarker(hover, MARKER_ATTACH);
      } else {
        setMarker(hover, canExecute ? MARKER_NEW_PARENT : MARKER_NOT_OK);
      }
    }
  });

  eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) {
    var hover = event.hover;

    if (hover) {
      setMarker(hover, null);
    }
  });

  eventBus.on('create.end', function(event) {
    var context = event.context,
        source = context.source,
        shape = context.shape,
        elements = context.elements,
        target = context.target,
        canExecute = context.canExecute,
        attach = canExecute && canExecute.attach,
        connect = canExecute && canExecute.connect,
        hints = context.hints || {};

    if (canExecute === false || !target) {
      return false;
    }

    ensureConstraints(event);

    var position = {
      x: event.x,
      y: event.y
    };

    if (connect) {
      shape = modeling.appendShape(source, shape, position, target, {
        attach: attach,
        connection: connect === true ? {} : connect,
        connectionTarget: hints.connectionTarget
      });
    } else {
      elements = modeling.createElements(elements, position, target, assign({}, hints, {
        attach: attach
      }));

      // update shape
      shape = find(elements, function(element) {
        return !isConnection(element);
      });
    }

    // update elements and shape
    assign(context, {
      elements: elements,
      shape: shape
    });

    assign(event, {
      elements: elements,
      shape: shape
    });
  });

  function cancel() {
    var context = dragging.context();

    if (context && context.prefix === PREFIX) {
      dragging.cancel();
    }
  }

  // cancel on <elements.changed> that is not result of <drag.end>
  eventBus.on('create.init', function() {
    eventBus.on('elements.changed', cancel);

    eventBus.once([ 'create.cancel', 'create.end' ], HIGH_PRIORITY, function() {
      eventBus.off('elements.changed', cancel);
    });
  });

  // API //////////

  /**
   * @param event
   * @param elements
   * @param {any} [context={}]
   */
  this.start = function(event, elements, context) {
    if (!isArray(elements)) {
      elements = [ elements ];
    }

    var shape = find(elements, function(element) {
      return !isConnection(element);
    });

    if (!shape) {

      // at least one shape is required
      return;
    }

    context = assign({
      elements: elements,
      hints: {},
      shape: shape
    }, context || {});

    // make sure each element has x and y
    forEach(elements, function(element) {
      if (!isNumber(element.x)) {
        element.x = 0;
      }

      if (!isNumber(element.y)) {
        element.y = 0;
      }
    });

    var visibleElements = filter(elements, function(element) {
      return !element.hidden;
    });

    var bbox = getBBox(visibleElements);

    // center elements around cursor
    forEach(elements, function(element) {
      if (isConnection(element)) {
        element.waypoints = map(element.waypoints, function(waypoint) {
          return {
            x: waypoint.x - bbox.x - bbox.width / 2,
            y: waypoint.y - bbox.y - bbox.height / 2
          };
        });
      }

      assign(element, {
        x: element.x - bbox.x - bbox.width / 2,
        y: element.y - bbox.y - bbox.height / 2
      });
    });

    dragging.init(event, PREFIX, {
      cursor: 'grabbing',
      autoActivate: true,
      data: {
        shape: shape,
        elements: elements,
        context: context
      }
    });
  };
}
Create new elements through drag and drop.

@param {Canvas} canvas
@param {Dragging} dragging
@param {EventBus} eventBus
@param {Modeling} modeling
@param {Rules} rules