cmmn-js
Version:
A cmmn 1.1 toolkit
1,933 lines (1,515 loc) • 1.09 MB
JavaScript
/*!
* cmmn-js - cmmn-modeler v0.20.0
*
* Copyright (c) 2014-present, camunda Services GmbH
*
* Released under the bpmn.io license
* http://bpmn.io/license
*
* Source Code: https://github.com/bpmn-io/cmmn-js
*
* Date: 2020-05-02
*/
(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.CmmnJS = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(253);
var Ids = _dereq_(252);
var Viewer = _dereq_(3);
var NavigatedViewer = _dereq_(2);
var initialDiagram = '<?xml version="1.0" encoding="UTF-8"?>' + '<cmmn:definitions xmlns:dc="http://www.omg.org/spec/CMMN/20151109/DC" ' + 'xmlns:di="http://www.omg.org/spec/CMMN/20151109/DI" ' + 'xmlns:cmmndi="http://www.omg.org/spec/CMMN/20151109/CMMNDI" ' + 'xmlns:cmmn="http://www.omg.org/spec/CMMN/20151109/MODEL" ' + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Test" ' + 'targetNamespace="http://bpmn.io/schema/cmmn">' + '<cmmn:case id="Case_1">' + '<cmmn:casePlanModel id="CasePlanModel_1" name="A CasePlanModel">' + '<cmmn:planItem id="PlanItem_1" definitionRef="Task_1" />' + '<cmmn:task id="Task_1" />' + '</cmmn:casePlanModel>' + '</cmmn:case>' + '<cmmndi:CMMNDI>' + '<cmmndi:CMMNDiagram id="CMMNDiagram_1">' + '<cmmndi:Size width="500" height="500" />' + '<cmmndi:CMMNShape id="DI_CasePlanModel_1" cmmnElementRef="CasePlanModel_1">' + '<dc:Bounds x="114" y="63" width="534" height="389" />' + '<cmmndi:CMMNLabel />' + '</cmmndi:CMMNShape>' + '<cmmndi:CMMNShape id="PlanItem_1_di" cmmnElementRef="PlanItem_1">' + '<dc:Bounds x="150" y="96" width="100" height="80" />' + '<cmmndi:CMMNLabel />' + '</cmmndi:CMMNShape>' + '</cmmndi:CMMNDiagram>' + '</cmmndi:CMMNDI>' + '</cmmn:definitions>';
/**
* A modeler for CMMN 1.1 diagrams.
*
* Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
* additional features.
*
* ## 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 cmmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
* cmmnModeler.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 cmmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
* ```
*
* @param {Object} [options] configuration options to pass to the viewer
* @param {DOMElement} [options.container] the container to attach to
* @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 CMMN 1.1 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_(110).default, _dereq_(225).default, _dereq_(227).default, _dereq_(230).default, _dereq_(50)];
Modeler.prototype._modelingModules = [
// modeling components
_dereq_(117).default, _dereq_(141).default, _dereq_(179).default, _dereq_(197).default, _dereq_(124).default, _dereq_(10), _dereq_(12), _dereq_(14), _dereq_(16), _dereq_(20), _dereq_(44), _dereq_(52), _dereq_(68), _dereq_(63), _dereq_(56)];
// 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);
},{"10":10,"110":110,"117":117,"12":12,"124":124,"14":14,"141":141,"16":16,"179":179,"197":197,"2":2,"20":20,"225":225,"227":227,"230":230,"252":252,"253":253,"3":3,"44":44,"50":50,"52":52,"56":56,"63":63,"68":68}],2:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(253);
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_(230).default, _dereq_(227).default];
NavigatedViewer.prototype._modules = [].concat(NavigatedViewer.prototype._modules, NavigatedViewer.prototype._navigationModules);
},{"227":227,"230":230,"253":253,"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_(254).assign,
omit = _dereq_(254).omit,
isNumber = _dereq_(254).isNumber;
var inherits = _dereq_(253);
var domify = _dereq_(255).domify,
domQuery = _dereq_(255).query,
domRemove = _dereq_(255).remove;
var innerSVG = _dereq_(275).innerSVG;
var Diagram = _dereq_(89).default,
CmmnModdle = _dereq_(78).default;
var Importer = _dereq_(71);
function checkValidationError(err) {
// check if we can help the user by indicating wrong CMMN 1.1 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 CMMN 1.1 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 CMMN 1.1 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 cmmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
* cmmnViewer.importXML(...);
* ```
*
* @param {Object} [options] configuration options to pass to the viewer
* @param {DOMElement} [options.container] the container to attach to
* @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 CMMN 1.1 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 CMMN 1.1 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, 'cmmn: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 CMMN 1.1 diagram as
* a CMMN 1.1 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 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 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);
});
};
Viewer.prototype.saveSVG = function (options, done) {
if (!done) {
done = options;
options = {};
}
var canvas = this.get('canvas');
var contentNode = canvas.getDefaultLayer(),
defsNode = domQuery('defs', canvas._svg);
var contents = innerSVG(contentNode),
defs = defsNode && defsNode.outerHTML || '';
var bbox = contentNode.getBBox();
var svg = '<?xml version="1.0" encoding="utf-8"?>\n' + '<!-- created with cmmn-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>';
done(null, svg);
};
Viewer.prototype.importDefinitions = function (definitions, done) {
// use try/catch to not swallow synchronous exceptions
// that may be raised during model parsing
try {
if (this._definitions) {
// clear existing rendered diagram
this.clear();
}
// update definitions
this._definitions = definitions;
// perform graphical import
Importer.importCmmnDiagram(this, definitions, done);
} catch (e) {
// handle synchronous errors
done(e);
}
};
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.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, moddle, options) {
var baseModules = options.modules || this.getModules(),
additionalModules = options.additionalModules || [],
staticModules = [{
cmmnjs: ['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="cjs-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 CmmnModdle(moddleOptions);
};
// modules the viewer is composed of
Viewer.prototype._modules = [_dereq_(5), _dereq_(206).default, _dereq_(184).default];
/* <project-logo> */
var PoweredBy = _dereq_(77),
domEvent = _dereq_(255).event;
/**
* Adds the project logo to the diagram container as
* required by the bpmn.io license.
*
* @see http://bpmn.io/license
*
* @param {Element} container
*/
function addProjectLogo(container) {
var 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; ' + PoweredBy.LINK_STYLES + '">' + PoweredBy.BPMNIO_IMG + '</a>';
var linkElement = domify(linkMarkup);
container.appendChild(linkElement);
domEvent.bind(linkElement, 'click', function (event) {
PoweredBy.open();
event.preventDefault();
});
}
/* </project-logo> */
},{"184":184,"206":206,"253":253,"254":254,"255":255,"275":275,"5":5,"71":71,"77":77,"78":78,"89":89}],4:[function(_dereq_,module,exports){
'use strict';
var ModelUtil = _dereq_(76),
getDefinition = ModelUtil.getDefinition,
getSentry = ModelUtil.getSentry;
var isAny = _dereq_(45).isAny;
var forEach = _dereq_(254).forEach,
isArray = _dereq_(254).isArray;
/**
* @class
*
* A registry that keeps track of all items in the model.
*/
function ItemRegistry(elementRegistry, eventBus) {
this._items = {};
this._referencedBy = {};
this._elementRegistry = elementRegistry;
this._eventBus = eventBus;
this._init();
}
ItemRegistry.$inject = ['elementRegistry', 'eventBus'];
module.exports = ItemRegistry;
ItemRegistry.prototype._init = function (config) {
var eventBus = this._eventBus;
eventBus.on('diagram.destroy', 500, this._clear, this);
eventBus.on('diagram.clear', 500, this._clear, this);
};
ItemRegistry.prototype._clear = function () {
this._items = {};
this._referencedBy = {};
};
/**
* Register a given item.
*
* @param {ModdleElement} item
*/
ItemRegistry.prototype.add = function (item) {
var items = this._items,
id = item.id,
definitions = this._referencedBy,
definition = getReference(item),
definitionId = definition && definition.id;
items[id] = item;
if (definition) {
definitions[definitionId] = definitions[definitionId] || [];
if (definitions[definitionId].indexOf(item) === -1) {
definitions[definitionId].push(item);
}
}
};
/**
* Removes an item from the registry.
*
* @param {ModdleElement} item
*/
ItemRegistry.prototype.remove = function (item) {
var items = this._items,
id = item.id,
definitions = this._referencedBy,
definition = getReference(item),
definitionId = definition && definition.id;
delete items[id];
if (definition) {
var referencingItems = definitions[definitionId] || [],
idx = referencingItems.indexOf(item);
if (idx !== -1) {
referencingItems.splice(idx, 1);
}
if (!referencingItems.length) {
delete definitions[definitionId];
}
}
};
/**
* Update the registration with the new id.
*
* @param {ModdleElement} item
* @param {String} newId
*/
ItemRegistry.prototype.updateId = function (element, newId) {
var items, item;
if (typeof element === 'string') {
element = this.get(element);
}
if (isDefinition(element)) {
items = this._referencedBy;
} else {
items = this._items;
}
if (element) {
item = items[element.id];
delete items[element.id];
items[newId] = item;
}
};
/**
* Update the registration.
*
* @param {ModdleElement} item
* @param {ModdleElement} newReference
*/
ItemRegistry.prototype.updateReference = function (item, newReference) {
var definitions = this._referencedBy,
oldDefinition = getReference(item),
oldDefinitionId = oldDefinition && oldDefinition.id;
if (oldDefinition) {
var referencingItems = definitions[oldDefinitionId] || [],
idx = referencingItems.indexOf(item);
if (idx !== -1) {
referencingItems.splice(idx, 1);
}
if (!referencingItems.length) {
delete definitions[oldDefinitionId];
}
}
if (newReference) {
var newReferenceId = newReference.id;
if (newReferenceId) {
definitions[newReferenceId] = definitions[newReferenceId] || [];
if (definitions[newReferenceId].indexOf(item) === -1) {
definitions[newReferenceId].push(item);
}
}
}
};
/**
* Return the item for a given id.
*
* @param {String} id for selecting the item
*
* @return {ModdleElement}
*/
ItemRegistry.prototype.get = function (id) {
return this._items[id];
};
/**
* Return all items that match a given filter function.
*
* @param {Function} fn
*
* @return {Array<ModdleElement>}
*/
ItemRegistry.prototype.filter = function (fn) {
var filtered = [];
this.forEach(function (element, definition) {
if (fn(element, definition)) {
filtered.push(element);
}
});
return filtered;
};
/**
* Return all items.
*
* @return {Array<ModdleElement>}
*/
ItemRegistry.prototype.getAll = function () {
return this.filter(function (e) {
return e;
});
};
/**
* Iterate over all items.
*
* @param {Function} fn
*/
ItemRegistry.prototype.forEach = function (fn) {
var items = this._items;
forEach(items, function (item) {
return fn(item, getReference(item));
});
};
/**
* Return for given definition all referenced items.
*
* @param {String|ModdleElement} filter
*/
ItemRegistry.prototype.getReferences = function (filter) {
var id = filter.id || filter;
return (this._referencedBy[id] || []).slice();
};
/**
* Return for a given item id the shape element.
*
* @param {String|ModdleElement} filter
*/
ItemRegistry.prototype.getShape = function (filter) {
var id = filter.id || filter;
return this._elementRegistry && this._elementRegistry.get(id);
};
/**
* Return for a given filter all shapes.
*
* @param {Array<String>|String|ModdleElement} filter
*/
ItemRegistry.prototype.getShapes = function (filter) {
var shapes = [],
self = this;
function add(shape) {
shape && shapes.push(shape);
}
if (isArray(filter)) {
forEach(filter, function (f) {
add(self.getShape(f));
});
} else if (isDefinition(filter)) {
var referencedBy = self.getReferences(filter);
forEach(referencedBy, function (reference) {
add(self.getShape(reference));
});
} else {
add(self.getShape(filter));
}
return shapes;
};
function getReference(item) {
return getDefinition(item) || getSentry(item);
}
function isDefinition(item) {
return isAny(item, ['cmmn:PlanItemDefinition', 'cmmn:Sentry', 'cmmn:CaseFileItemDefinition']);
}
},{"254":254,"45":45,"76":76}],5:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__depends__: [_dereq_(8), _dereq_(73)],
itemRegistry: ['type', _dereq_(4)]
};
},{"4":4,"73":73,"8":8}],6:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(253),
isArray = _dereq_(254).isArray,
isObject = _dereq_(254).isObject,
assign = _dereq_(254).assign;
var BaseRenderer = _dereq_(100).default,
TextUtil = _dereq_(249).default,
DiUtil = _dereq_(74),
ModelUtil = _dereq_(76);
var isStandardEventVisible = DiUtil.isStandardEventVisible;
var isPlanningTableCollapsed = DiUtil.isPlanningTableCollapsed;
var isCollapsed = DiUtil.isCollapsed;
var isCasePlanModel = ModelUtil.isCasePlanModel;
var getBusinessObject = ModelUtil.getBusinessObject;
var getDefinition = ModelUtil.getDefinition;
var isRequired = ModelUtil.isRequired;
var isRepeatable = ModelUtil.isRepeatable;
var isManualActivation = ModelUtil.isManualActivation;
var isAutoComplete = ModelUtil.isAutoComplete;
var hasPlanningTable = ModelUtil.hasPlanningTable;
var getName = ModelUtil.getName;
var is = ModelUtil.is;
var getStandardEvent = ModelUtil.getStandardEvent;
var domQuery = _dereq_(255).query;
var svgAppend = _dereq_(275).append,
svgAttr = _dereq_(275).attr,
svgClasses = _dereq_(275).classes,
svgCreate = _dereq_(275).create;
var translate = _dereq_(248).translate;
var createLine = _dereq_(247).createLine;
function CmmnRenderer(eventBus, styles, pathMap) {
BaseRenderer.call(this, eventBus);
var TASK_BORDER_RADIUS = 10;
var MILESTONE_BORDER_RADIUS = 24;
var STAGE_EDGE_OFFSET = 20;
var LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: '12px'
};
var textUtil = new TextUtil({
style: LABEL_STYLE,
size: { width: 100 }
});
var markers = {};
function addMarker(id, element) {
markers[id] = element;
}
function marker(id) {
return markers[id];
}
function initMarkers(svg) {
function createMarker(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', svg);
if (!defs) {
defs = svgCreate('defs');
svgAppend(svg, defs);
}
svgAppend(defs, marker);
return addMarker(id, marker);
}
var associationStart = svgCreate('path');
svgAttr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
createMarker('association-start', {
element: associationStart,
attrs: {
fill: 'none',
stroke: 'black',
strokeWidth: 1.5
},
ref: { x: 1, y: 10 },
scale: 0.5
});
var associationEnd = svgCreate('path');
svgAttr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
createMarker('association-end', {
element: associationEnd,
attrs: {
fill: 'none',
stroke: 'black',
strokeWidth: 1.5
},
ref: { x: 12, y: 10 },
scale: 0.5
});
}
// draw shape //////////////////////////////////////////////////////////////
function computeStyle(custom, traits, defaultStyles) {
if (!isArray(traits)) {
defaultStyles = traits;
traits = [];
}
return styles.style(traits || [], assign(defaultStyles, custom || {}));
}
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 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 drawOctagon(parentGfx, width, height, offset, attrs) {
offset = offset || 20;
var x1 = offset;
var y1 = height;
var x2 = 0;
var y2 = height - offset;
var x3 = 0;
var y3 = offset;
var x4 = offset;
var y4 = 0;
var x5 = width - offset;
var y5 = 0;
var x6 = width;
var y6 = offset;
var x7 = width;
var y7 = height - offset;
var x8 = width - offset;
var y8 = height;
var points = [{ x: x1, y: y1 }, { x: x2, y: y2 }, { x: x3, y: y3 }, { x: x4, y: y4 }, { x: x5, y: y5 }, { x: x6, y: y6 }, { x: x7, y: y7 }, { x: x8, y: y8 }];
attrs = attrs || {};
attrs.fill = 'white';
attrs.stroke = 'black';
attrs.strokeWidth = 2;
return drawPolygon(parentGfx, points, attrs);
}
function drawPolygon(parentGfx, points, attrs) {
var pointsString = points.map(function (point) {
return point.x + ',' + point.y;
}).join(' ');
var polygon = svgCreate('polygon');
svgAttr(polygon, {
points: pointsString
});
svgAttr(polygon, attrs);
svgAppend(parentGfx, polygon);
return polygon;
}
// draw connection ////////////////////////////////////////////
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 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;
}
// render label //////////////////////////////////////////////
function renderLabel(parentGfx, label, options) {
var text = textUtil.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(parentGfx, text);
return text;
}
function renderEmbeddedLabel(p, element, align) {
var name = getName(element);
return renderLabel(p, name, {
box: element,
align: align,
padding: 5
});
}
function renderExpandedStageLabel(p, element, align) {
var name = getName(element);
var textbox = renderLabel(p, name, { box: element, align: align, padding: 5 });
// reset the position of the text box
translate(textbox, STAGE_EDGE_OFFSET, 0);
return textbox;
}
function renderCasePlanModelLabel(p, element) {
var bo = getBusinessObject(element);
// default maximum textbox dimensions
var height = 18;
var width = element.width / 2 - 60;
var label = bo.name;
// create text box
var textBox = renderLabel(p, label, {
box: { height: height, width: width },
align: 'left-top'
});
var minWidth = 60,
padding = 40,
textBoxWidth = textBox.getBBox().width;
// set polygon width based on actual textbox size
var polygonWidth = textBoxWidth + padding;
if (textBoxWidth < minWidth) {
polygonWidth = minWidth + padding;
}
var polygonPoints = [{ x: 10, y: 0 }, { x: 20, y: -height }, { x: polygonWidth, y: -height }, { x: polygonWidth + 10, y: 0 }];
// The pointer-events attribute is needed to allow clicks on the polygon
// which otherwise would be prevented by the parent node ('djs-visual').
var polygon = drawPolygon(p, polygonPoints, {
fill: 'white',
stroke: 'black',
strokeWidth: 2,
fillOpacity: 0.95,
'pointer-events': 'all'
});
// make sure the textbox is visually on top of the polygon
textBox.parentNode.insertBefore(polygon, textBox);
// reset the position of the text box
translate(textBox, 25, -height + 5);
return textBox;
}
function renderExternalLabel(parentGfx, element) {
var name = getName(element),
hide = false;
var standardEvent = getStandardEvent(element);
if (standardEvent) {
var standardEventVisible = isStandardEventVisible(element);
standardEvent = '[' + standardEvent + ']';
if (!name) {
name = standardEvent;
element.hidden = hide = !standardEventVisible;
} else {
if (standardEventVisible) {
name = name + ' ' + standardEvent;
}
}
}
var box = {
width: 90,
height: 30,
x: element.width / 2 + element.x,
y: element.height / 2 + element.y
};
element.hidden = element.labelTarget.hidden || hide || !name;
return renderLabel(parentGfx, name, { box: box, style: { fontSize: '11px' } });
}
// render elements //////////////////////////////////////////
function renderer(type) {
return handlers[type];
}
var handlers = {
'cmmn:PlanItem': function cmmnPlanItem(p, element) {
var definition = getDefinition(element);
return renderer(definition.$type)(p, element);
},
'cmmn:DiscretionaryItem': function cmmnDiscretionaryItem(p, element) {
var definition = getDefinition(element);
var attrs = {
strokeDasharray: '10, 12'
};
if (is(definition, 'cmmn:Task')) {
assign(attrs, {
strokeDasharray: '12, 12.4',
strokeDashoffset: 13.6
});
}
return renderer(definition.$type)(p, element, attrs);
},
// STAGE
'cmmn:Stage': function cmmnStage(p, element, attrs) {
attrs = assign({ fillOpacity: 0.95 }, attrs);
var rect;
if (isCasePlanModel(element)) {
return handlers['cmmn:CasePlanModel'](p, element);
}
rect = drawOctagon(p, element.width, element.height, STAGE_EDGE_OFFSET, attrs);
if (!isCollapsed(element)) {
renderExpandedStageLabel(p, element, 'left-top');
} else {
renderEmbeddedLabel(p, element, 'center-middle');
}
attachPlanningTableMarker(p, element);
attachStageMarkers(p, element);
return rect;
},
// STAGE
'cmmn:PlanFragment': function cmmnPlanFragment(p, element, attrs) {
var rect = drawRect(p, element.width, element.height, TASK_BORDER_RADIUS, {
strokeDasharray: '10, 12',
fillOpacity: 0.95
});
renderEmbeddedLabel(p, element, isCollapsed(element) ? 'center-middle' : 'left-top');
attachStageMarkers(p, element);
return rect;
},
'cmmn:CasePlanModel': function cmmnCasePlanModel(p, element) {
var rect = drawRect(p, element.width, element.height, 0, {
fillOpacity: 0.95
});
renderCasePlanModelLabel(p, element);
attachPlanningTableMarker(p, element);
attachCasePlanModelMarkers(p, element);
return rect;
},
// MILESTONE
'cmmn:Milestone': function cmmnMilestone(p, element, attrs) {
var rect = drawRect(p, element.width, element.height, MILESTONE_BORDER_RADIUS, attrs);
renderEmbeddedLabel(p, element, 'center-middle');
attachTaskMarkers(p, element);
return rect;
},
// EVENT LISTENER
'cmmn:EventListener': function cmmnEventListener(p, element, attrs) {
var outerCircle = drawCircle(p, element.width, element.height, attrs);
attrs = attrs || {};
attrs.strokeWidth = 2;
drawCircle(p, element.width, element.height, 0.1 * element.height, attrs);
return outerCircle;
},
'cmmn:TimerEventListener': function cmmnTimerEventListener(p, element, attrs) {
var circle = renderer('cmmn:EventListener')(p, element, attrs);
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(p, pathData, {
strokeWidth: 2,
strokeLinecap: 'square'
});
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(p, linePathData, {
strokeWidth: 1,
strokeLinecap: 'square',
transform: 'rotate(' + i * 30 + ',' + height + ',' + width + ')'
});
}
return circle;
},
'cmmn:UserEventListener': function cmmnUserEventListener(p, element, attrs) {
var circle = renderer('cmmn:EventListener')(p, element, attrs);
// TODO: The user event decorator has to be
// scaled correctly!
var x = 20;
var y = 15;
var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
abspos: {
x: x,
y: y
}
});
/* user path */drawPath(p, pathData, {
strokeWidth: 0.5,
fill: 'none'
});
var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
abspos: {
x: x,
y: y
}
});
/* user2 path */drawPath(p, pathData2, {
strokeWidth: 0.5,
fill: 'none'
});
var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
abspos: {
x: x,
y: y
}
});
/* user3 path */drawPath(p, pathData3, {
strokeWidth: 0.5,
fill: 'black'
});
return circle;
},
// TASK
'cmmn:Task': function cmmnTask(p, element, attrs) {
var rect = drawRect(p, element.width, element.height, TASK_BORDER_RADIUS, attrs);
renderEmbeddedLabel(p, element, 'center-middle');
attachTaskMarkers(p, element);
return rect;
},
'cmmn:HumanTask': function cmmnHumanTask(p, element, attrs) {
var task = renderer('cmmn:Task')(p, element, attrs);
var bo = element.businessObject;
var definition = bo.definitionRef;
if (definition.isBlocking) {
var x = 15;
var y = 12;
var pathData1 = pathMap.getScaledPath('TASK_TYPE_USER_1', {
abspos: {
x: x,
y: y
}
});
/* user path */drawPath(p, pathData1, {
strokeWidth: 0.5,
fill: 'none'
});
var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
abspos: {
x: x,
y: y
}
});
/* user2 path */drawPath(p, pathData2, {
strokeWidth: 0.5,
fill: 'none'
});
var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
abspos: {
x: x,
y: y
}
});
/* user3 path */drawPath(p, pathData3, {
strokeWidth: 0.5,
fill: 'black'
});
} else {
var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
abspos: {
x: 17,
y: 15
}
});
/* manual path */drawPath(p, pathData, {
strokeWidth: 1.25,
fill: 'white',
stroke: 'black'
});
}
attachPlanningTableMarker(p, element);
return task;
},
'cmmn:CaseTask': function cmmnCaseTask(p, element, attrs) {
var task = renderer('cmmn:Task')(p, element, attrs);
var pathData = pathMap.getScaledPath('TASK_TYPE_FOLDER', {
abspos: {
x: 7,
y: 7
}
});
/* manual path */drawPath(p, pathData, {
strokeWidth: 1.25,
fill: 'white',
stroke: 'black'
});
return task;
},
'cmmn:ProcessTask': function cmmnProcessTask(p, element, attrs) {
var task = renderer('cmmn:Task')(p, element, attrs);
var pathData = pathMap.getScaledPath('TASK_TYPE_CHEVRON', {
abspos: {
x: 5,
y: 5
}
});
/* manual path */drawPath(p, pathData, {
strokeWidth: 1.25,
fill: 'white',
stroke: 'black'
});
return task;
},
'cmmn:DecisionTask': function cmmnDecisionTask(p, element, attrs) {
var task = renderer('cmmn:Task')(p, element, attrs);
var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
abspos: {
x: 8,
y: 8
}
});
drawPath(p, headerPathData, {
strokeWidth: 1,
fill: '000'
});
var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
abspos: {
x: 8,
y: 8
}
});
drawPath(p, headerData, {
strokeWidth: 1
});
return task;
},
'cmmn:CaseFileItem': function cmmnCaseFileItem(p, element, attrs) {
var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.474,
my: 0.296
}
});
return drawPath(p, pathData, { fill: 'white' });
},
// ARTIFACTS
'cmmn:TextAnnotation': function cmmnTextAnnotation(p, element) {
var style = {
'fill': 'none',
'stroke': 'none'
};
var textElement = drawRect(p, element.width, element.height, 0, 0, style);
var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.0,
my: 0.0
}
});
drawPath(p, textPathData);
var text = getBusinessObject(element).text || '';
renderLabel(p, text, { box: element, align: 'left-middle', padding: 5 });
return textElement;
},
'cmmn:Association': function cmmnAssociation(p, element, attrs) {
var semantic = getBusinessObject(element);
attrs = assign({
strokeDasharray: '0.5, 5',
strokeLinecap: 'round',
strokeLinejoin: 'round'
}, attrs || {});
if (semantic.associationDirection === 'One' || semantic.associationDirection === 'Both') {
attrs.markerEnd = marker('association-end');
}
if (semantic.associationDirection === 'Both') {
attrs.markerStart = marker('association-start');
}
return drawLine(p, element.waypoints, attrs);
},
// MARKERS
'StageMarker': function StageMarker(p, element) {
var markerRect = drawRect(p, 14, 14, 0, {
strokeWidth: 1,
stroke: 'black'
});
translate(markerRect, element.width / 2 - 7, element.height - 17);
var path = isCollapsed(element) ? 'MARKER_STAGE_COLLAPSED' : 'MARKER_STAGE_EXPANDED';
var stagePath = pathMap.getScaledPath(path, {
xScaleFactor: 1.5,
yScaleFactor: 1.5,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 - 7) / element.width,
my: (element.height - 17) / element.height
}
});
drawPath(p, stagePath);
},
'RequiredMarker': function RequiredMarker(p, element, position) {
var path = pathMap.getScaledPath('MARKER_REQUIRED', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 + position) / element.width,
my: (element.height - 17) / element.height
}
});
drawPath(p, path, { strokeWidth: 3 });
},
'AutoCompleteMarker': function AutoCompleteMarker(p, element, position) {
var markerRect = drawRect(p, 11, 14, 0, {
strokeWidth: 1,
stroke: 'black',
fill: 'black'
});
translate(markerRect, element.width / 2 + position + 2, element.height - 17);
},
'ManualActivationMarker': function ManualActivationMarker(p, element, position) {
var path = pathMap.getScaledPath('MARKER_MANUAL_ACTIVATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 + position) / element.width,
my: (element.height - 17) / element.height
}
});
drawPath(p, path, { strokeWidth: 1 });
},
'RepetitionMarker': function RepetitionMarker(p, element, position) {
var path = pathMap.getScaledPath('MARKER_REPEATABLE', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 + position) / element.width,
my: (element.height - 17) / element.height
}
});
drawPath(p, path);
},
'PlanningTableMarker': function PlanningTableMarker(p, element, position) {
var planningTableRect = drawRect(p, 30, 24, 0, {
strokeWidth: 1.5,
stroke: 'black'
});
translate(planningTableRect, element.width / 2 - 15, -17);
var isCollapsed = isPlanningTableCollapsed(element);
var marker = isCollapsed ? 'MARKER_PLANNING_TABLE_COLLAPSED' : 'MARKER_PLANNING_TABLE_EXPANDED';
var stagePath = pathMap.getScaledPath(marker, {
xScaleFactor: 1.5,
yScaleFactor: 1.5,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 - 15) / element.width,
my: -17 / element.height
}
});
drawPath(p, stagePath, {
strokeWidth: 1.5
});
},
'cmmn:OnPart': function cmmnOnPart(p, element) {
var pathData = createPathFromConnection(element);
var path = drawPath(p, pathData, {
strokeDasharray: '10, 5, 2, 5, 2, 5',
strokeWidth: 1.5
});
return path;
},
'cmmn:PlanItemOnPart': function cmmnPlanItemOnPart(p, element) {
return renderer('cmmn:OnPart')(p, element);
},
'cmmn:CaseFileItemOnPart': function cmmnCaseFileItemOnPart(p, element) {
return renderer('cmmn:OnPart')(p, element);
},
'cmmn:EntryCriterion': function cmmnEntryCriterion(p, element) {
return drawDiamond(p, element.width, element.height, {
fill: 'white'
});
},
'cmmn:ExitCriterion': fun