UNPKG

bpmn-js

Version:

A bpmn 2.0 toolkit and web modeler

2,266 lines (1,860 loc) 470 kB
/*! * bpmn-js - bpmn-viewer v8.9.1 * * 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: 2022-01-24 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory()); }(this, (function () { 'use strict'; var inherits_browser = {exports: {}}; if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module inherits_browser.exports = function inherits(ctor, superCtor) { if (superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); } }; } else { // old school shim for old browsers inherits_browser.exports = function inherits(ctor, superCtor) { if (superCtor) { ctor.super_ = superCtor; var TempCtor = function () {}; TempCtor.prototype = superCtor.prototype; ctor.prototype = new TempCtor(); ctor.prototype.constructor = ctor; } }; } var inherits$1 = inherits_browser.exports; /** * Flatten array, one level deep. * * @param {Array<?>} arr * * @return {Array<?>} */ var nativeToString = Object.prototype.toString; var nativeHasOwnProperty = Object.prototype.hasOwnProperty; function isUndefined$1(obj) { return obj === undefined; } function isDefined(obj) { return obj !== undefined; } function isArray$1(obj) { return nativeToString.call(obj) === '[object Array]'; } function isObject(obj) { return nativeToString.call(obj) === '[object Object]'; } function isNumber(obj) { return nativeToString.call(obj) === '[object Number]'; } function isFunction(obj) { var tag = nativeToString.call(obj); return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; } function isString(obj) { return nativeToString.call(obj) === '[object String]'; } /** * Return true, if target owns a property with the given key. * * @param {Object} target * @param {String} key * * @return {Boolean} */ function has(target, key) { return nativeHasOwnProperty.call(target, key); } /** * Find element in collection. * * @param {Array|Object} collection * @param {Function|Object} matcher * * @return {Object} */ function find(collection, matcher) { matcher = toMatcher(matcher); var match; forEach(collection, function (val, key) { if (matcher(val, key)) { match = val; return false; } }); return match; } /** * Find element in collection. * * @param {Array|Object} collection * @param {Function} matcher * * @return {Array} result */ function filter(collection, matcher) { var result = []; forEach(collection, function (val, key) { if (matcher(val, key)) { result.push(val); } }); return result; } /** * Iterate over collection; returning something * (non-undefined) will stop iteration. * * @param {Array|Object} collection * @param {Function} iterator * * @return {Object} return result that stopped the iteration */ function forEach(collection, iterator) { var val, result; if (isUndefined$1(collection)) { return; } var convertKey = isArray$1(collection) ? toNum : identity; for (var key in collection) { if (has(collection, key)) { val = collection[key]; result = iterator(val, convertKey(key)); if (result === false) { return val; } } } } /** * Reduce collection, returning a single result. * * @param {Object|Array} collection * @param {Function} iterator * @param {Any} result * * @return {Any} result returned from last iterator */ function reduce(collection, iterator, result) { forEach(collection, function (value, idx) { result = iterator(result, value, idx); }); return result; } /** * Return true if every element in the collection * matches the criteria. * * @param {Object|Array} collection * @param {Function} matcher * * @return {Boolean} */ function every(collection, matcher) { return !!reduce(collection, function (matches, val, key) { return matches && matcher(val, key); }, true); } /** * Return true if some elements in the collection * match the criteria. * * @param {Object|Array} collection * @param {Function} matcher * * @return {Boolean} */ function some(collection, matcher) { return !!find(collection, matcher); } /** * Create an object pattern matcher. * * @example * * const matcher = matchPattern({ id: 1 }); * * var element = find(elements, matcher); * * @param {Object} pattern * * @return {Function} matcherFn */ function matchPattern(pattern) { return function (el) { return every(pattern, function (val, key) { return el[key] === val; }); }; } function toMatcher(matcher) { return isFunction(matcher) ? matcher : function (e) { return e === matcher; }; } function identity(arg) { return arg; } function toNum(arg) { return Number(arg); } /** * Debounce fn, calling it only once if * the given time elapsed between calls. * * @param {Function} fn * @param {Number} timeout * * @return {Function} debounced function */ function debounce(fn, timeout) { var timer; var lastArgs; var lastThis; var lastNow; function fire() { var now = Date.now(); var scheduledDiff = lastNow + timeout - now; if (scheduledDiff > 0) { return schedule(scheduledDiff); } fn.apply(lastThis, lastArgs); timer = lastNow = lastArgs = lastThis = undefined; } function schedule(timeout) { timer = setTimeout(fire, timeout); } return function () { lastNow = Date.now(); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } lastArgs = args; lastThis = this; // ensure an execution is scheduled if (!timer) { schedule(timeout); } }; } /** * Bind function against target <this>. * * @param {Function} fn * @param {Object} target * * @return {Function} bound function */ function bind$2(fn, target) { return fn.bind(target); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /** * Convenience wrapper for `Object.assign`. * * @param {Object} target * @param {...Object} others * * @return {Object} the target */ function assign(target) { for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { others[_key - 1] = arguments[_key]; } return _extends.apply(void 0, [target].concat(others)); } /** * Pick given properties from the target object. * * @param {Object} target * @param {Array} properties * * @return {Object} target */ function pick(target, properties) { var result = {}; var obj = Object(target); forEach(properties, function (prop) { if (prop in obj) { result[prop] = target[prop]; } }); return result; } /** * Pick all target properties, excluding the given ones. * * @param {Object} target * @param {Array} properties * * @return {Object} target */ function omit(target, properties) { var result = {}; var obj = Object(target); forEach(obj, function (prop, key) { if (properties.indexOf(key) === -1) { result[key] = prop; } }); return result; } var DEFAULT_RENDER_PRIORITY$1 = 1000; /** * The base implementation of shape and connection renderers. * * @param {EventBus} eventBus * @param {number} [renderPriority=1000] */ function BaseRenderer(eventBus, renderPriority) { var self = this; renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1; eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) { var type = evt.type, element = context.element, visuals = context.gfx, attrs = context.attrs; if (self.canRender(element)) { if (type === 'render.shape') { return self.drawShape(visuals, element, attrs); } else { return self.drawConnection(visuals, element, attrs); } } }); eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) { if (self.canRender(element)) { if (evt.type === 'render.getShapePath') { return self.getShapePath(element); } else { return self.getConnectionPath(element); } } }); } /** * Should check whether *this* renderer can render * the element/connection. * * @param {element} element * * @returns {boolean} */ BaseRenderer.prototype.canRender = function() {}; /** * Provides the shape's snap svg element to be drawn on the `canvas`. * * @param {djs.Graphics} visuals * @param {Shape} shape * * @returns {Snap.svg} [returns a Snap.svg paper element ] */ BaseRenderer.prototype.drawShape = function() {}; /** * Provides the shape's snap svg element to be drawn on the `canvas`. * * @param {djs.Graphics} visuals * @param {Connection} connection * * @returns {Snap.svg} [returns a Snap.svg paper element ] */ BaseRenderer.prototype.drawConnection = function() {}; /** * Gets the SVG path of a shape that represents it's visual bounds. * * @param {Shape} shape * * @return {string} svg path */ BaseRenderer.prototype.getShapePath = function() {}; /** * Gets the SVG path of a connection that represents it's visual bounds. * * @param {Connection} connection * * @return {string} svg path */ BaseRenderer.prototype.getConnectionPath = function() {}; /** * Is an element of the given BPMN type? * * @param {djs.model.Base|ModdleElement} element * @param {string} type * * @return {boolean} */ function is$1(element, type) { var bo = getBusinessObject(element); return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type); } /** * Return the business object for a given element. * * @param {djs.model.Base|ModdleElement} element * * @return {ModdleElement} */ function getBusinessObject(element) { return (element && element.businessObject) || element; } function isExpanded(element) { if (is$1(element, 'bpmn:CallActivity')) { return false; } if (is$1(element, 'bpmn:SubProcess')) { return getBusinessObject(element).di && !!getBusinessObject(element).di.isExpanded; } if (is$1(element, 'bpmn:Participant')) { return !!getBusinessObject(element).processRef; } return true; } function isEventSubProcess(element) { return element && !!getBusinessObject(element).triggeredByEvent; } function getLabelAttr(semantic) { if ( is$1(semantic, 'bpmn:FlowElement') || is$1(semantic, 'bpmn:Participant') || is$1(semantic, 'bpmn:Lane') || is$1(semantic, 'bpmn:SequenceFlow') || is$1(semantic, 'bpmn:MessageFlow') || is$1(semantic, 'bpmn:DataInput') || is$1(semantic, 'bpmn:DataOutput') ) { return 'name'; } if (is$1(semantic, 'bpmn:TextAnnotation')) { return 'text'; } if (is$1(semantic, 'bpmn:Group')) { return 'categoryValueRef'; } } function getCategoryValue(semantic) { var categoryValueRef = semantic['categoryValueRef']; if (!categoryValueRef) { return ''; } return categoryValueRef.value || ''; } function getLabel(element) { var semantic = element.businessObject, attr = getLabelAttr(semantic); if (attr) { if (attr === 'categoryValueRef') { return getCategoryValue(semantic); } return semantic[attr] || ''; } } function ensureImported(element, target) { if (element.ownerDocument !== target.ownerDocument) { try { // may fail on webkit return target.ownerDocument.importNode(element, true); } catch (e) { // ignore } } return element; } /** * appendTo utility */ /** * Append a node to a target element and return the appended node. * * @param {SVGElement} element * @param {SVGElement} target * * @return {SVGElement} the appended node */ function appendTo(element, target) { return target.appendChild(ensureImported(element, target)); } /** * append utility */ /** * Append a node to an element * * @param {SVGElement} element * @param {SVGElement} node * * @return {SVGElement} the element */ function append(target, node) { appendTo(node, target); return target; } /** * attribute accessor utility */ var LENGTH_ATTR = 2; var CSS_PROPERTIES = { 'alignment-baseline': 1, 'baseline-shift': 1, 'clip': 1, 'clip-path': 1, 'clip-rule': 1, 'color': 1, 'color-interpolation': 1, 'color-interpolation-filters': 1, 'color-profile': 1, 'color-rendering': 1, 'cursor': 1, 'direction': 1, 'display': 1, 'dominant-baseline': 1, 'enable-background': 1, 'fill': 1, 'fill-opacity': 1, 'fill-rule': 1, 'filter': 1, 'flood-color': 1, 'flood-opacity': 1, 'font': 1, 'font-family': 1, 'font-size': LENGTH_ATTR, 'font-size-adjust': 1, 'font-stretch': 1, 'font-style': 1, 'font-variant': 1, 'font-weight': 1, 'glyph-orientation-horizontal': 1, 'glyph-orientation-vertical': 1, 'image-rendering': 1, 'kerning': 1, 'letter-spacing': 1, 'lighting-color': 1, 'marker': 1, 'marker-end': 1, 'marker-mid': 1, 'marker-start': 1, 'mask': 1, 'opacity': 1, 'overflow': 1, 'pointer-events': 1, 'shape-rendering': 1, 'stop-color': 1, 'stop-opacity': 1, 'stroke': 1, 'stroke-dasharray': 1, 'stroke-dashoffset': 1, 'stroke-linecap': 1, 'stroke-linejoin': 1, 'stroke-miterlimit': 1, 'stroke-opacity': 1, 'stroke-width': LENGTH_ATTR, 'text-anchor': 1, 'text-decoration': 1, 'text-rendering': 1, 'unicode-bidi': 1, 'visibility': 1, 'word-spacing': 1, 'writing-mode': 1 }; function getAttribute(node, name) { if (CSS_PROPERTIES[name]) { return node.style[name]; } else { return node.getAttributeNS(null, name); } } function setAttribute(node, name, value) { var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); var type = CSS_PROPERTIES[hyphenated]; if (type) { // append pixel unit, unless present if (type === LENGTH_ATTR && typeof value === 'number') { value = String(value) + 'px'; } node.style[hyphenated] = value; } else { node.setAttributeNS(null, name, value); } } function setAttributes(node, attrs) { var names = Object.keys(attrs), i, name; for (i = 0, name; (name = names[i]); i++) { setAttribute(node, name, attrs[name]); } } /** * Gets or sets raw attributes on a node. * * @param {SVGElement} node * @param {Object} [attrs] * @param {String} [name] * @param {String} [value] * * @return {String} */ function attr$1(node, name, value) { if (typeof name === 'string') { if (value !== undefined) { setAttribute(node, name, value); } else { return getAttribute(node, name); } } else { setAttributes(node, name); } return node; } /** * Clear utility */ function index(arr, obj) { if (arr.indexOf) { return arr.indexOf(obj); } for (var i = 0; i < arr.length; ++i) { if (arr[i] === obj) { return i; } } return -1; } var re$1 = /\s+/; var toString$1 = Object.prototype.toString; function defined(o) { return typeof o !== 'undefined'; } /** * Wrap `el` in a `ClassList`. * * @param {Element} el * @return {ClassList} * @api public */ function classes$1(el) { return new ClassList$1(el); } function ClassList$1(el) { if (!el || !el.nodeType) { throw new Error('A DOM element reference is required'); } this.el = el; this.list = el.classList; } /** * Add class `name` if not already present. * * @param {String} name * @return {ClassList} * @api public */ ClassList$1.prototype.add = function(name) { // classList if (this.list) { this.list.add(name); return this; } // fallback var arr = this.array(); var i = index(arr, name); if (!~i) { arr.push(name); } if (defined(this.el.className.baseVal)) { this.el.className.baseVal = arr.join(' '); } else { this.el.className = arr.join(' '); } return this; }; /** * Remove class `name` when present, or * pass a regular expression to remove * any which match. * * @param {String|RegExp} name * @return {ClassList} * @api public */ ClassList$1.prototype.remove = function(name) { if ('[object RegExp]' === toString$1.call(name)) { return this.removeMatching(name); } // classList if (this.list) { this.list.remove(name); return this; } // fallback var arr = this.array(); var i = index(arr, name); if (~i) { arr.splice(i, 1); } this.el.className.baseVal = arr.join(' '); return this; }; /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList$1.prototype.removeMatching = function(re) { var arr = this.array(); for (var i = 0; i < arr.length; i++) { if (re.test(arr[i])) { this.remove(arr[i]); } } return this; }; /** * Toggle class `name`, can force state via `force`. * * For browsers that support classList, but do not support `force` yet, * the mistake will be detected and corrected. * * @param {String} name * @param {Boolean} force * @return {ClassList} * @api public */ ClassList$1.prototype.toggle = function(name, force) { // classList if (this.list) { if (defined(force)) { if (force !== this.list.toggle(name, force)) { this.list.toggle(name); // toggle again to correct } } else { this.list.toggle(name); } return this; } // fallback if (defined(force)) { if (!force) { this.remove(name); } else { this.add(name); } } else { if (this.has(name)) { this.remove(name); } else { this.add(name); } } return this; }; /** * Return an array of classes. * * @return {Array} * @api public */ ClassList$1.prototype.array = function() { var className = this.el.getAttribute('class') || ''; var str = className.replace(/^\s+|\s+$/g, ''); var arr = str.split(re$1); if ('' === arr[0]) { arr.shift(); } return arr; }; /** * Check if class `name` is present. * * @param {String} name * @return {ClassList} * @api public */ ClassList$1.prototype.has = ClassList$1.prototype.contains = function(name) { return ( this.list ? this.list.contains(name) : !! ~index(this.array(), name) ); }; function remove$2(element) { var parent = element.parentNode; if (parent) { parent.removeChild(element); } return element; } /** * Clear utility */ /** * Removes all children from the given element * * @param {DOMElement} element * @return {DOMElement} the element (for chaining) */ function clear$1(element) { var child; while ((child = element.firstChild)) { remove$2(child); } return element; } var ns = { svg: 'http://www.w3.org/2000/svg' }; /** * DOM parsing utility */ var SVG_START = '<svg xmlns="' + ns.svg + '"'; function parse$1(svg) { var unwrap = false; // ensure we import a valid svg document if (svg.substring(0, 4) === '<svg') { if (svg.indexOf(ns.svg) === -1) { svg = SVG_START + svg.substring(4); } } else { // namespace svg svg = SVG_START + '>' + svg + '</svg>'; unwrap = true; } var parsed = parseDocument(svg); if (!unwrap) { return parsed; } var fragment = document.createDocumentFragment(); var parent = parsed.firstChild; while (parent.firstChild) { fragment.appendChild(parent.firstChild); } return fragment; } function parseDocument(svg) { var parser; // parse parser = new DOMParser(); parser.async = false; return parser.parseFromString(svg, 'text/xml'); } /** * Create utility for SVG elements */ /** * Create a specific type from name or SVG markup. * * @param {String} name the name or markup of the element * @param {Object} [attrs] attributes to set on the element * * @returns {SVGElement} */ function create$1(name, attrs) { var element; if (name.charAt(0) === '<') { element = parse$1(name).firstChild; element = document.importNode(element, true); } else { element = document.createElementNS(ns.svg, name); } if (attrs) { attr$1(element, attrs); } return element; } /** * Geometry helpers */ // fake node used to instantiate svg geometry elements var node = create$1('svg'); function extend$1(object, props) { var i, k, keys = Object.keys(props); for (i = 0; (k = keys[i]); i++) { object[k] = props[k]; } return object; } /** * Create matrix via args. * * @example * * createMatrix({ a: 1, b: 1 }); * createMatrix(); * createMatrix(1, 2, 0, 0, 30, 20); * * @return {SVGMatrix} */ function createMatrix(a, b, c, d, e, f) { var matrix = node.createSVGMatrix(); switch (arguments.length) { case 0: return matrix; case 1: return extend$1(matrix, a); case 6: return extend$1(matrix, { a: a, b: b, c: c, d: d, e: e, f: f }); } } function createTransform(matrix) { if (matrix) { return node.createSVGTransformFromMatrix(matrix); } else { return node.createSVGTransform(); } } /** * Serialization util */ var TEXT_ENTITIES = /([&<>]{1})/g; var ATTR_ENTITIES = /([\n\r"]{1})/g; var ENTITY_REPLACEMENT = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '\'' }; function escape$1(str, pattern) { function replaceFn(match, entity) { return ENTITY_REPLACEMENT[entity] || entity; } return str.replace(pattern, replaceFn); } function serialize(node, output) { var i, len, attrMap, attrNode, childNodes; switch (node.nodeType) { // TEXT case 3: // replace special XML characters output.push(escape$1(node.textContent, TEXT_ENTITIES)); break; // ELEMENT case 1: output.push('<', node.tagName); if (node.hasAttributes()) { attrMap = node.attributes; for (i = 0, len = attrMap.length; i < len; ++i) { attrNode = attrMap.item(i); output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"'); } } if (node.hasChildNodes()) { output.push('>'); childNodes = node.childNodes; for (i = 0, len = childNodes.length; i < len; ++i) { serialize(childNodes.item(i), output); } output.push('</', node.tagName, '>'); } else { output.push('/>'); } break; // COMMENT case 8: output.push('<!--', escape$1(node.nodeValue, TEXT_ENTITIES), '-->'); break; // CDATA case 4: output.push('<![CDATA[', node.nodeValue, ']]>'); break; default: throw new Error('unable to handle node ' + node.nodeType); } return output; } /** * innerHTML like functionality for SVG elements. * based on innerSVG (https://code.google.com/p/innersvg) */ function set(element, svg) { var parsed = parse$1(svg); // clear element contents clear$1(element); if (!svg) { return; } if (!isFragment(parsed)) { // extract <svg> from parsed document parsed = parsed.documentElement; } var nodes = slice$1(parsed.childNodes); // import + append each node for (var i = 0; i < nodes.length; i++) { appendTo(nodes[i], element); } } function get(element) { var child = element.firstChild, output = []; while (child) { serialize(child, output); child = child.nextSibling; } return output.join(''); } function isFragment(node) { return node.nodeName === '#document-fragment'; } function innerSVG(element, svg) { if (svg !== undefined) { try { set(element, svg); } catch (e) { throw new Error('error parsing SVG: ' + e.message); } return element; } else { return get(element); } } function slice$1(arr) { return Array.prototype.slice.call(arr); } /** * transform accessor utility */ function wrapMatrix(transformList, transform) { if (transform instanceof SVGMatrix) { return transformList.createSVGTransformFromMatrix(transform); } return transform; } function setTransforms(transformList, transforms) { var i, t; transformList.clear(); for (i = 0; (t = transforms[i]); i++) { transformList.appendItem(wrapMatrix(transformList, t)); } } /** * Get or set the transforms on the given node. * * @param {SVGElement} node * @param {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms] * * @return {SVGTransform} the consolidated transform */ function transform$1(node, transforms) { var transformList = node.transform.baseVal; if (transforms) { if (!Array.isArray(transforms)) { transforms = [ transforms ]; } setTransforms(transformList, transforms); } return transformList.consolidate(); } function componentsToPath(elements) { return elements.join(',').replace(/,?([A-z]),?/g, '$1'); } function toSVGPoints(points) { var result = ''; for (var i = 0, p; (p = points[i]); i++) { result += p.x + ',' + p.y + ' '; } return result; } function createLine(points, attrs) { var line = create$1('polyline'); attr$1(line, { points: toSVGPoints(points) }); if (attrs) { attr$1(line, attrs); } return line; } function updateLine(gfx, points) { attr$1(gfx, { points: toSVGPoints(points) }); return gfx; } // 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); }); } function isThrowEvent(event) { return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); } function isCollection(element) { var dataObject = element.dataObjectRef; return element.isCollection || (dataObject && dataObject.isCollection); } function getDi(element) { return element.businessObject.di; } function getSemantic(element) { return element.businessObject; } // color access ////////////////////// function getFillColor(element, defaultColor) { var di = getDi(element); return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white'; } function getStrokeColor(element, defaultColor) { var di = getDi(element); return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || 'black'; } function getLabelColor(element, defaultColor, defaultStrokeColor) { var di = getDi(element), label = di.get('label'); return label && label.get('color:color') || defaultColor || getStrokeColor(element, defaultStrokeColor); } // 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); } 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); } 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); } 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); } /** * Set attribute `name` to `val`, or get attr `name`. * * @param {Element} el * @param {String} name * @param {String} [val] * @api public */ function attr(el, name, val) { // get if (arguments.length == 2) { return el.getAttribute(name); } // remove if (val === null) { return el.removeAttribute(name); } // set el.setAttribute(name, val); return el; } var indexOf = [].indexOf; var indexof = function(arr, obj){ if (indexOf) return arr.indexOf(obj); for (var i = 0; i < arr.length; ++i) { if (arr[i] === obj) return i; } return -1; }; /** * Taken from https://github.com/component/classes * * Without the component bits. */ /** * Whitespace regexp. */ var re = /\s+/; /** * toString reference. */ var toString = Object.prototype.toString; /** * Wrap `el` in a `ClassList`. * * @param {Element} el * @return {ClassList} * @api public */ function classes(el) { return new ClassList(el); } /** * Initialize a new ClassList for `el`. * * @param {Element} el * @api private */ function ClassList(el) { if (!el || !el.nodeType) { throw new Error('A DOM element reference is required'); } this.el = el; this.list = el.classList; } /** * Add class `name` if not already present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.add = function (name) { // classList if (this.list) { this.list.add(name); return this; } // fallback var arr = this.array(); var i = indexof(arr, name); if (!~i) arr.push(name); this.el.className = arr.join(' '); return this; }; /** * Remove class `name` when present, or * pass a regular expression to remove * any which match. * * @param {String|RegExp} name * @return {ClassList} * @api public */ ClassList.prototype.remove = function (name) { if ('[object RegExp]' == toString.call(name)) { return this.removeMatching(name); } // classList if (this.list) { this.list.remove(name); return this; } // fallback var arr = this.array(); var i = indexof(arr, name); if (~i) arr.splice(i, 1); this.el.className = arr.join(' '); return this; }; /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList.prototype.removeMatching = function (re) { var arr = this.array(); for (var i = 0; i < arr.length; i++) { if (re.test(arr[i])) { this.remove(arr[i]); } } return this; }; /** * Toggle class `name`, can force state via `force`. * * For browsers that support classList, but do not support `force` yet, * the mistake will be detected and corrected. * * @param {String} name * @param {Boolean} force * @return {ClassList} * @api public */ ClassList.prototype.toggle = function (name, force) { // classList if (this.list) { if ('undefined' !== typeof force) { if (force !== this.list.toggle(name, force)) { this.list.toggle(name); // toggle again to correct } } else { this.list.toggle(name); } return this; } // fallback if ('undefined' !== typeof force) { if (!force) { this.remove(name); } else { this.add(name); } } else { if (this.has(name)) { this.remove(name); } else { this.add(name); } } return this; }; /** * Return an array of classes. * * @return {Array} * @api public */ ClassList.prototype.array = function () { var className = this.el.getAttribute('class') || ''; var str = className.replace(/^\s+|\s+$/g, ''); var arr = str.split(re); if ('' === arr[0]) arr.shift(); return arr; }; /** * Check if class `name` is present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.has = ClassList.prototype.contains = function (name) { return this.list ? this.list.contains(name) : !!~indexof(this.array(), name); }; /** * Remove all children from the given element. */ function clear(el) { var c; while (el.childNodes.length) { c = el.childNodes[0]; el.removeChild(c); } return el; } var proto = typeof Element !== 'undefined' ? Element.prototype : {}; var vendor = proto.matches || proto.matchesSelector || proto.webkitMatchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector; var matchesSelector = match; /** * Match `el` to `selector`. * * @param {Element} el * @param {String} selector * @return {Boolean} * @api public */ function match(el, selector) { if (!el || el.nodeType !== 1) return false; if (vendor) return vendor.call(el, selector); var nodes = el.parentNode.querySelectorAll(selector); for (var i = 0; i < nodes.length; i++) { if (nodes[i] == el) return true; } return false; } /** * Closest * * @param {Element} el * @param {String} selector * @param {Boolean} checkYourSelf (optional) */ function closest (element, selector, checkYourSelf) { var currentElem = checkYourSelf ? element : element.parentNode; while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) { if (matchesSelector(currentElem, selector)) { return currentElem; } currentElem = currentElem.parentNode; } return matchesSelector(currentElem, selector) ? currentElem : null; } var bind = window.addEventListener ? 'addEventListener' : 'attachEvent', unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent', prefix$6 = bind !== 'addEventListener' ? 'on' : ''; /** * Bind `el` event `type` to `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ var bind_1 = function(el, type, fn, capture){ el[bind](prefix$6 + type, fn, capture || false); return fn; }; /** * Unbind `el` event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ var unbind_1 = function(el, type, fn, capture){ el[unbind](prefix$6 + type, fn, capture || false); return fn; }; var componentEvent = { bind: bind_1, unbind: unbind_1 }; /** * Module dependencies. */ /** * Delegate event `type` to `selector` * and invoke `fn(e)`. A callback function * is returned which may be passed to `.unbind()`. * * @param {Element} el * @param {String} selector * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ // Some events don't bubble, so we want to bind to the capture phase instead // when delegating. var forceCaptureEvents = ['focus', 'blur']; function bind$1(el, selector, type, fn, capture) { if (forceCaptureEvents.indexOf(type) !== -1) { capture = true; } return componentEvent.bind(el, type, function (e) { var target = e.target || e.srcElement; e.delegateTarget = closest(target, selector, true); if (e.delegateTarget) { fn.call(el, e); } }, capture); } /** * Unbind event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @api public */ function unbind$1(el, type, fn, capture) { if (forceCaptureEvents.indexOf(type) !== -1) { capture = true; } return componentEvent.unbind(el, type, fn, capture); } var delegate = { bind: bind$1, unbind: unbind$1 }; /** * Expose `parse`. */ var domify = parse; /** * Tests for browser support. */ var innerHTMLBug = false; var bugTestDiv; if (typeof document !== 'undefined') { bugTestDiv = document.createElement('div'); // Setup bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>'; // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; bugTestDiv = undefined; } /** * Wrap map from jquery. */ var map = { legend: [1, '<fieldset>', '</fieldset>'], tr: [2, '<table><tbody>', '</tbody></table>'], col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], // for script/link/style tags to work in IE6-8, you have to wrap // in a div with a non-whitespace character in front, ha! _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', ''] }; map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>']; map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']; map.polyline = map.ellipse = map.polygon = map.circle = map.text = map.line = map.path = map.rect = map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']; /** * Parse `html` and return a DOM Node instance, which could be a TextNode, * HTML DOM Node of some kind (<div> for example), or a DocumentFragment * instance, depending on the contents of the `html` string. * * @param {String} html - HTML string to "domify" * @param {Document} doc - The `document` instance to create the Node for * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance * @api private */ function parse(html, doc) { if ('string' != typeof html) throw new TypeError('String expected'); // default to the global `document` object if (!doc) doc = document; // tag name var m = /<([\w:]+)/.exec(html); if (!m) return doc.createTextNode(html); html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace var tag = m[1]; // body support if (tag == 'body') { var el = doc.createElement('html'); el.innerHTML = html; return el.removeChild(el.lastChild); } // wrap map var wrap = map[tag] || map._default; var depth = wrap[0]; var prefix = wrap[1]; var suffix = wrap[2]; var el = doc.createElement('div'); el.innerHTML = prefix + html + suffix; while (depth--) el = el.lastChild; // one element if (el.firstChild == el.lastChild) { return el.removeChild(el.firstChild); } // several elements var fragment = doc.createDocumentFragment(); while (el.firstChild) { fragment.appendChild(el.removeChild(el.firstChild)); } return fragment; } function query(selector, el) { el = el || document; return el.querySelector(selector); } function all(selector, el) { el = el || document; return el.querySelectorAll(selector); } function remove$1(el) { el.parentNode && el.parentNode.removeChild(el); } /** * @param {<SVGElement>} element * @param {number} x * @param {number} y * @param {number} angle * @param {number} amount */ function transform(gfx, x, y, angle, amount) { var translate = createTransform(); translate.setTranslate(x, y); var rotate = createTransform(); rotate.setRotate(angle || 0, 0, 0); var scale = createTransform(); scale.setScale(amount || 1, amount || 1); transform$1(gfx, [ translate, rotate, scale ]); } /** * @param {SVGElement} element * @param {number} x * @param {number} y */ function translate$1(gfx, x, y) { var translate = createTransform(); translate.setTranslate(x, y); transform$1(gfx, translate); } /** * @param {SVGElement} element * @param {number} angle */ function rotate(gfx, angle) { var rotate = createTransform(); rotate.setRotate(angle, 0, 0); transform$1(gfx, rotate); } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var hat_1 = createCommonjsModule(function (module) { var hat = module.exports = function (bits, base) { if (!base) base = 16; if (bits === undefined) bits = 128; if (bits <= 0) return '0'; var digits = Math.log(Math.pow(2, bits)) / Math.log(base); for (var i = 2; digits === Infinity; i *= 2) { digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; } var rem = digits - Math.floor(digits); var res = ''; for (var i = 0; i < Math.floor(digits); i++) { var x = Math.floor(Math.random() * base).toString(base); res = x + res; } if (rem) { var b = Math.pow(base, rem); var x = Math.floor(Math.random() * b).toString(base); res = x + res; } var parsed = parseInt(res, base); if (parsed !== Infinity && parsed >= Math.pow(2, bits)) { return hat(bits, base) } else return res; }; hat.rack = function (bits, base, expandBy) { var fn = function (data) { var iters = 0; do { if (iters ++ > 10) { if (expandBy) bits += expandBy; else throw new Error('too many ID collisions, use more bits') } var id = hat(bits, base); } while (Object.hasOwnProperty.call(hats, id)); hats[id] = data; return id; }; var hats = fn.hats = {}; fn.get = function (id) { return fn.hats[id]; }; fn.set = function (id, value) { fn.hats[id] = value; return fn; }; fn.bits = bits || 128; fn.base = base || 16; return fn; }; }); /** * Create a new id generator / cache instance. * * You may optionally provide a seed that is used internally. * * @param {Seed} seed */ function Ids(seed) { if (!(this instanceof Ids)) { return new Ids(seed); } seed = seed || [128, 36, 1]; this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed; } /** * Generate a next id. * * @param {Object} [element] element to bind the id to * * @return {String} id */ Ids.prototype.next = function (element) { return this._seed(element || true); }; /** * Generate a next id with a given prefix. * * @param {Object} [element] element to bind the id to * * @return {String} id */ Ids.prototype.nextPrefixed = function (prefix, element) { var id; do { id = prefix + this.next(true); } while (this.assigned(id)); // claim {prefix}{random} this.claim(id, element); // return return id; }; /** * Manually claim an existing id. * * @param {String} id * @param {String} [element] element the id is claimed by */ Ids.prototype.claim = function (id, element) { this._seed.set(id, element || true); }; /** * Returns true if the given id has already been assigned. * * @param {String} id * @return {Boolean} */ Ids.prototype.assigned = function (id) { return this._seed.get(id) || false; }; /** * Unclaim an id. * * @param {String} id the id to unclaim */ Ids.prototype.unclaim = function (id) { delete this._seed.hats[id]; }; /** * Clear all claimed ids. */ Ids.prototype.clear = function () { var hats = this._seed.hats, id; for (id in hats) { this.unclaim(id); } }; var RENDERER_IDS = new Ids(); var TASK_BORDER_RADIUS = 10; var INNER_OUTER_DIST = 3; var DEFAULT_FILL_OPACITY = .95, HIGH_FILL_OPACITY = .35; var ELEMENT_LABEL_DISTANCE = 10; function BpmnRenderer( config, eventBus, styles, pathMap, canvas, textRenderer, priority) { BaseRenderer.call(this, eventBus, priority); var defaultFillColor = config && config.defaultFillColor, defaultStrokeColor = config && config.defaultStrokeColor, defaultLabelColor = config && config.defaultLabelColor; var rendererId = RENDERER_IDS.next(); 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 = create$1('marker'); attr$1(options.element, attrs); append(marker, options.element); attr$1(marker, { id: id, viewBox: '0 0 20 20', refX: ref.x, refY: ref.y, markerWidth: 20 * scale, markerHeight: 20 * scale, orient: 'auto' }); var defs = query('defs', canvas._svg); if (!defs) { defs = create$1('defs'); append(canvas._svg, defs); } append(defs, marker); markers[id] = marker; }