diff --git a/lib/Modeler.js b/lib/Modeler.js
index d766dcc6..91285309 100644
--- a/lib/Modeler.js
+++ b/lib/Modeler.js
@@ -109,8 +109,8 @@ Modeler.prototype.createDiagram = function(done) {
return this.importXML(initialDiagram, done);
};
-Modeler.prototype.createModdle = function() {
- var moddle = Viewer.prototype.createModdle.call(this);
+Modeler.prototype._createModdle = function(options) {
+ var moddle = Viewer.prototype._createModdle.call(this, options);
moddle.ids = new Ids([ 32, 36, 1 ]);
diff --git a/lib/Viewer.js b/lib/Viewer.js
index d7da7dfb..28376c63 100644
--- a/lib/Viewer.js
+++ b/lib/Viewer.js
@@ -18,17 +18,12 @@ var domify = require('min-dom/lib/domify'),
var Diagram = require('diagram-js'),
BpmnModdle = require('bpmn-moddle');
+
+var inherits = require('inherits');
+
var Importer = require('./import/Importer');
-function initListeners(diagram, listeners) {
- var events = diagram.get('eventBus');
-
- listeners.forEach(function(l) {
- events.on(l.event, l.priority, l.callback, l.that);
- });
-}
-
function checkValidationError(err) {
// check if we can help the user by indicating wrong BPMN 2.0 xml
@@ -110,39 +105,23 @@ function ensureUnit(val) {
*/
function Viewer(options) {
- this.options = options = assign({}, DEFAULT_OPTIONS, options || {});
+ options = assign({}, DEFAULT_OPTIONS, options);
- this.moddle = this.createModdle();
+ this.moddle = this._createModdle(options);
- var parent = options.container;
-
- // support jquery element
- // unwrap it if passed
- if (parent.get) {
- parent = parent.get(0);
- }
-
- // support selector
- if (isString(parent)) {
- parent = domQuery(parent);
- }
-
- var container = this.container = domify('
');
- parent.appendChild(container);
-
- assign(container.style, {
- width: ensureUnit(options.width),
- height: ensureUnit(options.height),
- position: options.position
- });
+ this.container = this._createContainer(options);
/* */
- addProjectLogo(container);
+ addProjectLogo(this.container);
/* */
+
+ this._init(this.container, this.moddle, options);
}
+inherits(Viewer, Diagram);
+
module.exports = Viewer;
@@ -206,10 +185,6 @@ Viewer.prototype.saveXML = function(options, done) {
this.moddle.toXML(definitions, options, done);
};
-Viewer.prototype.createModdle = function() {
- return new BpmnModdle(assign({}, this._moddleExtensions, this.options.moddleExtensions));
-};
-
/**
* Export the currently displayed BPMN 2.0 diagram as
* an SVG image.
@@ -258,15 +233,9 @@ Viewer.prototype.saveSVG = function(options, done) {
* @param {String} name
*
* @return {Object} diagram service instance
+ *
+ * @method Viewer#get
*/
-Viewer.prototype.get = function(name) {
-
- if (!this.diagram) {
- throw new Error('no diagram loaded');
- }
-
- return this.diagram.get(name);
-};
/**
* Invoke a function in the context of this viewer.
@@ -280,94 +249,61 @@ Viewer.prototype.get = function(name) {
* @param {Function} fn to be invoked
*
* @return {Object} the functions return value
+ *
+ * @method Viewer#invoke
*/
-Viewer.prototype.invoke = function(fn) {
-
- if (!this.diagram) {
- throw new Error('no diagram loaded');
- }
-
- return this.diagram.invoke(fn);
-};
-
-Viewer.prototype.importDefinitions = function(definitions, done) {
-
- // use try/catch to not swallow synchronous exceptions
- // that may be raised during model parsing
- try {
- if (this.diagram) {
- this.clear();
- }
-
- this.definitions = definitions;
-
- var diagram = this.diagram = this._createDiagram(this.options);
-
- this._init(diagram);
-
- Importer.importBpmnDiagram(diagram, definitions, done);
- } catch (e) {
- done(e);
- }
-};
-
-Viewer.prototype._init = function(diagram) {
- initListeners(diagram, this.__listeners || []);
-};
-
-Viewer.prototype._createDiagram = function(options) {
-
- var modules = [].concat(options.modules || this.getModules(), options.additionalModules || []);
-
- // add self as an available service
- modules.unshift({
- bpmnjs: [ 'value', this ],
- moddle: [ 'value', this.moddle ]
- });
-
- options = omit(options, 'additionalModules');
-
- options = assign(options, {
- canvas: assign({}, options.canvas, { container: this.container }),
- modules: modules
- });
-
- return new Diagram(options);
-};
-
-
-Viewer.prototype.getModules = function() {
- return this._modules;
-};
/**
* Remove all drawn elements from the viewer.
*
* After calling this method the viewer can still
* be reused for opening another diagram.
+ *
+ * @method Viewer#clear
*/
-Viewer.prototype.clear = function() {
- var diagram = this.diagram;
- if (diagram) {
- diagram.destroy();
+Viewer.prototype.importDefinitions = function(definitions, done) {
+
+ // use try/catch to not swallow synchronous exceptions
+ // that may be raised during model parsing
+ try {
+
+ if (this.definitions) {
+ // clear existing rendered diagram
+ this.clear();
+ }
+
+ // update definitions
+ this.definitions = definitions;
+
+ // perform graphical import
+ Importer.importBpmnDiagram(this, definitions, done);
+ } catch (e) {
+
+ // handle synchronous errors
+ done(e);
}
};
+Viewer.prototype.getModules = function() {
+ return this._modules;
+};
+
/**
- * Destroy the viewer instance and remove all its remainders
- * from the document tree.
+ * Destroy the viewer instance and remove all its
+ * remainders from the document tree.
*/
Viewer.prototype.destroy = function() {
- // clear underlying diagram
- this.clear();
- // remove container
+ // diagram destroy
+ Diagram.prototype.destroy.call(this);
+
+ // dom detach
domRemove(this.container);
};
/**
- * Register an event listener on the viewer
+ * Register an event listener
*
* Remove a previously added listener via {@link #off(event, callback)}.
*
@@ -376,50 +312,79 @@ Viewer.prototype.destroy = function() {
* @param {Function} callback
* @param {Object} [that]
*/
-Viewer.prototype.on = function(event, priority, callback, that) {
- var diagram = this.diagram,
- listeners = this.__listeners = this.__listeners || [];
-
- if (typeof priority === 'function') {
- that = callback;
- callback = priority;
- priority = 1000;
- }
-
- listeners.push({ event: event, priority: priority, callback: callback, that: that });
-
- if (diagram) {
- return diagram.get('eventBus').on(event, priority, callback, that);
- }
+Viewer.prototype.on = function(event, priority, callback, target) {
+ return this.get('eventBus').on(event, priority, callback, target);
};
/**
- * De-register an event callback
+ * De-register an event listener
*
* @param {String} event
* @param {Function} callback
*/
Viewer.prototype.off = function(event, callback) {
- var filter,
- diagram = this.diagram;
-
- if (callback) {
- filter = function(l) {
- return !(l.event === event && l.callback === callback);
- };
- } else {
- filter = function(l) {
- return l.event !== event;
- };
- }
-
- this.__listeners = (this.__listeners || []).filter(filter);
-
- if (diagram) {
- diagram.get('eventBus').off(event, callback);
- }
+ this.get('eventBus').off(event, callback);
};
+
+Viewer.prototype._init = function(container, moddle, options) {
+
+ var baseModules = options.modules || this.getModules(),
+ additionalModules = options.additionalModules || [],
+ staticModules = [
+ {
+ bpmnjs: [ 'value', this ],
+ moddle: [ 'value', moddle ]
+ }
+ ];
+
+ var diagramModules = [].concat(staticModules, baseModules, additionalModules);
+
+ var diagramOptions = assign(omit(options, 'additionalModules'), {
+ canvas: assign({}, options.canvas, { container: container }),
+ modules: diagramModules
+ });
+
+ // invoke diagram constructor
+ Diagram.call(this, diagramOptions);
+};
+
+Viewer.prototype._createContainer = function(options) {
+
+ var parent = options.container,
+ container;
+
+ // support jquery element
+ // unwrap it if passed
+ if (parent.get) {
+ parent = parent.get(0);
+ }
+
+ // support selector
+ if (isString(parent)) {
+ parent = domQuery(parent);
+ }
+
+ container = domify('');
+
+ assign(container.style, {
+ width: ensureUnit(options.width),
+ height: ensureUnit(options.height),
+ position: options.position
+ });
+
+ parent.appendChild(container);
+
+ return container;
+};
+
+Viewer.prototype._createModdle = function(options) {
+ var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
+
+ return new BpmnModdle(moddleOptions);
+};
+
+
// modules the viewer is composed of
Viewer.prototype._modules = [
require('./core'),
diff --git a/test/spec/ModelerSpec.js b/test/spec/ModelerSpec.js
index 6e4ea7a2..61b0cae2 100644
--- a/test/spec/ModelerSpec.js
+++ b/test/spec/ModelerSpec.js
@@ -107,37 +107,6 @@ describe('Modeler', function() {
});
- describe('ids', function() {
-
- it('should populate ids on import', function(done) {
-
- // given
- var xml = require('../fixtures/bpmn/simple.bpmn');
-
- var modeler = new Modeler({ container: container });
-
-
- // when
- modeler.importXML(xml, function(err) {
-
- var moddle = modeler.get('moddle');
- var elementRegistry = modeler.get('elementRegistry');
-
- var subProcess = elementRegistry.get('SubProcess_1').businessObject;
- var bpmnEdge = elementRegistry.get('SequenceFlow_3').businessObject.di;
-
- // then
- expect(moddle.ids.assigned('SubProcess_1')).to.eql(subProcess);
- expect(moddle.ids.assigned('BPMNEdge_SequenceFlow_3')).to.eql(bpmnEdge);
-
- done();
- });
-
- });
-
- });
-
-
describe('translate support', function() {
var xml = require('../fixtures/bpmn/simple.bpmn');
@@ -275,6 +244,81 @@ describe('Modeler', function() {
});
+ describe('ids', function() {
+
+ it('should provide ids with moddle', function() {
+
+ // given
+ var modeler = new Modeler({ container: container });
+
+ // when
+ var moddle = modeler.get('moddle');
+
+ // then
+ expect(moddle.ids).to.exist;
+ });
+
+
+ it('should populate ids on import', function(done) {
+
+ // given
+ var xml = require('../fixtures/bpmn/simple.bpmn');
+
+ var modeler = new Modeler({ container: container });
+
+ var moddle = modeler.get('moddle');
+ var elementRegistry = modeler.get('elementRegistry');
+
+ // when
+ modeler.importXML(xml, function(err) {
+
+ var subProcess = elementRegistry.get('SubProcess_1').businessObject;
+ var bpmnEdge = elementRegistry.get('SequenceFlow_3').businessObject.di;
+
+ // then
+ expect(moddle.ids.assigned('SubProcess_1')).to.eql(subProcess);
+ expect(moddle.ids.assigned('BPMNEdge_SequenceFlow_3')).to.eql(bpmnEdge);
+
+ done();
+ });
+
+ });
+
+
+ it('should clear ids before re-import', function(done) {
+
+ // given
+ var someXML = require('../fixtures/bpmn/simple.bpmn'),
+ otherXML = require('../fixtures/bpmn/basic.bpmn');
+
+ var modeler = new Modeler({ container: container });
+
+ var moddle = modeler.get('moddle');
+ var elementRegistry = modeler.get('elementRegistry');
+
+ // when
+ modeler.importXML(someXML, function() {
+
+ modeler.importXML(otherXML, function() {
+
+ var task = elementRegistry.get('Task_1').businessObject;
+
+ // then
+ // not in other.bpmn
+ expect(moddle.ids.assigned('SubProcess_1')).to.be.false;
+
+ // in other.bpmn
+ expect(moddle.ids.assigned('Task_1')).to.eql(task);
+
+ done();
+ });
+ });
+
+ });
+
+ });
+
+
it('should handle errors', function(done) {
var xml = 'invalid stuff';
@@ -298,7 +342,7 @@ describe('Modeler', function() {
describe('dependency injection', function() {
- it('should be available via di as ', function(done) {
+ it('should provide self as ', function(done) {
var xml = require('../fixtures/bpmn/simple.bpmn');
@@ -310,6 +354,49 @@ describe('Modeler', function() {
});
});
+
+ it('should allow Diagram#get before import', function() {
+
+ // when
+ var modeler = new Modeler({ container: container });
+
+ // then
+ var eventBus = modeler.get('eventBus');
+
+ expect(eventBus).to.exist;
+ });
+
+
+ it('should keep references to services across re-import', function(done) {
+
+ // given
+ var someXML = require('../fixtures/bpmn/simple.bpmn'),
+ otherXML = require('../fixtures/bpmn/basic.bpmn');
+
+ var modeler = new Modeler({ container: container });
+
+ var eventBus = modeler.get('eventBus'),
+ canvas = modeler.get('canvas');
+
+ // when
+ modeler.importXML(someXML, function() {
+
+ // then
+ expect(modeler.get('canvas')).to.equal(canvas);
+ expect(modeler.get('eventBus')).to.equal(eventBus);
+
+ modeler.importXML(otherXML, function() {
+
+ // then
+ expect(modeler.get('canvas')).to.equal(canvas);
+ expect(modeler.get('eventBus')).to.equal(eventBus);
+
+ done();
+ });
+ });
+
+ });
+
});
});
diff --git a/test/spec/ViewerSpec.js b/test/spec/ViewerSpec.js
index e7157dfb..94daecd7 100644
--- a/test/spec/ViewerSpec.js
+++ b/test/spec/ViewerSpec.js
@@ -2,6 +2,8 @@
var TestContainer = require('mocha-test-container-support');
+var Diagram = require('diagram-js/lib/Diagram');
+
var Viewer = require('../../lib/Viewer');
var inherits = require('inherits');
@@ -46,8 +48,11 @@ describe('Viewer', function() {
// mimic re-import of same diagram
viewer.importXML(xml, function(err, warnings) {
+ if (err) {
+ return done(err);
+ }
+
// then
- expect(err).to.exist;
expect(warnings.length).to.equal(0);
done();
@@ -57,6 +62,16 @@ describe('Viewer', function() {
});
+ it('should be instance of Diagram', function() {
+
+ // when
+ var viewer = new Viewer({ container: container });
+
+ // then
+ expect(viewer).to.be.instanceof(Diagram);
+ });
+
+
describe('defaults', function() {
it('should use as default parent', function(done) {
@@ -259,7 +274,7 @@ describe('Viewer', function() {
describe('dependency injection', function() {
- it('should be available via di as ', function(done) {
+ it('should provide self as ', function(done) {
var xml = require('../fixtures/bpmn/simple.bpmn');
@@ -271,6 +286,48 @@ describe('Viewer', function() {
});
});
+
+ it('should allow Diagram#get before import', function() {
+
+ // when
+ var viewer = new Viewer({ container: container });
+
+ // then
+ var eventBus = viewer.get('eventBus');
+
+ expect(eventBus).to.exist;
+ });
+
+
+ it('should keep references to services across re-import', function(done) {
+
+ // given
+ var someXML = require('../fixtures/bpmn/simple.bpmn'),
+ otherXML = require('../fixtures/bpmn/basic.bpmn');
+
+ var viewer = new Viewer({ container: container });
+
+ var eventBus = viewer.get('eventBus'),
+ canvas = viewer.get('canvas');
+
+ // when
+ viewer.importXML(someXML, function() {
+
+ // then
+ expect(viewer.get('canvas')).to.equal(canvas);
+ expect(viewer.get('eventBus')).to.equal(eventBus);
+
+ viewer.importXML(otherXML, function() {
+
+ // then
+ expect(viewer.get('canvas')).to.equal(canvas);
+ expect(viewer.get('eventBus')).to.equal(eventBus);
+
+ done();
+ });
+ });
+
+ });
});