bpmn-js/lib/Viewer.js

289 lines
7.4 KiB
JavaScript
Raw Normal View History

'use strict';
var Diagram = require('diagram-js'),
BpmnModdle = require('bpmn-moddle'),
$ = require('jquery'),
_ = require('lodash');
2014-08-01 05:47:36 +00:00
var Importer = require('./import/Importer');
function initListeners(diagram, listeners) {
var events = diagram.get('eventBus');
listeners.forEach(function(l) {
events.on(l.event, l.handler);
});
}
function checkValidationError(err) {
// check if we can help the user by indicating wrong BPMN 2.0 xml
// (in case he or the exporting tool did not get that right)
var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
var match = pattern.exec(err.message);
if (match) {
err.message =
'unparsable content <' + match[1] + '> detected; ' +
'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
}
return err;
}
var DEFAULT_OPTIONS = {
width: '100%',
height: '100%',
position: 'relative'
};
/**
* A viewer for BPMN 2.0 diagrams.
*
* Includes the basic viewing functionality.
*
* Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
* additional features.
*
* @param {Object} [options] configuration options to pass to the viewer
* @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
* @param {String|Number} [options.width] the width of the viewer
* @param {String|Number} [options.height] the height of the viewer
* @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
* @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
*/
function Viewer(options) {
this.options = options = _.extend({}, DEFAULT_OPTIONS, options || {});
var parent = options.container || $('body');
var container = $('<div class="bjs-container"></div>').appendTo(parent);
container.css({
width: options.width,
height: options.height,
position: options.position
});
// unwrap jquery
this.container = container.get(0);
2014-05-06 12:32:54 +00:00
/**
* The code in the <project-logo></project-logo> area
* must not be changed, see http://bpmn.io/license for more information
*
* <project-logo>
*/
/* jshint -W101 */
// inlined ../resources/bpmnjs.png
var logoData = 'iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAMAAADypuvZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADBQTFRFiMte9PrwldFwfcZPqtqN0+zEyOe1XLgjvuKncsJAZ70y6fXh3vDT////UrQV////G2zN+AAAABB0Uk5T////////////////////AOAjXRkAAAHDSURBVHjavJZJkoUgDEBJmAX8979tM8u3E6x20VlYJfFFMoL4vBDxATxZcakIOJTWSmxvKWVIkJ8jHvlRv1F2LFrVISCZI+tCtQx+XfewgVTfyY3plPiQEAzI3zWy+kR6NBhFBYeBuscJLOUuA2WVLpCjVIaFzrNQZArxAZKUQm6gsj37L9Cb7dnIBUKxENaaMJQqMpDXvSL+ktxdGRm2IsKgJGGPg7atwUG5CcFUEuSv+CwQqizTrvDTNXdMU2bMiDWZd8d7QIySWVRsb2vBBioxOFt4OinPBapL+neAb5KL5IJ8szOza2/DYoipUCx+CjO0Bpsv0V6mktNZ+k8rlABlWG0FrOpKYVo8DT3dBeLEjUBAj7moDogVii7nSS9QzZnFcOVBp1g2PyBQ3Vr5aIapN91VJy33HTJLC1iX2FY6F8gRdaAeIEfVONgtFCzZTmoLEdOjBDfsIOA6128gw3eu1shAajdZNAORxuQDJN5A5PbEG6gNIu24QJD5iNyRMZIr6bsHbCtCU/OaOaSvgkUyDMdDa1BXGf5HJ1To+/Ym6mCKT02Y+/Sa126ZKyd3jxhzpc1r8zVL6YM1Qy/kR4ABAFJ6iQUnivhAAAAAAElFTkSuQmCC';
/* jshint +W101 */
var a = $('<a href="http://bpmn.io" target="_blank" class="bjs-powered-by" title="Powered by bpmn.io" />').css({
position: 'absolute',
bottom: 15,
right: 15,
zIndex: 100
});
var logo = $('<img/>').attr('src', 'data:image/png;base64,' + logoData).appendTo(a);
a.appendTo(container);
2014-05-06 12:32:54 +00:00
/* </project-logo> */
}
Viewer.prototype.importXML = function(xml, done) {
var self = this;
this.moddle = this.createModdle();
this.moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions) {
if (err) {
err = checkValidationError(err);
return done(err);
}
self.importDefinitions(definitions, done);
});
};
Viewer.prototype.saveXML = function(options, done) {
if (!done) {
done = options;
options = {};
}
var definitions = this.definitions;
if (!definitions) {
return done(new Error('no definitions loaded'));
}
2014-09-04 11:27:36 +00:00
this.moddle.toXML(definitions, options, done);
};
Viewer.prototype.createModdle = function() {
return new BpmnModdle();
};
Viewer.prototype.saveSVG = function(options, done) {
if (!done) {
done = options;
options = {};
}
var canvas = this.get('canvas');
var contentNode = canvas.getDefaultLayer(),
defsNode = canvas._svg.select('defs');
var contents = contentNode.innerSVG(),
defs = (defsNode && defsNode.outerSVG()) || '';
var bbox = contentNode.getBBox();
var svg =
'<?xml version="1.0" encoding="utf-8"?>\n' +
'<!-- created with bpmn-js / http://bpmn.io -->\n' +
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
'width="' + bbox.width + '" height="' + bbox.height + '" ' +
'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' +
defs + contents +
'</svg>';
done(null, svg);
};
Viewer.prototype.get = function(name) {
if (!this.diagram) {
throw new Error('no diagram loaded');
}
return this.diagram.get(name);
};
Viewer.prototype.invoke = function(fn) {
if (!this.diagram) {
throw new Error('no diagram loaded');
}
return this.diagram.invoke(fn);
};
2014-08-01 05:47:36 +00:00
Viewer.prototype.importDefinitions = function(definitions, done) {
2014-08-01 05:47:36 +00:00
// use try/catch to not swallow synchronous exceptions
// that may be raised during model parsing
try {
2014-08-05 06:34:54 +00:00
if (this.diagram) {
2014-08-01 05:47:36 +00:00
this.clear();
}
2014-08-01 05:47:36 +00:00
this.definitions = definitions;
2014-08-05 06:34:54 +00:00
this.diagram = this._createDiagram(this.options);
2014-08-05 06:34:54 +00:00
this._init(this.diagram);
2014-08-05 06:34:54 +00:00
Importer.importBpmnDiagram(this.diagram, definitions, done);
2014-08-01 05:47:36 +00:00
} 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 = _.extend(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.
*/
Viewer.prototype.clear = function() {
var diagram = this.diagram;
if (diagram) {
diagram.destroy();
}
};
/**
* Destroy the viewer instance and remove all its remainders
* from the document tree.
*/
Viewer.prototype.destroy = function() {
// clear underlying diagram
this.clear();
// remove container
$(this.container).remove();
};
/**
* Register an event listener on the viewer
*
* @param {String} event
* @param {Function} handler
*/
Viewer.prototype.on = function(event, handler) {
var diagram = this.diagram,
listeners = this.__listeners = this.__listeners || [];
listeners = this.__listeners || [];
listeners.push({ event: event, handler: handler });
if (diagram) {
diagram.get('eventBus').on(event, handler);
}
};
// modules the viewer is composed of
Viewer.prototype._modules = [
require('./core'),
require('diagram-js/lib/features/selection'),
require('diagram-js/lib/features/overlays')
];
module.exports = Viewer;