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(); + }); + }); + + }); });