dmn-js-drd
Version:
A decision requirements diagram view for dmn-js
286 lines (261 loc) • 7.78 kB
JavaScript
/**
* The code in the <project-logo></project-logo> area
* must not be changed.
*
* @see http://bpmn.io/license for more information.
*/
import { domify, query as domQuery, remove as domRemove } from 'min-dom';
import Diagram from 'diagram-js';
import inherits from 'inherits-browser';
import { importDRD } from './import/Importer';
import { innerSVG } from 'tiny-svg';
import { wrapForCompatibility } from 'dmn-js-shared/lib/util/CompatibilityUtils';
import { addProjectLogo } from 'dmn-js-shared/lib/util/PoweredByUtil';
/**
* @typedef {import('dmn-js-shared/lib/base/View).OpenResult} OpenResult
*/
/**
* @typedef {import('dmn-js-shared/lib/base/View).OpenError} OpenError
*/
/**
* A viewer for DMN diagrams.
*
* Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
* additional features.
*
*
* ## 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 drdViewer = new Viewer({ additionalModules: [ extensionModule ] });
* drdViewer.importXML(...);
* ```
*
* @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 {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
*/
export default function Viewer(options) {
this._container = this._createContainer();
/* <project-logo> */
addProjectLogo(this._container);
/* </project-logo> */
this._init(this._container, options);
}
inherits(Viewer, Diagram);
/**
* The saveSVG result.
*
* @typedef {Object} SaveSVGResult
*
* @property {string} svg
*/
/**
* Export the currently displayed DMN diagram as
* an SVG image.
*
* @param {Object} [options]
*
* @return {Promise<SaveSVGResult>}
*/
Viewer.prototype.saveSVG = wrapForCompatibility(function (options) {
var self = this;
return new Promise(function (resolve) {
var canvas = self.get('canvas');
var contentNode = canvas.getActiveLayer(),
defsNode = domQuery('defs', canvas._svg);
var contents = innerSVG(contentNode),
defs = defsNode && defsNode.outerHTML || '';
var bbox = contentNode.getBBox();
/* eslint-disable max-len */
var svg = '<?xml version="1.0" encoding="utf-8"?>\n' + '<!-- created with dmn-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>';
/* eslint-enable */
resolve({
svg
});
});
});
Viewer.prototype.getModules = function () {
return this._modules;
};
/**
* Destroy the viewer instance and remove all its
* remainders from the document tree.
*/
Viewer.prototype.destroy = function () {
// diagram destroy
Diagram.prototype.destroy.call(this);
// dom detach
domRemove(this._container);
};
/**
* Register an event listener
*
* Remove a previously added listener via {@link #off(event, callback)}.
*
* @param {string} event
* @param {number} [priority]
* @param {Function} callback
* @param {Object} [that]
*/
Viewer.prototype.on = function (event, priority, callback, target) {
return this.get('eventBus').on(event, priority, callback, target);
};
/**
* De-register an event listener
*
* @param {string} event
* @param {Function} callback
*/
Viewer.prototype.off = function (event, callback) {
this.get('eventBus').off(event, callback);
};
Viewer.prototype._init = function (container, options) {
var {
additionalModules,
canvas,
...additionalOptions
} = options;
var baseModules = options.modules || this.getModules(),
staticModules = [{
drd: ['value', this]
}];
var modules = [...staticModules, ...baseModules, ...(additionalModules || [])];
var diagramOptions = {
...additionalOptions,
canvas: {
...canvas,
container
},
modules
};
// invoke diagram constructor
Diagram.call(this, diagramOptions);
if (options && options.container) {
this.attachTo(options.container);
}
};
/**
* Emit an event on the underlying {@link EventBus}
*
* @param {string} type
* @param {Object} event
*
* @return {Object} event processing result (if any)
*/
Viewer.prototype._emit = function (type, event) {
return this.get('eventBus').fire(type, event);
};
Viewer.prototype._createContainer = function () {
return domify('<div class="dmn-drd-container"></div>');
};
/**
* Open diagram element.
*
* @param {ModdleElement} definitions
* @returns {Promise} Resolves with {OpenResult} when successful
* or rejects with {OpenError}
*/
Viewer.prototype.open = function (definitions) {
var self = this;
return new Promise((resolve, reject) => {
var err;
// use try/catch to not swallow synchronous exceptions
// that may be raised during model parsing
try {
if (self._definitions) {
// clear existing rendered diagram
self.clear();
}
// update definitions
self._definitions = definitions;
// perform graphical import
return importDRD(self, definitions, function (err, warnings) {
if (err) {
err.warnings = warnings || [];
reject(err);
} else {
resolve({
warnings: warnings || []
});
}
});
} catch (e) {
err = e;
}
if (err) {
err.warnings = err.warnings || [];
reject(err);
} else {
resolve({
warnings: []
});
}
});
};
/**
* Attach viewer to given parent node.
*
* @param {Element} parentNode
*/
Viewer.prototype.attachTo = function (parentNode) {
if (!parentNode) {
throw new Error('parentNode required');
}
// ensure we detach from the
// previous, old parent
this.detach();
var container = this._container;
parentNode.appendChild(container);
this._emit('attach', {});
this.get('canvas').resized();
};
/**
* Detach viewer from parent node, if attached.
*/
Viewer.prototype.detach = function () {
var container = this._container,
parentNode = container.parentNode;
if (!parentNode) {
return;
}
this._emit('detach', {});
parentNode.removeChild(container);
};
import CoreModule from './core';
import TranslateModule from 'diagram-js/lib/i18n/translate';
import SelectionModule from 'diagram-js/lib/features/selection';
import OverlaysModule from 'diagram-js/lib/features/overlays';
import DefinitionPropertiesModule from './features/definition-properties/viewer';
import DrillDownModule from './features/drill-down';
Viewer.prototype._modules = [CoreModule, TranslateModule, SelectionModule, OverlaysModule, DefinitionPropertiesModule, DrillDownModule];
//# sourceMappingURL=Viewer.js.map