2016-01-12 07:47:25 +00:00
|
|
|
/**
|
|
|
|
* The code in the <project-logo></project-logo> area
|
|
|
|
* must not be changed.
|
|
|
|
*
|
|
|
|
* @see http://bpmn.io/license for more information.
|
|
|
|
*/
|
2014-06-11 12:41:55 +00:00
|
|
|
'use strict';
|
|
|
|
|
2015-02-02 13:46:21 +00:00
|
|
|
var assign = require('lodash/object/assign'),
|
2015-02-05 08:24:27 +00:00
|
|
|
omit = require('lodash/object/omit'),
|
|
|
|
isString = require('lodash/lang/isString'),
|
|
|
|
isNumber = require('lodash/lang/isNumber');
|
|
|
|
|
|
|
|
var domify = require('min-dom/lib/domify'),
|
|
|
|
domQuery = require('min-dom/lib/query'),
|
|
|
|
domRemove = require('min-dom/lib/remove');
|
2015-02-02 13:46:21 +00:00
|
|
|
|
2014-04-09 08:50:20 +00:00
|
|
|
var Diagram = require('diagram-js'),
|
2015-02-05 08:24:27 +00:00
|
|
|
BpmnModdle = require('bpmn-moddle');
|
2014-03-18 16:01:24 +00:00
|
|
|
|
2014-08-01 05:47:36 +00:00
|
|
|
var Importer = require('./import/Importer');
|
2014-04-09 08:50:20 +00:00
|
|
|
|
|
|
|
|
2014-03-26 16:45:04 +00:00
|
|
|
function initListeners(diagram, listeners) {
|
2014-04-03 09:47:26 +00:00
|
|
|
var events = diagram.get('eventBus');
|
2014-03-26 16:45:04 +00:00
|
|
|
|
|
|
|
listeners.forEach(function(l) {
|
2015-10-22 08:37:33 +00:00
|
|
|
events.on(l.event, l.priority, l.handler, l.that);
|
2014-03-26 16:45:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-06-30 17:04:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-12-18 08:45:45 +00:00
|
|
|
var DEFAULT_OPTIONS = {
|
|
|
|
width: '100%',
|
|
|
|
height: '100%',
|
2015-02-05 08:24:27 +00:00
|
|
|
position: 'relative',
|
|
|
|
container: 'body'
|
2014-12-18 08:45:45 +00:00
|
|
|
};
|
|
|
|
|
2015-02-05 08:24:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure the passed argument is a proper unit (defaulting to px)
|
|
|
|
*/
|
|
|
|
function ensureUnit(val) {
|
|
|
|
return val + (isNumber(val) ? 'px' : '');
|
|
|
|
}
|
|
|
|
|
2014-04-08 13:23:52 +00:00
|
|
|
/**
|
2015-01-10 11:04:26 +00:00
|
|
|
* A viewer for BPMN 2.0 diagrams.
|
2014-04-25 14:14:36 +00:00
|
|
|
*
|
2015-01-10 11:04:26 +00:00
|
|
|
* Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
|
|
|
|
* additional features.
|
2014-08-01 05:55:47 +00:00
|
|
|
*
|
2015-05-04 12:49:48 +00:00
|
|
|
*
|
|
|
|
* ## Extending the Viewer
|
|
|
|
*
|
|
|
|
* In order to extend the viewer pass extension modules to bootstrap via the
|
|
|
|
* `additionalModules` option. An extension module is an object that exposes
|
|
|
|
* named services.
|
|
|
|
*
|
|
|
|
* The following example depicts the integration of a simple
|
|
|
|
* logging component that integrates with interaction events:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
*
|
|
|
|
* // logging component
|
|
|
|
* function InteractionLogger(eventBus) {
|
|
|
|
* eventBus.on('element.hover', function(event) {
|
|
|
|
* console.log()
|
|
|
|
* })
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* InteractionLogger.$inject = [ 'eventBus' ]; // minification save
|
|
|
|
*
|
|
|
|
* // extension module
|
|
|
|
* var extensionModule = {
|
|
|
|
* __init__: [ 'interactionLogger' ],
|
|
|
|
* interactionLogger: [ 'type', InteractionLogger ]
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* // extend the viewer
|
|
|
|
* var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
|
|
|
|
* bpmnViewer.importXML(...);
|
|
|
|
* ```
|
|
|
|
*
|
2014-04-08 13:23:52 +00:00
|
|
|
* @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
|
2015-02-12 14:50:23 +00:00
|
|
|
* @param {Object} [options.moddleExtensions] extension packages to provide
|
2014-08-01 05:55:47 +00:00
|
|
|
* @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
|
2014-04-08 13:23:52 +00:00
|
|
|
*/
|
2014-04-04 16:48:37 +00:00
|
|
|
function Viewer(options) {
|
2014-12-18 08:45:45 +00:00
|
|
|
|
2015-02-02 13:46:21 +00:00
|
|
|
this.options = options = assign({}, DEFAULT_OPTIONS, options || {});
|
2014-04-08 13:23:52 +00:00
|
|
|
|
2015-02-05 08:24:27 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-04-04 16:48:37 +00:00
|
|
|
|
2015-02-05 08:24:27 +00:00
|
|
|
var container = this.container = domify('<div class="bjs-container"></div>');
|
|
|
|
parent.appendChild(container);
|
2014-04-08 13:23:52 +00:00
|
|
|
|
2015-02-05 08:24:27 +00:00
|
|
|
assign(container.style, {
|
|
|
|
width: ensureUnit(options.width),
|
|
|
|
height: ensureUnit(options.height),
|
2014-12-18 08:45:45 +00:00
|
|
|
position: options.position
|
|
|
|
});
|
2014-04-08 13:23:52 +00:00
|
|
|
|
2016-01-12 07:47:25 +00:00
|
|
|
/* <project-logo> */
|
2014-04-04 16:48:37 +00:00
|
|
|
|
2016-01-12 07:47:25 +00:00
|
|
|
addProjectLogo(container);
|
2014-05-06 12:32:54 +00:00
|
|
|
|
|
|
|
/* </project-logo> */
|
2014-03-18 16:01:24 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 07:47:25 +00:00
|
|
|
module.exports = Viewer;
|
|
|
|
|
|
|
|
|
2014-03-21 20:10:01 +00:00
|
|
|
Viewer.prototype.importXML = function(xml, done) {
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
2015-02-17 11:03:27 +00:00
|
|
|
this.moddle = this.createModdle();
|
2014-08-04 14:32:11 +00:00
|
|
|
|
2015-02-17 09:55:28 +00:00
|
|
|
this.moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions, context) {
|
2014-06-30 17:04:40 +00:00
|
|
|
|
2014-03-21 20:10:01 +00:00
|
|
|
if (err) {
|
2014-06-30 17:04:40 +00:00
|
|
|
err = checkValidationError(err);
|
2014-03-21 20:10:01 +00:00
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
2015-02-17 09:55:28 +00:00
|
|
|
var parseWarnings = context.warnings;
|
|
|
|
|
|
|
|
self.importDefinitions(definitions, function(err, importWarnings) {
|
2015-11-24 11:53:18 +00:00
|
|
|
var allWarnings = parseWarnings.concat(importWarnings || []);
|
2015-02-17 09:55:28 +00:00
|
|
|
|
2015-11-24 11:53:18 +00:00
|
|
|
done(err, allWarnings);
|
2015-02-17 09:55:28 +00:00
|
|
|
});
|
2014-03-21 20:10:01 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-03-24 10:02:58 +00:00
|
|
|
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);
|
2014-03-24 10:02:58 +00:00
|
|
|
};
|
|
|
|
|
2014-08-04 14:32:11 +00:00
|
|
|
Viewer.prototype.createModdle = function() {
|
2015-02-17 11:03:27 +00:00
|
|
|
return new BpmnModdle(this.options.moddleExtensions);
|
2014-08-04 14:32:11 +00:00
|
|
|
};
|
2014-03-24 10:02:58 +00:00
|
|
|
|
|
|
|
Viewer.prototype.saveSVG = function(options, done) {
|
2014-08-17 17:08:52 +00:00
|
|
|
|
2014-03-24 10:02:58 +00:00
|
|
|
if (!done) {
|
|
|
|
done = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
2014-08-17 17:08:52 +00:00
|
|
|
var canvas = this.get('canvas');
|
|
|
|
|
2014-11-26 10:31:23 +00:00
|
|
|
var contentNode = canvas.getDefaultLayer(),
|
|
|
|
defsNode = canvas._svg.select('defs');
|
2014-08-17 17:08:52 +00:00
|
|
|
|
|
|
|
var contents = contentNode.innerSVG(),
|
|
|
|
defs = (defsNode && defsNode.outerSVG()) || '';
|
2014-03-24 10:02:58 +00:00
|
|
|
|
2014-08-17 17:08:52 +00:00
|
|
|
var bbox = contentNode.getBBox();
|
2014-03-24 10:02:58 +00:00
|
|
|
|
2014-08-17 17:08:52 +00:00
|
|
|
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>';
|
2014-03-24 10:02:58 +00:00
|
|
|
|
|
|
|
done(null, svg);
|
|
|
|
};
|
|
|
|
|
2014-04-04 16:48:37 +00:00
|
|
|
Viewer.prototype.get = function(name) {
|
|
|
|
|
|
|
|
if (!this.diagram) {
|
2014-06-17 09:19:31 +00:00
|
|
|
throw new Error('no diagram loaded');
|
2014-04-04 16:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.diagram.get(name);
|
|
|
|
};
|
|
|
|
|
2014-06-17 09:19:31 +00:00
|
|
|
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-03-18 16:01:24 +00:00
|
|
|
|
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-03-18 16:01:24 +00:00
|
|
|
|
2014-08-01 05:47:36 +00:00
|
|
|
this.definitions = definitions;
|
2014-03-18 16:01:24 +00:00
|
|
|
|
2015-01-14 17:48:06 +00:00
|
|
|
var diagram = this.diagram = this._createDiagram(this.options);
|
2014-06-24 13:50:51 +00:00
|
|
|
|
2015-01-14 17:48:06 +00:00
|
|
|
this._init(diagram);
|
|
|
|
|
|
|
|
Importer.importBpmnDiagram(diagram, definitions, done);
|
2014-08-01 05:47:36 +00:00
|
|
|
} catch (e) {
|
|
|
|
done(e);
|
|
|
|
}
|
|
|
|
};
|
2014-03-18 16:01:24 +00:00
|
|
|
|
2014-06-24 13:50:51 +00:00
|
|
|
Viewer.prototype._init = function(diagram) {
|
2014-04-04 16:48:37 +00:00
|
|
|
initListeners(diagram, this.__listeners || []);
|
|
|
|
};
|
|
|
|
|
2014-08-01 05:55:47 +00:00
|
|
|
Viewer.prototype._createDiagram = function(options) {
|
2014-04-09 08:50:20 +00:00
|
|
|
|
2014-08-01 05:55:47 +00:00
|
|
|
var modules = [].concat(options.modules || this.getModules(), options.additionalModules || []);
|
2014-06-17 09:19:31 +00:00
|
|
|
|
|
|
|
// add self as an available service
|
|
|
|
modules.unshift({
|
2014-08-04 14:32:11 +00:00
|
|
|
bpmnjs: [ 'value', this ],
|
|
|
|
moddle: [ 'value', this.moddle ]
|
2014-06-17 09:19:31 +00:00
|
|
|
});
|
|
|
|
|
2015-02-02 13:46:21 +00:00
|
|
|
options = omit(options, 'additionalModules');
|
2014-08-01 05:55:47 +00:00
|
|
|
|
2015-02-02 13:46:21 +00:00
|
|
|
options = assign(options, {
|
2016-01-08 13:41:58 +00:00
|
|
|
canvas: assign({}, options.canvas, { container: this.container }),
|
2014-06-17 09:19:31 +00:00
|
|
|
modules: modules
|
2014-04-09 08:50:20 +00:00
|
|
|
});
|
2014-08-01 05:55:47 +00:00
|
|
|
|
|
|
|
return new Diagram(options);
|
2014-03-18 16:01:24 +00:00
|
|
|
};
|
|
|
|
|
2014-06-24 13:50:51 +00:00
|
|
|
|
2014-06-17 09:19:31 +00:00
|
|
|
Viewer.prototype.getModules = function() {
|
|
|
|
return this._modules;
|
|
|
|
};
|
|
|
|
|
2014-06-24 13:50:51 +00:00
|
|
|
/**
|
2014-12-18 08:45:45 +00:00
|
|
|
* Remove all drawn elements from the viewer.
|
|
|
|
*
|
|
|
|
* After calling this method the viewer can still
|
|
|
|
* be reused for opening another diagram.
|
2014-06-24 13:50:51 +00:00
|
|
|
*/
|
2014-03-20 15:18:23 +00:00
|
|
|
Viewer.prototype.clear = function() {
|
2014-04-04 16:48:37 +00:00
|
|
|
var diagram = this.diagram;
|
|
|
|
|
|
|
|
if (diagram) {
|
|
|
|
diagram.destroy();
|
|
|
|
}
|
2014-03-18 16:01:24 +00:00
|
|
|
};
|
|
|
|
|
2014-12-18 08:45:45 +00:00
|
|
|
/**
|
|
|
|
* Destroy the viewer instance and remove all its remainders
|
|
|
|
* from the document tree.
|
|
|
|
*/
|
|
|
|
Viewer.prototype.destroy = function() {
|
|
|
|
// clear underlying diagram
|
|
|
|
this.clear();
|
|
|
|
|
|
|
|
// remove container
|
2015-02-05 08:24:27 +00:00
|
|
|
domRemove(this.container);
|
2014-12-18 08:45:45 +00:00
|
|
|
};
|
|
|
|
|
2014-06-24 13:50:51 +00:00
|
|
|
/**
|
|
|
|
* Register an event listener on the viewer
|
|
|
|
*
|
|
|
|
* @param {String} event
|
|
|
|
* @param {Function} handler
|
|
|
|
*/
|
2015-10-22 08:37:33 +00:00
|
|
|
Viewer.prototype.on = function(event, priority, handler, that) {
|
2014-04-25 14:14:36 +00:00
|
|
|
var diagram = this.diagram,
|
|
|
|
listeners = this.__listeners = this.__listeners || [];
|
|
|
|
|
2015-10-22 08:37:33 +00:00
|
|
|
listeners.push({ event: event, priority: priority, handler: handler, that: that });
|
2014-04-25 14:14:36 +00:00
|
|
|
|
|
|
|
if (diagram) {
|
2015-10-22 08:37:33 +00:00
|
|
|
diagram.get('eventBus').on(event, priority, handler, that);
|
2014-04-25 14:14:36 +00:00
|
|
|
}
|
2014-03-26 16:45:04 +00:00
|
|
|
};
|
|
|
|
|
2014-06-24 13:50:51 +00:00
|
|
|
// modules the viewer is composed of
|
2014-06-17 09:19:31 +00:00
|
|
|
Viewer.prototype._modules = [
|
2014-06-19 07:49:26 +00:00
|
|
|
require('./core'),
|
2014-08-01 09:40:18 +00:00
|
|
|
require('diagram-js/lib/features/selection'),
|
|
|
|
require('diagram-js/lib/features/overlays')
|
2014-06-11 12:41:55 +00:00
|
|
|
];
|
|
|
|
|
2016-01-12 07:47:25 +00:00
|
|
|
|
|
|
|
/* <project-logo> */
|
|
|
|
|
|
|
|
var PoweredBy = require('./util/PoweredByUtil'),
|
|
|
|
domEvent = require('min-dom/lib/event');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the project logo to the diagram container as
|
|
|
|
* required by the bpmn.io license.
|
|
|
|
*
|
|
|
|
* @see http://bpmn.io/license
|
|
|
|
*
|
|
|
|
* @param {Element} container
|
|
|
|
*/
|
|
|
|
function addProjectLogo(container) {
|
|
|
|
var logoData = PoweredBy.BPMNIO_LOGO;
|
|
|
|
|
|
|
|
var linkMarkup =
|
|
|
|
'<a href="http://bpmn.io" ' +
|
|
|
|
'target="_blank" ' +
|
|
|
|
'class="bjs-powered-by" ' +
|
|
|
|
'title="Powered by bpmn.io" ' +
|
|
|
|
'style="position: absolute; bottom: 15px; right: 15px; z-index: 100">' +
|
|
|
|
'<img src="data:image/png;base64,' + logoData + '">' +
|
|
|
|
'</a>';
|
|
|
|
|
|
|
|
var linkElement = domify(linkMarkup);
|
|
|
|
|
|
|
|
container.appendChild(linkElement);
|
|
|
|
|
|
|
|
domEvent.bind(linkElement, 'click', function(event) {
|
|
|
|
PoweredBy.open();
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/* </project-logo> */
|