fix(features/bpmn-modeling): reuse created elements during redo
This commit fixes the append node command by caching and reusing created shapes and bpmn elements. This ensures we do not invalidate actions that build on these element references. Related to #6
This commit is contained in:
parent
0bb2f9c1ed
commit
f1b023f419
|
@ -1,83 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var Refs = require('object-refs');
|
||||
|
||||
function hasLabel(semantic) {
|
||||
return semantic.$instanceOf('bpmn:Event') ||
|
||||
semantic.$instanceOf('bpmn:Gateway') ||
|
||||
semantic.$instanceOf('bpmn:DataStoreReference') ||
|
||||
semantic.$instanceOf('bpmn:DataObjectReference') ||
|
||||
semantic.$instanceOf('bpmn:SequenceFlow') ||
|
||||
semantic.$instanceOf('bpmn:MessageFlow');
|
||||
}
|
||||
|
||||
|
||||
function isCollapsed(semantic) {
|
||||
return semantic.$instanceOf('bpmn:SubProcess') && !semantic.di.isExpanded;
|
||||
}
|
||||
|
||||
function getWaypointsMid(waypoints) {
|
||||
|
||||
var mid = waypoints.length / 2 - 1;
|
||||
|
||||
var first = waypoints[Math.floor(mid)];
|
||||
var second = waypoints[Math.ceil(mid + 0.01)];
|
||||
|
||||
return {
|
||||
x: first.x + (second.x - first.x) / 2,
|
||||
y: first.y + (second.y - first.y) / 2
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounds of an elements label, parsed from the elements DI or
|
||||
* generated from its bounds.
|
||||
*/
|
||||
function getLabelBounds(semantic, element) {
|
||||
|
||||
var mid,
|
||||
size,
|
||||
bounds,
|
||||
di = semantic.di,
|
||||
label = di.label;
|
||||
|
||||
if (label && label.bounds) {
|
||||
bounds = label.bounds;
|
||||
|
||||
size = {
|
||||
width: Math.max(150, bounds.width),
|
||||
height: bounds.height
|
||||
};
|
||||
|
||||
mid = {
|
||||
x: bounds.x + bounds.width / 2,
|
||||
y: bounds.y
|
||||
};
|
||||
} else {
|
||||
|
||||
if (element.waypoints) {
|
||||
mid = getWaypointsMid(element.waypoints);
|
||||
} else {
|
||||
mid = {
|
||||
x: element.x + element.width / 2,
|
||||
y: element.y + element.height - 5
|
||||
};
|
||||
}
|
||||
|
||||
size = {
|
||||
width: 90,
|
||||
height: 50
|
||||
};
|
||||
}
|
||||
|
||||
return _.extend({
|
||||
x: mid.x - size.width / 2,
|
||||
y: mid.y
|
||||
}, size);
|
||||
}
|
||||
|
||||
var hasExternalLabel = require('../util/Label').hasExternalLabel,
|
||||
isExpanded = require('../util/Di').isExpanded;
|
||||
|
||||
/**
|
||||
* An importer that adds bpmn elements to the canvas
|
||||
|
@ -85,97 +11,56 @@ function getLabelBounds(semantic, element) {
|
|||
* @param {EventBus} eventBus
|
||||
* @param {Canvas} canvas
|
||||
*/
|
||||
function BpmnImporter(eventBus, canvas) {
|
||||
|
||||
function BpmnImporter(eventBus, canvas, elementFactory) {
|
||||
this._eventBus = eventBus;
|
||||
this._canvas = canvas;
|
||||
|
||||
this._elementFactory = elementFactory;
|
||||
}
|
||||
|
||||
BpmnImporter.$inject = [ 'eventBus', 'canvas' ];
|
||||
BpmnImporter.$inject = [ 'eventBus', 'canvas', 'elementFactory' ];
|
||||
|
||||
|
||||
/**
|
||||
* Add bpmn element (semantic) to the canvas onto the
|
||||
* specified parent shape.
|
||||
*/
|
||||
BpmnImporter.prototype.add = function(semantic, parentShape) {
|
||||
BpmnImporter.prototype.add = function(semantic, parentElement) {
|
||||
|
||||
var events = this._eventBus,
|
||||
canvas = this._canvas;
|
||||
|
||||
var element,
|
||||
di = semantic.di;
|
||||
|
||||
/**
|
||||
* add label for the element
|
||||
*/
|
||||
function addLabel(semantic, element) {
|
||||
var labelBounds = getLabelBounds(semantic, element);
|
||||
|
||||
var label = canvas.create('label', _.extend({
|
||||
id: semantic.id + '_label',
|
||||
labelTarget: element,
|
||||
type: 'label',
|
||||
hidden: element.hidden,
|
||||
parent: element.parent,
|
||||
businessObject: semantic
|
||||
}, labelBounds));
|
||||
|
||||
canvas.addShape(label);
|
||||
}
|
||||
var di = semantic.di,
|
||||
element;
|
||||
|
||||
// handle the special case that we deal with a
|
||||
// invisible root element (process or collaboration)
|
||||
if (di.$instanceOf('bpmndi:BPMNPlane')) {
|
||||
|
||||
// add a virtual element (not being drawn)
|
||||
element = canvas.create('root', _.extend({
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
businessObject: semantic
|
||||
}));
|
||||
} else
|
||||
|
||||
if (di.$instanceOf('bpmndi:BPMNShape')) {
|
||||
|
||||
var bounds = di.bounds;
|
||||
|
||||
var collapsed = isCollapsed(semantic);
|
||||
var hidden = parentShape && (parentShape.hidden || parentShape.collapsed);
|
||||
|
||||
element = canvas.create('shape', {
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
collapsed: collapsed,
|
||||
hidden: hidden,
|
||||
parent: parentShape,
|
||||
businessObject: semantic
|
||||
});
|
||||
|
||||
canvas.addShape(element);
|
||||
} else {
|
||||
|
||||
var waypoints = _.collect(di.waypoint, function(p) {
|
||||
return { x: p.x, y: p.y };
|
||||
});
|
||||
|
||||
element = canvas.create('connection', {
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
waypoints: waypoints,
|
||||
parent: parentShape,
|
||||
businessObject: semantic
|
||||
});
|
||||
|
||||
element = canvas.addConnection(element);
|
||||
element = this._elementFactory.createRoot(semantic);
|
||||
}
|
||||
|
||||
if (hasLabel(semantic)) {
|
||||
addLabel(semantic, element);
|
||||
// SHAPE
|
||||
else if (di.$instanceOf('bpmndi:BPMNShape')) {
|
||||
|
||||
var collapsed = !isExpanded(semantic);
|
||||
var hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
|
||||
|
||||
element = this._elementFactory.createShape(semantic, {
|
||||
collapsed: collapsed,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
this._canvas.addShape(element, parentElement);
|
||||
}
|
||||
|
||||
// CONNECTION
|
||||
else {
|
||||
element = this._elementFactory.createConnection(semantic, parentElement);
|
||||
this._canvas.addConnection(element, parentElement);
|
||||
}
|
||||
|
||||
// (optional) LABEL
|
||||
if (hasExternalLabel(semantic)) {
|
||||
this.addLabel(semantic, element);
|
||||
}
|
||||
|
||||
this._eventBus.fire('bpmnElement.added', { element: element });
|
||||
|
@ -184,4 +69,13 @@ BpmnImporter.prototype.add = function(semantic, parentShape) {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* add label for an element
|
||||
*/
|
||||
BpmnImporter.prototype.addLabel = function (semantic, element) {
|
||||
var label = this._elementFactory.createLabel(semantic, element);
|
||||
return this._canvas.addShape(label, element.parent);
|
||||
};
|
||||
|
||||
|
||||
module.exports = BpmnImporter;
|
|
@ -0,0 +1,78 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
var LabelUtil = require('../util/Label');
|
||||
|
||||
var getExternalLabelBounds = LabelUtil.getExternalLabelBounds;
|
||||
|
||||
|
||||
/**
|
||||
* A factory for diagram-js shapes
|
||||
*
|
||||
* @param {ElementFactory} canvas
|
||||
*/
|
||||
function ElementFactory(canvas) {
|
||||
this._canvas = canvas;
|
||||
}
|
||||
|
||||
ElementFactory.$inject = [ 'canvas' ];
|
||||
|
||||
module.exports = ElementFactory;
|
||||
|
||||
|
||||
ElementFactory.prototype.createRoot = function(semantic) {
|
||||
|
||||
return this._canvas.create('root', _.extend({
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
businessObject: semantic
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
ElementFactory.prototype.createLabel = function(semantic, element) {
|
||||
var labelBounds = getExternalLabelBounds(semantic, element);
|
||||
|
||||
var labelData = _.extend({
|
||||
id: semantic.id + '_label',
|
||||
labelTarget: element,
|
||||
type: 'label',
|
||||
hidden: element.hidden,
|
||||
businessObject: semantic
|
||||
}, labelBounds);
|
||||
|
||||
return this._canvas.create('label', labelData);
|
||||
};
|
||||
|
||||
|
||||
ElementFactory.prototype.createShape = function(semantic, attrs) {
|
||||
var bounds = semantic.di.bounds;
|
||||
|
||||
var shapeData = _.extend({
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
x: bounds.x,
|
||||
y: bounds.y,
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
businessObject: semantic
|
||||
}, attrs);
|
||||
|
||||
return this._canvas.create('shape', shapeData);
|
||||
};
|
||||
|
||||
|
||||
ElementFactory.prototype.createConnection = function(semantic) {
|
||||
var waypoints = _.collect(semantic.di.waypoint, function(p) {
|
||||
return { x: p.x, y: p.y };
|
||||
});
|
||||
|
||||
return this._canvas.create('connection', {
|
||||
id: semantic.id,
|
||||
type: semantic.$type,
|
||||
waypoints: waypoints,
|
||||
businessObject: semantic
|
||||
});
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
module.exports = {
|
||||
bpmnImporter: [ 'type', require('./BpmnImporter') ]
|
||||
bpmnImporter: [ 'type', require('./BpmnImporter') ],
|
||||
elementFactory: [ 'type', require('./ElementFactory') ]
|
||||
};
|
|
@ -4,6 +4,7 @@ var _ = require('lodash');
|
|||
|
||||
var BpmnModdle = require('bpmn-moddle');
|
||||
|
||||
var Collections = require('diagram-js/lib/util/Collections');
|
||||
|
||||
|
||||
function BpmnFactory() {
|
||||
|
@ -14,20 +15,32 @@ function BpmnFactory() {
|
|||
BpmnFactory.$inject = [ ];
|
||||
|
||||
|
||||
BpmnFactory.prototype._needsId = function(element) {
|
||||
return element.$instanceOf('bpmn:RootElement') ||
|
||||
element.$instanceOf('bpmn:FlowElement') ||
|
||||
element.$instanceOf('bpmn:Artifact') ||
|
||||
element.$instanceOf('bpmndi:BPMNShape') ||
|
||||
element.$instanceOf('bpmndi:BPMNEdge') ||
|
||||
element.$instanceOf('bpmndi:BPMNDiagram') ||
|
||||
element.$instanceOf('bpmndi:BPMNPlane');
|
||||
};
|
||||
|
||||
BpmnFactory.prototype._ensureId = function(element) {
|
||||
if (element.id === undefined) {
|
||||
if (!element.id && this._needsId(element)) {
|
||||
element.id = '' + (++this._uuid);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BpmnFactory.prototype.create = function(type, attrs) {
|
||||
var element = this._model.create(type, attrs);
|
||||
var element = this._model.create(type, attrs || {});
|
||||
|
||||
this._ensureId(element);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
BpmnFactory.prototype.createDiShape = function(semantic, position, attrs) {
|
||||
|
||||
position = position || { x: 0, y: 0 };
|
||||
|
@ -51,18 +64,18 @@ BpmnFactory.prototype.createDiShape = function(semantic, position, attrs) {
|
|||
}, attrs));
|
||||
};
|
||||
|
||||
|
||||
BpmnFactory.prototype.createDiBounds = function(bounds) {
|
||||
return this.create('dc:Bounds', bounds);
|
||||
};
|
||||
|
||||
|
||||
BpmnFactory.prototype.createDiWaypoint = function(point) {
|
||||
return this.create('dc:Point', point);
|
||||
};
|
||||
|
||||
BpmnFactory.prototype.createDiEdge = function(sequenceFlow, points, attrs) {
|
||||
|
||||
var sourceDi = sequenceFlow.sourceRef.di,
|
||||
targetDi = sequenceFlow.targetRef.di;
|
||||
BpmnFactory.prototype.createDiEdge = function(sequenceFlow, points, attrs) {
|
||||
|
||||
var waypoints = _.map(points, function(pos) {
|
||||
return this.createDiWaypoint(pos);
|
||||
|
@ -74,17 +87,27 @@ BpmnFactory.prototype.createDiEdge = function(sequenceFlow, points, attrs) {
|
|||
}, attrs));
|
||||
};
|
||||
|
||||
BpmnFactory.prototype.createSequenceFlow = function(source, target, attrs) {
|
||||
|
||||
var sequenceFlow = this.create('bpmn:SequenceFlow', _.extend({
|
||||
BpmnFactory.prototype.disconnectSequenceFlow = function(sequenceFlow) {
|
||||
Collections.remove(sequenceFlow.sourceRef && sequenceFlow.sourceRef.get('outgoing'), sequenceFlow.sourceRef);
|
||||
Collections.remove(sequenceFlow.targetRef && sequenceFlow.targetRef.get('incoming'), sequenceFlow.targetRef);
|
||||
|
||||
_.extend(sequenceFlow, {
|
||||
sourceRef: null,
|
||||
targetRef: null
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
BpmnFactory.prototype.connectSequenceFlow = function(sequenceFlow, source, target) {
|
||||
|
||||
_.extend(sequenceFlow, {
|
||||
sourceRef: source,
|
||||
targetRef: target
|
||||
}, attrs));
|
||||
});
|
||||
|
||||
source.get('outgoing').push(sequenceFlow);
|
||||
target.get('incoming').push(sequenceFlow);
|
||||
|
||||
return sequenceFlow;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
var LayoutUtil = require('../LayoutUtil');
|
||||
var LayoutUtil = require('../LayoutUtil'),
|
||||
Collections = require('diagram-js/lib/util/Collections');
|
||||
|
||||
var hasExternalLabel = require('../../../util/Label').hasExternalLabel;
|
||||
|
||||
var AppendShapeHandler = require('diagram-js/lib/features/modeling/cmd/AppendShapeHandler');
|
||||
|
||||
|
@ -9,22 +12,27 @@ var Refs = require('object-refs');
|
|||
var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' });
|
||||
|
||||
|
||||
function AppendFlowNodeHandler(canvas, bpmnFactory, bpmnImporter) {
|
||||
function AppendFlowNodeHandler(canvas, bpmnFactory, bpmnImporter, elementFactory) {
|
||||
AppendShapeHandler.call(this);
|
||||
|
||||
this._bpmnImporter = bpmnImporter;
|
||||
this._bpmnFactory = bpmnFactory;
|
||||
|
||||
this._canvas = canvas;
|
||||
|
||||
this._bpmnFactory = bpmnFactory;
|
||||
this._bpmnImporter = bpmnImporter;
|
||||
this._elementFactory = elementFactory;
|
||||
}
|
||||
|
||||
AppendFlowNodeHandler.prototype = Object.create(AppendShapeHandler.prototype);
|
||||
|
||||
AppendFlowNodeHandler.$inject = [ 'canvas', 'bpmnFactory', 'bpmnImporter' ];
|
||||
AppendFlowNodeHandler.$inject = [ 'canvas', 'bpmnFactory', 'bpmnImporter', 'elementFactory' ];
|
||||
|
||||
|
||||
AppendFlowNodeHandler.prototype.createShape = function(source, position, parent, context) {
|
||||
|
||||
var bpmnFactory = this._bpmnFactory,
|
||||
elementFactory = this._elementFactory,
|
||||
canvas = this._canvas;
|
||||
|
||||
var sourceSemantic = source.businessObject,
|
||||
parentSemantic = parent.businessObject;
|
||||
|
||||
|
@ -32,83 +40,144 @@ AppendFlowNodeHandler.prototype.createShape = function(source, position, parent,
|
|||
targetSemantic,
|
||||
targetDi;
|
||||
|
||||
// create semantic
|
||||
targetSemantic = this._bpmnFactory.create(context.type, {
|
||||
id: target && target.businessObject.id
|
||||
});
|
||||
// we need to create shape + bpmn elements
|
||||
if (!target) {
|
||||
// create semantic
|
||||
targetSemantic = bpmnFactory.create(context.type);
|
||||
|
||||
// add to model
|
||||
// create di
|
||||
targetDi = bpmnFactory.createDiShape(targetSemantic, position, {
|
||||
id: targetSemantic.id + '_di'
|
||||
});
|
||||
|
||||
// bind semantic -> di -> semantic
|
||||
diRefs.bind(targetSemantic, 'di');
|
||||
targetSemantic.di = targetDi;
|
||||
|
||||
// create node
|
||||
target = elementFactory.createShape(targetSemantic);
|
||||
|
||||
// add label
|
||||
if (hasExternalLabel(targetSemantic)) {
|
||||
target.label = elementFactory.createLabel(targetSemantic, target);
|
||||
}
|
||||
}
|
||||
|
||||
// load di + semantic from target
|
||||
else {
|
||||
targetSemantic = target.businessObject;
|
||||
targetDi = targetSemantic.di;
|
||||
}
|
||||
|
||||
// reconnect everything
|
||||
|
||||
// wire semantic
|
||||
parentSemantic.get('flowElements').push(targetSemantic);
|
||||
targetSemantic.$parent = parentSemantic;
|
||||
|
||||
// create di
|
||||
targetDi = this._bpmnFactory.createDiShape(targetSemantic, position, {
|
||||
id: targetSemantic.id + '_di'
|
||||
});
|
||||
|
||||
diRefs.bind(targetSemantic, 'di');
|
||||
targetSemantic.di = targetDi;
|
||||
|
||||
// add to model
|
||||
// wire di
|
||||
sourceSemantic.di.$parent.get('planeElement').push(targetDi);
|
||||
targetDi.$parent = sourceSemantic.di.$parent;
|
||||
|
||||
return this._bpmnImporter.add(targetSemantic, parent);
|
||||
canvas.addShape(target, parent);
|
||||
|
||||
if (target.label) {
|
||||
canvas.addShape(target.label, parent);
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
|
||||
AppendFlowNodeHandler.prototype.createConnection = function(source, target, parent, context) {
|
||||
|
||||
var bpmnFactory = this._bpmnFactory,
|
||||
elementFactory = this._elementFactory,
|
||||
canvas = this._canvas;
|
||||
|
||||
var sourceSemantic = source.businessObject,
|
||||
targetSemantic = target.businessObject,
|
||||
parentSemantic = parent.businessObject;
|
||||
|
||||
var flowSemantic,
|
||||
flowDi;
|
||||
var connection = context.connection,
|
||||
connectionSemantic,
|
||||
connectionDi;
|
||||
|
||||
var connection = context.connection;
|
||||
|
||||
// create semantic
|
||||
flowSemantic = this._bpmnFactory.createSequenceFlow(sourceSemantic, targetSemantic, {
|
||||
id: connection && connection.businessObject.id
|
||||
});
|
||||
// we need to create connection + bpmn elements
|
||||
if (!connection) {
|
||||
|
||||
// add to model
|
||||
parentSemantic.get('flowElements').push(flowSemantic);
|
||||
flowSemantic.$parent = parentSemantic;
|
||||
// create semantic
|
||||
connectionSemantic = bpmnFactory.create('bpmn:SequenceFlow');
|
||||
|
||||
// create di
|
||||
var waypoints = LayoutUtil.getDirectConnectionPoints(sourceSemantic.di.bounds, targetSemantic.di.bounds);
|
||||
// create di
|
||||
var waypoints = LayoutUtil.getDirectConnectionPoints(sourceSemantic.di.bounds, targetSemantic.di.bounds);
|
||||
|
||||
flowDi = this._bpmnFactory.createDiEdge(flowSemantic, waypoints, {
|
||||
id: flowSemantic.id + '_di'
|
||||
});
|
||||
connectionDi = bpmnFactory.createDiEdge(connectionSemantic, waypoints, {
|
||||
id: connectionSemantic.id + '_di'
|
||||
});
|
||||
|
||||
diRefs.bind(flowSemantic, 'di');
|
||||
flowSemantic.di = flowDi;
|
||||
// bind semantic -> di -> semantic
|
||||
diRefs.bind(connectionSemantic, 'di');
|
||||
connectionSemantic.di = connectionDi;
|
||||
|
||||
// add to model
|
||||
sourceSemantic.di.$parent.get('planeElement').push(flowDi);
|
||||
flowDi.$parent = sourceSemantic.di.$parent;
|
||||
// create connection
|
||||
connection = elementFactory.createConnection(connectionSemantic);
|
||||
|
||||
return this._bpmnImporter.add(flowSemantic, parent);
|
||||
// add label
|
||||
if (hasExternalLabel(connectionSemantic)) {
|
||||
connection.label = elementFactory.createLabel(connectionSemantic, connection);
|
||||
}
|
||||
}
|
||||
|
||||
// load di + semantic from target
|
||||
else {
|
||||
connectionSemantic = connection.businessObject;
|
||||
connectionDi = connectionSemantic.di;
|
||||
}
|
||||
|
||||
// connect
|
||||
bpmnFactory.connectSequenceFlow(connectionSemantic, sourceSemantic, targetSemantic);
|
||||
|
||||
// TODO(nre): connect connection
|
||||
|
||||
// wire semantic
|
||||
parentSemantic.get('flowElements').push(connectionSemantic);
|
||||
connectionSemantic.$parent = parentSemantic;
|
||||
|
||||
// wire di
|
||||
sourceSemantic.di.$parent.get('planeElement').push(connectionDi);
|
||||
connectionDi.$parent = sourceSemantic.di.$parent;
|
||||
|
||||
canvas.addConnection(connection, parent);
|
||||
|
||||
if (connection.label) {
|
||||
canvas.addShape(connection.label, parent);
|
||||
}
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
|
||||
AppendFlowNodeHandler.prototype.removeShape = function(shape) {
|
||||
|
||||
var semantic = shape.businessObject,
|
||||
di = semantic.di,
|
||||
parentSemantic = semantic.$parent;
|
||||
|
||||
// remove semantic
|
||||
// undo wire semantic
|
||||
parentSemantic.get('flowElements').splice(parentSemantic.get('flowElements').indexOf(semantic), 1);
|
||||
semantic.$parent = null;
|
||||
|
||||
// remove di
|
||||
var di = semantic.di;
|
||||
// undo wire di
|
||||
di.$parent.get('planeElement').splice(di.$parent.get('planeElement').indexOf(di), 1);
|
||||
di.$parent = null;
|
||||
|
||||
// remove label
|
||||
if (shape.label) {
|
||||
this._canvas.removeShape(shape.label);
|
||||
}
|
||||
|
||||
// actual remove shape
|
||||
return this._canvas.removeShape(shape);
|
||||
};
|
||||
|
@ -117,23 +186,27 @@ AppendFlowNodeHandler.prototype.removeShape = function(shape) {
|
|||
AppendFlowNodeHandler.prototype.removeConnection = function(connection) {
|
||||
|
||||
var semantic = connection.businessObject,
|
||||
di = semantic.di,
|
||||
parentSemantic = semantic.$parent;
|
||||
|
||||
// remove semantic
|
||||
parentSemantic.get('flowElements').splice(parentSemantic.get('flowElements').indexOf(semantic), 1);
|
||||
// undo wire semantic
|
||||
Collections.remove(parentSemantic.get('flowElements'), semantic);
|
||||
semantic.$parent = null;
|
||||
|
||||
// remove di
|
||||
var di = semantic.di;
|
||||
di.$parent.get('planeElement').splice(di.$parent.get('planeElement').indexOf(di), 1);
|
||||
// undo wire di
|
||||
Collections.remove(di.$parent.get('planeElement'), di);
|
||||
di.$parent = null;
|
||||
|
||||
// remove refs in source / target
|
||||
semantic.sourceRef.outgoing.splice(semantic.sourceRef.outgoing.indexOf(semantic), 1);
|
||||
semantic.targetRef.incoming.splice(semantic.targetRef.incoming.indexOf(semantic), 1);
|
||||
// undo wire refs
|
||||
this._bpmnFactory.disconnectSequenceFlow(semantic);
|
||||
|
||||
semantic.sourceRef = null;
|
||||
semantic.targetRef = null;
|
||||
semantic.$parent = null;
|
||||
// TODO(nre): disconnect connection
|
||||
|
||||
|
||||
// remove label
|
||||
if (connection.label) {
|
||||
this._canvas.removeShape(connection.label);
|
||||
}
|
||||
|
||||
// actual remove connection
|
||||
return this._canvas.removeConnection(connection);
|
||||
|
|
|
@ -5,5 +5,5 @@ module.exports.isExpandedPool = function(semantic) {
|
|||
};
|
||||
|
||||
module.exports.isExpanded = function(semantic) {
|
||||
return semantic.di.isExpanded;
|
||||
return !semantic.$instanceOf('bpmn:SubProcess') || semantic.di.isExpanded;
|
||||
};
|
|
@ -0,0 +1,91 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the given semantic has an external label
|
||||
*
|
||||
* @param {BpmnElement} semantic
|
||||
* @return {Boolean} true if has label
|
||||
*/
|
||||
module.exports.hasExternalLabel = function(semantic) {
|
||||
|
||||
return semantic.$instanceOf('bpmn:Event') ||
|
||||
semantic.$instanceOf('bpmn:Gateway') ||
|
||||
semantic.$instanceOf('bpmn:DataStoreReference') ||
|
||||
semantic.$instanceOf('bpmn:DataObjectReference') ||
|
||||
semantic.$instanceOf('bpmn:SequenceFlow') ||
|
||||
semantic.$instanceOf('bpmn:MessageFlow');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the middle of a number of waypoints
|
||||
*
|
||||
* @param {Array<Point>} waypoints
|
||||
* @return {Point} the mid point
|
||||
*/
|
||||
module.exports.getWaypointsMid = function(waypoints) {
|
||||
|
||||
var mid = waypoints.length / 2 - 1;
|
||||
|
||||
var first = waypoints[Math.floor(mid)];
|
||||
var second = waypoints[Math.ceil(mid + 0.01)];
|
||||
|
||||
return {
|
||||
x: first.x + (second.x - first.x) / 2,
|
||||
y: first.y + (second.y - first.y) / 2
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bounds of an elements label, parsed from the elements DI or
|
||||
* generated from its bounds.
|
||||
*
|
||||
* @param {BpmnElement} semantic
|
||||
* @param {djs.model.Base} element
|
||||
*/
|
||||
module.exports.getExternalLabelBounds = function(semantic, element) {
|
||||
|
||||
var mid,
|
||||
size,
|
||||
bounds,
|
||||
di = semantic.di,
|
||||
label = di.label;
|
||||
|
||||
if (label && label.bounds) {
|
||||
bounds = label.bounds;
|
||||
|
||||
size = {
|
||||
width: Math.max(150, bounds.width),
|
||||
height: bounds.height
|
||||
};
|
||||
|
||||
mid = {
|
||||
x: bounds.x + bounds.width / 2,
|
||||
y: bounds.y
|
||||
};
|
||||
} else {
|
||||
|
||||
if (element.waypoints) {
|
||||
mid = module.exports.getWaypointsMid(element.waypoints);
|
||||
} else {
|
||||
mid = {
|
||||
x: element.x + element.width / 2,
|
||||
y: element.y + element.height - 5
|
||||
};
|
||||
}
|
||||
|
||||
size = {
|
||||
width: 90,
|
||||
height: 50
|
||||
};
|
||||
}
|
||||
|
||||
return _.extend({
|
||||
x: mid.x - size.width / 2,
|
||||
y: mid.y
|
||||
}, size);
|
||||
};
|
|
@ -128,6 +128,35 @@ describe('features - bpmn-modeling', function() {
|
|||
}));
|
||||
|
||||
|
||||
it('should undo add shape label', inject(function(elementRegistry, bpmnModeling, commandStack) {
|
||||
|
||||
// given
|
||||
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||
|
||||
var startEvent = startEventShape.businessObject,
|
||||
subProcess = subProcessShape.businessObject;
|
||||
|
||||
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:EndEvent'),
|
||||
target = targetShape.businessObject;
|
||||
|
||||
var connection = _.find(subProcess.get('flowElements'), function(e) {
|
||||
return e.sourceRef === startEvent && e.targetRef === target;
|
||||
});
|
||||
|
||||
// when
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(connection.sourceRef).toBe(null);
|
||||
expect(connection.targetRef).toBe(null);
|
||||
expect(connection.$parent).toBe(null);
|
||||
expect(subProcess.di.$parent.get('planeElement')).not.toContain(connection.di);
|
||||
|
||||
expect(elementRegistry.getById(targetShape.label.id)).not.toBeDefined();
|
||||
}));
|
||||
|
||||
|
||||
it('should undo add connection', inject(function(elementRegistry, bpmnModeling, commandStack) {
|
||||
|
||||
// given
|
||||
|
@ -152,6 +181,101 @@ describe('features - bpmn-modeling', function() {
|
|||
expect(connection.targetRef).toBe(null);
|
||||
expect(connection.$parent).toBe(null);
|
||||
expect(subProcess.di.$parent.get('planeElement')).not.toContain(connection.di);
|
||||
|
||||
expect(elementRegistry.getById(targetShape.id)).not.toBeDefined();
|
||||
}));
|
||||
|
||||
|
||||
it('should undo add connection label', inject(function(elementRegistry, bpmnModeling, commandStack) {
|
||||
|
||||
// given
|
||||
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||
|
||||
var startEvent = startEventShape.businessObject,
|
||||
subProcess = subProcessShape.businessObject;
|
||||
|
||||
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||
target = targetShape.businessObject;
|
||||
|
||||
var connection = _.find(subProcess.get('flowElements'), function(e) {
|
||||
return e.sourceRef === startEvent && e.targetRef === target;
|
||||
});
|
||||
|
||||
// when
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(connection.sourceRef).toBe(null);
|
||||
expect(connection.targetRef).toBe(null);
|
||||
expect(connection.$parent).toBe(null);
|
||||
expect(subProcess.di.$parent.get('planeElement')).not.toContain(connection.di);
|
||||
|
||||
expect(elementRegistry.getById(connection.id + '_label')).not.toBeDefined();
|
||||
}));
|
||||
|
||||
|
||||
it('should undo/redo appending multiple shapes', inject(function(elementRegistry, bpmnModeling, commandStack) {
|
||||
|
||||
// given
|
||||
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||
|
||||
var startEvent = startEventShape.businessObject,
|
||||
subProcess = subProcessShape.businessObject;
|
||||
|
||||
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||
target = targetShape.businessObject;
|
||||
|
||||
var targetShape2 = bpmnModeling.appendFlowNode(targetShape, null, 'bpmn:UserTask');
|
||||
|
||||
// when
|
||||
commandStack.undo();
|
||||
commandStack.undo();
|
||||
commandStack.redo();
|
||||
commandStack.redo();
|
||||
|
||||
// then
|
||||
// expect redo to work on original target object
|
||||
expect(targetShape.parent).toBe(subProcessShape);
|
||||
|
||||
// when
|
||||
commandStack.undo();
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(targetShape2.parent).toBe(null);
|
||||
expect(elementRegistry.getById(targetShape2.id)).not.toBeDefined();
|
||||
}));
|
||||
|
||||
|
||||
it('should undo/redo add connection', inject(function(elementRegistry, bpmnModeling, commandStack) {
|
||||
|
||||
// given
|
||||
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||
|
||||
var startEvent = startEventShape.businessObject,
|
||||
subProcess = subProcessShape.businessObject;
|
||||
|
||||
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||
target = targetShape.businessObject;
|
||||
|
||||
var connection = _.find(subProcess.get('flowElements'), function(e) {
|
||||
return e.sourceRef === startEvent && e.targetRef === target;
|
||||
});
|
||||
|
||||
// when
|
||||
commandStack.undo();
|
||||
commandStack.redo();
|
||||
commandStack.undo();
|
||||
|
||||
// then
|
||||
expect(connection.sourceRef).toBe(null);
|
||||
expect(connection.targetRef).toBe(null);
|
||||
expect(connection.$parent).toBe(null);
|
||||
|
||||
expect(subProcess.di.$parent.get('planeElement')).not.toContain(connection.di);
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue