'use strict'; var Diagram = require('diagram-js'), BpmnModdle = require('bpmn-moddle'), $ = require('jquery'), _ = require('lodash'); 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; } /** * A viewer for BPMN 2.0 diagrams * * @class * * @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} [options.modules] a list of modules to override the default modules * @param {Array} [options.additionalModules] a list of modules to use with the default modules */ function Viewer(options) { this.options = options = options || {}; var parent = options.container || $('body'); var container = $('
').appendTo(parent); container.css('width', options.width || '100%'); container.css('height', options.height || '100%'); // unwrap jquery this.container = container.get(0); /** * The code in the area * must not be changed, see http://bpmn.io/license for more information * * */ /* 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 = $('').css({ position: 'absolute', bottom: 15, right: 15, zIndex: 100 }); var logo = $('').attr('src', 'data:image/png;base64,' + logoData).appendTo(a); a.appendTo(container); /* */ } 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')); } 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 = '\n' + '\n' + '\n' + '' + defs + contents + ''; 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); }; 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; this.diagram = this._createDiagram(this.options); this._init(this.diagram); Importer.importBpmnDiagram(this.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 = _.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 */ Viewer.prototype.clear = function() { var diagram = this.diagram; if (diagram) { diagram.destroy(); } }; /** * 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;