feat(viewer): emit export events
This makes the viewer emit events during SVG and XML export. These events allow others to hook in, i.e. to trigger additional _save_ actions. Closes #811
This commit is contained in:
parent
e8dfccedea
commit
3c87716895
|
@ -199,6 +199,16 @@ Viewer.prototype.importXML = function(xml, done) {
|
|||
* Export the currently displayed BPMN 2.0 diagram as
|
||||
* a BPMN 2.0 XML document.
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During XML saving the viewer will fire life-cycle events:
|
||||
*
|
||||
* * saveXML.start (before serialization)
|
||||
* * saveXML.serialized (after xml generation)
|
||||
* * saveXML.done (everything done)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {Object} [options] export options
|
||||
* @param {Boolean} [options.format=false] output formated XML
|
||||
* @param {Boolean} [options.preamble=true] output preamble
|
||||
|
@ -212,19 +222,52 @@ Viewer.prototype.saveXML = function(options, done) {
|
|||
options = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var definitions = this._definitions;
|
||||
|
||||
if (!definitions) {
|
||||
return done(new Error('no definitions loaded'));
|
||||
}
|
||||
|
||||
this._moddle.toXML(definitions, options, done);
|
||||
// allow to fiddle around with definitions
|
||||
definitions = this._emit('saveXML.start', {
|
||||
definitions: definitions
|
||||
}) || definitions;
|
||||
|
||||
this._moddle.toXML(definitions, options, function(err, xml) {
|
||||
|
||||
try {
|
||||
xml = self._emit('saveXML.serialized', {
|
||||
error: err,
|
||||
xml: xml
|
||||
}) || xml;
|
||||
|
||||
self._emit('saveXML.done', {
|
||||
error: err,
|
||||
xml: xml
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('error in saveXML life-cycle listener', e);
|
||||
}
|
||||
|
||||
done(err, xml);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the currently displayed BPMN 2.0 diagram as
|
||||
* an SVG image.
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During SVG saving the viewer will fire life-cycle events:
|
||||
*
|
||||
* * saveSVG.start (before serialization)
|
||||
* * saveSVG.done (everything done)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Function} done invoked with (err, svgStr)
|
||||
*/
|
||||
|
@ -235,6 +278,8 @@ Viewer.prototype.saveSVG = function(options, done) {
|
|||
options = {};
|
||||
}
|
||||
|
||||
this._emit('saveSVG.start');
|
||||
|
||||
var svg, err;
|
||||
|
||||
try {
|
||||
|
@ -261,6 +306,11 @@ Viewer.prototype.saveSVG = function(options, done) {
|
|||
err = e;
|
||||
}
|
||||
|
||||
this._emit('saveSVG.done', {
|
||||
error: err,
|
||||
svg: svg
|
||||
});
|
||||
|
||||
done(err, svg);
|
||||
};
|
||||
|
||||
|
|
|
@ -294,162 +294,6 @@ describe('Viewer', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('export', function() {
|
||||
|
||||
function currentTime() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
function validSVG(svg) {
|
||||
var expectedStart = '<?xml version="1.0" encoding="utf-8"?>';
|
||||
var expectedEnd = '</svg>';
|
||||
|
||||
expect(svg.indexOf(expectedStart)).to.equal(0);
|
||||
expect(svg.indexOf(expectedEnd)).to.equal(svg.length - expectedEnd.length);
|
||||
|
||||
// ensure correct rendering of SVG contents
|
||||
expect(svg.indexOf('undefined')).to.equal(-1);
|
||||
|
||||
// expect header to be written only once
|
||||
expect(svg.indexOf('<svg width="100%" height="100%">')).to.equal(-1);
|
||||
expect(svg.indexOf('<g class="viewport"')).to.equal(-1);
|
||||
|
||||
var parser = new DOMParser();
|
||||
var svgNode = parser.parseFromString(svg, 'image/svg+xml');
|
||||
|
||||
// [comment, <!DOCTYPE svg>, svg]
|
||||
expect(svgNode.childNodes).to.have.length(3);
|
||||
|
||||
// no error body
|
||||
expect(svgNode.body).not.to.exist;
|
||||
|
||||
// FIXME(nre): make matcher
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
it('should export XML', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
// when
|
||||
viewer.saveXML({ format: true }, function(err, xml) {
|
||||
|
||||
// then
|
||||
expect(xml).to.contain('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
expect(xml).to.contain('<bpmn2:definitions');
|
||||
expect(xml).to.contain(' ');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should export svg', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should export huge svg', function(done) {
|
||||
|
||||
this.timeout(5000);
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/complex.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var time = currentTime();
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
|
||||
// no svg export should not take too long
|
||||
expect(currentTime() - time).to.be.below(1000);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should remove outer-makers on export', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
function appendTestRect(svgDoc) {
|
||||
var rect = document.createElementNS(svgDoc.namespaceURI, 'rect');
|
||||
rect.setAttribute('class', 'outer-bound-marker');
|
||||
rect.setAttribute('width', 500);
|
||||
rect.setAttribute('height', 500);
|
||||
rect.setAttribute('x', 10000);
|
||||
rect.setAttribute('y', 10000);
|
||||
svgDoc.appendChild(rect);
|
||||
}
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var svgDoc = viewer._container.childNodes[1].childNodes[1];
|
||||
|
||||
|
||||
|
||||
appendTestRect(svgDoc);
|
||||
appendTestRect(svgDoc);
|
||||
|
||||
expect(svgDoc.querySelectorAll('.outer-bound-marker')).to.exist;
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
var svgDoc = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svgDoc.innerHTML = svg;
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
expect(svgDoc.querySelector('.outer-bound-marker')).to.be.null;
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('creation', function() {
|
||||
|
||||
var testModules = [
|
||||
|
@ -812,6 +656,244 @@ describe('Viewer', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('#saveXML', function() {
|
||||
|
||||
it('should export XML', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
// when
|
||||
viewer.saveXML({ format: true }, function(err, xml) {
|
||||
|
||||
// then
|
||||
expect(xml).to.contain('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
expect(xml).to.contain('<bpmn2:definitions');
|
||||
expect(xml).to.contain(' ');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should emit <saveXML.*> events', function(done) {
|
||||
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
var events = [];
|
||||
|
||||
viewer.on([
|
||||
'saveXML.start',
|
||||
'saveXML.serialized',
|
||||
'saveXML.done'
|
||||
], function(e) {
|
||||
// log event type + event arguments
|
||||
events.push([
|
||||
e.type,
|
||||
Object.keys(e).filter(function(key) {
|
||||
return key !== 'type';
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
viewer.importXML(xml, function(err) {
|
||||
|
||||
// when
|
||||
viewer.saveXML(function(err) {
|
||||
// then
|
||||
expect(events).to.eql([
|
||||
[ 'saveXML.start', [ 'definitions' ] ],
|
||||
[ 'saveXML.serialized', ['error', 'xml' ] ],
|
||||
[ 'saveXML.done', ['error', 'xml' ] ]
|
||||
]);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('#saveSVG', function() {
|
||||
|
||||
function currentTime() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
function validSVG(svg) {
|
||||
var expectedStart = '<?xml version="1.0" encoding="utf-8"?>';
|
||||
var expectedEnd = '</svg>';
|
||||
|
||||
expect(svg.indexOf(expectedStart)).to.equal(0);
|
||||
expect(svg.indexOf(expectedEnd)).to.equal(svg.length - expectedEnd.length);
|
||||
|
||||
// ensure correct rendering of SVG contents
|
||||
expect(svg.indexOf('undefined')).to.equal(-1);
|
||||
|
||||
// expect header to be written only once
|
||||
expect(svg.indexOf('<svg width="100%" height="100%">')).to.equal(-1);
|
||||
expect(svg.indexOf('<g class="viewport"')).to.equal(-1);
|
||||
|
||||
var parser = new DOMParser();
|
||||
var svgNode = parser.parseFromString(svg, 'image/svg+xml');
|
||||
|
||||
// [comment, <!DOCTYPE svg>, svg]
|
||||
expect(svgNode.childNodes).to.have.length(3);
|
||||
|
||||
// no error body
|
||||
expect(svgNode.body).not.to.exist;
|
||||
|
||||
// FIXME(nre): make matcher
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
it('should export svg', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should export huge svg', function(done) {
|
||||
|
||||
this.timeout(5000);
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/complex.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var time = currentTime();
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
|
||||
// no svg export should not take too long
|
||||
expect(currentTime() - time).to.be.below(1000);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should remove outer-makers on export', function(done) {
|
||||
|
||||
// given
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
function appendTestRect(svgDoc) {
|
||||
var rect = document.createElementNS(svgDoc.namespaceURI, 'rect');
|
||||
rect.setAttribute('class', 'outer-bound-marker');
|
||||
rect.setAttribute('width', 500);
|
||||
rect.setAttribute('height', 500);
|
||||
rect.setAttribute('x', 10000);
|
||||
rect.setAttribute('y', 10000);
|
||||
svgDoc.appendChild(rect);
|
||||
}
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var svgDoc = viewer._container.childNodes[1].childNodes[1];
|
||||
|
||||
|
||||
|
||||
appendTestRect(svgDoc);
|
||||
appendTestRect(svgDoc);
|
||||
|
||||
expect(svgDoc.querySelectorAll('.outer-bound-marker')).to.exist;
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function(err, svg) {
|
||||
|
||||
var svgDoc = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svgDoc.innerHTML = svg;
|
||||
|
||||
// then
|
||||
expect(validSVG(svg)).to.be.true;
|
||||
expect(svgDoc.querySelector('.outer-bound-marker')).to.be.null;
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should emit <saveSVG.*> events', function(done) {
|
||||
|
||||
var xml = require('../fixtures/bpmn/simple.bpmn');
|
||||
|
||||
createViewer(xml, function(err, warnings, viewer) {
|
||||
|
||||
var events = [];
|
||||
|
||||
viewer.on([
|
||||
'saveSVG.start',
|
||||
'saveSVG.done'
|
||||
], function(e) {
|
||||
// log event type + event arguments
|
||||
events.push([
|
||||
e.type,
|
||||
Object.keys(e).filter(function(key) {
|
||||
return key !== 'type';
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
viewer.importXML(xml, function(err) {
|
||||
|
||||
// when
|
||||
viewer.saveSVG(function() {
|
||||
// then
|
||||
expect(events).to.eql([
|
||||
[ 'saveSVG.start', [ ] ],
|
||||
[ 'saveSVG.done', ['error', 'svg' ] ]
|
||||
]);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('#on', function() {
|
||||
|
||||
it('should fire with given three', function(done) {
|
||||
|
|
Loading…
Reference in New Issue