123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- import {
- assign
- } from 'min-dash';
- import { is } from '../util/ModelUtil';
- import {
- isLabelExternal,
- getExternalLabelBounds
- } from '../util/LabelUtil';
- import {
- getMid
- } from 'diagram-js/lib/layout/LayoutUtil';
- import {
- isExpanded
- } from '../util/DiUtil';
- import {
- getLabel
- } from '../features/label-editing/LabelUtil';
- import {
- elementToString
- } from './Util';
- function elementData(semantic, attrs) {
- return assign({
- id: semantic.id,
- type: semantic.$type,
- businessObject: semantic
- }, attrs);
- }
- function getWaypoints(bo, source, target) {
- var waypoints = bo.di.waypoint;
- if (!waypoints || waypoints.length < 2) {
- return [ getMid(source), getMid(target) ];
- }
- return waypoints.map(function(p) {
- return { x: p.x, y: p.y };
- });
- }
- function notYetDrawn(translate, semantic, refSemantic, property) {
- return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', {
- element: elementToString(refSemantic),
- referenced: elementToString(semantic),
- property: property
- }));
- }
- /**
- * An importer that adds bpmn elements to the canvas
- *
- * @param {EventBus} eventBus
- * @param {Canvas} canvas
- * @param {ElementFactory} elementFactory
- * @param {ElementRegistry} elementRegistry
- * @param {Function} translate
- * @param {TextRenderer} textRenderer
- */
- export default function BpmnImporter(
- eventBus, canvas, elementFactory,
- elementRegistry, translate, textRenderer) {
- this._eventBus = eventBus;
- this._canvas = canvas;
- this._elementFactory = elementFactory;
- this._elementRegistry = elementRegistry;
- this._translate = translate;
- this._textRenderer = textRenderer;
- }
- BpmnImporter.$inject = [
- 'eventBus',
- 'canvas',
- 'elementFactory',
- 'elementRegistry',
- 'translate',
- 'textRenderer'
- ];
- /**
- * Add bpmn element (semantic) to the canvas onto the
- * specified parent shape.
- */
- BpmnImporter.prototype.add = function(semantic, parentElement) {
- var di = semantic.di,
- element,
- translate = this._translate,
- hidden;
- var parentIndex;
- // ROOT ELEMENT
- // handle the special case that we deal with a
- // invisible root element (process or collaboration)
- if (is(di, 'bpmndi:BPMNPlane')) {
- // add a virtual element (not being drawn)
- element = this._elementFactory.createRoot(elementData(semantic));
- this._canvas.setRootElement(element);
- }
- // SHAPE
- else if (is(di, 'bpmndi:BPMNShape')) {
- var collapsed = !isExpanded(semantic),
- isFrame = isFrameElement(semantic);
- hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
- var bounds = semantic.di.bounds;
- element = this._elementFactory.createShape(elementData(semantic, {
- collapsed: collapsed,
- hidden: hidden,
- x: Math.round(bounds.x),
- y: Math.round(bounds.y),
- width: Math.round(bounds.width),
- height: Math.round(bounds.height),
- isFrame: isFrame
- }));
- if (is(semantic, 'bpmn:BoundaryEvent')) {
- this._attachBoundary(semantic, element);
- }
- // insert lanes behind other flow nodes (cf. #727)
- if (is(semantic, 'bpmn:Lane')) {
- parentIndex = 0;
- }
- if (is(semantic, 'bpmn:DataStoreReference')) {
- // check whether data store is inside our outside of its semantic parent
- if (!isPointInsideBBox(parentElement, getMid(bounds))) {
- parentElement = this._canvas.getRootElement();
- }
- }
- this._canvas.addShape(element, parentElement, parentIndex);
- }
- // CONNECTION
- else if (is(di, 'bpmndi:BPMNEdge')) {
- var source = this._getSource(semantic),
- target = this._getTarget(semantic);
- hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
- element = this._elementFactory.createConnection(elementData(semantic, {
- hidden: hidden,
- source: source,
- target: target,
- waypoints: getWaypoints(semantic, source, target)
- }));
- if (is(semantic, 'bpmn:DataAssociation')) {
- // render always on top; this ensures DataAssociations
- // are rendered correctly across different "hacks" people
- // love to model such as cross participant / sub process
- // associations
- parentElement = null;
- }
- // insert sequence flows behind other flow nodes (cf. #727)
- if (is(semantic, 'bpmn:SequenceFlow')) {
- parentIndex = 0;
- }
- this._canvas.addConnection(element, parentElement, parentIndex);
- } else {
- throw new Error(translate('unknown di {di} for element {semantic}', {
- di: elementToString(di),
- semantic: elementToString(semantic)
- }));
- }
- // (optional) LABEL
- if (isLabelExternal(semantic) && getLabel(element)) {
- this.addLabel(semantic, element);
- }
- this._eventBus.fire('bpmnElement.added', { element: element });
- return element;
- };
- /**
- * Attach the boundary element to the given host
- *
- * @param {ModdleElement} boundarySemantic
- * @param {djs.model.Base} boundaryElement
- */
- BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
- var translate = this._translate;
- var hostSemantic = boundarySemantic.attachedToRef;
- if (!hostSemantic) {
- throw new Error(translate('missing {semantic}#attachedToRef', {
- semantic: elementToString(boundarySemantic)
- }));
- }
- var host = this._elementRegistry.get(hostSemantic.id),
- attachers = host && host.attachers;
- if (!host) {
- throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef');
- }
- // wire element.host <> host.attachers
- boundaryElement.host = host;
- if (!attachers) {
- host.attachers = attachers = [];
- }
- if (attachers.indexOf(boundaryElement) === -1) {
- attachers.push(boundaryElement);
- }
- };
- /**
- * add label for an element
- */
- BpmnImporter.prototype.addLabel = function(semantic, element) {
- var bounds,
- text,
- label;
- bounds = getExternalLabelBounds(semantic, element);
- text = getLabel(element);
- if (text) {
- // get corrected bounds from actual layouted text
- bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
- }
- label = this._elementFactory.createLabel(elementData(semantic, {
- id: semantic.id + '_label',
- labelTarget: element,
- type: 'label',
- hidden: element.hidden || !getLabel(element),
- x: Math.round(bounds.x),
- y: Math.round(bounds.y),
- width: Math.round(bounds.width),
- height: Math.round(bounds.height)
- }));
- return this._canvas.addShape(label, element.parent);
- };
- /**
- * Return the drawn connection end based on the given side.
- *
- * @throws {Error} if the end is not yet drawn
- */
- BpmnImporter.prototype._getEnd = function(semantic, side) {
- var element,
- refSemantic,
- type = semantic.$type,
- translate = this._translate;
- refSemantic = semantic[side + 'Ref'];
- // handle mysterious isMany DataAssociation#sourceRef
- if (side === 'source' && type === 'bpmn:DataInputAssociation') {
- refSemantic = refSemantic && refSemantic[0];
- }
- // fix source / target for DataInputAssociation / DataOutputAssociation
- if (side === 'source' && type === 'bpmn:DataOutputAssociation' ||
- side === 'target' && type === 'bpmn:DataInputAssociation') {
- refSemantic = semantic.$parent;
- }
- element = refSemantic && this._getElement(refSemantic);
- if (element) {
- return element;
- }
- if (refSemantic) {
- throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref');
- } else {
- throw new Error(translate('{semantic}#{side} Ref not specified', {
- semantic: elementToString(semantic),
- side: side
- }));
- }
- };
- BpmnImporter.prototype._getSource = function(semantic) {
- return this._getEnd(semantic, 'source');
- };
- BpmnImporter.prototype._getTarget = function(semantic) {
- return this._getEnd(semantic, 'target');
- };
- BpmnImporter.prototype._getElement = function(semantic) {
- return this._elementRegistry.get(semantic.id);
- };
- // helpers ////////////////////
- function isPointInsideBBox(bbox, point) {
- var x = point.x,
- y = point.y;
- return x >= bbox.x &&
- x <= bbox.x + bbox.width &&
- y >= bbox.y &&
- y <= bbox.y + bbox.height;
- }
- function isFrameElement(semantic) {
- return is(semantic, 'bpmn:Group');
- }
|