UNPKG

cmmn-js

Version:
1,969 lines (1,530 loc) 408 kB
/*! * cmmn-js - cmmn-viewer 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){ /** * 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_(59).assign, omit = _dereq_(59).omit, isNumber = _dereq_(59).isNumber; var inherits = _dereq_(58); var domify = _dereq_(60).domify, domQuery = _dereq_(60).query, domRemove = _dereq_(60).remove; var innerSVG = _dereq_(79).innerSVG; var Diagram = _dereq_(24).default, CmmnModdle = _dereq_(17).default; var Importer = _dereq_(10); 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_(3), _dereq_(45).default, _dereq_(41).default]; /* <project-logo> */ var PoweredBy = _dereq_(16), domEvent = _dereq_(60).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> */ },{"10":10,"16":16,"17":17,"24":24,"3":3,"41":41,"45":45,"58":58,"59":59,"60":60,"79":79}],2:[function(_dereq_,module,exports){ 'use strict'; var ModelUtil = _dereq_(15), getDefinition = ModelUtil.getDefinition, getSentry = ModelUtil.getSentry; var isAny = _dereq_(7).isAny; var forEach = _dereq_(59).forEach, isArray = _dereq_(59).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']); } },{"15":15,"59":59,"7":7}],3:[function(_dereq_,module,exports){ 'use strict'; module.exports = { __depends__: [_dereq_(6), _dereq_(12)], itemRegistry: ['type', _dereq_(2)] }; },{"12":12,"2":2,"6":6}],4:[function(_dereq_,module,exports){ 'use strict'; var inherits = _dereq_(58), isArray = _dereq_(59).isArray, isObject = _dereq_(59).isObject, assign = _dereq_(59).assign; var BaseRenderer = _dereq_(32).default, TextUtil = _dereq_(56).default, DiUtil = _dereq_(13), ModelUtil = _dereq_(15); 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_(60).query; var svgAppend = _dereq_(79).append, svgAttr = _dereq_(79).attr, svgClasses = _dereq_(79).classes, svgCreate = _dereq_(79).create; var translate = _dereq_(55).translate; var createLine = _dereq_(54).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': function cmmnExitCriterion(p, element) { return drawDiamond(p, element.width, element.height, { fill: 'black' }); }, 'cmmndi:CMMNEdge': function cmmndiCMMNEdge(p, element) { var bo = getBusinessObject(element); if (bo.cmmnElementRef) { return renderer(bo.cmmnElementRef.$type)(p, element); } var pathData = createPathFromConnection(element); var path = drawPath(p, pathData, { strokeDasharray: '3, 5', strokeWidth: 1 }); return path; }, 'label': function label(parentGfx, element) { // Update external label size and bounds during rendering when // we have the actual rendered bounds anyway. var textElement = renderExternalLabel(parentGfx, element); var textBBox; try { textBBox = textElement.getBBox(); } catch (e) { textBBox = { width: 0, height: 0, x: 0 }; } // update element.x so that the layouted text is still // center alligned (newX = oldMidX - newWidth / 2) element.x = Math.ceil(element.x + element.width / 2) - Math.ceil(textBBox.width / 2); // take element width, height from actual bounds element.width = Math.ceil(textBBox.width); element.height = Math.ceil(textBBox.height); // compensate bounding box x svgAttr(textElement, { transform: 'translate(' + -1 * textBBox.x + ',0)' }); return textElement; } }; // attach markers ///////////////////////// function attachTaskMarkers(p, element) { var obj = getBusinessObject(element); var padding = 6; var markers = []; if (isRequired(obj)) { markers.push({ marker: 'RequiredMarker', width: 1 }); } if (isManualActivation(obj)) { markers.push({ marker: 'ManualActivationMarker', width: 14 }); } if (isRepeatable(obj)) { markers.push({ marker: 'RepetitionMarker', width: 14 }); } if (markers.length) { if (markers.length === 1) { // align marker in the middle of the element drawMarker(markers[0].marker, p, element, markers[0].width / 2 * -1); } else if (markers.length === 2) { /* align marker: * * | | * +-------------+ * ^ * | * +-+ +-+ * |0| |1| <-- markers * +-+ +-+ * (leftMarker) (rightMarker) */ drawMarker(markers[0].marker, p, element, markers[0].width * -1 - padding / 2); drawMarker(markers[1].marker, p, element, padding / 2); } else if (markers.length === 3) { /* align marker: * * | | * +-------------+ * ^ * | * +-+ +-+ +-+ * |0| |1| |2| <-- markers * +-+ +-+ +-+ */ /* 1 */drawMarker(markers[1].marker, p, element, markers[1].width / 2 * -1); /* 0 */drawMarker(markers[0].marker, p, element, markers[1].width / 2 * -1 - padding - markers[0].width); /* 2 */drawMarker(markers[2].marker, p, element, markers[1].width / 2 + padding); } } } function attachCasePlanModelMarkers(p, element) { var obj = getBusinessObject(element); if (isAutoComplete(obj)) { drawMarker('AutoCompleteMarker', p, element, -7); } } function attachStageMarkers(p, element, stage) { var obj = getBusinessObject(element); var padding = 6; drawMarker('StageMarker', p, element, -7); var leftMarkers = []; if (isRequired(obj)) { leftMarkers.push({ marker: 'RequiredMarker', width: 1 }); } if (isAutoComplete(obj)) { leftMarkers.push({ marker: 'AutoCompleteMarker', width: 14 }); } if (leftMarkers.length) { if (leftMarkers.length === 1) { drawMarker(leftMarkers[0].marker, p, element, leftMarkers[0].width * -1 - 7 - padding); } else if (leftMarkers.length === 2) { drawMarker(leftMarkers[0].marker, p, element, leftMarkers[1].width * -1 - 7 - padding - leftMarkers[0].width * -1 - padding); drawMarker(leftMarkers[1].marker, p, element, leftMarkers[1].width * -1 - 7 - padding); } } var rightMarkers = []; if (isManualActivation(obj)) { rightMarkers.push({ marker: 'ManualActivationMarker', width: 14 }); } if (isRepeatable(obj)) { rightMarkers.push({ marker: 'RepetitionMarker', width: 14 }); } if (rightMarkers.length) { if (rightMarkers.length === 1) { drawMarker(rightMarkers[0].marker, p, element, 7 + padding); } else if (rightMarkers.length === 2) { drawMarker(rightMarkers[0].marker, p, element, 7 + padding); drawMarker(rightMarkers[1].marker, p, element, 7 + padding + rightMarkers[0].width + padding); } } } function attachPlanningTableMarker(p, element) { if (hasPlanningTable(element)) { drawMarker('PlanningTableMarker', p, element); } } function drawMarker(marker, parent, element, position) { renderer(marker)(parent, element, position); } // draw shape and connection //////////////////////////////////// function drawShape(parent, element) { var h = handlers[element.type]; /* jshint -W040 */ if (!h) { return BaseRenderer.prototype.drawShape.apply(this, [parent, element]); } else { return h(parent, element); } } function drawConnection(parent, element) { var type = element.type; var h = handlers[type]; /* jshint -W040 */ if (!h) { return BaseRenderer.prototype.drawConnection.apply(this, [parent, element]); } else { return h(parent, element); } } this.canRender = function (element) { return is(element, 'cmmn:CMMNElement') || is(element, 'cmmndi:CMMNEdge'); }; this.drawShape = drawShape; this.drawConnection = drawConnection; // hook onto canvas init event to initialize // connection start/end markers on svg eventBus.on('canvas.init', function (event) { initMarkers(event.svg); }); } inherits(CmmnRenderer, BaseRenderer); CmmnRenderer.$inject = ['eventBus', 'styles', 'pathMap']; module.exports = CmmnRenderer; },{"13":13,"15":15,"32":32,"54":54,"55":55,"56":56,"58":58,"59":59,"60":60,"79":79}],5:[function(_dereq_,module,exports){ 'use strict'; /** * Map containing SVG paths needed by CmmnRenderer. */ function PathMap() { var PATH_USER_TYPE_1 = 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' + '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' + '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' + 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509