bpmn-js
Version:
A bpmn 2.0 toolkit and web modeler
1,955 lines (1,573 loc) • 1.39 MB
JavaScript
/*!
* bpmn-js - bpmn-modeler v0.27.2
*
* Copyright (c) 2014-present, camunda Services GmbH
*
* Released under the bpmn.io license
* http://bpmn.io/license
*
* Source Code: https://github.com/bpmn-io/bpmn-js
*
* Date: 2018-02-03
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.BpmnJS = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(292);
var Ids = _dereq_(290);
var Viewer = _dereq_(3);
var NavigatedViewer = _dereq_(2);
var initialDiagram =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
'xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" ' +
'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' +
'xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" ' +
'targetNamespace="http://bpmn.io/schema/bpmn" ' +
'id="Definitions_1">' +
'<bpmn:process id="Process_1" isExecutable="false">' +
'<bpmn:startEvent id="StartEvent_1"/>' +
'</bpmn:process>' +
'<bpmndi:BPMNDiagram id="BPMNDiagram_1">' +
'<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">' +
'<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">' +
'<dc:Bounds height="36.0" width="36.0" x="173.0" y="102.0"/>' +
'</bpmndi:BPMNShape>' +
'</bpmndi:BPMNPlane>' +
'</bpmndi:BPMNDiagram>' +
'</bpmn:definitions>';
/**
* A modeler for BPMN 2.0 diagrams.
*
*
* ## Extending the Modeler
*
* 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 bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
* bpmnModeler.importXML(...);
* ```
*
*
* ## Customizing / Replacing Components
*
* You can replace individual diagram components by redefining them in override modules.
* This works for all components, including those defined in the core.
*
* Pass in override modules via the `options.additionalModules` flag like this:
*
* ```javascript
* function CustomContextPadProvider(contextPad) {
*
* contextPad.registerProvider(this);
*
* this.getContextPadEntries = function(element) {
* // no entries, effectively disable the context pad
* return {};
* };
* }
*
* CustomContextPadProvider.$inject = [ 'contextPad' ];
*
* var overrideModule = {
* contextPadProvider: [ 'type', CustomContextPadProvider ]
* };
*
* var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
* ```
*
* @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
*/
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);
}
inherits(Modeler, Viewer);
module.exports = Modeler;
module.exports.Viewer = Viewer;
module.exports.NavigatedViewer = NavigatedViewer;
/**
* Create a new diagram to start modeling.
*
* @param {Function} [done]
*/
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 = [
// non-modeling components
_dereq_(259),
_dereq_(260),
_dereq_(263)
];
Modeler.prototype._modelingModules = [
// modeling components
_dereq_(142),
_dereq_(148),
_dereq_(209),
_dereq_(227),
_dereq_(15),
_dereq_(12),
_dereq_(23),
_dereq_(17),
_dereq_(27),
_dereq_(32),
_dereq_(72),
_dereq_(78),
_dereq_(83),
_dereq_(93)
];
// modules the modeler is composed of
//
// - viewer modules
// - interaction modules
// - modeling modules
Modeler.prototype._modules = [].concat(
Modeler.prototype._modules,
Modeler.prototype._interactionModules,
Modeler.prototype._modelingModules);
},{"12":12,"142":142,"148":148,"15":15,"17":17,"2":2,"209":209,"227":227,"23":23,"259":259,"260":260,"263":263,"27":27,"290":290,"292":292,"3":3,"32":32,"72":72,"78":78,"83":83,"93":93}],2:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(292);
var Viewer = _dereq_(3);
/**
* A viewer that includes mouse navigation facilities
*
* @param {Object} options
*/
function NavigatedViewer(options) {
Viewer.call(this, options);
}
inherits(NavigatedViewer, Viewer);
module.exports = NavigatedViewer;
NavigatedViewer.prototype._navigationModules = [
_dereq_(263),
_dereq_(259)
];
NavigatedViewer.prototype._modules = [].concat(
NavigatedViewer.prototype._modules,
NavigatedViewer.prototype._navigationModules);
},{"259":259,"263":263,"292":292,"3":3}],3:[function(_dereq_,module,exports){
/**
* The code in the <project-logo></project-logo> area
* must not be changed.
*
* @see http://bpmn.io/license for more information.
*/
'use strict';
var assign = _dereq_(434),
omit = _dereq_(439),
isNumber = _dereq_(428);
var domify = _dereq_(453),
domQuery = _dereq_(456),
domRemove = _dereq_(457);
var innerSVG = _dereq_(485);
var Diagram = _dereq_(120),
BpmnModdle = _dereq_(105);
var inherits = _dereq_(292);
var importBpmnDiagram = _dereq_(96).importBpmnDiagram;
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' : '');
}
/**
* A viewer for BPMN 2.0 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 bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
* bpmnViewer.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 {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
*/
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);
}
inherits(Viewer, Diagram);
module.exports = Viewer;
/**
* 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 {Function} [done] invoked with (err, warnings=[])
*/
Viewer.prototype.importXML = function(xml, done) {
// 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, function(err, importWarnings) {
var allWarnings = [].concat(parseWarnings, importWarnings || []);
self._emit('import.done', { error: err, warnings: allWarnings });
done(err, allWarnings);
});
});
};
/**
* Export the currently displayed BPMN 2.0 diagram as
* a BPMN 2.0 XML document.
*
* @param {Object} [options] export options
* @param {Boolean} [options.format=false] output formated 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 definitions = this._definitions;
if (!definitions) {
return done(new Error('no definitions loaded'));
}
this._moddle.toXML(definitions, options, done);
};
/**
* Export the currently displayed BPMN 2.0 diagram as
* an SVG image.
*
* @param {Object} [options]
* @param {Function} done invoked with (err, svgStr)
*/
Viewer.prototype.saveSVG = function(options, done) {
if (!done) {
done = options;
options = {};
}
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;
}
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
*/
/**
* 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.importDefinitions = function(definitions, done) {
// catch synchronous exceptions during #clear()
try {
if (this._definitions) {
// clear existing rendered diagram
this.clear();
}
// update definitions
this._definitions = definitions;
} catch (e) {
return done(e);
}
// perform graphical import
return importBpmnDiagram(this, definitions, done);
};
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.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);
};
// modules the viewer is composed of
Viewer.prototype._modules = [
_dereq_(4),
_dereq_(251),
_dereq_(236),
_dereq_(214)
];
// default moddle extensions the viewer is composed of
Viewer.prototype._moddleExtensions = {};
/* <project-logo> */
var PoweredBy = _dereq_(102),
domEvent = _dereq_(454);
/**
* 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 = PoweredBy.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) {
PoweredBy.open();
event.preventDefault();
});
}
/* </project-logo> */
},{"102":102,"105":105,"120":120,"214":214,"236":236,"251":251,"292":292,"4":4,"428":428,"434":434,"439":439,"453":453,"454":454,"456":456,"457":457,"485":485,"96":96}],4:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(8),
_dereq_(98)
]
};
},{"8":8,"98":98}],5:[function(_dereq_,module,exports){
'use strict';
var every = _dereq_(303),
some = _dereq_(312);
var componentsToPath = _dereq_(280).componentsToPath;
///////// element utils /////////////////////////////
/**
* Checks if eventDefinition of the given element matches with semantic type.
*
* @return {boolean} true if element is of the given semantic type
*/
function isTypedEvent(event, eventDefinitionType, filter) {
function matches(definition, filter) {
return every(filter, function(val, key) {
// we want a == conversion here, to be able to catch
// undefined == false and friends
/* jshint -W116 */
return definition[key] == val;
});
}
return some(event.eventDefinitions, function(definition) {
return definition.$type === eventDefinitionType && matches(event, filter);
});
}
module.exports.isTypedEvent = isTypedEvent;
function isThrowEvent(event) {
return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
}
module.exports.isThrowEvent = isThrowEvent;
function isCollection(element) {
var dataObject = element.dataObjectRef;
return element.isCollection || (dataObject && dataObject.isCollection);
}
module.exports.isCollection = isCollection;
function getDi(element) {
return element.businessObject.di;
}
module.exports.getDi = getDi;
function getSemantic(element) {
return element.businessObject;
}
module.exports.getSemantic = getSemantic;
/////// color access ////////////////////////////////////////
function getFillColor(element, defaultColor) {
return getDi(element).get('bioc:fill') || defaultColor || 'white';
}
module.exports.getFillColor = getFillColor;
function getStrokeColor(element, defaultColor) {
return getDi(element).get('bioc:stroke') || defaultColor || 'black';
}
module.exports.getStrokeColor = getStrokeColor;
/////// cropping path customizations /////////////////////////
function getCirclePath(shape) {
var cx = shape.x + shape.width / 2,
cy = shape.y + shape.height / 2,
radius = shape.width / 2;
var circlePath = [
['M', cx, cy],
['m', 0, -radius],
['a', radius, radius, 0, 1, 1, 0, 2 * radius],
['a', radius, radius, 0, 1, 1, 0, -2 * radius],
['z']
];
return componentsToPath(circlePath);
}
module.exports.getCirclePath = getCirclePath;
function getRoundRectPath(shape, borderRadius) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var roundRectPath = [
['M', x + borderRadius, y],
['l', width - borderRadius * 2, 0],
['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius],
['l', 0, height - borderRadius * 2],
['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius],
['l', borderRadius * 2 - width, 0],
['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius],
['l', 0, borderRadius * 2 - height],
['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius],
['z']
];
return componentsToPath(roundRectPath);
}
module.exports.getRoundRectPath = getRoundRectPath;
function getDiamondPath(shape) {
var width = shape.width,
height = shape.height,
x = shape.x,
y = shape.y,
halfWidth = width / 2,
halfHeight = height / 2;
var diamondPath = [
['M', x + halfWidth, y],
['l', halfWidth, halfHeight],
['l', -halfWidth, halfHeight],
['l', -halfWidth, -halfHeight],
['z']
];
return componentsToPath(diamondPath);
}
module.exports.getDiamondPath = getDiamondPath;
function getRectPath(shape) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var rectPath = [
['M', x, y],
['l', width, 0],
['l', 0, height],
['l', -width, 0],
['z']
];
return componentsToPath(rectPath);
}
module.exports.getRectPath = getRectPath;
},{"280":280,"303":303,"312":312}],6:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(292),
isObject = _dereq_(429),
assign = _dereq_(434),
forEach = _dereq_(306);
var BaseRenderer = _dereq_(131),
TextUtil = _dereq_(282),
DiUtil = _dereq_(99);
var is = _dereq_(101).is;
var createLine = _dereq_(280).createLine;
var BpmnRenderUtil = _dereq_(5);
var isTypedEvent = BpmnRenderUtil.isTypedEvent,
isThrowEvent = BpmnRenderUtil.isThrowEvent,
isCollection = BpmnRenderUtil.isCollection,
getDi = BpmnRenderUtil.getDi,
getSemantic = BpmnRenderUtil.getSemantic;
var getCirclePath = BpmnRenderUtil.getCirclePath,
getRoundRectPath = BpmnRenderUtil.getRoundRectPath,
getDiamondPath = BpmnRenderUtil.getDiamondPath,
getRectPath = BpmnRenderUtil.getRectPath,
getFillColor = BpmnRenderUtil.getFillColor,
getStrokeColor = BpmnRenderUtil.getStrokeColor;
var domQuery = _dereq_(456);
var svgAppend = _dereq_(477),
svgAttr = _dereq_(479),
svgCreate = _dereq_(483),
svgClasses = _dereq_(480);
var rotate = _dereq_(281).rotate,
transform = _dereq_(281).transform,
translate = _dereq_(281).translate;
var Ids = _dereq_(290),
RENDERER_IDS = new Ids();
var TASK_BORDER_RADIUS = 10;
var INNER_OUTER_DIST = 3;
var LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: 12
};
var DEFAULT_FILL_OPACITY = .95,
HIGH_FILL_OPACITY = .35;
function BpmnRenderer(eventBus, styles, pathMap, canvas, priority) {
BaseRenderer.call(this, eventBus, priority);
var rendererId = RENDERER_IDS.next();
var textUtil = new TextUtil({
style: LABEL_STYLE,
size: { width: 100 }
});
var markers = {};
var computeStyle = styles.computeStyle;
function addMarker(id, options) {
var attrs = assign({
fill: 'black',
strokeWidth: 1,
strokeLinecap: 'round',
strokeDasharray: 'none'
}, options.attrs);
var ref = options.ref || { x: 0, y: 0 };
var scale = options.scale || 1;
// fix for safari / chrome / firefox bug not correctly
// resetting stroke dash array
if (attrs.strokeDasharray === 'none') {
attrs.strokeDasharray = [10000, 1];
}
var marker = svgCreate('marker');
svgAttr(options.element, attrs);
svgAppend(marker, options.element);
svgAttr(marker, {
id: id,
viewBox: '0 0 20 20',
refX: ref.x,
refY: ref.y,
markerWidth: 20 * scale,
markerHeight: 20 * scale,
orient: 'auto'
});
var defs = domQuery('defs', canvas._svg);
if (!defs) {
defs = svgCreate('defs');
svgAppend(canvas._svg, defs);
}
svgAppend(defs, marker);
markers[id] = marker;
}
function marker(type, fill, stroke) {
var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
if (!markers[id]) {
createMarker(type, fill, stroke);
}
return 'url(#' + id + ')';
}
function createMarker(type, fill, stroke) {
var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
if (type === 'sequenceflow-end') {
var sequenceflowEnd = svgCreate('path');
svgAttr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
addMarker(id, {
element: sequenceflowEnd,
ref: { x: 11, y: 10 },
scale: 0.5,
attrs: {
fill: stroke,
stroke: stroke
}
});
}
if (type === 'messageflow-start') {
var messageflowStart = svgCreate('circle');
svgAttr(messageflowStart, { cx: 6, cy: 6, r: 3.5 });
addMarker(id, {
element: messageflowStart,
attrs: {
fill: fill,
stroke: stroke
},
ref: { x: 6, y: 6 }
});
}
if (type === 'messageflow-end') {
var messageflowEnd = svgCreate('path');
svgAttr(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' });
addMarker(id, {
element: messageflowEnd,
attrs: {
fill: fill,
stroke: stroke,
strokeLinecap: 'butt'
},
ref: { x: 8.5, y: 5 }
});
}
if (type === 'association-start') {
var associationStart = svgCreate('path');
svgAttr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
addMarker(id, {
element: associationStart,
attrs: {
fill: 'none',
stroke: stroke,
strokeWidth: 1.5
},
ref: { x: 1, y: 10 },
scale: 0.5
});
}
if (type === 'association-end') {
var associationEnd = svgCreate('path');
svgAttr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
addMarker(id, {
element: associationEnd,
attrs: {
fill: 'none',
stroke: stroke,
strokeWidth: 1.5
},
ref: { x: 12, y: 10 },
scale: 0.5
});
}
if (type === 'conditional-flow-marker') {
var conditionalflowMarker = svgCreate('path');
svgAttr(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' });
addMarker(id, {
element: conditionalflowMarker,
attrs: {
fill: fill,
stroke: stroke
},
ref: { x: -1, y: 10 },
scale: 0.5
});
}
if (type === 'conditional-default-flow-marker') {
var conditionaldefaultflowMarker = svgCreate('path');
svgAttr(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' });
addMarker(id, {
element: conditionaldefaultflowMarker,
attrs: {
stroke: stroke
},
ref: { x: 0, y: 10 },
scale: 0.5
});
}
}
function drawCircle(parentGfx, width, height, offset, attrs) {
if (isObject(offset)) {
attrs = offset;
offset = 0;
}
offset = offset || 0;
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var cx = width / 2,
cy = height / 2;
var circle = svgCreate('circle');
svgAttr(circle, {
cx: cx,
cy: cy,
r: Math.round((width + height) / 4 - offset)
});
svgAttr(circle, attrs);
svgAppend(parentGfx, circle);
return circle;
}
function drawRect(parentGfx, width, height, r, offset, attrs) {
if (isObject(offset)) {
attrs = offset;
offset = 0;
}
offset = offset || 0;
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var rect = svgCreate('rect');
svgAttr(rect, {
x: offset,
y: offset,
width: width - offset * 2,
height: height - offset * 2,
rx: r,
ry: r
});
svgAttr(rect, attrs);
svgAppend(parentGfx, rect);
return rect;
}
function drawDiamond(parentGfx, width, height, attrs) {
var x_2 = width / 2;
var y_2 = height / 2;
var points = [{ x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 }];
var pointsString = points.map(function(point) {
return point.x + ',' + point.y;
}).join(' ');
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var polygon = svgCreate('polygon');
svgAttr(polygon, {
points: pointsString
});
svgAttr(polygon, attrs);
svgAppend(parentGfx, polygon);
return polygon;
}
function drawLine(parentGfx, waypoints, attrs) {
attrs = computeStyle(attrs, [ 'no-fill' ], {
stroke: 'black',
strokeWidth: 2,
fill: 'none'
});
var line = createLine(waypoints, attrs);
svgAppend(parentGfx, line);
return line;
}
function drawPath(parentGfx, d, attrs) {
attrs = computeStyle(attrs, [ 'no-fill' ], {
strokeWidth: 2,
stroke: 'black'
});
var path = svgCreate('path');
svgAttr(path, { d: d });
svgAttr(path, attrs);
svgAppend(parentGfx, path);
return path;
}
function drawMarker(type, parentGfx, path, attrs) {
return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
}
function as(type) {
return function(parentGfx, element) {
return handlers[type](parentGfx, element);
};
}
function renderer(type) {
return handlers[type];
}
function renderEventContent(element, parentGfx) {
var event = getSemantic(element);
var isThrowing = isThrowEvent(event);
if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) {
return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) {
return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) {
return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element);
}
if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) {
return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: false })) {
return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: true })) {
return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) {
return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) {
return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) {
return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) {
return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) {
return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) {
return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing);
}
return null;
}
function renderLabel(parentGfx, label, options) {
var text = textUtil.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(parentGfx, text);
return text;
}
function renderEmbeddedLabel(parentGfx, element, align) {
var semantic = getSemantic(element);
return renderLabel(parentGfx, semantic.name, {
box: element,
align: align,
padding: 5,
style: {
fill: getStrokeColor(element)
}
});
}
function renderExternalLabel(parentGfx, element) {
var semantic = getSemantic(element);
var box = {
width: 90,
height: 30,
x: element.width / 2 + element.x,
y: element.height / 2 + element.y
};
return renderLabel(parentGfx, semantic.name, {
box: box,
fitBox: true,
style: { fontSize: '11px' }
});
}
function renderLaneLabel(parentGfx, text, element) {
var textBox = renderLabel(parentGfx, text, {
box: { height: 30, width: element.height },
align: 'center-middle',
style: {
fill: getStrokeColor(element)
}
});
var top = -1 * element.height;
transform(textBox, 0, -top, 270);
}
function createPathFromConnection(connection) {
var waypoints = connection.waypoints;
var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y;
for (var i = 1; i < waypoints.length; i++) {
pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' ';
}
return pathData;
}
var handlers = this.handlers = {
'bpmn:Event': function(parentGfx, element, attrs) {
if (!('fillOpacity' in attrs)) {
attrs.fillOpacity = DEFAULT_FILL_OPACITY;
}
return drawCircle(parentGfx, element.width, element.height, attrs);
},
'bpmn:StartEvent': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var semantic = getSemantic(element);
if (!semantic.isInterrupting) {
attrs = {
strokeDasharray: '6',
strokeLinecap: 'round'
};
}
var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
renderEventContent(element, parentGfx);
return circle;
},
'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.235,
my: 0.315
}
});
var fill = isThrowing ? getStrokeColor(element) : getFillColor(element);
var stroke = isThrowing ? getFillColor(element) : getStrokeColor(element);
var messagePath = drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: stroke
});
return messagePath;
},
'bpmn:TimerEventDefinition': function(parentGfx, element) {
var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
strokeWidth: 2,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
xScaleFactor: 0.75,
yScaleFactor: 0.75,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.5,
my: 0.5
}
});
drawPath(parentGfx, pathData, {
strokeWidth: 2,
strokeLinecap: 'square',
stroke: getStrokeColor(element)
});
for (var i = 0;i < 12;i++) {
var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
xScaleFactor: 0.75,
yScaleFactor: 0.75,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.5,
my: 0.5
}
});
var width = element.width / 2;
var height = element.height / 2;
drawPath(parentGfx, linePathData, {
strokeWidth: 1,
strokeLinecap: 'square',
transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')',
stroke: getStrokeColor(element)
});
}
return circle;
},
'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.2
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:ConditionalEventDefinition': function(parentGfx, event) {
var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.222
}
});
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
stroke: getStrokeColor(event)
});
},
'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_LINK', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.57,
my: 0.263
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_ERROR', {
xScaleFactor: 1.1,
yScaleFactor: 1.1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.2,
my: 0.722
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
xScaleFactor: 1.0,
yScaleFactor: 1.0,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.638,
my: -0.055
}
});
var fill = isThrowing ? 'black' : 'none';
var path = drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill
});
rotate(path, 45);
return path;
},
'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.22,
my: 0.5
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.2
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
xScaleFactor: 1.1,
yScaleFactor: 1.1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.222,
my: 0.36
}
});
var fill = isThrowing ? 'black' : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill
});
},
'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) {
var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
xScaleFactor: 1.2,
yScaleFactor: 1.2,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.458,
my: 0.194
}
});
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(event),
stroke: getStrokeColor(event)
});
},
'bpmn:EndEvent': function(parentGfx, element) {
var circle = renderer('bpmn:Event')(parentGfx, element, {
strokeWidth: 4,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
renderEventContent(element, parentGfx, true);
return circle;
},
'bpmn:TerminateEventDefinition': function(parentGfx, element) {
var circle = drawCircle(parentGfx, element.width, element.height, 8, {
strokeWidth: 4,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return circle;
},
'bpmn:IntermediateEvent': function(parentGfx, element) {
var outer = renderer('bpmn:Event')(parentGfx, element, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
/* inner */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
strokeWidth: 1,
fill: getFillColor(element, 'none'),
stroke: getStrokeColor(element)
});
renderEventContent(element, parentGfx);
return outer;
},
'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
'bpmn:Activity': function(parentGfx, element, attrs) {
attrs = attrs || {};
if (!('fillOpacity' in attrs)) {
attrs.fillOpacity = DEFAULT_FILL_OPACITY;
}
return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs);
},
'bpmn:Task': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
renderEmbeddedLabel(parentGfx, element, 'center-middle');
attachTaskMarkers(parentGfx, element);
return rect;
},
'bpmn:ServiceTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
abspos: {
x: 12,
y: 18
}
});
/* service bg */ drawPath(parentGfx, pathDataBG, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', {
abspos: {
x: 17.2,
y: 18
}
});
/* service fill */ drawPath(parentGfx, fillPathData, {
strokeWidth: 0,
fill: getFillColor(element)
});
var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
abspos: {
x: 17,
y: 22
}
});
/* service */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:UserTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var x = 15;
var y = 12;
var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
abspos: {
x: x,
y: y
}
});
/* user path */ drawPath(parentGfx, pathData, {
strokeWidth: 0.5,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
abspos: {
x: x,
y: y
}
});
/* user2 path */ drawPath(parentGfx, pathData2, {
strokeWidth: 0.5,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
abspos: {
x: x,
y: y
}
});
/* user3 path */ drawPath(parentGfx, pathData3, {
strokeWidth: 0.5,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:ManualTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
abspos: {
x: 17,
y: 15
}
});
/* manual path */ drawPath(parentGfx, pathData, {
strokeWidth: 0.5, // 0.25,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:SendTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: 21,
containerHeight: 14,
position: {
mx: 0.285,
my: 0.357
}
});
/* send path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(element),
stroke: getFillColor(element)
});
return task;
},
'bpmn:ReceiveTask' : function(parentGfx, element) {
var semantic = getSemantic(element);
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData;
if (semantic.instantiate) {
drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 });
pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
abspos: {
x: 7.77,
y: 9.52
}
});
} else {
pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: 21,
containerHeight: 14,
position: {
mx: 0.3,
my: 0.4
}
});
}
/* receive path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:ScriptTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
abspos: {
x: 15,
y: 20
}
});
/* script path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:BusinessRuleTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var headerPathData = pathMap.getScaledPath('T