feat(bpmn): make available BpmnRegistry in renderer

This commit upgrades the code base to the latest diagram-js changes and
adds a component called BpmnRegistry that can be used to retrieve a
certain BPMN/DI element from a shape/connection id.

Related to #19
This commit is contained in:
Nico Rehwaldt 2014-04-03 11:47:26 +02:00
parent 4c0c286ae7
commit 02313e6c1b
10 changed files with 275 additions and 20 deletions

View File

@ -24,8 +24,22 @@ function instance() {
return INSTANCE;
}
/**
* Instantiates a BPMN model tree from a given xml string.
*
* @param {String} xmlStr
* @param {String} [typeName] name of the root element, defaults to 'bpmn:Definitions'
* @param {Object} [options] to pass to the underlying reader
* @param {Function} callback the callback that is invoked with (err, result, parseContext)
*/
function fromXML(xmlStr, typeName, options, callback) {
if (!_.isString(typeName)) {
callback = options;
options = typeName;
typeName = 'bpmn:Definitions';
}
if (_.isFunction(options)) {
callback = options;
options = {};

View File

@ -3,10 +3,12 @@ var Diagram = require('diagram-js');
var bpmnModule = require('./di').defaultModule,
Viewer = require('./Viewer');
require('./core/BpmnRegistry');
require('./draw/BpmnRenderer');
require('diagram-js/src/features/DragUI');
require('diagram-js/src/features/SelectionUI');
require('diagram-js/lib/features/dnd/Visuals');
require('diagram-js/lib/features/selection/Visuals');
function Modeler(container) {
@ -20,7 +22,7 @@ Modeler.prototype.createDiagram = function() {
return new Diagram({
canvas: { container: this.container },
modules: [ bpmnModule ],
components: [ 'selectionUI', 'dragUI' ]
components: [ 'selectionVisuals', 'dragVisuals', 'bpmnRegistry']
});
};

24
lib/Util.js Normal file
View File

@ -0,0 +1,24 @@
var _ = require('lodash');
function failSafeAsync(fn) {
return function() {
var args = Array.prototype.slice.call(arguments);
var done = args[args.length - 1];
if (!done || !_.isFunction(done)) {
done = function(e) {
throw e;
};
}
try {
fn.apply(this, args);
} catch (e) {
done(e);
}
};
}
module.exports.failSafeAsync = failSafeAsync;

View File

@ -1,11 +1,12 @@
var Diagram = require('diagram-js');
var Importer = require('./import/Importer'),
Model = require('./Model');
Model = require('./Model'),
failSafeAsync = require('./Util').failSafeAsync;
function initListeners(diagram, listeners) {
var events = diagram.get('events');
var events = diagram.get('eventBus');
listeners.forEach(function(l) {
events.on(l.event, l.handler);
@ -72,7 +73,7 @@ Viewer.prototype.saveSVG = function(options, done) {
done(null, svg);
};
Viewer.prototype.importDefinitions = function(definitions, done) {
Viewer.prototype.importDefinitions = failSafeAsync(function(definitions, done) {
if (this.diagram) {
this.clear();
@ -85,7 +86,7 @@ Viewer.prototype.importDefinitions = function(definitions, done) {
this.definitions = definitions;
Importer.importBpmnDiagram(this.diagram, definitions, done);
};
});
Viewer.prototype.createDiagram = function() {
return new Diagram({ canvas: { container: this.container }});

55
lib/core/BpmnRegistry.js Normal file
View File

@ -0,0 +1,55 @@
var bpmnModule = require('../di').defaultModule;
/**
* @class
*
* A registry that keeps track of bpmn semantic / di elements and the
* corresponding shapes.
*
* @param {EventBus} events
* @param {ElementRegistry} elementRegistry
*/
function BpmnRegistry(events, elementRegistry) {
var elements = {
di: {},
semantic: {},
diagramElement: {}
};
events.on('bpmn.element.add', function(e) {
var semantic = e.semantic,
id = semantic.id;
elements.di[id] = e.di;
elements.semantic[id] = e.semantic;
elements.diagramElement[id] = e.diagramElement;
});
events.on('bpmn.element.removed', function(e) {
var semantic = e.semantic,
id = semantic.id;
delete elements.di[id];
delete elements.semantic[id];
delete elements.diagramElement[id];
});
function get(type) {
var collection = elements[type];
return function(id) {
return collection[id];
};
}
// API
this.getSemantic = get('semantic');
this.getDi = get('di');
this.getDiagramElement = get('diagramElement');
}
bpmnModule.type('bpmnRegistry', [ 'eventBus', 'elementRegistry', BpmnRegistry ]);
module.exports = BpmnRegistry;

View File

@ -1,14 +1,14 @@
var bpmnModule = require('../di').defaultModule;
require('diagram-js/src/core/Events');
require('diagram-js/src/draw/Styles');
require('diagram-js/lib/core/EventBus');
require('diagram-js/lib/draw/Styles');
var DefaultRenderer = require('diagram-js/src/draw/Renderer');
var DefaultRenderer = require('diagram-js/lib/draw/Renderer');
var flattenPoints = DefaultRenderer.flattenPoints;
function BpmnRenderer(events, styles) {
function BpmnRenderer(events, styles, bpmnRegistry) {
DefaultRenderer.apply(this, [ events, styles ]);
@ -394,6 +394,6 @@ function BpmnRenderer(events, styles) {
BpmnRenderer.prototype = Object.create(DefaultRenderer.prototype);
bpmnModule.type('renderer', [ 'events', 'styles', BpmnRenderer ]);
bpmnModule.type('renderer', [ 'eventBus', 'styles', 'bpmnRegistry', BpmnRenderer ]);
module.exports = BpmnRenderer;

View File

@ -1,12 +1,12 @@
var _ = require('lodash');
var BpmnTreeWalker = require('./BpmnTreeWalker');
var BpmnTreeWalker = require('./BpmnTreeWalker'),
Util = require('../Util');
function importBpmnDiagram(diagram, definitions, done) {
var canvas = diagram.get('canvas');
var shapes = {};
var events = diagram.get('eventBus');
var visitor = {
@ -14,6 +14,12 @@ function importBpmnDiagram(diagram, definitions, done) {
var shape;
function fire(type, shape) {
events.fire('bpmn.element.' + type, {
semantic: element, di: di, diagramElement: shape
});
}
if (di.$type === 'bpmndi:BPMNShape') {
var bounds = di.bounds;
@ -24,6 +30,7 @@ function importBpmnDiagram(diagram, definitions, done) {
parent: parent
};
fire('add', shape);
canvas.addShape(shape);
} else {
@ -32,10 +39,12 @@ function importBpmnDiagram(diagram, definitions, done) {
});
shape = { id: element.id, type: element.$type, waypoints: waypoints };
fire('add', shape);
canvas.addConnection(shape);
}
shapes[element.id] = shape;
fire('added', shape);
return shape;
},
@ -46,10 +55,9 @@ function importBpmnDiagram(diagram, definitions, done) {
};
var walker = new BpmnTreeWalker(visitor);
walker.handleDefinitions(definitions);
done();
}
module.exports.importBpmnDiagram = importBpmnDiagram;
module.exports.importBpmnDiagram = Util.failSafeAsync(importBpmnDiagram);

View File

@ -0,0 +1,71 @@
var fs = require('fs');
var BpmnModel = require('../../../lib/Model'),
Modeler = require('../../../lib/Modeler');
var Matchers = require('../Matchers');
describe('Modeler', function() {
var bpmnModel = BpmnModel.instance();
function read(xml, opts, callback) {
return BpmnModel.fromXML(xml, 'bpmn:Definitions', opts, callback);
}
var container;
beforeEach(Matchers.add);
beforeEach(function() {
container = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(container);
});
afterEach(function() {
container.parentNode.removeChild(container);
});
it('should import simple process', function(done) {
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
var renderer = new Modeler(container);
renderer.importXML(xml, function(err) {
done(err);
});
});
it('should import empty definitions', function(done) {
var xml = fs.readFileSync('test/fixtures/bpmn/empty-definitions.bpmn', 'utf8');
var renderer = new Modeler(container);
renderer.importXML(xml, function(err) {
done(err);
});
});
it('should handle errors', function(done) {
var xml = 'invalid stuff';
var renderer = new Modeler(container);
renderer.importXML(xml, function(err) {
expect(err).toBeDefined();
done();
});
});
});

View File

@ -5,7 +5,7 @@ var BpmnModel = require('../../../lib/Model'),
var Matchers = require('../Matchers');
describe('Importer', function() {
describe('Viewer', function() {
var bpmnModel = BpmnModel.instance();

View File

@ -0,0 +1,80 @@
var fs = require('fs'),
Diagram = require('diagram-js/lib/Diagram');
var BpmnModel = require('../../../../lib/Model');
var Importer = require('../../../../lib/import/Importer');
var bpmnModule = require('../../../../lib/di').defaultModule;
require('../../../../lib/core/BpmnRegistry');
require('../../../../lib/draw/BpmnRenderer');
var Matchers = require('../../Matchers');
describe('import/Importer', function() {
var bpmnModel = BpmnModel.instance();
function read(xml, opts, callback) {
return BpmnModel.fromXML(xml, 'bpmn:Definitions', opts, callback);
}
var container;
beforeEach(Matchers.add);
beforeEach(function() {
container = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(container);
});
afterEach(function() {
container.parentNode.removeChild(container);
});
function createDiagram() {
return new Diagram({
canvas: { container: container },
modules: [ bpmnModule ],
components: [ 'bpmnRegistry']
});
}
it('should fire bpmn.element.add during import', function(done) {
// given
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
var diagram = createDiagram();
var events = [];
// log events
diagram.get('eventBus').on('bpmn.element.add', function(e) {
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id });
});
BpmnModel.fromXML(xml, function(err, definitions) {
if (err) {
return done(err);
}
// when
Importer.importBpmnDiagram(diagram, definitions, function(err) {
// then
expect(events).toEqual([
{ type: 'add', semantic: 'SubProcess_1', di: '_BPMNShape_SubProcess_2', diagramElement: 'SubProcess_1' },
{ type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_2', diagramElement: 'StartEvent_1' },
{ type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_2', diagramElement: 'Task_1' },
{ type: 'add', semantic: 'SequenceFlow_1', di: 'BPMNEdge_SequenceFlow_1', diagramElement: 'SequenceFlow_1' }
]);
done(err);
});
});
});
});