UNPKG

bpmn-js

Version:

A bpmn 2.0 toolkit and web modeler

2,362 lines (1,921 loc) 583 kB
/*! * bpmn-js - bpmn-navigated-viewer v18.6.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: 2025-04-25 */ (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'; function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:false,writable:true,configurable:true}}));} /** * Flatten array, one level deep. * * @template T * * @param {T[][] | T[] | null} [arr] * * @return {T[]} */ const nativeToString$1 = Object.prototype.toString; const nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty; function isUndefined$2(obj) { return obj === undefined; } function isDefined(obj) { return obj !== undefined; } function isNil(obj) { return obj == null; } function isArray$2(obj) { return nativeToString$1.call(obj) === '[object Array]'; } function isObject(obj) { return nativeToString$1.call(obj) === '[object Object]'; } function isNumber(obj) { return nativeToString$1.call(obj) === '[object Number]'; } /** * @param {any} obj * * @return {boolean} */ function isFunction(obj) { const tag = nativeToString$1.call(obj); return ( tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]' ); } function isString(obj) { return nativeToString$1.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$1(target, key) { return nativeHasOwnProperty$1.call(target, key); } /** * @template T * @typedef { ( * ((e: T) => boolean) | * ((e: T, idx: number) => boolean) | * ((e: T, key: string) => boolean) | * string | * number * ) } Matcher */ /** * @template T * @template U * * @typedef { ( * ((e: T) => U) | string | number * ) } Extractor */ /** * @template T * @typedef { (val: T, key: any) => boolean } MatchFn */ /** * @template T * @typedef { T[] } ArrayCollection */ /** * @template T * @typedef { { [key: string]: T } } StringKeyValueCollection */ /** * @template T * @typedef { { [key: number]: T } } NumberKeyValueCollection */ /** * @template T * @typedef { StringKeyValueCollection<T> | NumberKeyValueCollection<T> } KeyValueCollection */ /** * @template T * @typedef { KeyValueCollection<T> | ArrayCollection<T> } Collection */ /** * Find element in collection. * * @template T * @param {Collection<T>} collection * @param {Matcher<T>} matcher * * @return {Object} */ function find(collection, matcher) { const matchFn = toMatcher(matcher); let match; forEach$1(collection, function(val, key) { if (matchFn(val, key)) { match = val; return false; } }); return match; } /** * Find element index in collection. * * @template T * @param {Collection<T>} collection * @param {Matcher<T>} matcher * * @return {number} */ function findIndex(collection, matcher) { const matchFn = toMatcher(matcher); let idx = isArray$2(collection) ? -1 : undefined; forEach$1(collection, function(val, key) { if (matchFn(val, key)) { idx = key; return false; } }); return idx; } /** * Filter elements in collection. * * @template T * @param {Collection<T>} collection * @param {Matcher<T>} matcher * * @return {T[]} result */ function filter(collection, matcher) { const matchFn = toMatcher(matcher); let result = []; forEach$1(collection, function(val, key) { if (matchFn(val, key)) { result.push(val); } }); return result; } /** * Iterate over collection; returning something * (non-undefined) will stop iteration. * * @template T * @param {Collection<T>} collection * @param { ((item: T, idx: number) => (boolean|void)) | ((item: T, key: string) => (boolean|void)) } iterator * * @return {T} return result that stopped the iteration */ function forEach$1(collection, iterator) { let val, result; if (isUndefined$2(collection)) { return; } const convertKey = isArray$2(collection) ? toNum$1 : identity$1; for (let key in collection) { if (has$1(collection, key)) { val = collection[key]; result = iterator(val, convertKey(key)); if (result === false) { return val; } } } } /** * Reduce collection, returning a single result. * * @template T * @template V * * @param {Collection<T>} collection * @param {(result: V, entry: T, index: any) => V} iterator * @param {V} result * * @return {V} result returned from last iterator */ function reduce(collection, iterator, result) { forEach$1(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); } /** * Transform a collection into another collection * by piping each member through the given fn. * * @param {Object|Array} collection * @param {Function} fn * * @return {Array} transformed collection */ function map$1(collection, fn) { let result = []; forEach$1(collection, function(val, key) { result.push(fn(val, key)); }); return result; } /** * Create an object pattern matcher. * * @example * * ```javascript * const matcher = matchPattern({ id: 1 }); * * let element = find(elements, matcher); * ``` * * @template T * * @param {T} pattern * * @return { (el: any) => boolean } matcherFn */ function matchPattern(pattern) { return function(el) { return every(pattern, function(val, key) { return el[key] === val; }); }; } /** * @template T * @param {Matcher<T>} matcher * * @return {MatchFn<T>} */ function toMatcher(matcher) { return isFunction(matcher) ? matcher : (e) => { return e === matcher; }; } function identity$1(arg) { return arg; } function toNum$1(arg) { return Number(arg); } /* global setTimeout clearTimeout */ /** * @typedef { { * (...args: any[]): any; * flush: () => void; * cancel: () => void; * } } DebouncedFunction */ /** * Debounce fn, calling it only once if the given time * elapsed between calls. * * Lodash-style the function exposes methods to `#clear` * and `#flush` to control internal behavior. * * @param {Function} fn * @param {Number} timeout * * @return {DebouncedFunction} debounced function */ function debounce(fn, timeout) { let timer; let lastArgs; let lastThis; let lastNow; function fire(force) { let now = Date.now(); let scheduledDiff = force ? 0 : (lastNow + timeout) - now; if (scheduledDiff > 0) { return schedule(scheduledDiff); } fn.apply(lastThis, lastArgs); clear(); } function schedule(timeout) { timer = setTimeout(fire, timeout); } function clear() { if (timer) { clearTimeout(timer); } timer = lastNow = lastArgs = lastThis = undefined; } function flush() { if (timer) { fire(true); } clear(); } /** * @type { DebouncedFunction } */ function callback(...args) { lastNow = Date.now(); lastArgs = args; lastThis = this; // ensure an execution is scheduled if (!timer) { schedule(timeout); } } callback.flush = flush; callback.cancel = clear; return callback; } /** * Bind function against target <this>. * * @param {Function} fn * @param {Object} target * * @return {Function} bound function */ function bind$2(fn, target) { return fn.bind(target); } /** * Convenience wrapper for `Object.assign`. * * @param {Object} target * @param {...Object} others * * @return {Object} the target */ function assign$1(target, ...others) { return Object.assign(target, ...others); } /** * Sets a nested property of a given object to the specified value. * * This mutates the object and returns it. * * @template T * * @param {T} target The target of the set operation. * @param {(string|number)[]} path The path to the nested value. * @param {any} value The value to set. * * @return {T} */ function set$1(target, path, value) { let currentTarget = target; forEach$1(path, function(key, idx) { if (typeof key !== 'number' && typeof key !== 'string') { throw new Error('illegal key type: ' + typeof key + '. Key should be of type number or string.'); } if (key === 'constructor') { throw new Error('illegal key: constructor'); } if (key === '__proto__') { throw new Error('illegal key: __proto__'); } let nextKey = path[idx + 1]; let nextTarget = currentTarget[key]; if (isDefined(nextKey) && isNil(nextTarget)) { nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : []; } if (isUndefined$2(nextKey)) { if (isUndefined$2(value)) { delete currentTarget[key]; } else { currentTarget[key] = value; } } else { currentTarget = nextTarget; } }); return target; } /** * Pick properties from the given target. * * @template T * @template {any[]} V * * @param {T} target * @param {V} properties * * @return Pick<T, V> */ function pick(target, properties) { let result = {}; let obj = Object(target); forEach$1(properties, function(prop) { if (prop in obj) { result[prop] = target[prop]; } }); return result; } /** * Pick all target properties, excluding the given ones. * * @template T * @template {any[]} V * * @param {T} target * @param {V} properties * * @return {Omit<T, V>} target */ function omit(target, properties) { let result = {}; let obj = Object(target); forEach$1(obj, function(prop, key) { if (properties.indexOf(key) === -1) { result[key] = prop; } }); return result; } var DEFAULT_RENDER_PRIORITY$1 = 1000; /** * @typedef {import('../core/Types').ElementLike} Element * @typedef {import('../core/Types').ConnectionLike} Connection * @typedef {import('../core/Types').ShapeLike} Shape * * @typedef {import('../core/EventBus').default} EventBus */ /** * 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); } } }); } /** * Checks whether an element can be rendered. * * @param {Element} element The element to be rendered. * * @return {boolean} Whether the element can be rendered. */ BaseRenderer.prototype.canRender = function(element) {}; /** * Draws a shape. * * @param {SVGElement} visuals The SVG element to draw the shape into. * @param {Shape} shape The shape to be drawn. * * @return {SVGElement} The SVG element of the shape drawn. */ BaseRenderer.prototype.drawShape = function(visuals, shape) {}; /** * Draws a connection. * * @param {SVGElement} visuals The SVG element to draw the connection into. * @param {Connection} connection The connection to be drawn. * * @return {SVGElement} The SVG element of the connection drawn. */ BaseRenderer.prototype.drawConnection = function(visuals, connection) {}; /** * Gets the SVG path of the graphical representation of a shape. * * @param {Shape} shape The shape. * * @return {string} The SVG path of the shape. */ BaseRenderer.prototype.getShapePath = function(shape) {}; /** * Gets the SVG path of the graphical representation of a connection. * * @param {Connection} connection The connection. * * @return {string} The SVG path of the connection. */ BaseRenderer.prototype.getConnectionPath = function(connection) {}; /** * @typedef { import('../model/Types').Element } Element * @typedef { import('../model/Types').ModdleElement } ModdleElement */ /** * Is an element of the given BPMN type? * * @param {Element|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 true if element has any of the given types. * * @param {Element|ModdleElement} element * @param {string[]} types * * @return {boolean} */ function isAny(element, types) { return some(types, function(t) { return is$1(element, t); }); } /** * Return the business object for a given element. * * @param {Element|ModdleElement} element * * @return {ModdleElement} */ function getBusinessObject(element) { return (element && element.businessObject) || element; } /** * Return the di object for a given element. * * @param {Element} element * * @return {ModdleElement} */ function getDi(element) { return element && element.di; } /** * @typedef {import('../model/Types').Element} Element * @typedef {import('../model/Types').ModdleElement} ModdleElement */ /** * @param {Element} element * @param {ModdleElement} [di] * * @return {boolean} */ function isExpanded(element, di) { if (is$1(element, 'bpmn:CallActivity')) { return false; } if (is$1(element, 'bpmn:SubProcess')) { di = di || getDi(element); if (di && is$1(di, 'bpmndi:BPMNPlane')) { return true; } return di && !!di.isExpanded; } if (is$1(element, 'bpmn:Participant')) { return !!getBusinessObject(element).processRef; } return true; } /** * @param {Element} element * * @return {boolean} */ function isHorizontal(element) { if (!is$1(element, 'bpmn:Participant') && !is$1(element, 'bpmn:Lane')) { return undefined; } var isHorizontal = getDi(element).isHorizontal; if (isHorizontal === undefined) { return true; } return isHorizontal; } /** * @param {Element} element * * @return {boolean} */ function isEventSubProcess(element) { return element && !!getBusinessObject(element).triggeredByEvent; } /** * Checks whether a value is an instance of Connection. * * @param {any} value * * @return {boolean} */ function isConnection(value) { return isObject(value) && has$1(value, 'waypoints'); } /** * @typedef {import('diagram-js/lib/util/Types').Point} Point * @typedef {import('diagram-js/lib/util/Types').Rect} Rect * * @typedef {import('../model/Types').Element} Element * @typedef {import('../model/Types').ModdleElement} ModdleElement */ var DEFAULT_LABEL_SIZE$1 = { width: 90, height: 20 }; var FLOW_LABEL_INDENT = 15; /** * Return true if the given semantic has an external label. * * @param {Element} semantic * * @return {boolean} */ function isLabelExternal(semantic) { return is$1(semantic, 'bpmn:Event') || is$1(semantic, 'bpmn:Gateway') || is$1(semantic, 'bpmn:DataStoreReference') || is$1(semantic, 'bpmn:DataObjectReference') || is$1(semantic, 'bpmn:DataInput') || is$1(semantic, 'bpmn:DataOutput') || is$1(semantic, 'bpmn:SequenceFlow') || is$1(semantic, 'bpmn:MessageFlow') || is$1(semantic, 'bpmn:Group'); } /** * Get the position of a sequence flow label. * * @param {Point[]} waypoints * * @return {Point} */ function getFlowLabelPosition(waypoints) { // get the waypoints mid var mid = waypoints.length / 2 - 1; var first = waypoints[Math.floor(mid)]; var second = waypoints[Math.ceil(mid + 0.01)]; // get position var position = getWaypointsMid(waypoints); // calculate angle var angle = Math.atan((second.y - first.y) / (second.x - first.x)); var x = position.x, y = position.y; if (Math.abs(angle) < Math.PI / 2) { y -= FLOW_LABEL_INDENT; } else { x += FLOW_LABEL_INDENT; } return { x: x, y: y }; } /** * Get the middle of a number of waypoints. * * @param {Point[]} waypoints * * @return {Point} */ function getWaypointsMid(waypoints) { var mid = waypoints.length / 2 - 1; var first = waypoints[Math.floor(mid)]; var second = waypoints[Math.ceil(mid + 0.01)]; return { x: first.x + (second.x - first.x) / 2, y: first.y + (second.y - first.y) / 2 }; } /** * Get the middle of the external label of an element. * * @param {Element} element * * @return {Point} */ function getExternalLabelMid(element) { if (element.waypoints) { return getFlowLabelPosition(element.waypoints); } else if (is$1(element, 'bpmn:Group')) { return { x: element.x + element.width / 2, y: element.y + DEFAULT_LABEL_SIZE$1.height / 2 }; } else { return { x: element.x + element.width / 2, y: element.y + element.height + DEFAULT_LABEL_SIZE$1.height / 2 }; } } /** * Return the bounds of an elements label, parsed from the elements DI or * generated from its bounds. * * @param {ModdleElement} di * @param {Element} element * * @return {Rect} */ function getExternalLabelBounds(di, element) { var mid, size, bounds, label = di.label; if (label && label.bounds) { bounds = label.bounds; size = { width: Math.max(DEFAULT_LABEL_SIZE$1.width, bounds.width), height: bounds.height }; mid = { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 }; } else { mid = getExternalLabelMid(element); size = DEFAULT_LABEL_SIZE$1; } return assign$1({ x: mid.x - size.width / 2, y: mid.y - size.height / 2 }, size); } /** * @param {ModdleElement} semantic * * @returns {string} */ 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'; } } /** * @param {ModdleElement} semantic * * @returns {string} */ function getCategoryValue(semantic) { var categoryValueRef = semantic['categoryValueRef']; if (!categoryValueRef) { return ''; } return categoryValueRef.value || ''; } /** * @param {Element} element * * @return {string} */ 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; } /** * Taken from https://github.com/component/classes * * Without the component bits. */ /** * toString reference. */ const toString$1 = Object.prototype.toString; /** * 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) { this.list.add(name); 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); } this.list.remove(name); return this; }; /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList$1.prototype.removeMatching = function(re) { const arr = this.array(); for (let 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) { 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; }; /** * Return an array of classes. * * @return {Array} * @api public */ ClassList$1.prototype.array = function() { return Array.from(this.list); }; /** * 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.contains(name); }; 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; name = name.trim(); 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 = null; function getNode() { if (node === null) { node = create$1('svg'); } return node; } 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 = getNode().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) { { return getNode().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; } function get(element) { var child = element.firstChild, output = []; while (child) { serialize(child, output); child = child.nextSibling; } return output.join(''); } function innerSVG(element, svg) { { return get(element); } } function remove$2(element) { var parent = element.parentNode; if (parent) { parent.removeChild(element); } return element; } /** * 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(); } /** * @typedef {(string|number)[]} Component * * @typedef {import('../util/Types').Point} Point */ /** * @param {Component[] | Component[][]} elements * * @return {string} */ function componentsToPath(elements) { return elements.flat().join(',').replace(/,?([A-Za-z]),?/g, '$1'); } /** * @param {Point} point * * @return {Component[]} */ function move(point) { return [ 'M', point.x, point.y ]; } /** * @param {Point} point * * @return {Component[]} */ function lineTo(point) { return [ 'L', point.x, point.y ]; } /** * @param {Point} p1 * @param {Point} p2 * @param {Point} p3 * * @return {Component[]} */ function curveTo(p1, p2, p3) { return [ 'C', p1.x, p1.y, p2.x, p2.y, p3.x, p3.y ]; } /** * @param {Point[]} waypoints * @param {number} [cornerRadius] * @return {Component[][]} */ function drawPath(waypoints, cornerRadius) { const pointCount = waypoints.length; const path = [ move(waypoints[0]) ]; for (let i = 1; i < pointCount; i++) { const pointBefore = waypoints[i - 1]; const point = waypoints[i]; const pointAfter = waypoints[i + 1]; if (!pointAfter || !cornerRadius) { path.push(lineTo(point)); continue; } const effectiveRadius = Math.min( cornerRadius, vectorLength(point.x - pointBefore.x, point.y - pointBefore.y), vectorLength(pointAfter.x - point.x, pointAfter.y - point.y) ); if (!effectiveRadius) { path.push(lineTo(point)); continue; } const beforePoint = getPointAtLength(point, pointBefore, effectiveRadius); const beforePoint2 = getPointAtLength(point, pointBefore, effectiveRadius * .5); const afterPoint = getPointAtLength(point, pointAfter, effectiveRadius); const afterPoint2 = getPointAtLength(point, pointAfter, effectiveRadius * .5); path.push(lineTo(beforePoint)); path.push(curveTo(beforePoint2, afterPoint2, afterPoint)); } return path; } function getPointAtLength(start, end, length) { const deltaX = end.x - start.x; const deltaY = end.y - start.y; const totalLength = vectorLength(deltaX, deltaY); const percent = length / totalLength; return { x: start.x + deltaX * percent, y: start.y + deltaY * percent }; } function vectorLength(x, y) { return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); } /** * @param {Point[]} points * @param {number|Object} [attrs] * @param {number} [radius] * * @return {SVGElement} */ function createLine(points, attrs, radius) { if (isNumber(attrs)) { radius = attrs; attrs = null; } if (!attrs) { attrs = {}; } const line = create$1('path', attrs); if (isNumber(radius)) { line.dataset.cornerRadius = String(radius); } return updateLine(line, points); } /** * @param {SVGElement} gfx * @param {Point[]} points * * @return {SVGElement} */ function updateLine(gfx, points) { const cornerRadius = parseInt(gfx.dataset.cornerRadius, 10) || 0; attr$1(gfx, { d: componentsToPath(drawPath(points, cornerRadius)) }); return gfx; } var black = 'hsl(225, 10%, 15%)'; var white = 'white'; // element utils ////////////////////// /** * Checks if eventDefinition of the given element matches with semantic type. * * @param {ModdleElement} event * @param {string} eventDefinitionType * * @return {boolean} */ function isTypedEvent(event, eventDefinitionType) { return some(event.eventDefinitions, function(definition) { return definition.$type === eventDefinitionType; }); } /** * Check if element is a throw event. * * @param {ModdleElement} event * * @return {boolean} */ function isThrowEvent(event) { return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); } /** * Check if element is a throw event. * * @param {ModdleElement} element * * @return {boolean} */ function isCollection(element) { var dataObject = element.dataObjectRef; return element.isCollection || (dataObject && dataObject.isCollection); } // color access ////////////////////// /** * @param {Element} element * @param {string} [defaultColor] * @param {string} [overrideColor] * * @return {string} */ function getFillColor(element, defaultColor, overrideColor) { var di = getDi(element); return overrideColor || di.get('color:background-color') || di.get('bioc:fill') || defaultColor || white; } /** * @param {Element} element * @param {string} [defaultColor] * @param {string} [overrideColor] * * @return {string} */ function getStrokeColor(element, defaultColor, overrideColor) { var di = getDi(element); return overrideColor || di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || black; } /** * @param {Element} element * @param {string} [defaultColor] * @param {string} [defaultStrokeColor] * @param {string} [overrideColor] * * @return {string} */ function getLabelColor(element, defaultColor, defaultStrokeColor, overrideColor) { var di = getDi(element), label = di.get('label'); return overrideColor || (label && label.get('color:color')) || defaultColor || getStrokeColor(element, defaultStrokeColor); } // cropping path customizations ////////////////////// /** * @param {ShapeLike} shape * * @return {string} path */ 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); } /** * @param {ShapeLike} shape * @param {number} [borderRadius] * * @return {string} path */ 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, -10, borderRadius ], [ 'l', borderRadius * 2 - width, 0 ], [ 'a', borderRadius, borderRadius, 0, 0, 1, -10, -10 ], [ 'l', 0, borderRadius * 2 - height ], [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -10 ], [ 'z' ] ]; return componentsToPath(roundRectPath); } /** * @param {ShapeLike} shape * * @return {string} path */ 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); } /** * @param {ShapeLike} shape * * @return {string} path */ 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); } /** * Get width and height from element or overrides. * * @param {Dimensions|Rect|ShapeLike} bounds * @param {Object} overrides * * @returns {Dimensions} */ function getBounds(bounds, overrides = {}) { return { width: getWidth(bounds, overrides), height: getHeight(bounds, overrides) }; } /** * Get width from element or overrides. * * @param {Dimensions|Rect|ShapeLike} bounds * @param {Object} overrides * * @returns {number} */ function getWidth(bounds, overrides = {}) { return has$1(overrides, 'width') ? overrides.width : bounds.width; } /** * Get height from element or overrides. * * @param {Dimensions|Rect|ShapeLike} bounds * @param {Object} overrides * * @returns {number} */ function getHeight(bounds, overrides = {}) { return has$1(overrides, 'height') ? overrides.height : bounds.height; } function _mergeNamespaces$1(n, m) { m.forEach(function (e) { e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) { if (k !== 'default' && !(k in n)) { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); }); return Object.freeze(n); } /** * Flatten array, one level deep. * * @template T * * @param {T[][] | T[] | null} [arr] * * @return {T[]} */ const nativeToString = Object.prototype.toString; const nativeHasOwnProperty = Object.prototype.hasOwnProperty; function isUndefined$1(obj) { return obj === undefined; } function isArray$1(obj) { return nativeToString.call(obj) === '[object Array]'; } /** * 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); } /** * Iterate over collection; returning something * (non-undefined) will stop iteration. * * @template T * @param {Collection<T>} collection * @param { ((item: T, idx: number) => (boolean|void)) | ((item: T, key: string) => (boolean|void)) } iterator * * @return {T} return result that stopped the iteration */ function forEach(collection, iterator) { let val, result; if (isUndefined$1(collection)) { return; } const convertKey = isArray$1(collection) ? toNum : identity; for (let key in collection) { if (has(collection, key)) { val = collection[key]; result = iterator(val, convertKey(key)); if (result === false) { return val; } } } } function identity(arg) { return arg; } function toNum(arg) { return Number(arg); } /** * Assigns style attributes in a style-src compliant way. * * @param {Element} element * @param {...Object} styleSources * * @return {Element} the element */ function assign(element, ...styleSources) { const target = element.style; forEach(styleSources, function(style) { if (!style) { return; } forEach(style, function(value, key) { target[key] = value; }); }); return element; } /** * 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; } /** * Taken from https://github.com/component/classes * * Without the component bits. */ /** * toString reference. */ const 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) { this.list.add(name); 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); } this.list.remove(name); return this; }; /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList.prototype.removeMatching = function(re) { const arr = this.array(); for (let 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) { 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; }; /** * Return an array of classes. * * @return {Array} * @api public */ ClassList.prototype.array = function() { return Array.from(this.list); }; /** * Check if class `name` is present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.has = ClassList.prototype.contains = function(name) { return this.list.contains(name); }; /** * Clear utility */ /** * Removes all children from the given element * * @param {Element} element * * @return {Element} the element (for chaining) */ function clear(element) { var child; while ((child = element.firstChild)) { element.removeChild(child); } return element; } /** * Closest * * @param {Element} el * @param {string} selector * @param {boolean} checkYourSelf (optional) */ function closest(element, selector, checkYourSelf) { var actualElement = checkYourSelf ? element : element.parentNode; return actualElement && typeof actualElement.closest === 'function' && actualElement.closest(selector) || null; } var componentEvent = {}; var bind$1, unbind$1, prefix$6; function detect () { bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent'; unbind$1 = window.removeEventListener ? 'removeEventListener' : 'detachEvent'; prefix$6 = bind$1 !== 'addEventListener' ? 'on' : ''; } /** * Bind `el` event `type` to `fn`. * * @param {Element}