feat(project): provide Base{Viewer|Modeler} distributions
This allows users to use the viewer / modeler features without inheriting our modules. Related to #258
This commit is contained in:
parent
8af3986332
commit
bb94b206a7
|
@ -0,0 +1,74 @@
|
|||
import inherits from 'inherits';
|
||||
|
||||
import Ids from 'ids';
|
||||
|
||||
import BaseViewer from './BaseViewer';
|
||||
|
||||
|
||||
/**
|
||||
* A base modeler for BPMN 2.0 diagrams.
|
||||
*
|
||||
* Have a look at {@link Modeler} for a bundle that includes actual 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 {Object} [options.moddleExtensions] extension packages to provide
|
||||
* @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 BaseModeler(options) {
|
||||
BaseViewer.call(this, options);
|
||||
|
||||
// hook ID collection into the modeler
|
||||
this.on('import.parse.complete', function(event) {
|
||||
if (!event.error) {
|
||||
this._collectIds(event.definitions, event.context);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.on('diagram.destroy', function() {
|
||||
this.get('moddle').ids.clear();
|
||||
}, this);
|
||||
}
|
||||
|
||||
inherits(BaseModeler, BaseViewer);
|
||||
|
||||
|
||||
/**
|
||||
* Create a moddle instance, attaching ids to it.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
BaseModeler.prototype._createModdle = function(options) {
|
||||
var moddle = BaseViewer.prototype._createModdle.call(this, options);
|
||||
|
||||
// attach ids to moddle to be able to track
|
||||
// and validated ids in the BPMN 2.0 XML document
|
||||
// tree
|
||||
moddle.ids = new Ids([ 32, 36, 1 ]);
|
||||
|
||||
return moddle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect ids processed during parsing of the
|
||||
* definitions object.
|
||||
*
|
||||
* @param {ModdleElement} definitions
|
||||
* @param {Context} context
|
||||
*/
|
||||
BaseModeler.prototype._collectIds = function(definitions, context) {
|
||||
|
||||
var moddle = definitions.$model,
|
||||
ids = moddle.ids,
|
||||
id;
|
||||
|
||||
// remove references from previous import
|
||||
ids.clear();
|
||||
|
||||
for (id in context.elementsById) {
|
||||
ids.claim(id, context.elementsById[id]);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,653 @@
|
|||
/**
|
||||
* The code in the <project-logo></project-logo> area
|
||||
* must not be changed.
|
||||
*
|
||||
* @see http://bpmn.io/license for more information.
|
||||
*/
|
||||
import {
|
||||
assign,
|
||||
find,
|
||||
isFunction,
|
||||
isNumber,
|
||||
omit
|
||||
} from 'min-dash';
|
||||
|
||||
import {
|
||||
domify,
|
||||
query as domQuery,
|
||||
remove as domRemove
|
||||
} from 'min-dom';
|
||||
|
||||
import {
|
||||
innerSVG
|
||||
} from 'tiny-svg';
|
||||
|
||||
import Diagram from 'diagram-js';
|
||||
import BpmnModdle from 'bpmn-moddle';
|
||||
|
||||
import inherits from 'inherits';
|
||||
|
||||
import {
|
||||
importBpmnDiagram
|
||||
} from './import/Importer';
|
||||
|
||||
|
||||
/**
|
||||
* A base viewer for BPMN 2.0 diagrams.
|
||||
*
|
||||
* Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for
|
||||
* bundles that include actual 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 {Object} [options.moddleExtensions] extension packages to provide
|
||||
* @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 BaseViewer(options) {
|
||||
|
||||
options = assign({}, DEFAULT_OPTIONS, options);
|
||||
|
||||
this._moddle = this._createModdle(options);
|
||||
|
||||
this._container = this._createContainer(options);
|
||||
|
||||
/* <project-logo> */
|
||||
|
||||
addProjectLogo(this._container);
|
||||
|
||||
/* </project-logo> */
|
||||
|
||||
this._init(this._container, this._moddle, options);
|
||||
}
|
||||
|
||||
inherits(BaseViewer, Diagram);
|
||||
|
||||
|
||||
/**
|
||||
* Parse and render a BPMN 2.0 diagram.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During import the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.parse.start (about to read model from xml)
|
||||
* * import.parse.complete (model read; may have worked or not)
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
* * import.done (everything done)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {String} xml the BPMN 2.0 xml
|
||||
* @param {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
BaseViewer.prototype.importXML = function(xml, bpmnDiagram, done) {
|
||||
|
||||
if (isFunction(bpmnDiagram)) {
|
||||
done = bpmnDiagram;
|
||||
bpmnDiagram = null;
|
||||
}
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
var self = this;
|
||||
|
||||
// hook in pre-parse listeners +
|
||||
// allow xml manipulation
|
||||
xml = this._emit('import.parse.start', { xml: xml }) || xml;
|
||||
|
||||
this._moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions, context) {
|
||||
|
||||
// hook in post parse listeners +
|
||||
// allow definitions manipulation
|
||||
definitions = self._emit('import.parse.complete', {
|
||||
error: err,
|
||||
definitions: definitions,
|
||||
context: context
|
||||
}) || definitions;
|
||||
|
||||
var parseWarnings = context.warnings;
|
||||
|
||||
if (err) {
|
||||
err = checkValidationError(err);
|
||||
|
||||
self._emit('import.done', { error: err, warnings: parseWarnings });
|
||||
|
||||
return done(err, parseWarnings);
|
||||
}
|
||||
|
||||
self.importDefinitions(definitions, bpmnDiagram, function(err, importWarnings) {
|
||||
var allWarnings = [].concat(parseWarnings, importWarnings || []);
|
||||
|
||||
self._emit('import.done', { error: err, warnings: allWarnings });
|
||||
|
||||
done(err, allWarnings);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Import parsed definitions and render a BPMN 2.0 diagram.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During import the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {ModdleElement<Definitions>} definitions parsed BPMN 2.0 definitions
|
||||
* @param {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
BaseViewer.prototype.importDefinitions = function(definitions, bpmnDiagram, done) {
|
||||
|
||||
if (isFunction(bpmnDiagram)) {
|
||||
done = bpmnDiagram;
|
||||
bpmnDiagram = null;
|
||||
}
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
this._setDefinitions(definitions);
|
||||
|
||||
return this.open(bpmnDiagram, done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open diagram of previously imported XML.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During switch the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {String|ModdleElement<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
BaseViewer.prototype.open = function(bpmnDiagramOrId, done) {
|
||||
|
||||
if (isFunction(bpmnDiagramOrId)) {
|
||||
done = bpmnDiagramOrId;
|
||||
bpmnDiagramOrId = null;
|
||||
}
|
||||
|
||||
var definitions = this._definitions;
|
||||
var bpmnDiagram = bpmnDiagramOrId;
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
if (!definitions) {
|
||||
return done(new Error('no XML imported'));
|
||||
}
|
||||
|
||||
if (typeof bpmnDiagramOrId === 'string') {
|
||||
bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);
|
||||
|
||||
if (!bpmnDiagram) {
|
||||
return done(new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'));
|
||||
}
|
||||
}
|
||||
|
||||
// clear existing rendered diagram
|
||||
// catch synchronous exceptions during #clear()
|
||||
try {
|
||||
this.clear();
|
||||
} catch (error) {
|
||||
return done(error);
|
||||
}
|
||||
|
||||
// perform graphical import
|
||||
return importBpmnDiagram(this, definitions, bpmnDiagram, 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 formatted XML
|
||||
* @param {Boolean} [options.preamble=true] output preamble
|
||||
*
|
||||
* @param {Function} done invoked with (err, xml)
|
||||
*/
|
||||
BaseViewer.prototype.saveXML = function(options, done) {
|
||||
|
||||
if (!done) {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var definitions = this._definitions;
|
||||
|
||||
if (!definitions) {
|
||||
return done(new Error('no definitions loaded'));
|
||||
}
|
||||
|
||||
// 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)
|
||||
*/
|
||||
BaseViewer.prototype.saveSVG = function(options, done) {
|
||||
|
||||
if (!done) {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
this._emit('saveSVG.start');
|
||||
|
||||
var svg, err;
|
||||
|
||||
try {
|
||||
var canvas = this.get('canvas');
|
||||
|
||||
var contentNode = canvas.getDefaultLayer(),
|
||||
defsNode = domQuery('defs', canvas._svg);
|
||||
|
||||
var contents = innerSVG(contentNode),
|
||||
defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';
|
||||
|
||||
var bbox = contentNode.getBBox();
|
||||
|
||||
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>';
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
this._emit('saveSVG.done', {
|
||||
error: err,
|
||||
svg: svg
|
||||
});
|
||||
|
||||
done(err, svg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a named diagram service.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var elementRegistry = viewer.get('elementRegistry');
|
||||
* var startEventShape = elementRegistry.get('StartEvent_1');
|
||||
*
|
||||
* @param {String} name
|
||||
*
|
||||
* @return {Object} diagram service instance
|
||||
*
|
||||
* @method BaseViewer#get
|
||||
*/
|
||||
|
||||
/**
|
||||
* Invoke a function in the context of this viewer.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* viewer.invoke(function(elementRegistry) {
|
||||
* var startEventShape = elementRegistry.get('StartEvent_1');
|
||||
* });
|
||||
*
|
||||
* @param {Function} fn to be invoked
|
||||
*
|
||||
* @return {Object} the functions return value
|
||||
*
|
||||
* @method BaseViewer#invoke
|
||||
*/
|
||||
|
||||
|
||||
BaseViewer.prototype._setDefinitions = function(definitions) {
|
||||
this._definitions = definitions;
|
||||
};
|
||||
|
||||
BaseViewer.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.
|
||||
*
|
||||
* @method BaseViewer#clear
|
||||
*/
|
||||
BaseViewer.prototype.clear = function() {
|
||||
if (!this.getDefinitions()) {
|
||||
|
||||
// no diagram to clear
|
||||
return;
|
||||
}
|
||||
|
||||
// remove businessObject#di binding
|
||||
//
|
||||
// this is necessary, as we establish the bindings
|
||||
// in the BpmnTreeWalker (and assume none are given
|
||||
// on reimport)
|
||||
this.get('elementRegistry').forEach(function(element) {
|
||||
var bo = element.businessObject;
|
||||
|
||||
if (bo && bo.di) {
|
||||
delete bo.di;
|
||||
}
|
||||
});
|
||||
|
||||
// remove drawn elements
|
||||
Diagram.prototype.clear.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the viewer instance and remove all its
|
||||
* remainders from the document tree.
|
||||
*/
|
||||
BaseViewer.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]
|
||||
*/
|
||||
BaseViewer.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
|
||||
*/
|
||||
BaseViewer.prototype.off = function(event, callback) {
|
||||
this.get('eventBus').off(event, callback);
|
||||
};
|
||||
|
||||
BaseViewer.prototype.attachTo = function(parentNode) {
|
||||
|
||||
if (!parentNode) {
|
||||
throw new Error('parentNode required');
|
||||
}
|
||||
|
||||
// ensure we detach from the
|
||||
// previous, old parent
|
||||
this.detach();
|
||||
|
||||
// unwrap jQuery if provided
|
||||
if (parentNode.get && parentNode.constructor.prototype.jquery) {
|
||||
parentNode = parentNode.get(0);
|
||||
}
|
||||
|
||||
if (typeof parentNode === 'string') {
|
||||
parentNode = domQuery(parentNode);
|
||||
}
|
||||
|
||||
parentNode.appendChild(this._container);
|
||||
|
||||
this._emit('attach', {});
|
||||
|
||||
this.get('canvas').resized();
|
||||
};
|
||||
|
||||
BaseViewer.prototype.getDefinitions = function() {
|
||||
return this._definitions;
|
||||
};
|
||||
|
||||
BaseViewer.prototype.detach = function() {
|
||||
|
||||
var container = this._container,
|
||||
parentNode = container.parentNode;
|
||||
|
||||
if (!parentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._emit('detach', {});
|
||||
|
||||
parentNode.removeChild(container);
|
||||
};
|
||||
|
||||
BaseViewer.prototype._init = function(container, moddle, options) {
|
||||
|
||||
var baseModules = options.modules || this.getModules(),
|
||||
additionalModules = options.additionalModules || [],
|
||||
staticModules = [
|
||||
{
|
||||
bpmnjs: [ 'value', this ],
|
||||
moddle: [ 'value', moddle ]
|
||||
}
|
||||
];
|
||||
|
||||
var diagramModules = [].concat(staticModules, baseModules, additionalModules);
|
||||
|
||||
var diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
|
||||
canvas: assign({}, options.canvas, { container: container }),
|
||||
modules: diagramModules
|
||||
});
|
||||
|
||||
// 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)
|
||||
*/
|
||||
BaseViewer.prototype._emit = function(type, event) {
|
||||
return this.get('eventBus').fire(type, event);
|
||||
};
|
||||
|
||||
BaseViewer.prototype._createContainer = function(options) {
|
||||
|
||||
var container = domify('<div class="bjs-container"></div>');
|
||||
|
||||
assign(container.style, {
|
||||
width: ensureUnit(options.width),
|
||||
height: ensureUnit(options.height),
|
||||
position: options.position
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
BaseViewer.prototype._createModdle = function(options) {
|
||||
var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
|
||||
|
||||
return new BpmnModdle(moddleOptions);
|
||||
};
|
||||
|
||||
BaseViewer.prototype._modules = [];
|
||||
|
||||
|
||||
// helpers ///////////////
|
||||
|
||||
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'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Ensure the passed argument is a proper unit (defaulting to px)
|
||||
*/
|
||||
function ensureUnit(val) {
|
||||
return val + (isNumber(val) ? 'px' : '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find BPMNDiagram in definitions by ID
|
||||
*
|
||||
* @param {ModdleElement<Definitions>} definitions
|
||||
* @param {String} diagramId
|
||||
*
|
||||
* @return {ModdleElement<BPMNDiagram>|null}
|
||||
*/
|
||||
function findBPMNDiagram(definitions, diagramId) {
|
||||
if (!diagramId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return find(definitions.diagrams, function(element) {
|
||||
return element.id === diagramId;
|
||||
}) || null;
|
||||
}
|
||||
|
||||
|
||||
/* <project-logo> */
|
||||
|
||||
import {
|
||||
open as openPoweredBy,
|
||||
BPMNIO_IMG
|
||||
} from './util/PoweredByUtil';
|
||||
|
||||
import {
|
||||
event as domEvent
|
||||
} from 'min-dom';
|
||||
|
||||
/**
|
||||
* 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 img = BPMNIO_IMG;
|
||||
|
||||
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 +
|
||||
'</a>';
|
||||
|
||||
var linkElement = domify(linkMarkup);
|
||||
|
||||
container.appendChild(linkElement);
|
||||
|
||||
domEvent.bind(linkElement, 'click', function(event) {
|
||||
openPoweredBy();
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
/* </project-logo> */
|
|
@ -1,9 +1,8 @@
|
|||
import inherits from 'inherits';
|
||||
|
||||
import Ids from 'ids';
|
||||
import BaseModeler from './BaseModeler';
|
||||
|
||||
import Viewer from './Viewer';
|
||||
|
||||
import NavigatedViewer from './NavigatedViewer';
|
||||
|
||||
import KeyboardMoveModule from 'diagram-js/lib/navigation/keyboard-move';
|
||||
|
@ -131,21 +130,11 @@ var initialDiagram =
|
|||
* @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
|
||||
*/
|
||||
export default function Modeler(options) {
|
||||
Viewer.call(this, options);
|
||||
|
||||
// hook ID collection into the modeler
|
||||
this.on('import.parse.complete', function(event) {
|
||||
if (!event.error) {
|
||||
this._collectIds(event.definitions, event.context);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.on('diagram.destroy', function() {
|
||||
this.get('moddle').ids.clear();
|
||||
}, this);
|
||||
BaseModeler.call(this, options);
|
||||
}
|
||||
|
||||
inherits(Modeler, Viewer);
|
||||
inherits(Modeler, BaseModeler);
|
||||
|
||||
|
||||
Modeler.Viewer = Viewer;
|
||||
Modeler.NavigatedViewer = NavigatedViewer;
|
||||
|
@ -159,42 +148,6 @@ Modeler.prototype.createDiagram = function(done) {
|
|||
return this.importXML(initialDiagram, done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a moddle instance, attaching ids to it.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
Modeler.prototype._createModdle = function(options) {
|
||||
var moddle = Viewer.prototype._createModdle.call(this, options);
|
||||
|
||||
// attach ids to moddle to be able to track
|
||||
// and validated ids in the BPMN 2.0 XML document
|
||||
// tree
|
||||
moddle.ids = new Ids([ 32, 36, 1 ]);
|
||||
|
||||
return moddle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collect ids processed during parsing of the
|
||||
* definitions object.
|
||||
*
|
||||
* @param {ModdleElement} definitions
|
||||
* @param {Context} context
|
||||
*/
|
||||
Modeler.prototype._collectIds = function(definitions, context) {
|
||||
|
||||
var moddle = definitions.$model,
|
||||
ids = moddle.ids,
|
||||
id;
|
||||
|
||||
// remove references from previous import
|
||||
ids.clear();
|
||||
|
||||
for (id in context.elementsById) {
|
||||
ids.claim(id, context.elementsById[id]);
|
||||
}
|
||||
};
|
||||
|
||||
Modeler.prototype._interactionModules = [
|
||||
|
||||
|
@ -242,6 +195,7 @@ Modeler.prototype._modelingModules = [
|
|||
// - modeling modules
|
||||
|
||||
Modeler.prototype._modules = [].concat(
|
||||
Modeler.prototype._modules,
|
||||
Viewer.prototype._modules,
|
||||
Modeler.prototype._interactionModules,
|
||||
Modeler.prototype._modelingModules);
|
||||
Modeler.prototype._modelingModules
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ import KeyboardMoveModule from 'diagram-js/lib/navigation/keyboard-move';
|
|||
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
|
||||
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
|
||||
|
||||
|
||||
/**
|
||||
* A viewer that includes mouse navigation facilities
|
||||
*
|
||||
|
@ -17,6 +18,7 @@ export default function NavigatedViewer(options) {
|
|||
|
||||
inherits(NavigatedViewer, Viewer);
|
||||
|
||||
|
||||
NavigatedViewer.prototype._navigationModules = [
|
||||
KeyboardMoveModule,
|
||||
MoveCanvasModule,
|
||||
|
@ -24,5 +26,6 @@ NavigatedViewer.prototype._navigationModules = [
|
|||
];
|
||||
|
||||
NavigatedViewer.prototype._modules = [].concat(
|
||||
NavigatedViewer.prototype._modules,
|
||||
NavigatedViewer.prototype._navigationModules);
|
||||
Viewer.prototype._modules,
|
||||
NavigatedViewer.prototype._navigationModules
|
||||
);
|
631
lib/Viewer.js
631
lib/Viewer.js
|
@ -1,91 +1,12 @@
|
|||
/**
|
||||
* The code in the <project-logo></project-logo> area
|
||||
* must not be changed.
|
||||
*
|
||||
* @see http://bpmn.io/license for more information.
|
||||
*/
|
||||
import {
|
||||
assign,
|
||||
find,
|
||||
isFunction,
|
||||
isNumber,
|
||||
omit
|
||||
} from 'min-dash';
|
||||
|
||||
import {
|
||||
domify,
|
||||
query as domQuery,
|
||||
remove as domRemove
|
||||
} from 'min-dom';
|
||||
|
||||
import {
|
||||
innerSVG
|
||||
} from 'tiny-svg';
|
||||
|
||||
import Diagram from 'diagram-js';
|
||||
import BpmnModdle from 'bpmn-moddle';
|
||||
|
||||
import inherits from 'inherits';
|
||||
|
||||
import {
|
||||
importBpmnDiagram
|
||||
} from './import/Importer';
|
||||
|
||||
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 BaseViewer from './BaseViewer';
|
||||
|
||||
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'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Ensure the passed argument is a proper unit (defaulting to px)
|
||||
*/
|
||||
function ensureUnit(val) {
|
||||
return val + (isNumber(val) ? 'px' : '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find BPMNDiagram in definitions by ID
|
||||
*
|
||||
* @param {ModdleElement<Definitions>} definitions
|
||||
* @param {String} diagramId
|
||||
*
|
||||
* @return {ModdleElement<BPMNDiagram>|null}
|
||||
*/
|
||||
function findBPMNDiagram(definitions, diagramId) {
|
||||
if (!diagramId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return find(definitions.diagrams, function(element) {
|
||||
return element.id === diagramId;
|
||||
}) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A viewer for BPMN 2.0 diagrams.
|
||||
|
@ -135,510 +56,10 @@ function findBPMNDiagram(definitions, diagramId) {
|
|||
* @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
|
||||
*/
|
||||
export default function Viewer(options) {
|
||||
|
||||
options = assign({}, DEFAULT_OPTIONS, options);
|
||||
|
||||
this._moddle = this._createModdle(options);
|
||||
|
||||
this._container = this._createContainer(options);
|
||||
|
||||
/* <project-logo> */
|
||||
|
||||
addProjectLogo(this._container);
|
||||
|
||||
/* </project-logo> */
|
||||
|
||||
this._init(this._container, this._moddle, options);
|
||||
BaseViewer.call(this, options);
|
||||
}
|
||||
|
||||
inherits(Viewer, Diagram);
|
||||
|
||||
|
||||
/**
|
||||
* Parse and render a BPMN 2.0 diagram.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During import the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.parse.start (about to read model from xml)
|
||||
* * import.parse.complete (model read; may have worked or not)
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
* * import.done (everything done)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {String} xml the BPMN 2.0 xml
|
||||
* @param {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
Viewer.prototype.importXML = function(xml, bpmnDiagram, done) {
|
||||
|
||||
if (isFunction(bpmnDiagram)) {
|
||||
done = bpmnDiagram;
|
||||
bpmnDiagram = null;
|
||||
}
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
var self = this;
|
||||
|
||||
// hook in pre-parse listeners +
|
||||
// allow xml manipulation
|
||||
xml = this._emit('import.parse.start', { xml: xml }) || xml;
|
||||
|
||||
this._moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions, context) {
|
||||
|
||||
// hook in post parse listeners +
|
||||
// allow definitions manipulation
|
||||
definitions = self._emit('import.parse.complete', {
|
||||
error: err,
|
||||
definitions: definitions,
|
||||
context: context
|
||||
}) || definitions;
|
||||
|
||||
var parseWarnings = context.warnings;
|
||||
|
||||
if (err) {
|
||||
err = checkValidationError(err);
|
||||
|
||||
self._emit('import.done', { error: err, warnings: parseWarnings });
|
||||
|
||||
return done(err, parseWarnings);
|
||||
}
|
||||
|
||||
self.importDefinitions(definitions, bpmnDiagram, function(err, importWarnings) {
|
||||
var allWarnings = [].concat(parseWarnings, importWarnings || []);
|
||||
|
||||
self._emit('import.done', { error: err, warnings: allWarnings });
|
||||
|
||||
done(err, allWarnings);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Import parsed definitions and render a BPMN 2.0 diagram.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During import the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {ModdleElement<Definitions>} definitions parsed BPMN 2.0 definitions
|
||||
* @param {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
Viewer.prototype.importDefinitions = function(definitions, bpmnDiagram, done) {
|
||||
|
||||
if (isFunction(bpmnDiagram)) {
|
||||
done = bpmnDiagram;
|
||||
bpmnDiagram = null;
|
||||
}
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
this._setDefinitions(definitions);
|
||||
|
||||
return this.open(bpmnDiagram, done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open diagram of previously imported XML.
|
||||
*
|
||||
* Once finished the viewer reports back the result to the
|
||||
* provided callback function with (err, warnings).
|
||||
*
|
||||
* ## Life-Cycle Events
|
||||
*
|
||||
* During switch the viewer will fire life-cycle events:
|
||||
*
|
||||
* * import.render.start (graphical import start)
|
||||
* * import.render.complete (graphical import finished)
|
||||
*
|
||||
* You can use these events to hook into the life-cycle.
|
||||
*
|
||||
* @param {String|ModdleElement<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open
|
||||
* @param {Function} [done] invoked with (err, warnings=[])
|
||||
*/
|
||||
Viewer.prototype.open = function(bpmnDiagramOrId, done) {
|
||||
|
||||
if (isFunction(bpmnDiagramOrId)) {
|
||||
done = bpmnDiagramOrId;
|
||||
bpmnDiagramOrId = null;
|
||||
}
|
||||
|
||||
var definitions = this._definitions;
|
||||
var bpmnDiagram = bpmnDiagramOrId;
|
||||
|
||||
// done is optional
|
||||
done = done || function() {};
|
||||
|
||||
if (!definitions) {
|
||||
return done(new Error('no XML imported'));
|
||||
}
|
||||
|
||||
if (typeof bpmnDiagramOrId === 'string') {
|
||||
bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);
|
||||
|
||||
if (!bpmnDiagram) {
|
||||
return done(new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'));
|
||||
}
|
||||
}
|
||||
|
||||
// clear existing rendered diagram
|
||||
// catch synchronous exceptions during #clear()
|
||||
try {
|
||||
this.clear();
|
||||
} catch (error) {
|
||||
return done(error);
|
||||
}
|
||||
|
||||
// perform graphical import
|
||||
return importBpmnDiagram(this, definitions, bpmnDiagram, 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 formatted XML
|
||||
* @param {Boolean} [options.preamble=true] output preamble
|
||||
*
|
||||
* @param {Function} done invoked with (err, xml)
|
||||
*/
|
||||
Viewer.prototype.saveXML = function(options, done) {
|
||||
|
||||
if (!done) {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var definitions = this._definitions;
|
||||
|
||||
if (!definitions) {
|
||||
return done(new Error('no definitions loaded'));
|
||||
}
|
||||
|
||||
// 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)
|
||||
*/
|
||||
Viewer.prototype.saveSVG = function(options, done) {
|
||||
|
||||
if (!done) {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
this._emit('saveSVG.start');
|
||||
|
||||
var svg, err;
|
||||
|
||||
try {
|
||||
var canvas = this.get('canvas');
|
||||
|
||||
var contentNode = canvas.getDefaultLayer(),
|
||||
defsNode = domQuery('defs', canvas._svg);
|
||||
|
||||
var contents = innerSVG(contentNode),
|
||||
defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';
|
||||
|
||||
var bbox = contentNode.getBBox();
|
||||
|
||||
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>';
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
this._emit('saveSVG.done', {
|
||||
error: err,
|
||||
svg: svg
|
||||
});
|
||||
|
||||
done(err, svg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a named diagram service.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var elementRegistry = viewer.get('elementRegistry');
|
||||
* var startEventShape = elementRegistry.get('StartEvent_1');
|
||||
*
|
||||
* @param {String} name
|
||||
*
|
||||
* @return {Object} diagram service instance
|
||||
*
|
||||
* @method Viewer#get
|
||||
*/
|
||||
|
||||
/**
|
||||
* Invoke a function in the context of this viewer.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* viewer.invoke(function(elementRegistry) {
|
||||
* var startEventShape = elementRegistry.get('StartEvent_1');
|
||||
* });
|
||||
*
|
||||
* @param {Function} fn to be invoked
|
||||
*
|
||||
* @return {Object} the functions return value
|
||||
*
|
||||
* @method Viewer#invoke
|
||||
*/
|
||||
|
||||
|
||||
Viewer.prototype._setDefinitions = function(definitions) {
|
||||
this._definitions = definitions;
|
||||
};
|
||||
|
||||
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.
|
||||
*
|
||||
* @method Viewer#clear
|
||||
*/
|
||||
Viewer.prototype.clear = function() {
|
||||
if (!this.getDefinitions()) {
|
||||
|
||||
// no diagram to clear
|
||||
return;
|
||||
}
|
||||
|
||||
// remove businessObject#di binding
|
||||
//
|
||||
// this is necessary, as we establish the bindings
|
||||
// in the BpmnTreeWalker (and assume none are given
|
||||
// on reimport)
|
||||
this.get('elementRegistry').forEach(function(element) {
|
||||
var bo = element.businessObject;
|
||||
|
||||
if (bo && bo.di) {
|
||||
delete bo.di;
|
||||
}
|
||||
});
|
||||
|
||||
// remove drawn elements
|
||||
Diagram.prototype.clear.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.attachTo = function(parentNode) {
|
||||
|
||||
if (!parentNode) {
|
||||
throw new Error('parentNode required');
|
||||
}
|
||||
|
||||
// ensure we detach from the
|
||||
// previous, old parent
|
||||
this.detach();
|
||||
|
||||
// unwrap jQuery if provided
|
||||
if (parentNode.get && parentNode.constructor.prototype.jquery) {
|
||||
parentNode = parentNode.get(0);
|
||||
}
|
||||
|
||||
if (typeof parentNode === 'string') {
|
||||
parentNode = domQuery(parentNode);
|
||||
}
|
||||
|
||||
parentNode.appendChild(this._container);
|
||||
|
||||
this._emit('attach', {});
|
||||
|
||||
this.get('canvas').resized();
|
||||
};
|
||||
|
||||
Viewer.prototype.getDefinitions = function() {
|
||||
return this._definitions;
|
||||
};
|
||||
|
||||
Viewer.prototype.detach = function() {
|
||||
|
||||
var container = this._container,
|
||||
parentNode = container.parentNode;
|
||||
|
||||
if (!parentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._emit('detach', {});
|
||||
|
||||
parentNode.removeChild(container);
|
||||
};
|
||||
|
||||
Viewer.prototype._init = function(container, moddle, options) {
|
||||
|
||||
var baseModules = options.modules || this.getModules(),
|
||||
additionalModules = options.additionalModules || [],
|
||||
staticModules = [
|
||||
{
|
||||
bpmnjs: [ 'value', this ],
|
||||
moddle: [ 'value', moddle ]
|
||||
}
|
||||
];
|
||||
|
||||
var diagramModules = [].concat(staticModules, baseModules, additionalModules);
|
||||
|
||||
var diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
|
||||
canvas: assign({}, options.canvas, { container: container }),
|
||||
modules: diagramModules
|
||||
});
|
||||
|
||||
// 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(options) {
|
||||
|
||||
var container = domify('<div class="bjs-container"></div>');
|
||||
|
||||
assign(container.style, {
|
||||
width: ensureUnit(options.width),
|
||||
height: ensureUnit(options.height),
|
||||
position: options.position
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
Viewer.prototype._createModdle = function(options) {
|
||||
var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
|
||||
|
||||
return new BpmnModdle(moddleOptions);
|
||||
};
|
||||
inherits(Viewer, BaseViewer);
|
||||
|
||||
// modules the viewer is composed of
|
||||
Viewer.prototype._modules = [
|
||||
|
@ -649,48 +70,4 @@ Viewer.prototype._modules = [
|
|||
];
|
||||
|
||||
// default moddle extensions the viewer is composed of
|
||||
Viewer.prototype._moddleExtensions = {};
|
||||
|
||||
/* <project-logo> */
|
||||
|
||||
import {
|
||||
open as openPoweredBy,
|
||||
BPMNIO_IMG
|
||||
} from './util/PoweredByUtil';
|
||||
|
||||
import {
|
||||
event as domEvent
|
||||
} from 'min-dom';
|
||||
|
||||
/**
|
||||
* 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 img = BPMNIO_IMG;
|
||||
|
||||
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 +
|
||||
'</a>';
|
||||
|
||||
var linkElement = domify(linkMarkup);
|
||||
|
||||
container.appendChild(linkElement);
|
||||
|
||||
domEvent.bind(linkElement, 'click', function(event) {
|
||||
openPoweredBy();
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
/* </project-logo> */
|
||||
Viewer.prototype._moddleExtensions = {};
|
|
@ -0,0 +1,20 @@
|
|||
import BaseModeler from 'lib/BaseModeler';
|
||||
import BaseViewer from 'lib/BaseViewer';
|
||||
|
||||
|
||||
describe('BaseModeler', function() {
|
||||
|
||||
it('should instantiate', function() {
|
||||
|
||||
// when
|
||||
var instance = new BaseModeler();
|
||||
|
||||
// then
|
||||
expect(instance.importXML).to.exist;
|
||||
expect(instance.saveXML).to.exist;
|
||||
|
||||
expect(instance instanceof BaseModeler).to.be.true;
|
||||
expect(instance instanceof BaseViewer).to.be.true;
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import BaseViewer from 'lib/BaseViewer';
|
||||
|
||||
|
||||
describe('BaseViewer', function() {
|
||||
|
||||
it('should instantiate', function() {
|
||||
|
||||
// when
|
||||
var instance = new BaseViewer();
|
||||
|
||||
// then
|
||||
expect(instance.importXML).to.exist;
|
||||
expect(instance.saveXML).to.exist;
|
||||
|
||||
expect(instance instanceof BaseViewer).to.be.true;
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue