bpmn-js
Version:
A bpmn 2.0 toolkit and web modeler
2,362 lines (1,921 loc) • 583 kB
JavaScript
/*!
* 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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '\''
};
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}