parent
5185c55f68
commit
4fe5bbc0f5
|
@ -0,0 +1,91 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var BpmnModdle = require('bpmn-moddle');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function BpmnFactory() {
|
||||||
|
this._model = BpmnModdle.instance();
|
||||||
|
this._uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BpmnFactory.$inject = [ ];
|
||||||
|
|
||||||
|
|
||||||
|
BpmnFactory.prototype._ensureId = function(element) {
|
||||||
|
if (element.id === undefined) {
|
||||||
|
element.id = '' + (++this._uuid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BpmnFactory.prototype.create = function(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 };
|
||||||
|
|
||||||
|
var bounds;
|
||||||
|
|
||||||
|
if (semantic.$instanceOf('bpmn:Task')) {
|
||||||
|
bounds = { width: 100, height: 80 };
|
||||||
|
} else {
|
||||||
|
bounds = { width: 50, height: 50 };
|
||||||
|
}
|
||||||
|
|
||||||
|
_.extend(bounds, {
|
||||||
|
x: (position.x || 0) - bounds.width / 2,
|
||||||
|
y: (position.y || 0) - bounds.height / 2
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.create('bpmndi:BPMNShape', _.extend({
|
||||||
|
bpmnElement: semantic,
|
||||||
|
bounds: this.createDiBounds(bounds)
|
||||||
|
}, 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;
|
||||||
|
|
||||||
|
var waypoints = _.map(points, function(pos) {
|
||||||
|
return this.createDiWaypoint(pos);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
return this.create('bpmndi:BPMNEdge', _.extend({
|
||||||
|
bpmnElement: sequenceFlow,
|
||||||
|
waypoint: waypoints
|
||||||
|
}, attrs));
|
||||||
|
};
|
||||||
|
|
||||||
|
BpmnFactory.prototype.createSequenceFlow = function(source, target, attrs) {
|
||||||
|
|
||||||
|
var sequenceFlow = this.create('bpmn:SequenceFlow', _.extend({
|
||||||
|
sourceRef: source,
|
||||||
|
targetRef: target
|
||||||
|
}, attrs));
|
||||||
|
|
||||||
|
source.get('outgoing').push(sequenceFlow);
|
||||||
|
target.get('incoming').push(sequenceFlow);
|
||||||
|
|
||||||
|
return sequenceFlow;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = BpmnFactory;
|
|
@ -0,0 +1,37 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var AppendFlowNodeHandler = require('./cmd/AppendFlowNodeHandler');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BPMN 2.0 modeling features activator
|
||||||
|
*
|
||||||
|
* @param {CommandStack} commandStack
|
||||||
|
*/
|
||||||
|
function BpmnModeling(commandStack) {
|
||||||
|
commandStack.registerHandler('shape.appendNode', AppendFlowNodeHandler);
|
||||||
|
this._commandStack = commandStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
BpmnModeling.$inject = [ 'commandStack' ];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a flow node to the element with the given source
|
||||||
|
* at the specified position.
|
||||||
|
*/
|
||||||
|
BpmnModeling.prototype.appendFlowNode = function(source, parent, type, position) {
|
||||||
|
|
||||||
|
var context = {
|
||||||
|
source: source,
|
||||||
|
type: type,
|
||||||
|
parent: parent,
|
||||||
|
position: position
|
||||||
|
};
|
||||||
|
|
||||||
|
this._commandStack.execute('shape.appendNode', context);
|
||||||
|
|
||||||
|
return context.target;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = BpmnModeling;
|
|
@ -0,0 +1,15 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var getMidPoint = module.exports.getMidPoint = function(bounds) {
|
||||||
|
return {
|
||||||
|
x: bounds.x + bounds.width / 2,
|
||||||
|
y: bounds.y + bounds.height / 2
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.getDirectConnectionPoints = function(boundsA, boundsB) {
|
||||||
|
return [
|
||||||
|
getMidPoint(boundsA),
|
||||||
|
getMidPoint(boundsB)
|
||||||
|
];
|
||||||
|
};
|
|
@ -0,0 +1,142 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var LayoutUtil = require('../LayoutUtil');
|
||||||
|
|
||||||
|
var AppendShapeHandler = require('diagram-js/lib/features/modeling/cmd/AppendShapeHandler');
|
||||||
|
|
||||||
|
var Refs = require('object-refs');
|
||||||
|
|
||||||
|
var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' });
|
||||||
|
|
||||||
|
|
||||||
|
function AppendFlowNodeHandler(canvas, bpmnFactory, bpmnImporter) {
|
||||||
|
AppendShapeHandler.call(this);
|
||||||
|
|
||||||
|
this._bpmnImporter = bpmnImporter;
|
||||||
|
this._bpmnFactory = bpmnFactory;
|
||||||
|
|
||||||
|
this._canvas = canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.prototype = Object.create(AppendShapeHandler.prototype);
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.$inject = [ 'canvas', 'bpmnFactory', 'bpmnImporter' ];
|
||||||
|
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.prototype.createShape = function(source, position, parent, context) {
|
||||||
|
|
||||||
|
var sourceSemantic = source.businessObject,
|
||||||
|
parentSemantic = parent.businessObject;
|
||||||
|
|
||||||
|
var target = context.target,
|
||||||
|
targetSemantic,
|
||||||
|
targetDi;
|
||||||
|
|
||||||
|
// create semantic
|
||||||
|
targetSemantic = this._bpmnFactory.create(context.type, {
|
||||||
|
id: target && target.businessObject.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// add to model
|
||||||
|
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
|
||||||
|
sourceSemantic.di.$parent.get('planeElement').push(targetDi);
|
||||||
|
targetDi.$parent = sourceSemantic.di.$parent;
|
||||||
|
|
||||||
|
return this._bpmnImporter.add(targetSemantic, parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.prototype.createConnection = function(source, target, parent, context) {
|
||||||
|
|
||||||
|
var sourceSemantic = source.businessObject,
|
||||||
|
targetSemantic = target.businessObject,
|
||||||
|
parentSemantic = parent.businessObject;
|
||||||
|
|
||||||
|
var flowSemantic,
|
||||||
|
flowDi;
|
||||||
|
|
||||||
|
var connection = context.connection;
|
||||||
|
|
||||||
|
// create semantic
|
||||||
|
flowSemantic = this._bpmnFactory.createSequenceFlow(sourceSemantic, targetSemantic, {
|
||||||
|
id: connection && connection.businessObject.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// add to model
|
||||||
|
parentSemantic.get('flowElements').push(flowSemantic);
|
||||||
|
flowSemantic.$parent = parentSemantic;
|
||||||
|
|
||||||
|
// create di
|
||||||
|
var waypoints = LayoutUtil.getDirectConnectionPoints(sourceSemantic.di.bounds, targetSemantic.di.bounds);
|
||||||
|
|
||||||
|
flowDi = this._bpmnFactory.createDiEdge(flowSemantic, waypoints, {
|
||||||
|
id: flowSemantic.id + '_di'
|
||||||
|
});
|
||||||
|
|
||||||
|
diRefs.bind(flowSemantic, 'di');
|
||||||
|
flowSemantic.di = flowDi;
|
||||||
|
|
||||||
|
// add to model
|
||||||
|
sourceSemantic.di.$parent.get('planeElement').push(flowDi);
|
||||||
|
flowDi.$parent = sourceSemantic.di.$parent;
|
||||||
|
|
||||||
|
return this._bpmnImporter.add(flowSemantic, parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.prototype.removeShape = function(shape) {
|
||||||
|
|
||||||
|
var semantic = shape.businessObject,
|
||||||
|
parentSemantic = semantic.$parent;
|
||||||
|
|
||||||
|
// remove semantic
|
||||||
|
parentSemantic.get('flowElements').splice(parentSemantic.get('flowElements').indexOf(semantic), 1);
|
||||||
|
semantic.$parent = null;
|
||||||
|
|
||||||
|
// remove di
|
||||||
|
var di = semantic.di;
|
||||||
|
di.$parent.get('planeElement').splice(di.$parent.get('planeElement').indexOf(di), 1);
|
||||||
|
di.$parent = null;
|
||||||
|
|
||||||
|
// actual remove shape
|
||||||
|
return this._canvas.removeShape(shape);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AppendFlowNodeHandler.prototype.removeConnection = function(connection) {
|
||||||
|
|
||||||
|
var semantic = connection.businessObject,
|
||||||
|
parentSemantic = semantic.$parent;
|
||||||
|
|
||||||
|
// remove semantic
|
||||||
|
parentSemantic.get('flowElements').splice(parentSemantic.get('flowElements').indexOf(semantic), 1);
|
||||||
|
|
||||||
|
// remove di
|
||||||
|
var di = semantic.di;
|
||||||
|
di.$parent.get('planeElement').splice(di.$parent.get('planeElement').indexOf(di), 1);
|
||||||
|
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);
|
||||||
|
|
||||||
|
semantic.sourceRef = null;
|
||||||
|
semantic.targetRef = null;
|
||||||
|
semantic.$parent = null;
|
||||||
|
|
||||||
|
// actual remove connection
|
||||||
|
return this._canvas.removeConnection(connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = AppendFlowNodeHandler;
|
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
__init__: [ 'bpmnModeling' ],
|
||||||
|
__depends__: [
|
||||||
|
require('../../core'),
|
||||||
|
require('diagram-js/lib/cmd')
|
||||||
|
],
|
||||||
|
bpmnFactory: [ 'type', require('./BpmnFactory') ],
|
||||||
|
bpmnModeling: [ 'type', require('./BpmnModeling') ]
|
||||||
|
};
|
|
@ -59,6 +59,7 @@
|
||||||
"didi": "~0.0.4",
|
"didi": "~0.0.4",
|
||||||
"jquery": "~2.1.0",
|
"jquery": "~2.1.0",
|
||||||
"jquery-mousewheel": "~3.1.11",
|
"jquery-mousewheel": "~3.1.11",
|
||||||
"lodash": "~2.4.0"
|
"lodash": "~2.4.0",
|
||||||
|
"object-refs": "^0.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Matchers = require('../../../Matchers'),
|
||||||
|
TestHelper = require('../../../TestHelper');
|
||||||
|
|
||||||
|
/* global bootstrapBpmnJS, inject */
|
||||||
|
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var bpmnFactoryModule = require('../../../../../lib/features/bpmn-modeling');
|
||||||
|
|
||||||
|
|
||||||
|
xdescribe('features - bpmn-factory', function() {
|
||||||
|
|
||||||
|
beforeEach(Matchers.add);
|
||||||
|
|
||||||
|
|
||||||
|
var diagramXML = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf-8');
|
||||||
|
|
||||||
|
var testModules = [ bpmnFactoryModule ];
|
||||||
|
|
||||||
|
beforeEach(bootstrapBpmnJS(diagramXML, { modules: testModules }));
|
||||||
|
|
||||||
|
|
||||||
|
describe('create task', function() {
|
||||||
|
|
||||||
|
it('should create', inject(function(bpmnFactory) {
|
||||||
|
var result = bpmnFactory.createNode('bpmn:Task');
|
||||||
|
|
||||||
|
expect(result.semantic.id).toBeDefined();
|
||||||
|
expect(result.di.id).toBeDefined();
|
||||||
|
|
||||||
|
expect(result.di.bounds.width).toBe(100);
|
||||||
|
expect(result.di.bounds.height).toBe(80);
|
||||||
|
expect(result.di.bounds.x).toBe(-50);
|
||||||
|
expect(result.di.bounds.y).toBe(-40);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should create with position', inject(function(bpmnFactory) {
|
||||||
|
|
||||||
|
var result = bpmnFactory.createNode('bpmn:Task', { x: 100, y: 100 });
|
||||||
|
|
||||||
|
expect(result.semantic.id).toBeDefined();
|
||||||
|
expect(result.di.id).toBeDefined();
|
||||||
|
|
||||||
|
expect(result.di.bounds.width).toBe(100);
|
||||||
|
expect(result.di.bounds.height).toBe(80);
|
||||||
|
expect(result.di.bounds.x).toBe(50);
|
||||||
|
expect(result.di.bounds.y).toBe(60);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,163 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Matchers = require('../../../Matchers'),
|
||||||
|
TestHelper = require('../../../TestHelper');
|
||||||
|
|
||||||
|
/* global bootstrapBpmnJS, inject */
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var bpmnFactoryModule = require('../../../../../lib/features/bpmn-modeling'),
|
||||||
|
bpmnDrawModule = require('../../../../../lib/draw');
|
||||||
|
|
||||||
|
|
||||||
|
describe('features - bpmn-modeling', function() {
|
||||||
|
|
||||||
|
beforeEach(Matchers.add);
|
||||||
|
|
||||||
|
|
||||||
|
var diagramXML = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf-8');
|
||||||
|
|
||||||
|
var testModules = [ bpmnFactoryModule, bpmnDrawModule ];
|
||||||
|
|
||||||
|
beforeEach(bootstrapBpmnJS(diagramXML, { modules: testModules }));
|
||||||
|
|
||||||
|
|
||||||
|
describe('commands', function() {
|
||||||
|
|
||||||
|
|
||||||
|
describe('shape.appendNode', function() {
|
||||||
|
|
||||||
|
it('should execute', inject(function(elementRegistry, bpmnModeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||||
|
|
||||||
|
// when
|
||||||
|
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||||
|
target = targetShape.businessObject;
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(targetShape).toBeDefined();
|
||||||
|
expect(target.$instanceOf('bpmn:Task')).toBe(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should create DI', inject(function(elementRegistry, bpmnModeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||||
|
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||||
|
|
||||||
|
var startEvent = startEventShape.businessObject,
|
||||||
|
subProcess = subProcessShape.businessObject;
|
||||||
|
|
||||||
|
// when
|
||||||
|
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||||
|
target = targetShape.businessObject;
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(target.di).toBeDefined();
|
||||||
|
expect(target.di.$parent).toBe(startEvent.di.$parent);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should add to parent (sub process)', inject(function(elementRegistry, bpmnModeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||||
|
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||||
|
|
||||||
|
var startEvent = startEventShape.businessObject,
|
||||||
|
subProcess = subProcessShape.businessObject;
|
||||||
|
|
||||||
|
// when
|
||||||
|
var targetShape = bpmnModeling.appendFlowNode(startEventShape, null, 'bpmn:Task'),
|
||||||
|
target = targetShape.businessObject;
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(subProcess.get('flowElements')).toContain(target);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should add connection', inject(function(elementRegistry, bpmnModeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var startEventShape = elementRegistry.getById('StartEvent_1');
|
||||||
|
var subProcessShape = elementRegistry.getById('SubProcess_1');
|
||||||
|
|
||||||
|
var startEvent = startEventShape.businessObject,
|
||||||
|
subProcess = subProcessShape.businessObject;
|
||||||
|
|
||||||
|
// when
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(connection).toBeDefined();
|
||||||
|
expect(connection.$instanceOf('bpmn:SequenceFlow')).toBe(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
describe('undo support', function() {
|
||||||
|
|
||||||
|
it('should undo add to parent', 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;
|
||||||
|
|
||||||
|
// when
|
||||||
|
commandStack.undo();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(subProcess.get('flowElements')).not.toContain(target);
|
||||||
|
expect(subProcess.di.$parent.get('planeElement')).not.toContain(target.di);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should undo 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();
|
||||||
|
|
||||||
|
// 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