bpmn-js-element-templates
Version:
Element templates for bpmn-js
17,928 lines • 497 kB
JavaScript
import { isNil, flatten, values, isObject, filter, isString, isUndefined as isUndefined$1, reduce, has, find, isArray, forEach, isFunction, without, assign, groupBy, isNumber, sortBy, keys, bind, set, pick, findIndex } from 'min-dash';
import { getBusinessObject, is, isAny } from 'bpmn-js/lib/util/ModelUtil';
import { v4 } from 'uuid';
import { satisfies, coerce, valid, validRange } from 'semver';
import semverCompare from 'semver-compare';
import { getSchemaVersion as getSchemaVersion$1, validate as validate$1, getZeebeSchemaVersion, getZeebeSchemaPackage, validateZeebe } from '@bpmn-io/element-templates-validator';
import Ids from 'ids';
import { isEventSubProcess } from 'bpmn-js/lib/util/DiUtil';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import translateModule from 'diagram-js/lib/i18n/translate';
import { useLayoutState, HeaderButton, ArrowIcon, CreateIcon, DropdownButton, TextAreaEntry, TextFieldEntry, FeelTextAreaEntry as FeelTextAreaEntry$1, FeelEntry as FeelEntry$1, SelectEntry, FeelCheckboxEntry, CheckboxEntry, FeelNumberEntry, NumberFieldEntry, Group, isFeelEntryEdited, isNumberFieldEntryEdited, isCheckboxEntryEdited, isSelectEntryEdited, isTextFieldEntryEdited, isTextAreaEntryEdited, usePrevious, ToggleSwitchEntry, ListGroup } from '@bpmn-io/properties-panel';
import classnames from 'classnames';
import { useService, CamundaPlatformPropertiesProviderModule } from 'bpmn-js-properties-panel';
import { jsxs, jsx, Fragment } from '@bpmn-io/properties-panel/preact/jsx-runtime';
import { createElement as createElement$1, h, Component } from '@bpmn-io/properties-panel/preact';
import { useCallback, useState, useEffect, useMemo } from '@bpmn-io/properties-panel/preact/hooks';
import { getVariablesForElement } from '@bpmn-io/extract-process-variables/zeebe';
import { query } from 'min-dom';
import { getLabel, setLabel } from 'bpmn-js/lib/features/label-editing/LabelUtil';
import { isPlane, getShapeIdFromPlane } from 'bpmn-js/lib/util/DrilldownUtil';
import StaticResolver from 'bpmnlint/lib/resolver/static-resolver';
import EventBus from 'diagram-js/lib/core/EventBus';
/**
* Check if the property is cast to FEEL expression:
* - Boolean and Number properties with feel set to 'optional' or 'static'
* - Boolean and Number input/output parameters have default feel=static
*
* @returns {boolean}
*/
const shouldCastToFeel = property => {
const feel = getFeelValue(property);
return ['optional', 'static'].includes(feel) && ['Boolean', 'Number'].includes(property.type);
};
const ALWAYS_CAST_TO_FEEL = ['zeebe:input', 'zeebe:output'];
function getFeelValue(property) {
if (ALWAYS_CAST_TO_FEEL.includes(property.binding.type)) {
return property.feel || 'static';
}
return property.feel;
}
const toFeelExpression = (value, type) => {
if (typeof value === 'string' && value.startsWith('=')) {
return value;
}
if (type === 'Boolean') {
value = value === 'false' ? false : value;
return '=' + !!value;
}
if (typeof value === 'undefined') {
return value;
}
return '=' + value.toString();
};
/**
* The BPMN 2.0 extension attribute name under
* which the element template ID is stored.
*
* @type {String}
*/
const TEMPLATE_ID_ATTR$1 = 'zeebe:modelerTemplate';
/**
* The BPMN 2.0 extension attribute name under
* which the element template version is stored.
*
* @type {String}
*/
const TEMPLATE_VERSION_ATTR$1 = 'zeebe:modelerTemplateVersion';
/**
* The BPMN 2.0 extension attribute name under
* which the element template icon is stored.
*
* @type {String}
*/
const TEMPLATE_ICON_ATTR = 'zeebe:modelerTemplateIcon';
/**
* Get template id for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateId$1(element) {
const businessObject = getBusinessObject(element);
if (businessObject) {
return businessObject.get(TEMPLATE_ID_ATTR$1);
}
}
/**
* Get template version for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateVersion$1(element) {
const businessObject = getBusinessObject(element);
if (businessObject) {
return businessObject.get(TEMPLATE_VERSION_ATTR$1);
}
}
/**
* Get template icon for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateIcon(element) {
const businessObject = getBusinessObject(element);
if (businessObject) {
return businessObject.get(TEMPLATE_ICON_ATTR);
}
}
/**
* Find extension with given type in
* BPMN element, diagram element or ExtensionElement.
*
* @param {ModdleElement|djs.model.Base} element
* @param {String} type
*
* @return {ModdleElement} the extension
*/
function findExtension$1(element, type) {
const businessObject = getBusinessObject(element);
let extensionElements;
if (is(businessObject, 'bpmn:ExtensionElements')) {
extensionElements = businessObject;
} else {
extensionElements = businessObject.get('extensionElements');
}
if (!extensionElements) {
return;
}
return extensionElements.get('values').find(value => {
return is(value, type);
});
}
function findZeebeProperty(zeebeProperties, binding) {
return zeebeProperties.get('properties').find(value => {
return value.name === binding.name;
});
}
function findInputParameter$1(ioMapping, binding) {
const parameters = ioMapping.get('inputParameters');
return parameters.find(parameter => {
return parameter.target === binding.name;
});
}
function findOutputParameter$1(ioMapping, binding) {
const parameters = ioMapping.get('outputParameters');
return parameters.find(parameter => {
return parameter.source === binding.source;
});
}
function findTaskHeader(taskHeaders, binding) {
const headers = taskHeaders.get('values');
return headers.find(header => {
return header.key === binding.key;
});
}
/**
* Find message referred to in an event, an event definition, or a task.
* @param {ModdleElement} businessObject
*/
function findMessage(businessObject) {
if (is(businessObject, 'bpmn:Event')) {
const eventDefinitions = businessObject.get('eventDefinitions');
if (!eventDefinitions || !eventDefinitions.length) {
return;
}
businessObject = eventDefinitions[0];
}
if (!businessObject) {
return;
}
return businessObject.get('messageRef');
}
/**
* Find signal referred to in an event or an event definition.
* @param {ModdleElement} businessObject
*/
function findSignal(businessObject) {
if (is(businessObject, 'bpmn:Event')) {
const eventDefinitions = businessObject.get('eventDefinitions');
if (!eventDefinitions || !eventDefinitions.length) {
return;
}
businessObject = eventDefinitions[0];
}
if (!businessObject) {
return;
}
return businessObject.get('signalRef');
}
/**
* Find timer event definition in an event.
* @param {ModdleElement|element} element
*/
function findTimerEventDefinition(element) {
const businessObject = getBusinessObject(element);
if (is(businessObject, 'bpmn:Event')) {
const eventDefinitions = businessObject.get('eventDefinitions');
if (!eventDefinitions.length) {
return;
}
return eventDefinitions.find(def => is(def, 'bpmn:TimerEventDefinition'));
}
}
/**
* Get the default value disregarding generated values.
*/
function getDefaultFixedValue(property) {
if (shouldCastToFeel(property) || property.feel === 'required') {
return toFeelExpression(property.value, property.type);
}
return property.value;
}
function getDefaultValue(property) {
const value = getDefaultFixedValue(property);
if (value !== undefined) {
return value;
}
if (property.generatedValue) {
const {
type
} = property.generatedValue;
if (type === 'uuid') {
return v4();
}
}
}
/**
* The BPMN 2.0 extension attribute name under
* which the element template ID is stored.
*
* @type {String}
*/
const TEMPLATE_ID_ATTR = 'camunda:modelerTemplate';
/**
* The BPMN 2.0 extension attribute name under
* which the element template version is stored.
*
* @type {String}
*/
const TEMPLATE_VERSION_ATTR = 'camunda:modelerTemplateVersion';
/**
* Returns incompatible engines for a given template.
*
* @param {Object} template
* @param {Object} checkEngines
*
* @return {Object}
*/
function getIncompatibleEngines(template, checkEngines) {
const templateEngines = template.engines;
return reduce(templateEngines, (result, _, engine) => {
if (!has(checkEngines, engine)) {
return result;
}
if (!satisfies(checkEngines[engine], templateEngines[engine])) {
result[engine] = {
actual: checkEngines[engine],
required: templateEngines[engine]
};
}
return result;
}, {});
}
/**
* Returns whether a template is compatible with the given engines.
*
* @param {Object} template
* @param {Object} checkEngines
*
* @return {boolean}
*/
function isCompatible(template, checkEngines) {
return !Object.keys(getIncompatibleEngines(template, checkEngines)).length;
}
/**
* Build a map of templates grouped by id.
*
* @param {Array<Object>} templates
* @param {Object} engines
*
* @return {Object}
*/
function buildTemplatesById(templates, engines) {
const templatesById = {};
templates.forEach(template => {
const id = template.id;
const version = isUndefined$1(template.version) ? '_' : template.version;
if (!templatesById[id]) {
templatesById[id] = {};
}
templatesById[id][version] = template;
const latest = templatesById[id].latest;
if (isCompatible(template, engines)) {
if (!latest || isUndefined$1(latest.version) || latest.version < version) {
templatesById[id].latest = template;
}
}
});
return templatesById;
}
/**
* Finds the list of templates that match the given criteria within a template index.
*
* @param {string|djs.model.Base} [elementOrTemplateId]
* @param {Object} templatesIndex
* @param {Object} [options]
* @param {boolean} [options.latest]
* @param {boolean} [options.deprecated]
*
* @return {Array<Object>}
*/
function findTemplates(elementOrTemplateId, templatesIndex, options = {}) {
const {
latest: includeLatestOnly,
deprecated: includeDeprecated
} = options;
const getVersions = template => {
const {
latest,
...versions
} = template;
return includeLatestOnly ? !includeDeprecated && latest && latest.deprecated ? [] : latest ? [latest] : [] : values(versions);
};
if (isNil(elementOrTemplateId)) {
return flatten(values(templatesIndex).map(getVersions));
}
if (isObject(elementOrTemplateId)) {
const element = elementOrTemplateId;
return filter(findTemplates(null, templatesIndex, options), function (template) {
return isAny(element, template.appliesTo);
}) || [];
}
if (isString(elementOrTemplateId)) {
return templatesIndex[elementOrTemplateId] && getVersions(templatesIndex[elementOrTemplateId]);
}
throw new Error('argument must be of type {string|djs.model.Base|undefined}');
}
/**
* Get template id for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateId(element) {
const businessObject = getBusinessObject(element);
if (businessObject) {
return businessObject.get(TEMPLATE_ID_ATTR);
}
}
/**
* Get template version for a given diagram element.
*
* @param {djs.model.Base} element
*
* @return {String}
*/
function getTemplateVersion(element) {
const businessObject = getBusinessObject(element);
if (businessObject) {
return businessObject.get(TEMPLATE_VERSION_ATTR);
}
}
/**
* Find extension with given type in
* BPMN element, diagram element or ExtensionElement.
*
* @param {ModdleElement|djs.model.Base} element
* @param {String} type
*
* @return {ModdleElement} the extension
*/
function findExtension(element, type) {
const businessObject = getBusinessObject(element);
let extensionElements;
if (is(businessObject, 'bpmn:ExtensionElements')) {
extensionElements = businessObject;
} else {
extensionElements = businessObject.get('extensionElements');
}
if (!extensionElements) {
return null;
}
return extensionElements.get('values').find(value => {
return is(value, type);
});
}
function findExtensions(element, types) {
const extensionElements = getExtensionElements(element);
if (!extensionElements) {
return [];
}
return extensionElements.get('values').filter(value => {
return isAny(value, types);
});
}
function findCamundaInOut(element, binding) {
const extensionElements = getExtensionElements(element);
if (!extensionElements) {
return;
}
const {
type
} = binding;
let matcher;
if (type === 'camunda:in') {
matcher = element => {
return is(element, 'camunda:In') && isInOut(element, binding);
};
} else if (type === 'camunda:out') {
matcher = element => {
return is(element, 'camunda:Out') && isInOut(element, binding);
};
} else if (type === 'camunda:in:businessKey') {
matcher = element => {
return is(element, 'camunda:In') && 'businessKey' in element;
};
}
return extensionElements.get('values').find(matcher);
}
function findCamundaProperty(camundaProperties, binding) {
return camundaProperties.get('values').find(value => {
return value.name === binding.name;
});
}
function findInputParameter(inputOutput, binding) {
const parameters = inputOutput.get('inputParameters');
return parameters.find(parameter => {
return parameter.name === binding.name;
});
}
function findOutputParameter(inputOutput, binding) {
const parameters = inputOutput.get('outputParameters');
return parameters.find(function (parameter) {
const {
value
} = parameter;
if (!binding.scriptFormat) {
return value === binding.source;
}
const definition = parameter.get('camunda:definition');
if (!definition || binding.scriptFormat !== definition.get('camunda:scriptFormat')) {
return false;
}
return definition.get('camunda:value') === binding.source;
});
}
function findCamundaErrorEventDefinition(element, errorRef) {
const errorEventDefinitions = findExtensions(element, ['camunda:ErrorEventDefinition']);
let error;
// error ID has to start with <Error_${ errorRef }_>
return errorEventDefinitions.find(definition => {
error = definition.get('bpmn:errorRef');
if (error) {
return error.get('bpmn:id').startsWith(`Error_${errorRef}`);
}
});
}
// helpers //////////
function getExtensionElements(element) {
const businessObject = getBusinessObject(element);
if (is(businessObject, 'bpmn:ExtensionElements')) {
return businessObject;
} else {
return businessObject.get('extensionElements');
}
}
function isInOut(element, binding) {
if (binding.type === 'camunda:in') {
// find based on target attribute
if (binding.target) {
return element.target === binding.target;
}
}
if (binding.type === 'camunda:out') {
// find based on source / sourceExpression
if (binding.source) {
return element.source === binding.source;
}
if (binding.sourceExpression) {
return element.sourceExpression === binding.sourceExpression;
}
}
// find based variables / local combination
if (binding.variables) {
return element.variables === 'all' && (binding.variables !== 'local' || element.local);
}
}
// eslint-disable-next-line no-undef
const packageVersion = "2.18.0";
/**
* @typedef {import('bpmn-js/lib/model/Types').Element} Element
*/
/**
* Registry for element templates.
*/
let ElementTemplates$1 = class ElementTemplates {
constructor(commandStack, eventBus, modeling, injector, config) {
this._commandStack = commandStack;
this._eventBus = eventBus;
this._injector = injector;
this._modeling = modeling;
this._templatesById = {};
this._templates = [];
config = config || {};
this._engines = this._coerceEngines(config.engines || {});
eventBus.on('elementTemplates.engines.changed', event => {
this.set(this._templates);
});
}
/**
* Get template with given ID and optional version or for element.
*
* @param {String|Element} elementOrTemplateId
* @param {number} [version]
*
* @return {ElementTemplate}
*/
get(elementOrTemplateId, version) {
const templates = this._templatesById;
let element;
if (isUndefined$1(elementOrTemplateId)) {
return null;
} else if (isString(elementOrTemplateId)) {
if (isUndefined$1(version)) {
version = '_';
}
if (templates[elementOrTemplateId] && templates[elementOrTemplateId][version]) {
return templates[elementOrTemplateId][version];
} else {
return null;
}
} else {
element = elementOrTemplateId;
return this.get(this._getTemplateId(element), this._getTemplateVersion(element));
}
}
/**
* Get default template for given element.
*
* @param {Element} element
*
* @return {ElementTemplate}
*/
getDefault(element) {
return find(this.getAll(element), function (template) {
return template.isDefault;
}) || null;
}
/**
* Get all templates (with given ID or applicable to element).
*
* @param {string|Element} [elementOrTemplateId]
* @return {Array<ElementTemplate>}
*/
getAll(elementOrTemplateId) {
return findTemplates(elementOrTemplateId, this._templatesById, {
deprecated: true
});
}
/**
* Get all templates (with given ID or applicable to element) with the latest
* version.
*
* @param {String|Element} [elementOrTemplateId]
* @param {{ deprecated?: boolean }} [options]
*
* @return {Array<ElementTemplate>}
*/
getLatest(elementOrTemplateId, options = {}) {
return findTemplates(elementOrTemplateId, this._templatesById, {
...options,
latest: true
});
}
/**
* Get templates compatible with a given engine configuration override.
*
* @param {string|Element} [elementOrTemplateId]
* @param {Object} enginesOverrides
* @param {Object} [options]
* @param {boolean} [options.deprecated=false]
* @param {boolean} [options.latest=true]
*
* @returns {Array<ElementTemplate>}
*/
getCompatible(elementOrTemplateId, enginesOverrides = {}, options = {}) {
const overridenEngines = this._coerceEngines({
...this._engines,
...enginesOverrides
});
const templatesById = buildTemplatesById(this._templates, overridenEngines);
return findTemplates(elementOrTemplateId, templatesById, {
latest: true,
...options
});
}
/**
* Set templates.
*
* @param {Array<ElementTemplate>} templates
*/
set(templates) {
this._templates = templates;
this._templatesById = buildTemplatesById(this._templates, this._engines);
this._fire('changed');
}
getEngines() {
return this._engines;
}
setEngines(engines) {
this._engines = this._coerceEngines(engines);
this._fire('engines.changed');
}
/**
* Ensures that only valid engines are kept around
*
* @param { Record<string, string> } engines
*
* @return { Record<string, string> } filtered, valid engines
*/
_coerceEngines(engines) {
// we provide <elementTemplates> engine with the current
// package version; templates may use that engine to declare
// compatibility with this library
engines = {
elementTemplates: packageVersion,
...engines
};
return reduce(engines, (validEngines, version, engine) => {
const coercedVersion = coerce(version);
if (!valid(coercedVersion)) {
console.error(new Error(`Engine <${engine}> specifies unparseable version <${version}>`));
return validEngines;
}
return {
...validEngines,
[engine]: coercedVersion.raw
};
}, {});
}
/**
* Check if template is compatible with currently set engine version.
*
* @param {ElementTemplate} template
*
* @return {boolean} - true if compatible or no engine is set for elementTemplates or template.
*/
isCompatible(template) {
return isCompatible(template, this._engines);
}
/**
* Get engines that are incompatible with the template.
*
* @param {any} template
*
* @return { Record<string, { required: string, found: string } } - incompatible engines along with their template and local versions
*/
getIncompatibleEngines(template) {
return getIncompatibleEngines(template, this._engines);
}
/**
* Get template versions for a given element or template ID.
*
* @param {object|string|null} id
* @param { { latest?: boolean, deprecated?: boolean } [options]
*
* @return {Array<ElementTemplate>}
*/
_getTemplateVerions(id, options = {}) {
return findTemplates(id, this._templatesById, options);
}
_getTemplateId(element) {
return getTemplateId(element);
}
_getTemplateVersion(element) {
return getTemplateVersion(element);
}
/**
* Apply element template to a given element.
*
* @param {Element} element
* @param {ElementTemplate} newTemplate
*
* @return {Element} the updated element
*/
applyTemplate(element, newTemplate) {
const oldTemplate = this.get(element);
const context = {
element,
newTemplate,
oldTemplate
};
const event = oldTemplate?.id === newTemplate?.id ? 'update' : 'apply';
this._commandStack.execute('propertiesPanel.camunda.changeTemplate', context);
this._fire(event, {
element,
newTemplate,
oldTemplate
});
return context.element;
}
_fire(action, payload) {
return this._eventBus.fire(`elementTemplates.${action}`, payload);
}
/**
* Remove template from a given element.
*
* @param {Element} element
*
* @return {Element} the updated element
*/
removeTemplate(element) {
const oldTemplate = this.get(element);
const context = {
element,
oldTemplate
};
this._commandStack.execute('propertiesPanel.removeTemplate', context);
this._fire('remove', {
element,
oldTemplate
});
return context.newElement;
}
/**
* Unlink template from a given element.
*
* @param {Element} element
*
* @return {Element} the updated element
*/
unlinkTemplate(element) {
const oldTemplate = this.get(element);
const context = {
element,
oldTemplate
};
this._commandStack.execute('propertiesPanel.unlinkTemplate', context);
this._fire('unlink', {
element,
oldTemplate
});
return context.element;
}
};
ElementTemplates$1.$inject = ['commandStack', 'eventBus', 'modeling', 'injector', 'config.elementTemplates'];
const PROPERTY_TYPE$1 = 'property';
const ZEBBE_PROPERTY_TYPE = 'zeebe:property';
const ZEBBE_INPUT_TYPE = 'zeebe:input';
const ZEEBE_OUTPUT_TYPE = 'zeebe:output';
const ZEEBE_PROPERTY_TYPE = 'zeebe:property';
const ZEEBE_TASK_DEFINITION_TYPE_TYPE = 'zeebe:taskDefinition:type';
const ZEEBE_TASK_DEFINITION = 'zeebe:taskDefinition';
const ZEEBE_TASK_HEADER_TYPE = 'zeebe:taskHeader';
const MESSAGE_PROPERTY_TYPE = 'bpmn:Message#property';
const MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE = 'bpmn:Message#zeebe:subscription#property';
const SIGNAL_PROPERTY_TYPE = 'bpmn:Signal#property';
const TIMER_EVENT_DEFINITION_PROPERTY_TYPE = 'bpmn:TimerEventDefinition#property';
const ZEEBE_CALLED_ELEMENT = 'zeebe:calledElement';
const ZEEBE_LINKED_RESOURCE_PROPERTY = 'zeebe:linkedResource';
const ZEEBE_USER_TASK = 'zeebe:userTask';
const ZEEBE_CALLED_DECISION = 'zeebe:calledDecision';
const ZEEBE_FORM_DEFINITION = 'zeebe:formDefinition';
const ZEEBE_SCRIPT_TASK = 'zeebe:script';
const ZEEBE_ASSIGNMENT_DEFINITION = 'zeebe:assignmentDefinition';
const ZEEBE_PRIORITY_DEFINITION = 'zeebe:priorityDefinition';
const ZEEBE_AD_HOC = 'zeebe:adHoc';
const ZEEBE_TASK_SCHEDULE = 'zeebe:taskSchedule';
const EXTENSION_BINDING_TYPES$1 = [MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE, ZEBBE_INPUT_TYPE, ZEEBE_OUTPUT_TYPE, ZEEBE_PROPERTY_TYPE, ZEEBE_TASK_DEFINITION_TYPE_TYPE, ZEEBE_TASK_DEFINITION, ZEEBE_TASK_HEADER_TYPE, ZEEBE_CALLED_ELEMENT, ZEEBE_LINKED_RESOURCE_PROPERTY, ZEEBE_CALLED_DECISION, ZEEBE_FORM_DEFINITION, ZEEBE_SCRIPT_TASK, ZEEBE_ASSIGNMENT_DEFINITION, ZEEBE_PRIORITY_DEFINITION, ZEEBE_AD_HOC, ZEEBE_TASK_SCHEDULE];
const TASK_DEFINITION_TYPES = [ZEEBE_TASK_DEFINITION_TYPE_TYPE, ZEEBE_TASK_DEFINITION];
const IO_BINDING_TYPES$1 = [ZEBBE_INPUT_TYPE, ZEEBE_OUTPUT_TYPE];
const MESSAGE_BINDING_TYPES = [MESSAGE_PROPERTY_TYPE, MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE];
const PROPERTY_BINDING_TYPES = [PROPERTY_TYPE$1, MESSAGE_PROPERTY_TYPE, SIGNAL_PROPERTY_TYPE];
/**
* Check whether a given timer expression type is supported for a given element.
*
* @param {string} type - 'timeDate', 'timeCycle', or 'timeDuration'
* @param {Element} element
* @return {boolean}
*/
function isTimerExpressionTypeSupported(type, element) {
const businessObject = getBusinessObject(element);
switch (type) {
case 'timeDate':
return isAny(element, ['bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:StartEvent']);
case 'timeCycle':
if (is(element, 'bpmn:StartEvent') && (!hasParentEventSubProcess(businessObject) || !isInterrupting(businessObject))) {
return true;
}
if (is(element, 'bpmn:BoundaryEvent') && !isInterrupting(businessObject)) {
return true;
}
return false;
case 'timeDuration':
if (isAny(element, ['bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent'])) {
return true;
}
if (is(element, 'bpmn:StartEvent') && hasParentEventSubProcess(businessObject)) {
return true;
}
return false;
default:
return false;
}
}
/**
* Check if the template is a timer template and if so, whether it is applicable
*
* @param {Object} template
* @param {Element} element
* @return {boolean}
*/
function isTimerTemplateApplicable(template, element) {
// Find timer binding in template
const timerBinding = template.properties?.find(property => {
return property.binding?.type === TIMER_EVENT_DEFINITION_PROPERTY_TYPE;
});
// No timer binding - template is applicable
if (!timerBinding) {
return true;
}
const timerType = timerBinding.binding.name;
// Check if the timer type can be applied (possibly with auto-conversion)
return canTimerTypeBeAppliedWithConversion(timerType, element);
}
/**
* Check if a timer type can be applied to an element, possibly with auto-conversion.
*
* Only blocks truly impossible combinations:
* - timeDuration on process-level start events (can't be converted)
*
* @param {string} type - 'timeDate', 'timeCycle', or 'timeDuration'
* @param {Element} element
* @return {boolean}
*/
function canTimerTypeBeAppliedWithConversion(type, element) {
const businessObject = getBusinessObject(element);
switch (type) {
case 'timeDate':
return isAny(element, ['bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:StartEvent']);
case 'timeCycle':
if (isAny(element, ['bpmn:StartEvent', 'bpmn:BoundaryEvent'])) {
return true;
}
return false;
case 'timeDuration':
if (isAny(element, ['bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent'])) {
return true;
}
// timeDuration can only be applied to start events in event subprocesses
// Process-level start events cannot support timeDuration (no conversion possible)
if (is(element, 'bpmn:StartEvent') && hasParentEventSubProcess(businessObject)) {
return true;
}
return false;
default:
return false;
}
}
// helpers //////////
function isInterrupting(businessObject) {
if (is(businessObject, 'bpmn:BoundaryEvent')) {
return businessObject.get('cancelActivity') !== false;
}
return businessObject.get('isInterrupting') !== false;
}
function hasParentEventSubProcess(businessObject) {
const parent = businessObject.$parent;
return parent && is(parent, 'bpmn:SubProcess') && parent.get('triggeredByEvent');
}
/**
* @typedef {import('bpmn-js/lib/model/Types').Element} Element
*/
/**
* Filters to determine whether a template is applicable to a given element.
* @type {Array<(template: ElementTemplate, element: Element) => boolean>}
*/
const TEMPLATE_FILTERS = [isTimerTemplateApplicable];
/**
* Registry for element templates.
*/
class ElementTemplates extends ElementTemplates$1 {
constructor(templateElementFactory, commandStack, eventBus, modeling, injector, config) {
super(commandStack, eventBus, modeling, injector, config);
this._templateElementFactory = templateElementFactory;
}
_getTemplateId(element) {
return getTemplateId$1(element);
}
_getTemplateVersion(element) {
return getTemplateVersion$1(element);
}
/**
* Get all templates (with given ID or applicable to element).
*
* @param {string|djs.model.Base} [elementOrTemplateId]
* @return {Array<ElementTemplate>}
*/
getAll(elementOrTemplateId) {
const templates = super.getAll(elementOrTemplateId);
if (isObject(elementOrTemplateId)) {
return this._filterApplicableTemplates(templates, elementOrTemplateId);
}
return templates;
}
/**
* Get all templates (with given ID or applicable to element) with the latest version.
*
* @param {String|djs.model.Base} [elementOrTemplateId]
* @param {{ deprecated?: boolean }} [options]
*
* @return {Array<ElementTemplate>}
*/
getLatest(elementOrTemplateId, options = {}) {
const templates = super.getLatest(elementOrTemplateId, options);
if (isObject(elementOrTemplateId)) {
return this._filterApplicableTemplates(templates, elementOrTemplateId);
}
return templates;
}
/**
* Get compatible templates for element with optional engine overrides.
*
* @param {String|djs.model.Base} [elementOrTemplateId]
* @param {Object} [enginesOverrides]
* @param {Object} [options]
*
* @return {Array<ElementTemplate>}
*/
getCompatible(elementOrTemplateId, enginesOverrides = {}, options = {}) {
const templates = super.getCompatible(elementOrTemplateId, enginesOverrides, options);
if (isObject(elementOrTemplateId)) {
return this._filterApplicableTemplates(templates, elementOrTemplateId);
}
return templates;
}
_filterApplicableTemplates(templates, element) {
return TEMPLATE_FILTERS.reduce((filteredTemplates, filterFn) => filteredTemplates.filter(template => filterFn(template, element)), templates);
}
/**
* Create an element based on an element template. This is, for example,
* called from the create-append anything menu.
*
* @param {ElementTemplate} template
* @returns {djs.model.Base}
*/
createElement(template) {
if (!template) {
throw new Error('template is missing');
}
const element = this._templateElementFactory.create(template);
return element;
}
/**
* Apply element template to a given element.
*
* @param {Element} element
* @param {ElementTemplate} newTemplate
*
* @return {Element} the updated element
*/
applyTemplate(element, newTemplate) {
const oldTemplate = this.get(element);
const context = {
element,
newTemplate,
oldTemplate
};
const event = oldTemplate?.id === newTemplate?.id ? 'update' : 'apply';
this._commandStack.execute('propertiesPanel.zeebe.changeTemplate', context);
this._fire(event, {
element,
newTemplate,
oldTemplate
});
return context.element;
}
/**
* Remove template from a given element.
*
* @param {Element} element
*
* @return {Element} the updated element
*/
removeTemplate(element) {
const oldTemplate = this.get(element);
const context = {
element,
oldTemplate
};
this._commandStack.execute('propertiesPanel.removeTemplate', context);
this._fire('remove', {
element,
oldTemplate
});
return context.element;
}
}
ElementTemplates.$inject = ['templateElementFactory', 'commandStack', 'eventBus', 'modeling', 'injector', 'config.elementTemplates'];
const SUPPORTED_SCHEMA_VERSION$1 = getSchemaVersion$1();
const MORPHABLE_TYPES = ['bpmn:Activity', 'bpmn:Event', 'bpmn:Gateway'];
/**
* A element template validator.
*/
let Validator$1 = class Validator {
constructor(moddle) {
this._templatesById = {};
this._validTemplates = [];
this._errors = [];
this._moddle = moddle;
}
/**
* Adds the templates.
*
* @param {Array<TemplateDescriptor>} templates
*
* @return {Validator}
*/
addAll(templates) {
if (!isArray(templates)) {
this._logError('templates must be []');
} else {
templates.forEach(this.add, this);
}
return this;
}
/**
* Add the given element template, if it is valid.
*
* @param {TemplateDescriptor} template
*
* @return {Validator}
*/
add(template) {
const err = this._validateTemplate(template);
let id, version;
if (!err) {
id = template.id;
version = template.version || '_';
if (!this._templatesById[id]) {
this._templatesById[id] = {};
}
this._templatesById[id][version] = template;
this._validTemplates.push(template);
}
return this;
}
/**
* Validate given template and return error (if any).
*
* @param {TemplateDescriptor} template
*
* @return {Error} validation error, if any
*/
_validateTemplate(template) {
const id = template.id,
version = template.version || '_',
schemaVersion = template.$schema && getSchemaVersion(template.$schema);
// (1) compatibility
if (schemaVersion && semverCompare(SUPPORTED_SCHEMA_VERSION$1, schemaVersion) < 0) {
return this._logError(`unsupported element template schema version <${schemaVersion}>. Your installation only supports up to version <${SUPPORTED_SCHEMA_VERSION$1}>. Please update your installation`, template);
}
// (2) versioning
if (this._templatesById[id] && this._templatesById[id][version]) {
if (version === '_') {
return this._logError(`template id <${id}> already used`, template);
} else {
return this._logError(`template id <${id}> and version <${version}> already used`, template);
}
}
// (3) elementType validation
const elementTypeError = this._validateElementType(template);
if (elementTypeError) {
return elementTypeError;
}
// (4) JSON schema compliance
const schemaValidationResult = validate$1(template);
const {
errors: schemaErrors,
valid
} = schemaValidationResult;
if (!valid) {
filteredSchemaErrors(schemaErrors).forEach(error => {
this._logError(error.message, template);
});
return new Error('invalid template');
}
// (5) engines validation
const enginesError = this._validateEngines(template);
if (enginesError) {
return enginesError;
}
return null;
}
_validateEngines(template) {
let err;
forEach(template.engines, (rangeStr, engine) => {
if (!validRange(rangeStr)) {
err = this._logError(new Error(`Engine <${engine}> specifies invalid semver range <${rangeStr}>`), template);
}
});
return err;
}
/**
* Validate elementType for given template and return error (if any).
*
* @param {TemplateDescriptor} template
*
* @return {Error} validation error, if any
*/
_validateElementType(template) {
if (template.elementType && template.appliesTo) {
const elementType = template.elementType.value,
appliesTo = template.appliesTo;
// (3.1) template can be applied to elementType
// prevents cases where the elementType is not part of appliesTo
if (!appliesTo.find(type => this._isType(elementType, type))) {
return this._logError(`template does not apply to requested element type <${elementType}>`, template);
}
// (3.2) template only applies to same type of element
// prevent elementTemplates to morph into incompatible types, e.g. Task -> SequenceFlow
for (const sourceType of appliesTo) {
if (!this._canMorph(sourceType, elementType)) {
return this._logError(`can not morph <${sourceType}> into <${elementType}>`, template);
}
}
}
}
/**
* Check if given type is a subtype of given base type.
*
* @param {String} type
* @param {String} baseType
* @returns {Boolean}
*/
_isType(type, baseType) {
const moddleType = this._moddle.getType(type);
return moddleType && baseType in this._moddle.getElementDescriptor(moddleType).allTypesByName;
}
/**
* Checks if a given type can be morphed into another type.
*
* @param {String} sourceType
* @param {String} targetType
* @returns {Boolean}
*/
_canMorph(sourceType, targetType) {
if (sourceType === targetType) {
return true;
}
const baseType = MORPHABLE_TYPES.find(type => this._isType(sourceType, type));
if (!baseType) {
return false;
}
return this._isType(targetType, baseType);
}
/**
* Log an error for the given template
*
* @param {(String|Error)} err
* @param {TemplateDescriptor} template
*
* @return {Error} logged validation errors
*/
_logError(err, template) {
if (isString(err)) {
if (template) {
const {
id,
name
} = template;
err = `template(id: <${id}>, name: <${name}>): ${err}`;
}
err = new Error(err);
}
this._errors.push(err);
return err;
}
getErrors() {
return this._errors;
}
getValidTemplates() {
return this._validTemplates;
}
};
// helpers //////////
/**
* Extract schema version from schema URI
*
* @param {String} schemaUri - for example https://unpkg.com/@camunda/element-templates-json-schema@99.99.99/resources/schema.json
*
* @return {String} for example '99.99.99'
*/
function getSchemaVersion(schemaUri) {
const re = /\d+\.\d+\.\d+/g;
const match = schemaUri.match(re);
return match === null ? undefined : match[0];
}
/**
* Extract only relevant errors of the validation result.
*
* The JSON Schema we use under the hood produces more errors than we need for a
* detected schema violation (for example, unmatched sub-schemas, if-then-rules,
* `oneOf`-definitions ...).
*
* We call these errors "relevant" that have a custom error message defined by us OR
* are basic data type errors.
*
* @param {Array} schemaErrors
*
* @return {Array}
*/
function filteredSchemaErrors(schemaErrors) {
return filter(schemaErrors, err => {
const {
instancePath,
keyword
} = err;
// (1) regular errors are customized from the schema
if (keyword === 'errorMessage') {
return true;
}
// (2) data type errors
// ignore type errors nested in scopes
if (keyword === 'type' && instancePath && !instancePath.startsWith('/scopes/')) {
return true;
}
return false;
});
}
const SUPPORTED_SCHEMA_VERSION = getZeebeSchemaVersion();
const SUPPORTED_SCHEMA_PACKAGE = getZeebeSchemaPackage();
/**
* A Camunda Cloud element template validator.
*/
class Validator extends Validator$1 {
constructor(moddle) {
super(moddle);
}
/**
* Validate given template and return error (if any).
*
* @param {TemplateDescriptor} template
*
* @return {Error} validation error, if any
*/
_validateTemplate(template) {
const id = template.id,
version = template.version || '_',
schema = template.$schema,
schemaVersion = schema && getSchemaVersion(schema);
// (1) $schema attribute defined
if (!schema) {
return this._logError('missing $schema attribute.', template);
}
if (!this.isSchemaValid(schema)) {
return this._logError(`unsupported $schema attribute <${schema}>.`, template);
}
// (2) compatibility
if (schemaVersion && semverCompare(SUPPORTED_SCHEMA_VERSION, schemaVersion) < 0) {
return this._logError(`unsupported element template schema version <${schemaVersion}>. Your installation only supports up to version <${SUPPORTED_SCHEMA_VERSION}>. Please update your installation`, template);
}
// (3) versioning
if (this._templatesById[id] && this._templatesById[id][version]) {
if (version === '_') {
return this._logError(`template id <${id}> already used`, template);
} else {
return this._logError(`template id <${id}> and version <${version}> already used`, template);
}
}
// (4) elementType validation
const elementTypeError = this._validateElementType(template);
if (elementTypeError) {
return elementTypeError;
}
// (5) JSON schema compliance
const schemaValidationResult = validateZeebe(template);
const {
errors: schemaErrors,
valid
} = schemaValidationResult;
if (!valid) {
filteredSchemaErrors(schemaErrors).forEach(error => {
this._logError(error.message, template);
});
return new Error('invalid template');
}
// (6) engines validation
const enginesError = this._validateEngines(template);
if (enginesError) {
return enginesError;
}
return null;
}
isSchemaValid(schema) {
return schema && schema.includes(SUPPORTED_SCHEMA_PACKAGE);
}
_validateEngines(template) {
let err;
forEach(template.engines, (rangeStr, engine) => {
if (!validRange(rangeStr)) {
err = this._logError(new Error(`Engine <${engine}> specifies invalid semver range <${rangeStr}>`), template);
}
});
return err;
}
}
/**
* The guy responsible for template loading.
*
* Provide the actual templates via the `config.elementTemplates`.
*
* That configuration can either be an array of template
* descriptors or a node style callback to retrieve
* the templates asynchronously.
*
* @param {Array<TemplateDescriptor>|Function} config
* @param {EventBus} eventBus
* @param {ElementTemplates} elementTemplates
* @param {Moddle} moddle
*/
let ElementTemplatesLoader$1 = class ElementTemplatesLoader {
constructor(config, eventBus, elementTemplates, moddle) {
this._loadTemplates;
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
this._moddle = moddle;
if (isArray(config) || isFunction(config)) {
this._loadTemplates = config;
}
if (config && config.loadTemplates) {
this._loadTemplates = config.loadTemplates;
}
eventBus.on('diagram.init', () => {
this.reload();
});
}
reload() {
const loadTemplates = this._loadTemplates;
// no templates specified
if (isUndefined$1(loadTemplates)) {
return;
}
// template loader function specified
if (isFunction(loadTemplates)) {
return loadTemplates((err, templates) => {
if (err) {
return this._templateErrors([err]);
}
this.setTemplates(templates);
});
}
// templates array specified
if (loadTemplates.length) {
return this.setTemplates(loadTemplates);
}
}
setTemplates(templates) {
const elementTemplates = this._elementTemplates,
moddle = this._moddle;
const validator = new Validator$1(moddle).addAll(templates);
const errors = validator.getErrors(),
validTemplates = validator.getValidTemplates();
elementTemplates.set(validTemplates);
if (errors.length) {
this._templateErrors(errors);
}
}
_templateErrors(errors) {
this._elementTemplates._fire('errors', {
errors: errors
});
}
};
ElementTemplatesLoader$1.$inject = ['config.elementTemplates', 'eventBus', 'elementTemplates', 'moddle'];
/**
* @param {Object|Array<TemplateDescriptor>|Function} config
* @param {EventBus} eventBus
* @param {ElementTemplates} elementTemplates
* @param {Moddle} moddle
*/
class ElementTemplatesLoader extends ElementTemplatesLoader$1 {
constructor(config, eventBus, elementTemplates, moddle) {
super(config, eventBus, elementTemplates, moddle);
this._elementTemplates = elementTemplates;
}
setTemplates(templates) {
const elementTemplates = this._elementTemplates,
moddle = this._moddle;
const validator = new Validator(moddle).addAll(templates);
const errors = validator.getErrors(),
validTemplates = validator.getValidTemplates();
elementTemplates.set(validTemplates);
if (errors.length) {
this._templateErrors(errors);
}
}
}
ElementTemplatesLoader.$inject = ['config.elementTemplates', 'eventBus', 'elementTemplates', 'moddle'];
/**
* Create a new element and set its parent.
*
* @param {String} elementType of the new element
* @param {Object} properties of the new element in key-value pairs
* @param {moddle.object} parent of the new element
* @param {BpmnFactory} factory which creates the new element
*
* @returns {djs.model.Base} element which is created
*/
function createElement(elementType, properties, parent, factory) {
const element = factory.create(elementType, properties);
if (parent) {
element.$parent = parent;
}
return element;
}
/**
* generate a semantic id with given prefix
*/
function nextId(prefix) {
const ids = new Ids([32, 32, 1]);
return ids.nextPrefixed(prefix);
}
function getRoot(businessObject) {
let parent = businessObject;
while (parent.$parent) {
parent = parent.$parent;
}
return parent;
}
function filterElementsByType(objectList, type) {
const list = objectList || [];
return list.filter(element => is(element, type));
}
function findRootElementsByType(businessObject, referencedType) {
const root = getRoot(businessObject);
return filterElementsByType(root.get('rootElements'), referencedType);
}
function findRootElementById(businessObject, type, id) {
const elements = findRootElementsByType(businessObject, type);
return elements.find(element => element.id === id);
}
/**
* Check if element type is a subprocess (either regular or AdHoc).
*
* @param {string} elementType
* @returns {boolean}
*/
function isSubprocess(elementType) {
return elementType === 'bpmn:SubProcess' || elementType === 'bpmn:AdHocSubProcess' || elementType === 'bpmn:Transaction';
}
/**
* Create an input parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createInputParameter$1(binding, value, bpmnFactory) {
const {
name
} = binding;
return bpmnFactory.create('zeebe:Input', {
source: value,
target: name
});
}
/**
* Create an output parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createOutputParameter$1(binding, value, bpmnFactory) {
const {
source
} = binding;
return bpmnFactory.create('zeebe:Output', {
source,
target: value
});
}
/**
* Create a task header representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createTaskHeader(binding, value, bpmnFactory) {
const {
key
} = binding;
return bpmnFactory.create('zeebe:Header', {
key,
value
});
}
/**
* Create a task definition representing the given value.
*
* @param {object} attrs
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createTaskDefinition(attrs = {}, bpmnFactory) {
return bpmnFactory.create('zeebe:TaskDefinition', attrs);
}
/**
* Create zeebe:Property from the given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createZeebeProperty(binding, value = '', bpmnFactory) {
const {
name
} = binding;
return bpmnFactory.create('zeebe:Property', {
name,
value
});
}
/**
* Retrieves whether an element should be updated for a given property.
*
* That matches once
* a) the property value is not empty, or
* b) the property is not optional
*
* @param {String} value
* @param {Object} property
* @returns {Boolean}
*/
function shouldUpdate(value, property) {
const {
optional
} = property;
return value || !optional;
}
/**
* Gets or, in case not existent, creates extension element for given element.
*
* @param {djs.model.Base} element
* @param {String} type
* @param {BpmnFactory} bpmnFactory
* @returns {ModdleElement}
*/
function ensureExtension(element, type, bpmnFactory) {
const businessObject = getBusinessObject(element);
let extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = createElement('bpmn:ExtensionElements', {}, businessObject, bpmnFactory);
businessObject.set('extensionElements', extensionElements);
}
let extension = findExtension$1(extensionElements, type);
if (!extension) {
extension = bpmnFactory.create(type);
extension.$parent = extensionElements;
extensionElements.get('values').push(extension);
}
return extension;
}
function getTaskDefinitionPropertyName(binding) {
return binding.type === ZEEBE_TASK_DEFINITION_TYPE_TYPE ? 'type' : binding.property;
}
function removeRootElement(rootElement, injector) {
const modeling = injector.get('modeling'),
canvas = injector.get('canvas'),
bpmnjs = injector.get('bpmnjs');
const element = canvas.getRootElement(),
definitions = bpmnjs.getDefinitions(),
rootElements = definitions.get('rootElements');
const newRootElements = rootElements.filter(e => e !== rootElement);
// short-circuit to prevent unnecessary updates
if (newRootElements.length === rootElements.length) {
return;
}
modeling.updateModdleProperties(element, definitions, {
rootElements: newRootElements
});
}
/**
* Remove message from element and the diagram.
*
* @param {import('bpmn-js/lib/model/Types').Element} element
* @param {import('didi').Injector} injector
*/
function removeMessage(element, injector) {
const modeling = injector.get('modeling');
const bo = getReferringElement(element);
// Event does not have an event definition
if (!bo) {
return;
}
const message = findMessage(bo);
if (!message) {
return;
}
modeling.updateModdleProperties(element, bo, {
messageRef: undefined
});
removeRootElement(message, injector);
}
/**
* Remove signal from element and the diagram.
*
* @param {import('bpmn-js/lib/model/Types').Element} element
* @param {import('didi').Injector} injector
*/
function removeSignal(element, injector) {
const modeling = injector.get('modeling');
const bo = getReferringElement(element);
// Event does not have an event definition
if (!bo) {
return;
}
const signal = findSignal(bo);
if (!signal) {
return;
}
modeling.updateModdleProperties(element, bo, {
signalRef: undefined
});
removeRootElement(signal, injector);
}
function getReferringElement(element) {
const bo = getBusinessObject(element);
if (is(bo, 'bpmn:Event')) {
return bo.get('eventDefinitions')[0];
}
return bo;
}
const EXPRESSION_TYPES$1 = ['bpmn:Expression', 'bpmn:FormalExpression'];
function isExpression(element, propertyName) {
const bo = getBusinessObject(element);
const descriptor = bo.$descriptor.propertiesByName[propertyName];
return descriptor && EXPRESSION_TYPES$1.includes(descriptor.type);
}
function createExpression(value, parent, bpmnFactory) {
const expression = createElement('bpmn:FormalExpression', {
body: value
}, parent, bpmnFactory);
return expression;
}
function getExpressionValue(element, propertyName) {
const bo = getBusinessObject(element);
const expression = bo.get(propertyName);
return expression?.get('body') || undefined;
}
/**
* @typedef {import('bpmn-js/lib/model/Types').Element} Element
*/
/**
* Applies an element template to an element. Sets `zeebe:modelerTemplate` and
* `zeebe:modelerTemplateVersion`.
*/
let ChangeElementTemplateHandler$1 = class ChangeElementTemplateHandler {
constructor(bpmnFactory, bpmnReplace, commandStack, modeling, moddleCopy, injector) {
this._bpmnFactory = bpmnFactory;
this._bpmnReplace = bpmnReplace;
this._modeling = modeling;
this._moddleCopy = moddleCopy;
this._commandStack = commandStack;
this._injector = injector;
}
/**
* Change element template. If new template is specified, apply it. If old
* template is specified, too, update from old to new. If only old template is
* specified, remove it. Optionally, remove old template properties.
*
* @param {Object} context
* @param {Element} context.element
* @param {Object} [context.oldTemplate]
* @param {Object} [context.newTemplate]
* @param {boolean} [context.removeProperties=false]
*/
preExecute(context) {
const {
newTemplate,
oldTemplate,
removeProperties = false
} = context;
let element = context.element;
// update zeebe:modelerTemplate attribute
this._updateZeebeModelerTemplate(element, newTemplate);
// update zeebe:modelerTemplateIcon
this._updateZeebeModelerTemplateIcon(element, newTemplate);
// update element type
element = context.element = this._updateElementType(element, oldTemplate, newTemplate);
if (!newTemplate && !removeProperties) {
return;
}
// update properties
this._updateProperties(element, oldTemplate, newTemplate);
// update zeebe:TaskDefinition
this._updateZeebeTaskDefinition(element, oldTemplate, newTemplate);
// update zeebe:Input and zeebe:Output properties
this._updateZeebeInputOutputParameterProperties(element, oldTemplate, newTemplate);
// update zeebe:Header properties
this._updateZeebeTaskHeaderProperties(element, oldTemplate, newTemplate);
// update zeebe:Property properties
this._updateZeebePropertyProperties(element, oldTemplate, newTemplate);
this._updateMessage(element, oldTemplate, newTemplate);
this._updateSignal(element, oldTemplate, newTemplate);
this._updateTimerEventDefinition(element, oldTemplate, newTemplate);
this._updateZeebeModelerTemplateOnReferencedElement(element, oldTemplate, newTemplate);
this._updateCalledElement(element, oldTemplate, newTemplate);
this._updateLinkedResources(element, oldTemplate, newTemplate);
this._updateZeebeUserTask(element, newTemplate);
this._updateCalledDecision(element, oldTemplate, newTemplate);
this._updateZeebeFormDefinition(element, oldTemplate, newTemplate);
this._updateScriptTask(element, oldTemplate, newTemplate);
this._updateZeebeAssignmentDefinition(element, oldTemplate, newTemplate);
this._updateZeebePriorityDefinition(element, oldTemplate, newTemplate);
this._updateAdHoc(element, oldTemplate, newTemplate);
this._updateZeebeTaskSchedule(element, oldTemplate, newTemplate);
}
_getOrCreateExtensionElements(element, businessObject = getBusinessObject(element)) {
const bpmnFactory = this._bpmnFactory,
modeling = this._modeling;
let extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
values: []
});
extensionElements.$parent = businessObject;
modeling.updateModdleProperties(element, businessObject, {
extensionElements: extensionElements
});
}
return extensionElements;
}
/**
* Update zeebe:modelerTemplate property.
*
* @param {Element} element
* @param {Object} [newTemplate]
*/
_updateZeebeModelerTemplate(element, newTemplate) {
const modeling = this._modeling;
if (!newTemplate) {
modeling.updateProperties(element, {
'zeebe:modelerTemplate': undefined,
'zeebe:modelerTemplateVersion': undefined
});
return;
}
const newId = newTemplate.id;
const newVersion = newTemplate.version;
if (getTemplateId$1(element) !== newId || getTemplateVersion$1(element) !== newVersion) {
modeling.updateProperties(element, {
'zeebe:modelerTemplate': newId,
'zeebe:modelerTemplateVersion': newVersion
});
}
}
/**
* Update zeebe:modelerTemplateIcon property.
*
* @param {Element} element
* @param {Object} [newTemplate]
*/
_updateZeebeModelerTemplateIcon(element, newTemplate) {
const modeling = this._modeling;
if (!newTemplate) {
modeling.updateProperties(element, {
'zeebe:modelerTemplateIcon': undefined
});
return;
}
const newIcon = newTemplate.icon;
const newIconContents = newIcon && newIcon.contents;
if (getTemplateIcon(element) !== newIconContents) {
modeling.updateProperties(element, {
'zeebe:modelerTemplateIcon': newIconContents
});
}
}
/**
* Update BPMN properties.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*
* @returns {Element}
*/
_updateProperties(element, oldTemplate, newTemplate) {
const commandStack = this._commandStack;
const businessObject = getBusinessObject(element);
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === 'property';
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'property';
});
}
// Remove old properties
const propertiesToRemove = oldProperties.filter(oldProperty => {
return !newProperties.find(newProperty => newProperty.binding.name === oldProperty.binding.name);
});
if (propertiesToRemove.length) {
const properties = propertiesToRemove.reduce((properties, property) => {
properties[property.binding.name] = undefined;
return properties;
}, {});
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties
});
}
if (!newProperties.length) {
return;
}
// Add new or update old properties
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = getDefaultValue(newProperty),
changedElement = businessObject;
const properties = {};
if (shouldKeepValue(changedElement, oldProperty, newProperty)) {
return;
}
let assignedValue = newPropertyValue;
if (isExpression(businessObject, newBindingName)) {
assignedValue = createExpression(newPropertyValue, businessObject, this._bpmnFactory);
}
properties[newBindingName] = assignedValue;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties
});
});
}
/**
* Update `zeebe:TaskDefinition` properties of specified business object. This
* can only exist in `bpmn:ExtensionElements`.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateZeebeTaskDefinition(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: TASK_DEFINITION_TYPES,
extensionType: 'zeebe:TaskDefinition',
getPropertyName: getTaskDefinitionPropertyName
});
}
/**
* Update `zeebe:Input` and `zeebe:Output` properties of specified business
* object. Both can only exist in `zeebe:ioMapping` which can exist in `bpmn:ExtensionElements`.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateZeebeInputOutputParameterProperties(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
let newProperties = [];
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'zeebe:input' || newBindingType === 'zeebe:output';
});
}
const businessObject = this._getOrCreateExtensionElements(element);
let ioMapping = findExtension$1(businessObject, 'zeebe:IoMapping');
// (1) remove old mappings if no new specified
if (!newProperties.length) {
if (!ioMapping) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: without(businessObject.get('values'), ioMapping)
}
});
}
if (!ioMapping) {
ioMapping = bpmnFactory.create('zeebe:IoMapping');
ioMapping.$parent = businessObject;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), ioMapping]
}
});
}
const oldInputs = ioMapping.get('zeebe:inputParameters') ? ioMapping.get('zeebe:inputParameters').slice() : [];
const oldOutputs = ioMapping.get('zeebe:outputParameters') ? ioMapping.get('zeebe:outputParameters').slice() : [];
let propertyName;
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
inputOrOutput = findBusinessObject(businessObject, newProperty),
newPropertyValue = getDefaultValue(newProperty),
newBinding = newProperty.binding,
newBindingType = newBinding.type;
let newInputOrOutput, properties;
// (2) update old inputs and outputs
if (inputOrOutput) {
// (2a) exclude old inputs and outputs from cleanup, unless
// a) optional and has empty value, and
// b) not changed
if (shouldUpdate(newPropertyValue, newProperty) || shouldKeepValue(inputOrOutput, oldProperty, newProperty)) {
if (is(inputOrOutput, 'zeebe:Input')) {
remove$1(oldInputs, inputOrOutput);
} else {
remove$1(oldOutputs, inputOrOutput);
}
}
// (2a) do updates (unless changed)
if (!shouldKeepValue(inputOrOutput, oldProperty, newProperty)) {
if (is(inputOrOutput, 'zeebe:Input')) {
properties = {
source: newPropertyValue
};
} else {
properties = {
target: newPropertyValue
};
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: inputOrOutput,
properties
});
}
}
// (3) add new inputs and outputs (unless optional)
else if (shouldUpdate(newPropertyValue, newProperty)) {
if (newBindingType === 'zeebe:input') {
propertyName = 'inputParameters';
newInputOrOutput = createInputParameter$1(newBinding, newPropertyValue, bpmnFactory);
} else {
propertyName = 'outputParameters';
newInputOrOutput = createOutputParameter$1(newBinding, newPropertyValue, bpmnFactory);
}
newInputOrOutput.$parent = ioMapping;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: ioMapping,
properties: {
[propertyName]: [...ioMapping.get(propertyName), newInputOrOutput]
}
});
}
});
// (4) remove old inputs and outputs
if (oldInputs.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: ioMapping,
properties: {
inputParameters: without(ioMapping.get('inputParameters'), inputParameter => oldInputs.includes(inputParameter))
}
});
}
if (oldOutputs.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: ioMapping,
properties: {
outputParameters: without(ioMapping.get('outputParameters'), outputParameter => oldOutputs.includes(outputParameter))
}
});
}
}
/**
* Update `zeebe:Header` properties of specified business
* object. Those can only exist in `zeebe:taskHeaders` which can exist in `bpmn:ExtensionElements`.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateZeebeTaskHeaderProperties(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
let newProperties = [];
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'zeebe:taskHeader';
});
}
const businessObject = this._getOrCreateExtensionElements(element);
let taskHeaders = findExtension$1(businessObject, 'zeebe:TaskHeaders');
// (1) remove old headers if no new specified
if (!newProperties.length) {
if (!taskHeaders) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: without(businessObject.get('values'), taskHeaders)
}
});
}
if (!taskHeaders) {
taskHeaders = bpmnFactory.create('zeebe:TaskHeaders');
taskHeaders.$parent = businessObject;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), taskHeaders]
}
});
}
const oldHeaders = taskHeaders.get('zeebe:values') ? taskHeaders.get('zeebe:values').slice() : [];
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
oldHeader = findBusinessObject(businessObject, newProperty),
newPropertyValue = getDefaultValue(newProperty),
newBinding = newProperty.binding;
// (2) update old headers
if (oldHeader) {
if (!shouldKeepValue(oldHeader, oldProperty, newProperty)) {
const properties = {
value: newPropertyValue
};
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldHeader,
properties
});
}
remove$1(oldHeaders, oldHeader);
}
// (3) add new (non-empty) headers
else if (newPropertyValue) {
const newHeader = createTaskHeader(newBinding, newPropertyValue, bpmnFactory);
newHeader.$parent = taskHeaders;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: taskHeaders,
properties: {
values: [...taskHeaders.get('values'), newHeader]
}
});
}
});
// (4) remove old headers
if (oldHeaders.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: taskHeaders,
properties: {
values: without(taskHeaders.get('values'), header => oldHeaders.includes(header))
}
});
}
}
/**
* Update zeebe:Property properties of zeebe:Properties extension element.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateZeebePropertyProperties(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
let newProperties = [];
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'zeebe:property';
});
}
const businessObject = this._getOrCreateExtensionElements(element);
let zeebeProperties = findExtension$1(businessObject, 'zeebe:Properties');
// (1) remove old zeebe:Properties if no new zeebe:Property properties
if (!newProperties.length) {
if (!zeebeProperties) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: without(businessObject.get('values'), zeebeProperties)
}
});
}
if (!zeebeProperties) {
zeebeProperties = bpmnFactory.create('zeebe:Properties');
zeebeProperties.$parent = businessObject;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), zeebeProperties]
}
});
}
const oldZeebeProperties = zeebeProperties.get('properties') ? zeebeProperties.get('properties').slice() : [];
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
oldZeebeProperty = findBusinessObject(businessObject, newProperty),
newPropertyValue = getDefaultValue(newProperty),
newBinding = newProperty.binding;
// (2) update old zeebe:Property
if (oldZeebeProperty) {
if (shouldUpdate(newPropertyValue, newProperty) || shouldKeepValue(oldZeebeProperty, oldProperty, newProperty)) {
remove$1(oldZeebeProperties, oldZeebeProperty);
}
if (!shouldKeepValue(oldZeebeProperty, oldProperty, newProperty)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldZeebeProperty,
properties: {
value: newPropertyValue
}
});
}
}
// (3) add new zeebe:Property
else if (shouldUpdate(newPropertyValue, newProperty)) {
const newProperty = createZeebeProperty(newBinding, newPropertyValue, bpmnFactory);
newProperty.$parent = zeebeProperties;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: zeebeProperties,
properties: {
properties: [...zeebeProperties.get('properties'), newProperty]
}
});
}
});
// (4) remove old zeebe:Property
if (oldZeebeProperties.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: zeebeProperties,
properties: {
properties: without(zeebeProperties.get('properties'), zeebeProperty => oldZeebeProperties.includes(zeebeProperty))
}
});
}
}
_updateMessage(element, oldTemplate, newTemplate) {
// update bpmn:Message properties
this._updateMessageProperties(element, oldTemplate, newTemplate);
// update bpmn:Message zeebe:subscription properties
this._updateMessageZeebeSubscriptionProperties(element, oldTemplate, newTemplate);
if (!newTemplate || !hasMessageProperties(newTemplate)) {
removeMessage(element, this._injector);
}
}
_updateSignal(element, oldTemplate, newTemplate) {
// update bpmn:Signal properties
this._updateSignalProperties(element, oldTemplate, newTemplate);
if (!newTemplate || !hasSignalProperties(newTemplate)) {
removeSignal(element, this._injector);
}
}
/**
* Update bpmn:Message properties.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateMessageProperties(element, oldTemplate, newTemplate) {
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === MESSAGE_PROPERTY_TYPE;
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === MESSAGE_PROPERTY_TYPE;
});
}
const removedProperties = oldProperties.filter(oldProperty => {
return !newProperties.find(newProperty => newProperty.binding.name === oldProperty.binding.name);
});
let message = this._getMessage(element);
message && removedProperties.forEach(removedProperty => {
this._modeling.updateModdleProperties(element, message, {
[removedProperty.binding.name]: undefined
});
});
if (!newProperties.length) {
return;
}
message = this._ensureMessage(element, newTemplate);
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = getDefaultValue(newProperty),
changedElement = message;
let properties = {};
if (shouldKeepValue(changedElement, oldProperty, newProperty)) {
return;
}
properties[newBindingName] = newPropertyValue;
this._modeling.updateModdleProperties(element, changedElement, properties);
});
}
/**
* Update bpmn:Signal properties.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateSignalProperties(element, oldTemplate, newTemplate) {
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === SIGNAL_PROPERTY_TYPE;
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === SIGNAL_PROPERTY_TYPE;
});
}
const removedProperties = oldProperties.filter(oldProperty => {
return !newProperties.find(newProperty => newProperty.binding.name === oldProperty.binding.name);
});
let signal = this._getSignal(element);
signal && removedProperties.forEach(removedProperty => {
this._modeling.updateModdleProperties(element, signal, {
[removedProperty.binding.name]: undefined
});
});
if (!newProperties.length) {
return;
}
// The template schema guarantees that if signal#property is active
signal = this._ensureSignal(element, newTemplate);
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = getDefaultValue(newProperty),
changedElement = signal;
let properties = {};
if (shouldKeepValue(changedElement, oldProperty, newProperty)) {
return;
}
properties[newBindingName] = newPropertyValue;
this._modeling.updateModdleProperties(element, changedElement, properties);
});
}
/**
* Update bpmn:TimerEventDefinition properties.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateTimerEventDefinition(element, oldTemplate, newTemplate) {
const commandStack = this._commandStack;
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === TIMER_EVENT_DEFINITION_PROPERTY_TYPE;
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === TIMER_EVENT_DEFINITION_PROPERTY_TYPE;
});
}
const removedProperties = oldProperties.filter(oldProperty => {
return !newProperties.find(newProperty => newProperty.binding.name === oldProperty.binding.name);
});
const timerEventDefinition = findTimerEventDefinition(element);
if (!timerEventDefinition) {
return;
}
// Remove old timer properties that are no longer in the template
removedProperties.forEach(removedProperty => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: timerEventDefinition,
properties: {
[removedProperty.binding.name]: undefined
}
});
});
if (!newProperties.length) {
return;
}
// First, check if we need to auto-convert to non-interrupting for timeCycle
// This must happen BEFORE setting the timer property, otherwise the
// CleanUpTimerExpressionBehavior will remove it
const hasTimeCycle = newProperties.some(p => p.binding.name === 'timeCycle');
if (hasTimeCycle) {
this._ensureNonInterruptingForTimeCycle(element);
}
// Set new timer properties
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = getDefaultValue(newProperty);
// Check if we should keep the existing value
if (shouldKeepValue(timerEventDefinition, oldProperty, newProperty)) {
return;
}
// Create or update the FormalExpression
let expression = timerEventDefinition.get(newBindingName);
if (!expression) {
expression = createExpression(newPropertyValue, timerEventDefinition, this._bpmnFactory);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: timerEventDefinition,
properties: {
// remove any existing timer values, satisfying that there is only one
timeDate: undefined,
timeDuration: undefined,
timeCycle: undefined,
[newBindingName]: expression
}
});
} else {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: expression,
properties: {
body: newPropertyValue
}
});
}
});
}
/**
* Ensure element is non-interrupting when using timeCycle.
* - Boundary events: set cancelActivity to false
* - Event subprocess start events: set isInterrupting to false
*
* @param {djs.model.Base} element
*/
_ensureNonInterruptingForTimeCycle(element) {
const commandStack = this._commandStack;
const businessObject = getBusinessObject(element);
// Boundary events with timeCycle must be non-interrupting
if (is(element, 'bpmn:BoundaryEvent')) {
if (businessObject.get('cancelActivity') !== false) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
cancelActivity: false
}
});
}
return;
}
// Start events in event subprocess with timeCycle must be non-interrupting
if (is(element, 'bpmn:StartEvent')) {
const parent = businessObject.$parent;
if (is(parent, 'bpmn:SubProcess') && parent.get('triggeredByEvent')) {
if (businessObject.get('isInterrupting') !== false) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
isInterrupting: false
}
});
}
}
}
}
/**
* Update bpmn:Message#zeebe:subscription properties.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateMessageZeebeSubscriptionProperties(element, oldTemplate, newTemplate) {
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE;
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE;
});
}
const removedProperties = oldProperties.filter(oldProperty => {
return !newProperties.find(newProperty => newProperty.binding.name === oldProperty.binding.name);
});
if (!newProperties.length && !removedProperties.length) {
return;
}
let message = this._getMessage(element);
if (!newProperties.length && !message) {
return;
}
message = this._ensureMessage(element, newTemplate);
const messageExtensionElements = this._getOrCreateExtensionElements(element, message);
const zeebeSubscription = this._getSubscription(element, message);
const propertiesToSet = newProperties.reduce((properties, newProperty) => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = getDefaultValue(newProperty),
changedElement = zeebeSubscription;
if (shouldKeepValue(changedElement, oldProperty, newProperty)) {
return properties;
}
properties[newBindingName] = newPropertyValue;
return properties;
}, {});
// Update zeebe Subscription
if (zeebeSubscription) {
this._modeling.updateModdleProperties(element, zeebeSubscription, propertiesToSet);
} else {
// create new Subscription
const newSubscription = createElement('zeebe:Subscription', propertiesToSet, message, this._bpmnFactory);
this._modeling.updateModdleProperties(element, messageExtensionElements, {
values: [...messageExtensionElements.get('values'), newSubscription]
});
}
// Remove old properties
if (!oldTemplate || !zeebeSubscription) {
return;
}
const propertiesToRemove = removedProperties.reduce((properties, removedProperty) => {
properties[removedProperty.binding.name] = undefined;
return properties;
}, {});
this._modeling.updateModdleProperties(element, zeebeSubscription, propertiesToRemove);
}
_updateZeebeModelerTemplateOnReferencedElement(element, oldTemplate, newTemplate) {
const businessObject = getBusinessObject(element);
const referencedElement = findMessage(businessObject) || findSignal(businessObject);
if (referencedElement && newTemplate && getTemplateId$1(referencedElement) !== newTemplate.id) {
this._modeling.updateModdleProperties(element, referencedElement, {
'zeebe:modelerTemplate': newTemplate.id
});
}
}
_getSubscription(element, bo) {
const extensionElements = this._getOrCreateExtensionElements(element, bo);
const extension = findExtension$1(extensionElements, 'zeebe:Subscription');
if (extension) {
return extension;
}
}
_createMessage(element, template) {
let bo = getBusinessObject(element);
if (is(bo, 'bpmn:Event')) {
bo = bo.get('eventDefinitions')[0];
}
const message = this._bpmnFactory.create('bpmn:Message', {
'zeebe:modelerTemplate': template.id
});
message.$parent = getRoot(bo);
this._modeling.updateModdleProperties(element, bo, {
messageRef: message
});
return message;
}
_createSignal(element, template) {
let bo = getBusinessObject(element);
if (is(bo, 'bpmn:Event')) {
bo = bo.get('eventDefinitions')[0];
}
const signal = this._bpmnFactory.create('bpmn:Signal', {
'zeebe:modelerTemplate': template.id
});
signal.$parent = getRoot(bo);
this._modeling.updateModdleProperties(element, bo, {
signalRef: signal
});
return signal;
}
_getMessage(element) {
let bo = getBusinessObject(element);
if (is(bo, 'bpmn:Event')) {
bo = bo.get('eventDefinitions')[0];
}
return bo && bo.get('messageRef');
}
_getSignal(element) {
let bo = getBusinessObject(element);
if (!is(bo, 'bpmn:Event')) {
return;
}
const eventDefinition = bo.get('eventDefinitions')[0];
return eventDefinition && eventDefinition.get('signalRef');
}
_ensureMessage(element, newTemplate) {
const message = this._getMessage(element);
if (!newTemplate) {
return message;
}
// message is already templated, so we use it
if (message && message.get('zeebe:modelerTemplate')) {
return message;
}
const newMessage = this._createMessage(element, newTemplate);
// no message is set on the element, so no properties to copy
if (!message) {
return newMessage;
}
return this._moddleCopy.copyElement(message, newMessage, ['name', 'extensionElements']);
}
_ensureSignal(element, newTemplate) {
const signal = this._getSignal(element);
if (!newTemplate) {
return signal;
}
// signal is already templated, so we use it
if (signal && signal.get('zeebe:modelerTemplate')) {
return signal;
}
const newSignal = this._createSignal(element, newTemplate);
// no signal is set on the element, so no properties to copy
if (!signal) {
return newSignal;
}
return this._moddleCopy.copyElement(signal, newSignal, ['name']);
}
/**
* Update `zeebe:CalledElement` properties of specified business object. This
* can only exist in `bpmn:ExtensionElements`.
*
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateCalledElement(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_CALLED_ELEMENT],
extensionType: 'zeebe:CalledElement',
getPropertyName: binding => binding.property
});
}
/**
* Update `zeebe:CalledDecision` properties of specified business object. This
* can only exist in `bpmn:ExtensionElements`.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateCalledDecision(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_CALLED_DECISION],
extensionType: 'zeebe:CalledDecision',
getPropertyName: binding => binding.property
});
}
/**
* Update `zeebe:Script` properties of specified business object. This
* can only exist in `bpmn:ExtensionElements`.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateScriptTask(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_SCRIPT_TASK],
extensionType: 'zeebe:Script',
getPropertyName: binding => binding.property
});
}
_updateZeebeAssignmentDefinition(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_ASSIGNMENT_DEFINITION],
extensionType: 'zeebe:AssignmentDefinition',
getPropertyName: binding => binding.property
});
}
_updateZeebePriorityDefinition(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_PRIORITY_DEFINITION],
extensionType: 'zeebe:PriorityDefinition',
getPropertyName: binding => binding.property
});
}
_updateAdHoc(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_AD_HOC],
extensionType: 'zeebe:AdHoc',
getPropertyName: binding => binding.property
});
}
_updateZeebeTaskSchedule(element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_TASK_SCHEDULE],
extensionType: 'zeebe:TaskSchedule',
getPropertyName: binding => binding.property
});
}
/**
* Replaces the element with the specified elementType.
* Takes into account the eventDefinition for events.
*
* @param {djs.model.Base} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
*/
_updateElementType(element, oldTemplate, newTemplate) {
if (!newTemplate) {
return element;
}
// determine new task type
const newType = newTemplate.elementType;
if (!newType) {
return element;
}
// Do not replace if the element type did not change
if (!shouldUpdateElementType(element, oldTemplate, newType)) {
return element;
}
const replacement = {
type: newType.value
};
if (newType.eventDefinition) {
replacement.eventDefinitionType = newType.eventDefinition;
}
const replacedElement = this._bpmnReplace.replaceElement(element, replacement);
return replacedElement;
}
_updateLinkedResources(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
let oldProperties = [],
newProperties = [];
if (oldTemplate) {
oldProperties = oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === ZEEBE_LINKED_RESOURCE_PROPERTY;
});
}
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === ZEEBE_LINKED_RESOURCE_PROPERTY;
});
}
const extensionElements = this._getOrCreateExtensionElements(element);
let linkedResources = findExtension$1(extensionElements, 'zeebe:LinkedResources');
// (1) remove properties if no new specified
if (!newProperties.length) {
if (!linkedResources) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: without(extensionElements.get('values'), linkedResources)
}
});
return;
}
if (!linkedResources) {
linkedResources = bpmnFactory.create('zeebe:LinkedResources');
linkedResources.$parent = extensionElements;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), linkedResources]
}
});
}
const unusedLinkedResources = linkedResources.get('values').slice();
const propertiesToRemove = oldProperties.slice();
newProperties.forEach(newLinkedResource => {
const oldProperty = findOldProperty$1(oldTemplate, newLinkedResource),
oldLinkedResource = findBusinessObject(extensionElements, newLinkedResource),
newPropertyValue = getDefaultValue(newLinkedResource),
newBinding = newLinkedResource.binding;
if (oldProperty) {
remove$1(propertiesToRemove, oldProperty);
}
// (2) update old LinkedResources
if (oldLinkedResource) {
if (shouldUpdate(newPropertyValue, newLinkedResource) || shouldKeepValue(oldLinkedResource, oldProperty, newLinkedResource)) {
remove$1(unusedLinkedResources, oldLinkedResource);
}
if (!shouldKeepValue(oldLinkedResource, oldProperty, newLinkedResource)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldLinkedResource,
properties: {
[newBinding.property]: newPropertyValue
}
});
}
}
// (3) add new linkedResources
else if (shouldUpdate(newPropertyValue, newLinkedResource)) {
const newProperties = {
linkName: newBinding.linkName,
[newBinding.property]: newPropertyValue
};
const newLinkedResource = createElement('zeebe:LinkedResource', newProperties, extensionElements, bpmnFactory);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: linkedResources,
properties: {
values: [...linkedResources.get('values'), newLinkedResource]
}
});
}
});
// (4) remove unused linkedResources
if (unusedLinkedResources.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: linkedResources,
properties: {
values: without(linkedResources.get('values'), linkedResource => unusedLinkedResources.includes(linkedResource))
}
});
}
// (5) remove unused resource properties
propertiesToRemove.forEach(unusedResourceProperty => {
const oldLinkedResource = findBusinessObject(extensionElements, unusedResourceProperty);
const oldBinding = unusedResourceProperty.binding;
// No property was reused and element was removed in previous step
if (!oldLinkedResource) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldLinkedResource,
properties: {
[oldBinding.property]: undefined
}
});
});
}
_updateZeebeUserTask = function (element, newTemplate) {
const commandStack = this._commandStack;
const bpmnFactory = this._bpmnFactory;
// check if template has zeebe:userTask binding property
const hasBinding = newTemplate?.properties.some(property => property.binding.type === ZEEBE_USER_TASK);
// check if element has zeebe:UserTask extension element
const extensionElements = getBusinessObject(element).get('extensionElements');
const userTaskExtension = findExtension$1(element, 'zeebe:UserTask');
if (newTemplate && newTemplate.elementType?.value !== 'bpmn:UserTask') {
return;
}
// remove zeebe:UserTask if no binding
if (userTaskExtension) {
!hasBinding && commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: without(extensionElements.get('values'), userTaskExtension)
}
});
return;
}
if (!hasBinding) {
return;
}
// create new zeebe:UserTask extension element
const zeebeUserTask = bpmnFactory.create('zeebe:UserTask');
zeebeUserTask.$parent = extensionElements;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), zeebeUserTask]
}
});
};
/**
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateZeebeFormDefinition = function (element, oldTemplate, newTemplate) {
this._updateSingleExtensionElement(element, oldTemplate, newTemplate, {
bindingTypes: [ZEEBE_FORM_DEFINITION],
extensionType: 'zeebe:FormDefinition',
getPropertyName: binding => binding.property
});
};
/**
* Generic handler for updating extension elements that are single instances with properties.
*
* @example
* For this template
* ```json
* {
* properties: [
* { binding: { type: 'zeebe:taskDefinition', property: 'type' }, value: 'myTaskType' },
* {binding: { type: 'zeebe:taskDefinition', property: 'retries' }, value: 3}
* ]
* }
*```
* `getPropertyName` should return `binding.property`.
*
* `bindingTypes` should be an array of binding types to consider, e.g. `['zeebe:taskDefinition']`.
*
* `extensionType` should be the type of the extension element to update, e.g. `'zeebe:TaskDefinition'`.
*
* The resulting XML will look like this:
*
* ```xml
* <bpmn:extensionElements>
* <zeebe:taskDefinition type="myTaskType" retries="3" />
* </bpmn:extensionElements>
*```
* @param {Element} element
* @param {Object} [oldTemplate]
* @param {Object} [newTemplate]
* @param {Object} options
* @param {Array<string>} options.bindingTypes - binding types to consider
* @param {string} options.extensionType - type of the extension element to update
* @param {(binding: Object) => string} options.getPropertyName - function to get the property name for the binding
*/
_updateSingleExtensionElement(element, oldTemplate, newTemplate, options) {
const {
bindingTypes,
extensionType,
getPropertyName
} = options;
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
let newProperties = [];
if (newTemplate) {
newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return bindingTypes.includes(newBindingType);
});
}
const businessObject = this._getOrCreateExtensionElements(element);
let extensionElement = findExtension$1(businessObject, extensionType);
// (1) Remove extension if no new properties
if (!newProperties.length) {
if (!extensionElement) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: without(businessObject.get('values'), extensionElement)
}
});
return;
}
// If there are new properties:
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty$1(oldTemplate, newProperty),
newPropertyValue = getDefaultValue(newProperty),
newBinding = newProperty.binding,
propertyName = getPropertyName(newBinding);
// (2) Update old extension with new property values
if (extensionElement) {
if (!shouldKeepValue(extensionElement, oldProperty, newProperty)) {
const properties = {
[propertyName]: newPropertyValue
};
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElement,
properties
});
}
}
// (3) Add new extension with properties if it does not exist
else {
const properties = {
[propertyName]: newPropertyValue
};
extensionElement = bpmnFactory.create(extensionType, properties);
extensionElement.$parent = businessObject;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), extensionElement]
}
});
}
});
// (4) Remove properties no longer templated
const oldProperties = oldTemplate && oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type,
oldPropertyName = getPropertyName(oldBinding);
return bindingTypes.includes(oldBindingType) && !newProperties.find(newProperty => oldPropertyName === getPropertyName(newProperty.binding));
}) || [];
oldProperties.forEach(oldProperty => {
const properties = {
[getPropertyName(oldProperty.binding)]: undefined
};
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElement,
properties
});
});
}
};
ChangeElementTemplateHandler$1.$inject = ['bpmnFactory', 'bpmnReplace', 'commandStack', 'modeling', 'moddleCopy', 'injector'];
// helpers //////////
/**
* Find business object matching specified property.
*
* @param {djs.model.Base|ModdleElement} element
* @param {Object} property
*
* @returns {ModdleElement}
*/
function findBusinessObject(element, property) {
const businessObject = getBusinessObject(element);
const binding = property.binding,
bindingType = binding.type;
if (TASK_DEFINITION_TYPES.includes(bindingType)) {
return findExtension$1(businessObject, 'zeebe:TaskDefinition');
}
if (bindingType === 'zeebe:input' || bindingType === 'zeebe:output') {
const extensionElements = findExtension$1(businessObject, 'zeebe:IoMapping');
if (!extensionElements) {
return;
}
if (bindingType === 'zeebe:input') {
return find(extensionElements.get('zeebe:inputParameters'), function (input) {
return input.get('zeebe:target') === binding.name;
});
} else {
return find(extensionElements.get('zeebe:outputParameters'), function (output) {
return output.get('zeebe:source') === binding.source;
});
}
}
if (bindingType === 'zeebe:taskHeader') {
const extensionElements = findExtension$1(businessObject, 'zeebe:TaskHeaders');
if (!extensionElements) {
return;
}
return find(extensionElements.get('zeebe:values'), function (value) {
return value.get('zeebe:key') === binding.key;
});
}
if (bindingType === 'zeebe:property') {
const zeebeProperties = findExtension$1(businessObject, 'zeebe:Properties');
if (!zeebeProperties) {
return;
}
return zeebeProperties.get('properties').find(value => {
return value.get('name') === binding.name;
});
}
if (bindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
const linkedResources = findExtension$1(businessObject, 'zeebe:LinkedResources');
if (!linkedResources) {
return;
}
return linkedResources.get('values').find(value => {
return value.get('linkName') === binding.linkName;
});
}
}
/**
* Find old property matching specified new property.
*
* @param {Object} oldTemplate
* @param {Object} newProperty
*
* @returns {Object}
*/
function findOldProperty$1(oldTemplate, newProperty) {
if (!oldTemplate) {
return;
}
const oldProperties = oldTemplate.properties,
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newBindingType = newBinding.type;
if (newBindingType === 'property') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
return oldBindingType === 'property' && oldBindingName === newBindingName;
});
}
if (TASK_DEFINITION_TYPES.includes(newBindingType)) {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldPropertyName = getTaskDefinitionPropertyName(oldBinding),
newPropertyName = getTaskDefinitionPropertyName(newBinding);
return oldPropertyName === newPropertyName;
});
}
if (newBindingType === 'zeebe:input') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'zeebe:input') {
return;
}
return oldBindingName === newBindingName;
});
}
if (newBindingType === 'zeebe:output') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'zeebe:output') {
return;
}
return oldBinding.source === newBinding.source;
});
}
if (newBindingType === 'zeebe:taskHeader') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'zeebe:taskHeader') {
return;
}
return oldBinding.key === newBinding.key;
});
}
if (newBindingType === 'zeebe:property') {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'zeebe:property') {
return;
}
return oldBinding.name === newBinding.name;
});
}
if (newBindingType === MESSAGE_PROPERTY_TYPE) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== MESSAGE_PROPERTY_TYPE) {
return;
}
return oldBinding.name === newBinding.name;
});
}
if (newBindingType === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
return;
}
return oldBinding.name === newBinding.name;
});
}
if (newBindingType === SIGNAL_PROPERTY_TYPE) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== SIGNAL_PROPERTY_TYPE) {
return;
}
return oldBinding.name === newBinding.name;
});
}
if (newBindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_LINKED_RESOURCE_PROPERTY) {
return;
}
return oldBinding.linkName === newBinding.linkName && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_CALLED_DECISION) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_CALLED_DECISION) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_SCRIPT_TASK) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_SCRIPT_TASK) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_FORM_DEFINITION) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_FORM_DEFINITION) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_ASSIGNMENT_DEFINITION) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_ASSIGNMENT_DEFINITION) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_PRIORITY_DEFINITION) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_PRIORITY_DEFINITION) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_AD_HOC) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_AD_HOC) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === ZEEBE_TASK_SCHEDULE) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== ZEEBE_TASK_SCHEDULE) {
return;
}
return oldBindingType === newBindingType && oldBinding.property === newBinding.property;
});
}
if (newBindingType === TIMER_EVENT_DEFINITION_PROPERTY_TYPE) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== TIMER_EVENT_DEFINITION_PROPERTY_TYPE) {
return;
}
return oldBinding.name === newBinding.name;
});
}
}
/**
* Check whether the existing property should be kept. This is the case if
* - an old template was set and the value differs from the default
* - no template was set but the property was set manually
*
* @param {djs.model.Base|ModdleElement} element
* @param {Object} oldProperty
* @param {Object} newProperty
*
* @returns {boolean}
*/
function shouldKeepValue(element, oldProperty, newProperty) {
// "Hidden" values are treated as a constant
if (newProperty.type === 'Hidden') {
return false;
}
// Dropdowns should keep existing configuration
// cf. https://github.com/bpmn-io/bpmn-js-properties-panel/issues/767
if (newProperty.type === 'Dropdown') {
const currentValue = getPropertyValue$1(element, newProperty);
// only keep value if old value is a valid option
return newProperty.choices && newProperty.choices.some(choice => choice.value === currentValue);
}
// keep existing old property if
// user changed it from the original
if (oldProperty) {
return propertyChanged$1(element, oldProperty);
}
// keep existing property value
return !!getPropertyValue$1(element, newProperty);
}
/**
* Check whether property was changed after being set by template.
*
* @param {djs.model.Base|ModdleElement} element
* @param {Object} oldProperty
*
* @returns {boolean}
*/
function propertyChanged$1(element, oldProperty) {
const oldPropertyValue = getDefaultFixedValue(oldProperty);
return getPropertyValue$1(element, oldProperty) !== oldPropertyValue;
}
function getPropertyValue$1(element, property) {
const businessObject = getBusinessObject(element);
if (!businessObject) {
return;
}
const binding = property.binding,
bindingName = binding.name,
bindingType = binding.type,
bindingProperty = binding.property;
if (bindingType === 'property') {
return businessObject.get(bindingName);
}
if (TASK_DEFINITION_TYPES.includes(bindingType)) {
return businessObject.get(getTaskDefinitionPropertyName(binding));
}
if (bindingType === 'zeebe:input') {
return businessObject.get('zeebe:source');
}
if (bindingType === 'zeebe:output') {
return businessObject.get('zeebe:target');
}
if (bindingType === 'zeebe:taskHeader') {
return businessObject.get('zeebe:value');
}
if (bindingType === 'zeebe:property') {
return businessObject.get('zeebe:value');
}
if (bindingType === MESSAGE_PROPERTY_TYPE) {
return businessObject.get(bindingName);
}
if (bindingType === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
return businessObject.get(bindingName);
}
if (bindingType === SIGNAL_PROPERTY_TYPE) {
return businessObject.get(bindingName);
}
if (bindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_CALLED_DECISION) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_CALLED_ELEMENT) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_FORM_DEFINITION) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_SCRIPT_TASK) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_ASSIGNMENT_DEFINITION) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_PRIORITY_DEFINITION) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_AD_HOC) {
return businessObject.get(bindingProperty);
}
if (bindingType === ZEEBE_TASK_SCHEDULE) {
return businessObject.get(bindingProperty);
}
if (bindingType === TIMER_EVENT_DEFINITION_PROPERTY_TYPE) {
// the actual value is nested in an Expression
return businessObject.get(bindingName)?.get('body');
}
}
function remove$1(array, item) {
const index = array.indexOf(item);
if (index < 0) {
return array;
}
array.splice(index, 1);
return array;
}
function hasMessageProperties(template) {
return template.properties.some(p => MESSAGE_BINDING_TYPES.includes(p.binding.type));
}
function hasSignalProperties(template) {
return template.properties.some(p => p.binding.type === SIGNAL_PROPERTY_TYPE);
}
function shouldUpdateElementType(element, oldTemplate, newType) {
// Never reuse existing eventDefinition when applying a new template
if (!oldTemplate && newType.eventDefinition) {
return true;
}
const oldType = oldTemplate && oldTemplate.elementType || {
value: element.type,
eventDefinition: getEventDefinitionType(element)
};
// Do not update if the element type did not change
if (oldType && oldType.value === newType.value && oldType.eventDefinition === newType.eventDefinition) {
return false;
}
return true;
}
function getEventDefinitionType(element) {
const businessObject = getBusinessObject(element);
if (!businessObject.eventDefinitions) {
return;
}
const eventDefinition = businessObject.eventDefinitions[0];
if (!eventDefinition) {
return;
}
return eventDefinition.$type;
}
let RemoveElementTemplateHandler$1 = class RemoveElementTemplateHandler {
constructor(commandStack) {
this._commandStack = commandStack;
}
preExecute(context) {
const {
element,
oldTemplate
} = context;
this._commandStack.execute('propertiesPanel.zeebe.changeTemplate', {
element,
oldTemplate,
newTemplate: null,
removeProperties: true
});
}
};
RemoveElementTemplateHandler$1.$inject = ['commandStack'];
let UnlinkElementTemplateHandler$1 = class UnlinkElementTemplateHandler {
constructor(commandStack) {
this._commandStack = commandStack;
}
preExecute(context) {
const {
element,
oldTemplate
} = context;
this._commandStack.execute('propertiesPanel.zeebe.changeTemplate', {
element,
oldTemplate,
newTemplate: null,
removeProperties: false
});
}
};
UnlinkElementTemplateHandler$1.$inject = ['commandStack'];
/**
* A handler that combines and executes multiple commands.
*
* All updates are bundled on the command stack and executed in one step.
* This also makes it possible to revert the changes in one step.
*
* Example use case: remove the camunda:formKey attribute and in addition
* add all form fields needed for the camunda:formData property.
*/
class MultiCommandHandler {
constructor(commandStack) {
this._commandStack = commandStack;
}
preExecute(context) {
const commandStack = this._commandStack;
forEach(context, function (command) {
commandStack.execute(command.cmd, command.context);
});
}
}
MultiCommandHandler.$inject = ['commandStack'];
let ElementTemplatesCommands$1 = class ElementTemplatesCommands {
constructor(commandStack, elementTemplates, eventBus) {
commandStack.registerHandler('element-templates.multi-command-executor', MultiCommandHandler);
commandStack.registerHandler('propertiesPanel.zeebe.changeTemplate', ChangeElementTemplateHandler$1);
commandStack.registerHandler('propertiesPanel.removeTemplate', RemoveElementTemplateHandler$1);
commandStack.registerHandler('propertiesPanel.unlinkTemplate', UnlinkElementTemplateHandler$1);
// apply default element templates on shape creation
eventBus.on(['commandStack.shape.create.postExecuted'], function (event) {
const {
context: {
hints = {},
shape
}
} = event;
if (hints.createElementsBehavior !== false) {
applyDefaultTemplate$1(shape, elementTemplates, commandStack);
}
});
// apply default element templates on connection creation
eventBus.on(['commandStack.connection.create.postExecuted'], function (event) {
const {
context: {
hints = {},
connection
}
} = event;
if (hints.createElementsBehavior !== false) {
applyDefaultTemplate$1(connection, elementTemplates, commandStack);
}
});
}
};
ElementTemplatesCommands$1.$inject = ['commandStack', 'elementTemplates', 'eventBus'];
function applyDefaultTemplate$1(element, elementTemplates, commandStack) {
if (!elementTemplates.get(element) && elementTemplates.getDefault(element)) {
const command = 'propertiesPanel.zeebe.changeTemplate';
const commandContext = {
element: element,
newTemplate: elementTemplates.getDefault(element)
};
commandStack.execute(command, commandContext);
}
}
var commandsModule$1 = {
__init__: ['elementTemplateCommands'],
elementTemplateCommands: ['type', ElementTemplatesCommands$1]
};
class PropertyBindingProvider {
static create(element, options) {
const {
bpmnFactory,
property
} = options;
const {
binding
} = property;
const {
name
} = binding;
let value = getDefaultValue(property);
const businessObject = getBusinessObject(element);
if (isExpression(businessObject, name)) {
const expression = createExpression(value, businessObject, bpmnFactory);
value = expression;
}
businessObject[name] = value;
}
}
class TaskDefinitionTypeBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const value = getDefaultValue(property);
const propertyName = getTaskDefinitionPropertyName(property.binding);
const taskDefinition = ensureExtension(element, 'zeebe:TaskDefinition', bpmnFactory);
taskDefinition.set(propertyName, value);
}
}
class InputBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const value = getDefaultValue(property);
const ioMapping = ensureExtension(element, 'zeebe:IoMapping', bpmnFactory);
if (!shouldUpdate(value, property)) {
return;
}
const input = createInputParameter$1(binding, value, bpmnFactory);
input.$parent = ioMapping;
ioMapping.get('inputParameters').push(input);
}
}
class OutputBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const value = getDefaultValue(property);
const ioMapping = ensureExtension(element, 'zeebe:IoMapping', bpmnFactory);
if (!shouldUpdate(value, property)) {
return;
}
const output = createOutputParameter$1(binding, value, bpmnFactory);
output.$parent = ioMapping;
ioMapping.get('outputParameters').push(output);
}
}
class TaskHeaderBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const value = getDefaultValue(property);
const taskHeaders = ensureExtension(element, 'zeebe:TaskHeaders', bpmnFactory);
const header = createTaskHeader(binding, value, bpmnFactory);
header.$parent = taskHeaders;
taskHeaders.get('values').push(header);
}
}
/**
* A provider that sets up the `zeebe:property` binding
* on a newly created moddle element.
*/
class ZeebePropertiesProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const value = getDefaultValue(property);
const zeebeProperties = ensureExtension(element, 'zeebe:Properties', bpmnFactory);
if (!shouldUpdate(value, property)) {
return;
}
const zeebeProperty = createZeebeProperty(binding, value, bpmnFactory);
zeebeProperty.$parent = zeebeProperties;
zeebeProperties.get('properties').push(zeebeProperty);
}
}
class MessagePropertyBindingProvider {
static create(element, options) {
const {
bpmnFactory,
property
} = options;
const {
binding
} = property;
const {
name
} = binding;
const value = getDefaultValue(property);
let businessObject = getBusinessObject(element);
if (is(businessObject, 'bpmn:Event')) {
businessObject = businessObject.get('eventDefinitions')[0];
}
let message = businessObject.get('messageRef');
if (!message) {
message = bpmnFactory.create('bpmn:Message', {
'zeebe:modelerTemplate': getTemplateId$1(element)
});
businessObject.set('messageRef', message);
}
message.set(name, value);
}
}
class MessageZeebeSubscriptionBindingProvider {
static create(element, options) {
const {
bpmnFactory,
property
} = options;
const {
binding
} = property;
const {
name
} = binding;
const value = getDefaultValue(property);
let businessObject = getBusinessObject(element);
if (is(businessObject, 'bpmn:Event')) {
businessObject = businessObject.get('eventDefinitions')[0];
}
let message = businessObject.get('messageRef');
if (!message) {
message = bpmnFactory.create('bpmn:Message', {
'zeebe:modelerTemplate': getTemplateId$1(element)
});
businessObject.set('messageRef', message);
}
const subscription = ensureExtension(message, 'zeebe:Subscription', bpmnFactory);
if (!shouldUpdate(value, property)) {
return;
}
subscription.set(name, value);
}
}
class SignalPropertyBindingProvider {
static create(element, options) {
const {
bpmnFactory,
property
} = options;
const {
binding
} = property;
const {
name
} = binding;
const value = getDefaultValue(property);
let businessObject = getBusinessObject(element);
if (is(businessObject, 'bpmn:Event')) {
businessObject = businessObject.get('eventDefinitions')[0];
}
let signal = businessObject.get('signalRef');
if (!signal) {
signal = bpmnFactory.create('bpmn:Signal', {
'zeebe:modelerTemplate': getTemplateId$1(element)
});
businessObject.set('signalRef', signal);
}
signal.set(name, value);
}
}
class TimerPropertyBindingProvider {
static create(element, options) {
const {
bpmnFactory,
property
} = options;
const {
binding
} = property;
const {
name
} = binding;
const value = getDefaultValue(property);
const businessObject = getBusinessObject(element);
let timerEventDefinition = findTimerEventDefinition(businessObject);
if (!timerEventDefinition) {
timerEventDefinition = createElement('bpmn:TimerEventDefinition', {}, businessObject, bpmnFactory);
businessObject.set('eventDefinitions', [timerEventDefinition]);
}
const expression = createElement('bpmn:FormalExpression', {
body: value
}, timerEventDefinition, bpmnFactory);
timerEventDefinition.set(name, expression);
if (name === 'timeCycle' && is(businessObject, 'bpmn:BoundaryEvent')) {
businessObject.set('cancelActivity', false);
}
}
}
class CalledElementBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const calledElement = ensureExtension(element, 'zeebe:CalledElement', bpmnFactory);
// TODO(@barmac): remove if we decide to support propagation in templates
ensureNoPropagation(calledElement);
calledElement.set(propertyName, value);
}
}
function ensureNoPropagation(calledElement) {
calledElement.set('propagateAllChildVariables', false);
calledElement.set('propagateAllParentVariables', false);
}
class LinkedResourcePropertyBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding: {
property: bindingProperty,
linkName
}
} = property;
const value = getDefaultValue(property);
const bo = getBusinessObject(element);
const linkedResources = ensureExtension(element, 'zeebe:LinkedResources', bpmnFactory);
let linkedResource = linkedResources.get('values').find(linkedResource => linkedResource.get('linkName') === linkName);
if (!linkedResource) {
linkedResource = createElement('zeebe:LinkedResource', {
linkName
}, bo, bpmnFactory);
linkedResources.get('values').push(linkedResource);
}
linkedResource.set(bindingProperty, value);
}
}
class ZeebeUserTaskBindingProvider {
static create(element, options) {
const {
bpmnFactory
} = options;
ensureExtension(element, 'zeebe:UserTask', bpmnFactory);
}
}
class CalledDecisionBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const calledDecision = ensureExtension(element, 'zeebe:CalledDecision', bpmnFactory);
calledDecision.set(propertyName, value);
}
}
class ScriptTaskBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const scriptTask = ensureExtension(element, 'zeebe:Script', bpmnFactory);
scriptTask.set(propertyName, value);
}
}
class ZeebeFormDefinitionBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const formDefinition = ensureExtension(element, 'zeebe:FormDefinition', bpmnFactory);
formDefinition.set(propertyName, value);
}
}
class AssignmentDefinitionBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const assignmentDefinition = ensureExtension(element, 'zeebe:AssignmentDefinition', bpmnFactory);
assignmentDefinition.set(propertyName, value);
}
}
class PriorityDefinitionBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const priorityDefinition = ensureExtension(element, 'zeebe:PriorityDefinition', bpmnFactory);
priorityDefinition.set(propertyName, value);
}
}
/**
* Provider for the `zeebe:adHoc` binding. Ensures a single zeebe:AdHoc extension
* element exists and sets the configured property on it.
*/
class AdHocBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const adHoc = ensureExtension(element, 'zeebe:AdHoc', bpmnFactory);
adHoc.set(propertyName, value);
}
}
class TaskScheduleBindingProvider {
static create(element, options) {
const {
property,
bpmnFactory
} = options;
const {
binding
} = property;
const {
property: propertyName
} = binding;
const value = getDefaultValue(property);
const taskSchedule = ensureExtension(element, 'zeebe:TaskSchedule', bpmnFactory);
taskSchedule.set(propertyName, value);
}
}
const EXPRESSION_TYPES = ['bpmn:Expression', 'bpmn:FormalExpression'];
function getPropertyValue(element, property) {
const rawValue = getRawPropertyValue(element, property);
const {
type
} = property;
if (type === 'Boolean') {
return getBooleanPropertyValue(rawValue);
}
return rawValue;
}
function getRawPropertyValue(element, property) {
let businessObject = getBusinessObject(element);
const defaultValue = '';
const {
binding
} = property;
const {
name,
property: bindingProperty,
type,
linkName
} = binding;
// property
if (type === 'property') {
const value = isExpression(element, name) ? getExpressionValue(element, name) : businessObject.get(name);
if (!isUndefined$1(value)) {
return value;
}
return defaultValue;
}
// zeebe:taskDefinition
if (TASK_DEFINITION_TYPES.includes(type)) {
const taskDefinition = findExtension$1(businessObject, 'zeebe:TaskDefinition');
if (taskDefinition) {
if (type === ZEEBE_TASK_DEFINITION_TYPE_TYPE) {
return taskDefinition.get('type');
} else if (type === ZEEBE_TASK_DEFINITION) {
return taskDefinition.get(bindingProperty);
}
}
return defaultValue;
}
if (IO_BINDING_TYPES$1.includes(type)) {
const ioMapping = findExtension$1(businessObject, 'zeebe:IoMapping');
if (!ioMapping) {
return defaultValue;
}
// zeebe:Input
if (type === ZEBBE_INPUT_TYPE) {
const inputParameter = findInputParameter$1(ioMapping, binding);
if (inputParameter) {
return inputParameter.get('source');
}
return defaultValue;
}
// zeebe:Output
if (type === ZEEBE_OUTPUT_TYPE) {
const outputParameter = findOutputParameter$1(ioMapping, binding);
if (outputParameter) {
return outputParameter.get('target');
}
return defaultValue;
}
}
// zeebe:taskHeaders
if (type === ZEEBE_TASK_HEADER_TYPE) {
const taskHeaders = findExtension$1(businessObject, 'zeebe:TaskHeaders');
if (!taskHeaders) {
return defaultValue;
}
const header = findTaskHeader(taskHeaders, binding);
if (header) {
return header.get('value');
}
return defaultValue;
}
// zeebe:Property
if (type === ZEEBE_PROPERTY_TYPE) {
const zeebeProperties = findExtension$1(businessObject, 'zeebe:Properties');
if (zeebeProperties) {
const zeebeProperty = findZeebeProperty(zeebeProperties, binding);
if (zeebeProperty) {
return zeebeProperty.get('value');
}
}
return defaultValue;
}
// bpmn:Message#property
if (type === MESSAGE_PROPERTY_TYPE) {
const message = findMessage(businessObject);
const value = message ? message.get(name) : undefined;
if (!isUndefined$1(value)) {
return value;
}
return defaultValue;
}
// bpmn:Message#zeebe:subscription#property
if (type === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
const message = findMessage(businessObject);
if (message) {
const subscription = findExtension$1(message, 'zeebe:Subscription');
const value = subscription ? subscription.get(name) : undefined;
if (!isUndefined$1(value)) {
return subscription.get(name);
}
}
return defaultValue;
}
// bpmn:Signal#property
if (type === SIGNAL_PROPERTY_TYPE) {
const signal = findSignal(businessObject);
const value = signal ? signal.get(name) : undefined;
if (!isUndefined$1(value)) {
return value;
}
return defaultValue;
}
// bpmn:TimerEventDefinition#property
if (type === TIMER_EVENT_DEFINITION_PROPERTY_TYPE) {
const timerEventDefinition = findTimerEventDefinition(businessObject);
if (!timerEventDefinition) {
return defaultValue;
}
const expression = timerEventDefinition.get(name);
if (expression) {
return expression.get('body');
}
return defaultValue;
}
// zeebe:calledElement
if (type === ZEEBE_CALLED_ELEMENT) {
const calledElement = findExtension$1(businessObject, 'zeebe:CalledElement');
return calledElement ? calledElement.get(bindingProperty) : defaultValue;
}
if (type === ZEEBE_LINKED_RESOURCE_PROPERTY) {
const linkedResources = findExtension$1(businessObject, 'zeebe:LinkedResources');
if (!linkedResources) {
return defaultValue;
}
const linkedResource = linkedResources.get('values').find(value => value.get('linkName') === linkName);
return linkedResource ? linkedResource.get(bindingProperty) : defaultValue;
}
// zeebe:userTask
if (type === ZEEBE_USER_TASK) {
const userTask = findExtension$1(businessObject, 'zeebe:userTask');
return userTask ? userTask.get(bindingProperty) : defaultValue;
}
// zeebe:calledDecision
if (type === ZEEBE_CALLED_DECISION) {
const calledDecision = findExtension$1(businessObject, 'zeebe:CalledDecision');
return calledDecision ? calledDecision.get(bindingProperty) : defaultValue;
}
// zeebe:formDefinition
if (type === ZEEBE_FORM_DEFINITION) {
const formDefinition = findExtension$1(businessObject, 'zeebe:FormDefinition');
return formDefinition ? formDefinition.get(bindingProperty) : defaultValue;
}
// zeebe:script
if (type === ZEEBE_SCRIPT_TASK) {
const scriptTask = findExtension$1(businessObject, 'zeebe:Script');
return scriptTask ? scriptTask.get(bindingProperty) : defaultValue;
}
if (type === ZEEBE_ASSIGNMENT_DEFINITION) {
const assignmentDefinition = findExtension$1(businessObject, 'zeebe:AssignmentDefinition');
return assignmentDefinition ? assignmentDefinition.get(bindingProperty) : defaultValue;
}
if (type === ZEEBE_TASK_SCHEDULE) {
const taskSchedule = findExtension$1(businessObject, 'zeebe:TaskSchedule');
return taskSchedule ? taskSchedule.get(bindingProperty) : defaultValue;
}
if (type === ZEEBE_PRIORITY_DEFINITION) {
const priorityDefinition = findExtension$1(businessObject, 'zeebe:PriorityDefinition');
return priorityDefinition ? priorityDefinition.get(bindingProperty) : defaultValue;
}
if (type === ZEEBE_AD_HOC) {
const adHoc = findExtension$1(businessObject, 'zeebe:AdHoc');
return adHoc ? adHoc.get(bindingProperty) : defaultValue;
}
// should never throw as templates are validated beforehand
throw unknownBindingError$1(element, property);
}
/**
* Cast a string value to a boolean if possible. Otherwise return the value.
* Cannot always cast due to FEEL expressions.
*
* @param {string|boolean} value
*/
function getBooleanPropertyValue(value) {
switch (value) {
case 'true':
return true;
case 'false':
return false;
}
return value;
}
const NO_OP = null;
function setPropertyValue(bpmnFactory, commandStack, element, property, value) {
let businessObject = getBusinessObject(element);
const {
binding
} = property;
const {
name,
type,
property: bindingProperty,
linkName
} = binding;
let extensionElements;
let propertyValue;
const commands = [];
const context = {
element,
property
};
// ensure message exists
if (MESSAGE_BINDING_TYPES.includes(type)) {
if (is(businessObject, 'bpmn:Event')) {
businessObject = businessObject.get('eventDefinitions')[0];
}
let message = findMessage(businessObject);
if (!message) {
message = bpmnFactory.create('bpmn:Message', {
'zeebe:modelerTemplate': getTemplateId$1(element)
});
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: businessObject,
properties: {
messageRef: message
}
}
});
}
businessObject = message;
}
// ensure signal exists
if (type === SIGNAL_PROPERTY_TYPE) {
if (is(businessObject, 'bpmn:Event')) {
businessObject = businessObject.get('eventDefinitions')[0];
}
let signal = findSignal(businessObject);
if (!signal) {
signal = bpmnFactory.create('bpmn:Signal', {
'zeebe:modelerTemplate': getTemplateId$1(element)
});
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: businessObject,
properties: {
signalRef: signal
}
}
});
}
businessObject = signal;
}
// handle timer event definition property
if (type === TIMER_EVENT_DEFINITION_PROPERTY_TYPE) {
const timerEventDefinition = findTimerEventDefinition(businessObject);
if (!timerEventDefinition) {
throw new Error('cannot set timer property on element without TimerEventDefinition');
}
let expression = timerEventDefinition.get(name);
if (!expression) {
expression = bpmnFactory.create('bpmn:FormalExpression', {
body: value || ''
});
expression.$parent = timerEventDefinition;
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: timerEventDefinition,
properties: {
[name]: expression
}
}
});
} else {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: expression,
properties: {
body: value || ''
}
}
});
}
}
// ensure extension elements
if (EXTENSION_BINDING_TYPES$1.includes(type)) {
extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = createElement('bpmn:ExtensionElements', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: businessObject,
properties: {
extensionElements
}
}
});
} else {
commands.push(NO_OP);
}
}
// property
if (PROPERTY_BINDING_TYPES.includes(type)) {
const propertyType = businessObject.$descriptor.propertiesByName[name]?.type;
let propertyName = name;
if (!propertyType || propertyType === 'String') {
// make sure we create and don't remove the property
propertyValue = value || '';
} else if (propertyType === 'Boolean') {
propertyValue = !!value;
} else if (propertyType === 'Integer') {
propertyValue = parseInt(value, 10);
if (isNaN(propertyValue)) {
// do not set NaN value
propertyValue = undefined;
}
} else if (EXPRESSION_TYPES.includes(propertyType)) {
const existingExpression = businessObject.get(name);
if (existingExpression && is(existingExpression, 'bpmn:FormalExpression')) {
// re-use existing expression
businessObject = existingExpression;
propertyName = 'body';
propertyValue = value || '';
} else {
propertyValue = createExpression(value, businessObject, bpmnFactory);
}
} else {
// unsupported non-primitive types cannot be set
throw new Error(`cannot set property of type <${propertyType}>`);
}
if (!isUndefined$1(propertyValue)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: businessObject,
properties: {
[propertyName]: propertyValue
}
}
});
} else {
commands.push(NO_OP);
}
}
// zeebe:taskDefinition
if (TASK_DEFINITION_TYPES.includes(type)) {
const oldTaskDefinition = findExtension$1(extensionElements, 'zeebe:TaskDefinition'),
propertyName = getTaskDefinitionPropertyName(binding),
properties = {
[propertyName]: value || ''
};
if (oldTaskDefinition) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
properties,
moddleElement: oldTaskDefinition
}
});
} else {
const newTaskDefinition = createTaskDefinition(properties, bpmnFactory);
newTaskDefinition.$parent = businessObject;
const values = extensionElements.get('values');
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...values, newTaskDefinition]
}
}
});
}
}
if (IO_BINDING_TYPES$1.includes(type)) {
let ioMapping = findExtension$1(extensionElements, 'zeebe:IoMapping');
if (!ioMapping) {
ioMapping = createElement('zeebe:IoMapping', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), ioMapping]
}
}
});
}
// zeebe:Input
if (type === ZEBBE_INPUT_TYPE) {
const oldZeebeInputParameter = findInputParameter$1(ioMapping, binding);
const values = ioMapping.get('inputParameters').filter(value => value !== oldZeebeInputParameter);
// do not persist empty parameters when configured as <optional>
if (shouldUpdate(value, property)) {
const newZeebeInputParameter = createInputParameter$1(binding, value, bpmnFactory);
values.push(newZeebeInputParameter);
}
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: ioMapping,
properties: {
inputParameters: [...values]
}
}
});
}
// zeebe:Output
if (type === ZEEBE_OUTPUT_TYPE) {
const oldZeebeOutputParameter = findOutputParameter$1(ioMapping, binding);
const values = ioMapping.get('outputParameters').filter(value => value !== oldZeebeOutputParameter);
// do not persist empty parameters when configured as <optional>
if (shouldUpdate(value, property)) {
const newZeebeOutputParameter = createOutputParameter$1(binding, value, bpmnFactory);
values.push(newZeebeOutputParameter);
}
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: ioMapping,
properties: {
'outputParameters': [...values]
}
}
});
}
}
// zeebe:taskHeaders
if (type === ZEEBE_TASK_HEADER_TYPE) {
let taskHeaders = findExtension$1(extensionElements, 'zeebe:TaskHeaders');
if (!taskHeaders) {
taskHeaders = createElement('zeebe:TaskHeaders', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), taskHeaders]
}
}
});
}
const oldTaskHeader = findTaskHeader(taskHeaders, binding);
const values = taskHeaders.get('values').filter(value => value !== oldTaskHeader);
// do not persist task headers with empty value
if (!value) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: taskHeaders,
properties: {
values
}
}
});
} else {
const newTaskHeader = createTaskHeader(binding, value, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: taskHeaders,
properties: {
values: [...values, newTaskHeader]
}
}
});
}
}
// zeebe:Property
if (type === ZEEBE_PROPERTY_TYPE) {
let zeebeProperties = findExtension$1(extensionElements, 'zeebe:Properties');
if (!zeebeProperties) {
zeebeProperties = createElement('zeebe:Properties', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), zeebeProperties]
}
}
});
}
const oldZeebeProperty = findZeebeProperty(zeebeProperties, binding);
const properties = zeebeProperties.get('properties').filter(property => property !== oldZeebeProperty);
if (shouldUpdate(value, property)) {
const newZeebeProperty = createZeebeProperty(binding, value, bpmnFactory);
properties.push(newZeebeProperty);
}
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: zeebeProperties,
properties: {
properties
}
}
});
}
// bpmn:Message#zeebe:subscription#property
if (type === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
let subscription = findExtension$1(extensionElements, 'zeebe:Subscription');
const properties = {
[name]: value || ''
};
if (subscription) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: subscription
}
});
} else {
subscription = createElement('zeebe:Subscription', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), subscription]
}
}
});
}
}
// zeebe:calledElement
if (type === ZEEBE_CALLED_ELEMENT) {
let calledElement = findExtension$1(element, 'zeebe:CalledElement');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (calledElement) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: calledElement
}
});
} else {
calledElement = createElement('zeebe:CalledElement', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), calledElement]
}
}
});
}
}
// zeebe:linkedResource
if (type === ZEEBE_LINKED_RESOURCE_PROPERTY) {
let linkedResources = findExtension$1(businessObject, 'zeebe:LinkedResources');
if (!linkedResources) {
linkedResources = createElement('zeebe:LinkedResources', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), linkedResources]
}
}
});
}
let linkedResource = linkedResources.get('values').find(value => value.get('linkName') === linkName);
if (!linkedResource) {
linkedResource = createElement('zeebe:LinkedResource', {
linkName
}, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: linkedResources,
properties: {
values: [...linkedResources.get('values'), linkedResource]
}
}
});
}
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: linkedResource,
properties: {
[bindingProperty]: value
}
}
});
}
// zeebe:calledDecision
if (type === ZEEBE_CALLED_DECISION) {
let calledDecision = findExtension$1(element, 'zeebe:CalledDecision');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (calledDecision) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: calledDecision
}
});
} else {
calledDecision = createElement('zeebe:CalledDecision', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), calledDecision]
}
}
});
}
}
// zeebe:script
if (type === ZEEBE_SCRIPT_TASK) {
let scriptTask = findExtension$1(element, 'zeebe:Script');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (scriptTask) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: scriptTask
}
});
} else {
scriptTask = createElement('zeebe:Script', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), scriptTask]
}
}
});
}
}
// zeebe:formDefinition
if (type === ZEEBE_FORM_DEFINITION) {
let formDefinition = findExtension$1(element, 'zeebe:FormDefinition');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (formDefinition) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: formDefinition
}
});
} else {
formDefinition = createElement('zeebe:FormDefinition', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), formDefinition]
}
}
});
}
}
// zeebe:assignmentDefinition
if (type === ZEEBE_ASSIGNMENT_DEFINITION) {
let assignmentDefinition = findExtension$1(element, 'zeebe:AssignmentDefinition');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (assignmentDefinition) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: assignmentDefinition
}
});
} else {
assignmentDefinition = createElement('zeebe:AssignmentDefinition', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), assignmentDefinition]
}
}
});
}
}
// zeebe:taskSchedule
if (type === ZEEBE_TASK_SCHEDULE) {
let taskSchedule = findExtension$1(element, 'zeebe:TaskSchedule');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (taskSchedule) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: taskSchedule
}
});
} else {
taskSchedule = createElement('zeebe:TaskSchedule', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), taskSchedule]
}
}
});
}
}
// zeebe:priorityDefinition
if (type === ZEEBE_PRIORITY_DEFINITION) {
let priorityDefinition = findExtension$1(element, 'zeebe:PriorityDefinition');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (priorityDefinition) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: priorityDefinition
}
});
} else {
priorityDefinition = createElement('zeebe:PriorityDefinition', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), priorityDefinition]
}
}
});
}
}
// zeebe:adHoc
if (type === ZEEBE_AD_HOC) {
let adHoc = findExtension$1(element, 'zeebe:AdHoc');
const propertyName = binding.property;
const properties = {
[propertyName]: value || ''
};
if (adHoc) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
properties,
moddleElement: adHoc
}
});
} else {
adHoc = createElement('zeebe:AdHoc', properties, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), adHoc]
}
}
});
}
}
if (commands.length) {
const commandsToExecute = commands.filter(command => command !== NO_OP);
commandsToExecute.length && commandStack.execute('element-templates.multi-command-executor', commandsToExecute);
return;
}
// should never throw as templates are validated beforehand
throw unknownBindingError$1(element, property);
}
// TODO(@barmac): fix translate usage (https://github.com/bpmn-io/bpmn-js-element-templates/pull/53#issuecomment-1906203270)
function validateProperty(value, property, translate = defaultTranslate) {
const {
constraints = {},
label
} = property;
const {
maxLength,
minLength,
notEmpty
} = constraints;
if (notEmpty && isEmpty$1(value)) {
return `${label} ${translate('must not be empty.')}`;
}
if (property.feel && isFeel$1(value)) {
return;
}
if (maxLength && (value || '').length > maxLength) {
return `${label} ${translate('must have max length {maxLength}.', {
maxLength
})}`;
}
if (minLength && (value || '').length < minLength) {
return `${label} ${translate('must have min length {minLength}.', {
minLength
})}`;
}
let {
pattern
} = constraints;
if (pattern) {
let message;
if (!isString(pattern)) {
message = pattern.message;
pattern = pattern.value;
}
if (!matchesPattern$1(value, pattern)) {
if (message) {
return `${label} ${translate(message)}`;
}
return `${label} ${translate('must match pattern {pattern}.', {
pattern
})}`;
}
}
}
// helpers
function unknownBindingError$1(element, property) {
const businessObject = getBusinessObject(element);
const id = businessObject.get('id');
const {
binding
} = property;
const {
type
} = binding;
return new Error(`unknown binding <${type}> for element <${id}>, this should never happen`);
}
function isEmpty$1(value) {
if (typeof value === 'string') {
return !value.trim().length;
}
return value === undefined;
}
function matchesPattern$1(string, pattern) {
return new RegExp(pattern).test(string);
}
function defaultTranslate(template, replacements) {
replacements = replacements || {};
return template.replace(/{([^}]+)}/g, function (_, key) {
return replacements[key] || '{' + key + '}';
});
}
function isFeel$1(value) {
return isString(value) && value.trim().startsWith('=');
}
/**
* Based on conditions, remove properties from the template.
*/
function applyConditions(element, elementTemplate) {
const {
properties
} = elementTemplate;
const filteredProperties = properties.filter(property => {
return isPropertyAllowed(element, property) && isConditionMet(element, properties, property);
});
return {
...elementTemplate,
properties: filteredProperties
};
}
function isConditionMet(element, properties, property) {
const {
condition
} = property;
// If no condition is defined, return true.
if (!condition) {
return true;
}
// multiple ("and") conditions
if (condition.allMatch) {
const conditions = condition.allMatch;
return conditions.every(condition => isSimpleConditionMet(element, properties, condition));
}
// single condition
return isSimpleConditionMet(element, properties, condition);
}
function isSimpleConditionMet(element, properties, condition) {
const {
property,
equals,
oneOf,
isActive
} = condition;
if (typeof isActive !== 'undefined') {
const relatedCondition = properties.find(p => p.id === property);
if (!relatedCondition) {
return !isActive;
}
return isActive ? isConditionMet(element, properties, relatedCondition) : !isConditionMet(element, properties, relatedCondition);
}
const propertyObject = getProperty(properties, property);
if (propertyObject) {
// if the property is a FEEL [optional, static, required] property,
// we need to get the value removing the '='
const propertyValue = shouldCastToFeel(propertyObject) || propertyObject.feel === 'required' ? getPropertyValue(element, propertyObject).slice(1) : getPropertyValue(element, propertyObject);
if (hasProperty(condition, 'equals')) {
return compareProperty(propertyObject, propertyValue, equals);
}
if (oneOf) {
return oneOf.includes(propertyValue);
}
}
return false;
}
function getProperty(properties, propertyId) {
const property = properties.find(p => p.id === propertyId);
if (!property) {
return;
}
return property;
}
function isPropertyAllowed(element, property) {
const {
binding
} = property;
const {
type
} = binding;
if (type === 'bpmn:Message#zeebe:subscription#property' && binding.name === 'correlationKey' && is(element, 'bpmn:StartEvent') && !isEventSubProcess(element.parent)) {
return false;
}
return true;
}
function compareProperty(propertyObj, propertyValue, value) {
// Number
if (propertyObj.type === 'Number') {
return Number(propertyValue) === value;
}
// Boolean
else if (propertyObj.type === 'Boolean') {
return Boolean(propertyValue) === value;
}
return propertyValue === value;
}
// helpers //////////////////////
function hasProperty(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
class TemplateElementFactory {
constructor(bpmnFactory, elementFactory) {
this._bpmnFactory = bpmnFactory;
this._elementFactory = elementFactory;
this._providers = {
[PROPERTY_TYPE$1]: PropertyBindingProvider,
[ZEEBE_TASK_DEFINITION_TYPE_TYPE]: TaskDefinitionTypeBindingProvider,
[ZEEBE_TASK_DEFINITION]: TaskDefinitionTypeBindingProvider,
[ZEBBE_PROPERTY_TYPE]: ZeebePropertiesProvider,
[ZEBBE_INPUT_TYPE]: InputBindingProvider,
[ZEEBE_OUTPUT_TYPE]: OutputBindingProvider,
[ZEEBE_TASK_HEADER_TYPE]: TaskHeaderBindingProvider,
[MESSAGE_PROPERTY_TYPE]: MessagePropertyBindingProvider,
[MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE]: MessageZeebeSubscriptionBindingProvider,
[SIGNAL_PROPERTY_TYPE]: SignalPropertyBindingProvider,
[TIMER_EVENT_DEFINITION_PROPERTY_TYPE]: TimerPropertyBindingProvider,
[ZEEBE_CALLED_ELEMENT]: CalledElementBindingProvider,
[ZEEBE_LINKED_RESOURCE_PROPERTY]: LinkedResourcePropertyBindingProvider,
[ZEEBE_USER_TASK]: ZeebeUserTaskBindingProvider,
[ZEEBE_CALLED_DECISION]: CalledDecisionBindingProvider,
[ZEEBE_FORM_DEFINITION]: ZeebeFormDefinitionBindingProvider,
[ZEEBE_SCRIPT_TASK]: ScriptTaskBindingProvider,
[ZEEBE_ASSIGNMENT_DEFINITION]: AssignmentDefinitionBindingProvider,
[ZEEBE_PRIORITY_DEFINITION]: PriorityDefinitionBindingProvider,
[ZEEBE_AD_HOC]: AdHocBindingProvider,
[ZEEBE_TASK_SCHEDULE]: TaskScheduleBindingProvider
};
}
/**
* Create an element based on an element template.
*
* @param {ElementTemplate} template
* @returns {djs.model.Base}
*/
create(template) {
const {
properties
} = template;
// (1) base shape
const element = this._createShape(template);
// (2) apply template
this._setModelerTemplate(element, template);
// (3) apply icon
if (hasIcon(template)) {
this._setModelerTemplateIcon(element, template);
}
// (4) apply properties
this._applyProperties(element, properties);
return element;
}
_createShape(template) {
const {
appliesTo,
elementType = {}
} = template;
const elementFactory = this._elementFactory;
const attrs = {
type: elementType.value || appliesTo[0]
};
if (isSubprocess(attrs.type)) {
attrs.isExpanded = true;
}
// apply eventDefinition
if (elementType.eventDefinition) {
attrs.eventDefinitionType = elementType.eventDefinition;
}
const element = elementFactory.createShape(attrs);
return element;
}
_ensureExtensionElements(element) {
const bpmnFactory = this._bpmnFactory;
const businessObject = getBusinessObject(element);
let extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
values: []
});
extensionElements.$parent = businessObject;
businessObject.set('extensionElements', extensionElements);
}
return extensionElements;
}
_setModelerTemplate(element, template) {
const {
id,
version
} = template;
const businessObject = getBusinessObject(element);
businessObject.set('zeebe:modelerTemplate', id);
businessObject.set('zeebe:modelerTemplateVersion', version);
}
_setModelerTemplateIcon(element, template) {
const {
icon
} = template;
const {
contents
} = icon;
const businessObject = getBusinessObject(element);
businessObject.set('zeebe:modelerTemplateIcon', contents);
}
/**
* Apply properties to a given element.
*
* @param {djs.model.Base} element
* @param {Array<Object>} properties
*/
_applyProperties(element, properties) {
const processedProperties = [];
properties.forEach(property => this._applyProperty(element, property, properties, processedProperties));
}
/**
* Apply a property and its parent properties to an element based on conditions.
*
* @param {djs.model.Base} element
* @param {Object} property
* @param {Array<Object>} properties
* @param {Array<Object>} processedProperties
*/
_applyProperty(element, property, properties, processedProperties) {
// skip if already processed
if (processedProperties.includes(property)) {
return;
}
// apply dependant property first if not already applied
const dependentProperties = findDependentProperties(property, properties);
dependentProperties.forEach(property => this._applyProperty(element, property, properties, processedProperties));
// check condition and apply property if condition is met
if (isConditionMet(element, properties, property)) {
this._bindProperty(property, element);
}
processedProperties.push(property);
}
/**
* Bind property to element.
* @param {Object} property
* @param {djs.Model.Base} element
*/
_bindProperty(property, element) {
const {
binding
} = property;
const {
type: bindingType
} = binding;
const bindingProvider = this._providers[bindingType];
bindingProvider.create(element, {
property,
bpmnFactory: this._bpmnFactory
});
}
}
TemplateElementFactory.$inject = ['bpmnFactory', 'elementFactory'];
// helper ////////////////
function hasIcon(template) {
const {
icon
} = template;
return !!(icon && icon.contents);
}
function findDependentProperties(property, properties) {
const {
condition
} = property;
if (!condition) {
return [];
}
const dependentProperty = findPropertyById(properties, condition.property);
if (dependentProperty) {
return [dependentProperty];
}
return [];
}
function findPropertyById(properties, id) {
return find(properties, function (property) {
return property.id === id;
});
}
var createModule = {
__init__: ['templateElementFactory'],
templateElementFactory: ['type', TemplateElementFactory]
};
/**
* Checks the conditions of an element template and sets/resets the
* corresponding properties on the element.
*/
class ConditionalBehavior extends CommandInterceptor {
/**
* @param {import('diagram-js/lib/core/EventBus').default} eventBus
* @param {import('../ElementTemplates').default} elementTemplates
* @param {import('diagram-js/lib/command/CommandStack').default} commandStack
* @param {import('bpmn-js/lib/features/modeling/BpmnFactory').default} bpmnFactory
* @param {import('didi').Injector} injector
*/
constructor(eventBus, elementTemplates, commandStack, bpmnFactory, injector) {
super(eventBus);
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
this._commandStack = commandStack;
this._bpmnFactory = bpmnFactory;
this._injector = injector;
// (1) save pre-conditional state before updating a property
this.preExecute(['element.updateProperties', 'element.updateModdleProperties', 'element.move'], this._saveConditionalState, true, this);
// (2) check if we need to apply post-conditional updates
//
// if [additional bindings activate] then
// (re-)trigger setting the template
// else
// else we're done
//
this.postExecute(['element.updateProperties', 'element.updateModdleProperties', 'propertiesPanel.zeebe.changeTemplate', 'element.move'], this._applyConditions, true, this);
// (3) set conditions before changing the template
this.preExecute(['propertiesPanel.zeebe.changeTemplate'], this._ensureConditional, true, this);
}
_saveConditionalState(context) {
const {
element
} = context;
const template = this._elementTemplates.get(element);
if (!template) {
return;
}
context.oldTemplateWithConditions = applyConditions(element, template);
}
_applyConditions(context, event) {
const {
element,
newTemplate,
oldTemplateWithConditions
} = context;
// do not apply conditions if we only update meta information
if (isMetaUpdate(event, context)) {
return;
}
const template = this._elementTemplates.get(element);
// new Template is persisted before applying default values,
// new conditions might apply after the defaults are present.
const oldTemplate = oldTemplateWithConditions || newTemplate;
if (!template || !oldTemplate) {
return;
}
const newTemplateWithConditions = applyConditions(element, template);
// verify that new bindings were activated
if (!hasDifferentPropertyBindings(newTemplateWithConditions, oldTemplate)) {
return;
}
// do another pass to apply further conditional bindings
// newTemplate will always be the original template; it is filtered
// at a later step (3)
const changeContext = {
element,
newTemplate: template,
oldTemplate
};
this._commandStack.execute('propertiesPanel.zeebe.changeTemplate', changeContext);
}
_ensureConditional(context) {
const {
element,
newTemplate
} = context;
if (!newTemplate) {
return;
}
// ensure conditions are applied before changing the template.
// `newTemplate` will always be the original template.
context.newTemplate = applyConditions(element, newTemplate);
}
}
ConditionalBehavior.$inject = ['eventBus', 'elementTemplates', 'commandStack', 'bpmnFactory', 'injector'];
// helpers
function hasDifferentPropertyBindings(sourceTemplate, targetTemplate) {
return hasNewProperties(sourceTemplate, targetTemplate) || hasRemovedProperties(sourceTemplate, targetTemplate);
}
function hasNewProperties(sourceTemplate, targetTemplate) {
let properties = targetTemplate.properties;
return properties.some(targetProp => !sourceTemplate.properties.find(sourceProp => compareProps(sourceProp, targetProp)));
}
function hasRemovedProperties(oldTemplate, newTemplate) {
const oldProperties = getMissingProperties(newTemplate, oldTemplate);
// ensure XML properties are mantained for properties with
// different conditions but same bindings
return oldProperties.some(property => !findPropertyWithBinding(newTemplate, property));
}
function getMissingProperties(sourceTemplate, targetTemplate) {
let properties = targetTemplate.properties;
return properties.filter(targetProp => !sourceTemplate.properties.find(sourceProp => compareProps(sourceProp, targetProp)));
}
function compareProps(sourceProp, targetProp) {
return areBindingsEqual(sourceProp.binding, targetProp.binding) && equals(sourceProp.condition, targetProp.condition);
}
function findPropertyWithBinding(template, prop1) {
return template.properties.some(prop2 => areBindingsEqual(prop1.binding, prop2.binding));
}
function normalizeReplacer$1(key, value) {
if (isObject(value)) {
const keys = Object.keys(value).sort();
return keys.reduce((obj, key) => {
obj[key] = value[key];
return obj;
}, {});
}
return value;
}
function areBindingsEqual(binding1, binding2) {
binding1 = normalizeBinding(binding1);
binding2 = normalizeBinding(binding2);
return equals(binding1, binding2);
}
/**
* Convert deprecated binding type to new type.
*/
function normalizeBinding(binding) {
if (binding.type === ZEEBE_TASK_DEFINITION_TYPE_TYPE) {
return {
...binding,
type: ZEEBE_TASK_DEFINITION,
property: 'type'
};
}
return binding;
}
function equals(a, b) {
return JSON.stringify(a, normalizeReplacer$1) === JSON.stringify(b, normalizeReplacer$1);
}
/**
* Checks if the event only updates "meta" properties:
*
* * `zeebe:modelerTemplate`
* * `zeebe:modelerTemplateVersion`
*
* @param {string} event
* @param {any} context
*
* @return {boolean}
*/
function isMetaUpdate(event, context) {
return event === 'element.updateProperties' && Object.keys(context.properties).every(key => ['zeebe:modelerTemplate', 'zeebe:modelerTemplateVersion'].includes(key));
}
/**
* This Behavior checks if the new element's type is in
* the list of elements the template applies to and unlinks
* it if not.
*/
let ReplaceBehavior$1 = class ReplaceBehavior extends CommandInterceptor {
constructor(elementTemplates, injector) {
super(injector.get('eventBus'));
this.postExecuted('shape.replace', function (e) {
var context = e.context,
oldShape = context.oldShape,
oldBo = getBusinessObject(oldShape),
newShape = context.newShape,
newBo = getBusinessObject(newShape);
if (!oldBo.modelerTemplate) {
return;
}
const template = newBo.modelerTemplate;
const version = newBo.modelerTemplateVersion;
const elementTemplate = elementTemplates.get(template, version);
if (!elementTemplate) {
elementTemplates.unlinkTemplate(newShape);
return;
}
const {
appliesTo,
elementType
} = elementTemplate;
if (elementType) {
if (!is(newShape, elementType.value) || shouldUnlinkEvent(newShape, elementType)) {
elementTemplates.unlinkTemplate(newShape);
}
return;
}
const allowed = appliesTo.reduce((allowed, type) => {
return allowed || is(newBo, type);
}, false);
if (!allowed) {
elementTemplates.unlinkTemplate(newShape);
}
});
}
};
ReplaceBehavior$1.$inject = ['elementTemplates', 'injector'];
function shouldUnlinkEvent(newShape, elementType) {
if (!is(newShape, 'bpmn:Event')) {
return false;
}
const {
eventDefinition
} = elementType,
bo = getBusinessObject(newShape),
eventDefinitions = bo.get('eventDefinitions');
if (!eventDefinition) {
return eventDefinitions.length !== 0;
}
return !is(eventDefinitions[0], eventDefinition);
}
/**
* This Behavior monitors changes to timer events and unlinks templates
* when constraints are violated:
* - timeCycle requires non-interrupting for boundary events (cancelActivity: false)
* - timeCycle requires non-interrupting for event subprocess starts (isInterrupting: false)
* - timeDuration is not valid for process-level start events
*/
class TimerTemplateBehavior extends CommandInterceptor {
constructor(eventBus, elementTemplates) {
super(eventBus);
this._elementTemplates = elementTemplates;
// Handle property changes - only react to cancelActivity/isInterrupting changes
this.postExecuted(['element.updateProperties', 'element.updateModdleProperties'], this._handlePropertiesChange, true, this);
// Handle shape move - element may have moved to a different context
this.postExecuted('shape.move', context => {
const element = context.shape;
this._unlinkIfInvalidTimerTemplate(element);
}, true);
// Handle copy-paste: unlink templates that become invalid in the new context
this.postExecuted('elements.create', context => {
if (context.elements) {
context.elements.forEach(el => this._unlinkIfInvalidTimerTemplate(el));
}
}, true);
}
_handlePropertiesChange(context) {
const {
element,
properties
} = context;
const isRelevantPropertyChange = properties && ('cancelActivity' in properties || 'isInterrupting' in properties);
if (!isRelevantPropertyChange) {
return;
}
this._unlinkIfInvalidTimerTemplate(element);
}
_unlinkIfInvalidTimerTemplate(element) {
if (!is(element, 'bpmn:Event')) {
return;
}
const timerEventDefinition = findTimerEventDefinition(element);
if (!timerEventDefinition) {
return;
}
const elementTemplates = this._elementTemplates;
const template = elementTemplates.get(element);
if (!template) {
return;
}
const timerBinding = template.properties.find(property => {
return property.binding?.type === TIMER_EVENT_DEFINITION_PROPERTY_TYPE;
});
if (!timerBinding) {
return;
}
const timerType = timerBinding.binding.name;
if (!isTimerExpressionTypeSupported(timerType, element)) {
elementTemplates.unlinkTemplate(element);
}
}
}
TimerTemplateBehavior.$inject = ['eventBus', 'elementTemplates'];
/**
* Restores the original order of the template properties
* on the moddle element.
*/
class UpdateTemplatePropertiesOrder extends CommandInterceptor {
constructor(eventBus, elementTemplates, commandStack, bpmnFactory) {
super(eventBus);
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
this._commandStack = commandStack;
this._bpmnFactory = bpmnFactory;
this.postExecute(['element.updateProperties', 'element.updateModdleProperties'], this._updatePropertiesOrder, true, this);
}
_updatePropertiesOrder(context) {
const {
element
} = context;
const template = this._elementTemplates.get(element);
const businessObject = getBusinessObject(element);
const commands = [];
if (!template) {
return;
}
const templateProperties = applyConditions(element, template).properties;
// zeebe:Property
const zeebeProperties = findExtension$1(businessObject, 'zeebe:Properties');
if (zeebeProperties) {
this._updateZeebePropertiesOrder(zeebeProperties, templateProperties, commands, context);
}
// zeebe:IoMapping
const ioMapping = findExtension$1(businessObject, 'zeebe:IoMapping');
if (ioMapping) {
// zeebe:Input
this._updateInputOrder(ioMapping, templateProperties, commands, context);
// zeebe:Output
this._updateOutputOrder(ioMapping, templateProperties, commands, context);
}
// zeebe:TaskHeaders
const taskHeaders = findExtension$1(businessObject, 'zeebe:TaskHeaders');
if (taskHeaders) {
this._updateTaskHeadersOrder(taskHeaders, templateProperties, commands, context);
}
if (commands.length) {
const commandsToExecute = commands.filter(command => command !== null);
commandsToExecute.length && this._commandStack.execute('element-templates.multi-command-executor', commandsToExecute);
return;
}
}
_updateZeebePropertiesOrder(zeebeProperties, templateProperties, commands, context) {
const findIndex = (properties, propertyToFind) => properties.findIndex(prop => prop.binding.type == 'zeebe:property' && prop.binding.name === propertyToFind.get('name'));
const properties = zeebeProperties.get('properties');
if (properties.length < 1) return;
let newPropertiesOrder = [...properties];
sortProperties(newPropertiesOrder, findIndex, templateProperties);
if (!arrayEquals(newPropertiesOrder, properties)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: zeebeProperties,
properties: {
properties: newPropertiesOrder
}
}
});
}
}
_updateInputOrder(ioMapping, templateProperties, commands, context) {
const findIndex = (properties, propertyToFind) => properties.findIndex(prop => prop.binding.type == 'zeebe:input' && prop.binding.name === propertyToFind.get('target'));
const inputParameters = ioMapping.get('inputParameters');
if (inputParameters.length < 1) return;
let newInputOrder = [...inputParameters];
sortProperties(newInputOrder, findIndex, templateProperties);
if (!arrayEquals(newInputOrder, inputParameters)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: ioMapping,
properties: {
inputParameters: newInputOrder
}
}
});
}
}
_updateOutputOrder(ioMapping, templateProperties, commands, context) {
const findIndex = (properties, propertyToFind) => properties.findIndex(prop => prop.binding.type == 'zeebe:output' && prop.binding.source === propertyToFind.get('source'));
const outputParameters = ioMapping.get('outputParameters');
if (outputParameters.length < 1) return;
let newOutputOrder = [...outputParameters];
sortProperties(newOutputOrder, findIndex, templateProperties);
if (!arrayEquals(newOutputOrder, outputParameters)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: ioMapping,
properties: {
outputParameters: newOutputOrder
}
}
});
}
}
_updateTaskHeadersOrder(taskHeaders, templateProperties, commands, context) {
const findIndex = (properties, propertyToFind) => properties.findIndex(prop => prop.binding.type == 'zeebe:taskHeader' && prop.binding.key === propertyToFind.get('key'));
const headers = taskHeaders.get('zeebe:values');
if (headers.length < 1) return;
let newHeadersOrder = [...headers];
sortProperties(newHeadersOrder, findIndex, templateProperties);
if (!arrayEquals(newHeadersOrder, headers)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: taskHeaders,
properties: {
values: newHeadersOrder
}
}
});
}
}
}
UpdateTemplatePropertiesOrder.$inject = ['eventBus', 'elementTemplates', 'commandStack', 'bpmnFactory'];
// helpers
function normalizeReplacer(key, value) {
if (isObject(value)) {
const keys = Object.keys(value).sort();
return keys.reduce((obj, key) => {
obj[key] = value[key];
return obj;
}, {});
}
return value;
}
function objectEquals(a, b) {
return JSON.stringify(a, normalizeReplacer) === JSON.stringify(b, normalizeReplacer);
}
function arrayEquals(a, b) {
return a.every((element, idx) => objectEquals(element, b[idx]));
}
function sortProperties(array, findIndex, templateProperties) {
return array.sort((a, b) => {
const aIndex = findIndex(templateProperties, a);
const bIndex = findIndex(templateProperties, b);
return aIndex - bIndex;
});
}
/**
* Handles referenced elements.
*/
class ReferencedElementBehavior extends CommandInterceptor {
constructor(eventBus, elementTemplates, modeling, injector, moddleCopy, bpmnFactory) {
super(eventBus);
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
this._modeling = modeling;
this._injector = injector;
this.postExecuted(['element.updateProperties', 'element.updateModdleProperties'], this._handlePropertiesUpdate, true, this);
this.postExecuted('shape.replace', this._handleReplacement, true, this);
this.postExecuted('shape.delete', this._handleRemoval, true, this);
// copy templated root element when pasting
eventBus.on('copyPaste.pasteElement', function (context) {
const {
referencedRootElement
} = context.descriptor;
if (!referencedRootElement) {
return;
}
if (!getTemplateId$1(referencedRootElement)) {
return;
}
context.descriptor.referencedRootElement = moddleCopy.copyElement(referencedRootElement, bpmnFactory.create(referencedRootElement.$type));
});
}
/**
* Unlink referenced element when template is unlinked.
*/
_handlePropertiesUpdate(context) {
const {
element,
properties
} = context;
if (!canHaveReferencedElement(element)) {
return;
}
if (!(TEMPLATE_ID_ATTR$1 in properties) || isString(properties[TEMPLATE_ID_ATTR$1])) {
return;
}
const bo = getBusinessObject(element);
const referencedElement = findMessage(bo) || findSignal(bo);
if (referencedElement && getTemplateId$1(referencedElement)) {
this._modeling.updateModdleProperties(element, referencedElement, {
[TEMPLATE_ID_ATTR$1]: null
});
}
}
/**
* Remove referenced element when template is removed.
* Keep referenced element when template is replaced.
*/
_handleReplacement(context) {
const {
oldShape,
newShape
} = context;
const oldTemplate = getTemplateId$1(oldShape),
newTemplate = getTemplateId$1(newShape);
if (!canHaveReferencedElement(oldShape) || !oldTemplate) {
return;
}
const bo = getBusinessObject(oldShape);
const message = findMessage(bo);
const signal = findSignal(bo);
if (message && getTemplateId$1(message)) {
if (!newTemplate || !canHaveMessage(newShape)) {
removeRootElement(message, this._injector);
} else {
this._addMessage(newShape, message);
}
}
if (signal && getTemplateId$1(signal)) {
if (!newTemplate || !canHaveSignal(newShape)) {
removeRootElement(signal, this._injector);
} else {
this._addSignal(newShape, signal);
}
}
}
_handleRemoval(context) {
const {
shape
} = context;
if (isLabel(shape)) {
return;
}
if (!canHaveReferencedElement(shape)) {
return;
}
if (!getTemplateId$1(shape)) {
return;
}
const bo = getBusinessObject(shape);
const referencedElement = findMessage(bo) || findSignal(bo);
if (referencedElement && getTemplateId$1(referencedElement)) {
removeRootElement(referencedElement, this._injector);
}
}
_addMessage(element, message) {
const bo = getReferringElement(element);
this._modeling.updateModdleProperties(element, bo, {
'messageRef': message
});
}
_addSignal(element, signal) {
const bo = getReferringElement(element);
this._modeling.updateModdleProperties(element, bo, {
'signalRef': signal
});
}
}
ReferencedElementBehavior.$inject = ['eventBus', 'elementTemplates', 'modeling', 'injector', 'moddleCopy', 'bpmnFactory'];
function canHaveReferencedElement(element) {
// Blank-Events can't have referenced elements
if (is(element, 'bpmn:Event')) {
const bo = getBusinessObject(element);
return bo.get('eventDefinitions') && bo.get('eventDefinitions')[0];
}
return isAny(element, ['bpmn:ReceiveTask', 'bpmn:SendTask']);
}
function canHaveMessage(element) {
if (is(element, 'bpmn:ReceiveTask') || is(element, 'bpmn:SendTask')) {
return true;
}
if (is(element, 'bpmn:Event')) {
const bo = getBusinessObject(element);
const eventDefinitions = bo.get('eventDefinitions');
if (!eventDefinitions || !eventDefinitions.length) {
return false;
}
return is(eventDefinitions[0], 'bpmn:MessageEventDefinition');
}
return false;
}
function canHaveSignal(element) {
if (is(element, 'bpmn:Event')) {
const bo = getBusinessObject(element);
const eventDefinitions = bo.get('eventDefinitions');
if (!eventDefinitions || !eventDefinitions.length) {
return false;
}
return is(eventDefinitions[0], 'bpmn:SignalEventDefinition');
}
return false;
}
function isLabel(element) {
return element.type === 'label';
}
/**
* Handles generated value properties.
*/
class GeneratedValueBehavior extends CommandInterceptor {
constructor(eventBus, elementTemplates, modeling, commandStack, bpmnFactory) {
super(eventBus);
this._eventBus = eventBus;
this._elementTemplates = elementTemplates;
this._modeling = modeling;
this.preExecute('shape.create', context => {
const element = context.shape;
const template = elementTemplates.get(element);
if (!template) {
return;
}
const generatedProps = template.properties.filter(p => p.generatedValue);
generatedProps.forEach(p => {
if (!getPropertyValue(element, p)) {
return;
}
const value = getDefaultValue(p);
setPropertyValue(bpmnFactory, commandStack, element, p, value);
});
}, true);
}
}
GeneratedValueBehavior.$inject = ['eventBus', 'elementTemplates', 'modeling', 'commandStack', 'bpmnFactory'];
/**
* Enforces no variable propagation for templated call activities.
*/
class CalledElementBehavior extends CommandInterceptor {
/**
* @param {*} eventBus
* @param {*} modeling
* @param {import('../ElementTemplates').default} elementTemplates
*/
constructor(eventBus, modeling, elementTemplates) {
super(eventBus);
this._modeling = modeling;
this._elementTemplates = elementTemplates;
this.postExecuted(['element.updateProperties', 'element.updateModdleProperties'], this._ensureNoPropagation, true, this);
}
_ensureNoPropagation(context) {
const {
element
} = context;
if (!this._elementTemplates.get(element)) {
return;
}
if (!is(element, 'bpmn:CallActivity')) {
return;
}
const calledElement = findExtension$1(element, 'zeebe:CalledElement');
if (!calledElement) {
return;
}
for (const property of ['propagateAllChildVariables', 'propagateAllParentVariables']) {
if (calledElement.get(property) !== false) {
this._modeling.updateModdleProperties(element, calledElement, {
[property]: false
});
}
}
}
}
CalledElementBehavior.$inject = ['eventBus', 'modeling', 'elementTemplates'];
/**
* Disable the default Camunda behavior for a newly created element
* if it is a templated `<bpmn:UserTask>`.
*/
class UserTaskBehavior extends CommandInterceptor {
constructor(eventBus, elementTemplates) {
super(eventBus);
this.preExecute('shape.create', function (event) {
const {
context: {
shape,
hints = {}
}
} = event;
if (is(shape, 'bpmn:UserTask') && elementTemplates.get(shape)) {
assign(hints, {
createElementsBehavior: false
});
}
});
}
}
UserTaskBehavior.$inject = ['eventBus', 'elementTemplates'];
var behaviorModule$1 = {
__init__: ['elementTemplatesReplaceBehavior', 'elementTemplatesConditionalBehavior', 'elementTemplatesGeneratedValueBehavior', 'elementTemplatesReferencedElementBehavior', 'elementTemplatesUpdatePropertiesOrderBehavior', 'elementTemplatesCalledElementBehavior', 'elementTemplatesUserTaskBehavior', 'elementTemplatesTimerTemplateBehavior'],
elementTemplatesReplaceBehavior: ['type', ReplaceBehavior$1],
elementTemplatesConditionalBehavior: ['type', ConditionalBehavior],
elementTemplatesGeneratedValueBehavior: ['type', GeneratedValueBehavior],
elementTemplatesReferencedElementBehavior: ['type', ReferencedElementBehavior],
elementTemplatesUpdatePropertiesOrderBehavior: ['type', UpdateTemplatePropertiesOrder],
elementTemplatesCalledElementBehavior: ['type', CalledElementBehavior],
elementTemplatesUserTaskBehavior: ['type', UserTaskBehavior],
elementTemplatesTimerTemplateBehavior: ['type', TimerTemplateBehavior]
};
var coreModule$1 = {
__depends__: [commandsModule$1, behaviorModule$1, createModule],
__init__: ['elementTemplatesLoader'],
elementTemplates: ['type', ElementTemplates],
elementTemplatesLoader: ['type', ElementTemplatesLoader]
};
function getVersionOrDateFromTemplate(template) {
const metadata = template.metadata,
version = template.version;
if (metadata) {
if (!isUndefined$1(metadata.created)) {
return toDateString(metadata.created);
} else if (!isUndefined$1(metadata.updated)) {
return toDateString(metadata.updated);
}
}
if (isUndefined$1(version)) {
return null;
}
return version;
}
// helper ///////////
/**
* Example: 01.01.1900 01:01
*
* @param {number} timestamp
* @returns {string}
*/
function toDateString(timestamp) {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = withLeadingZeros(String(date.getMonth() + 1));
const day = withLeadingZeros(String(date.getDate()));
const hours = withLeadingZeros(String(date.getHours()));
const minutes = withLeadingZeros(String(date.getMinutes()));
return day + '.' + month + '.' + year + ' ' + hours + ':' + minutes;
}
function withLeadingZeros(string) {
return leftPad(string, 2, '0');
}
function leftPad(string, length, character) {
while (string.length < length) {
string = character + string;
}
return string;
}
/**
* @typedef {NoTemplate|KnownTemplate|UnknownTemplate|OutdatedTemplate} TemplateState
*/
/**
* @typedef NoTemplate
* @property {'NO_TEMPLATE'} type
*
* @typedef KnownTemplate
* @property {'KNOWN_TEMPLATE'} type
* @property {object} template
*
* @typedef UnknownTemplate
* @property {'UNKNOWN_TEMPLATE'} type
* @property {string} templateId
*
* @typedef OutdatedTemplate
* @property {'OUTDATED_TEMPLATE'} type
* @property {object} template
* @property {object} newerTemplate
*/
/**
* Factory to create an element templates group.
*
* @param {object} [props]
* @param {function} props.getTemplateId
* @param {function} [props.unlinkTemplate]
* @param {function} [props.updateTemplate]
*/
function createElementTemplatesGroup(props = {}) {
const {
getTemplateId
} = props;
return function ElementTemplatesGroup(props) {
const {
id,
label,
element,
entries = []
} = props;
const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
const empty = !entries.length;
const toggleOpen = () => !empty && setOpen(!open);
return jsxs("div", {
class: "bio-properties-panel-group bio-properties-panel-templates-group",
"data-group-id": 'group-' + id,
children: [jsxs("div", {
class: classnames('bio-properties-panel-group-header', {
empty,
open: open && !empty
}),
onClick: toggleOpen,
children: [jsx("div", {
class: "bio-properties-panel-group-header-title",
children: label
}), jsxs("div", {
class: "bio-properties-panel-group-header-buttons",
children: [jsx(TemplateGroupButtons, {
element: element,
getTemplateId: getTemplateId
}), !empty && jsx(SectionToggle, {
open: open
})]
})]
}), jsx("div", {
class: classnames('bio-properties-panel-group-entries', {
open: open && !empty
}),
children: entries.map(entry => {
const {
component: Component,
id
} = entry;
return createElement$1(Component, {
...entry,
key: id,
element: element
});
})
})]
});
};
}
function SectionToggle({
open
}) {
return jsx(HeaderButton, {
title: "Toggle section",
class: "bio-properties-panel-arrow",
children: jsx(ArrowIcon, {
class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
})
});
}
/**
*
* @param {object} props
* @param {object} props.element
* @param {function} props.getTemplateId
* @param {function} props.unlinkTemplate
* @param {function} props.updateTemplate
*/
function TemplateGroupButtons({
element,
getTemplateId
}) {
const elementTemplates = useService('elementTemplates');
const templateState = getTemplateState(elementTemplates, element, getTemplateId);
if (templateState.type === 'NO_TEMPLATE') {
return jsx(SelectEntryTemplate, {
element: element
});
} else if (templateState.type === 'KNOWN_TEMPLATE') {
return jsx(AppliedTemplate, {
element: element
});
} else if (templateState.type === 'UNKNOWN_TEMPLATE') {
return jsx(UnknownTemplate, {
element: element
});
} else if (templateState.type === 'DEPRECATED_TEMPLATE') {
return jsx(DeprecatedTemplate, {
element: element,
templateState: templateState
});
} else if (templateState.type === 'INCOMPATIBLE_TEMPLATE') {
return jsx(IncompatibleTemplate, {
element: element
});
} else if (templateState.type === 'OUTDATED_TEMPLATE') {
return jsx(OutdatedTemplate, {
element: element,
templateState: templateState
});
}
}
function SelectEntryTemplate({
element
}) {
const translate = useService('translate');
const eventBus = useService('eventBus');
const selectTemplate = () => eventBus.fire('elementTemplates.select', {
element
});
return jsxs(HeaderButton, {
title: "Select a template",
class: "bio-properties-panel-select-template-button",
onClick: selectTemplate,
children: [jsx(CreateIcon, {}), jsx("span", {
children: translate('Select')
})]
});
}
function AppliedTemplate({
element
}) {
const translate = useService('translate'),
elementTemplates = useService('elementTemplates');
const menuItems = [{
entry: translate('Unlink'),
action: () => elementTemplates.unlinkTemplate(element)
}, {
entry: jsx(RemoveTemplate, {}),
action: () => elementTemplates.removeTemplate(element)
}];
return jsx(DropdownButton, {
menuItems: menuItems,
class: "bio-properties-panel-applied-template-button",
children: jsxs(HeaderButton, {
children: [jsx("span", {
children: translate('Applied')
}), jsx(ArrowIcon, {
class: "bio-properties-panel-arrow-down"
})]
})
});
}
function RemoveTemplate() {
const translate = useService('translate');
return jsx("span", {
class: "bio-properties-panel-remove-template",
children: translate('Remove')
});
}
function UnknownTemplate({
element
}) {
const translate = useService('translate'),
elementTemplates = useService('elementTemplates');
const menuItems = [{
entry: jsx(NotFoundText, {})
}, {
separator: true
}, {
entry: translate('Unlink'),
action: () => elementTemplates.unlinkTemplate(element)
}, {
entry: jsx(RemoveTemplate, {}),
action: () => elementTemplates.removeTemplate(element)
}];
return jsx(DropdownButton, {
menuItems: menuItems,
class: "bio-properties-panel-template-not-found",
children: jsxs(HeaderButton, {
children: [jsx("span", {
children: translate('Not found')
}), jsx(ArrowIcon, {
class: "bio-properties-panel-arrow-down"
})]
})
});
}
function NotFoundText() {
const translate = useService('translate');
return jsx("div", {
class: "bio-properties-panel-template-not-found-text",
children: translate('The template applied was not found. Therefore, its properties cannot be shown. Unlink to access the data.')
});
}
/**
*
* @param {object} props
* @param {object} element
* @param {UnknownTemplate} templateState
* @param {function} unlinkTemplate
* @param {function} updateTemplate
*/
function OutdatedTemplate({
element,
templateState
}) {
const {
newerTemplate,
compatible
} = templateState;
const translate = useService('translate'),
elementTemplates = useService('elementTemplates');
const menuItems = [{
entry: jsx(UpdateAvailableText, {
newerTemplate: newerTemplate,
compatible: compatible
})
}, {
separator: true
}, {
entry: translate('Update'),
action: () => elementTemplates.applyTemplate(element, newerTemplate)
}, {
entry: translate('Unlink'),
action: () => elementTemplates.unlinkTemplate(element)
}, {
entry: jsx(RemoveTemplate, {}),
action: () => elementTemplates.removeTemplate(element)
}];
const cls = compatible ? 'bio-properties-panel-template-update-available' : 'bio-properties-panel-template-incompatible';
const text = compatible ? translate('Update available') : translate('Incompatible');
return jsx(DropdownButton, {
menuItems: menuItems,
class: cls,
children: jsxs(HeaderButton, {
children: [jsx("span", {
children: text
}), jsx(ArrowIcon, {
class: "bio-properties-panel-arrow-down"
})]
})
});
}
function UpdateAvailableText({
newerTemplate,
compatible
}) {
const translate = useService('translate');
const text = compatible ? translate('A new version of the template is available: {templateVersion}', {
templateVersion: getVersionOrDateFromTemplate(newerTemplate)
}) : translate('A version of this template is available that supports your environment: {templateVersion}', {
templateVersion: getVersionOrDateFromTemplate(newerTemplate)
});
return jsx("div", {
class: "bio-properties-panel-template-update-available-text",
children: text
});
}
function DeprecatedTemplate({
element,
templateState
}) {
const {
template
} = templateState;
const translate = useService('translate'),
elementTemplates = useService('elementTemplates');
const menuItems = [{
entry: jsx(DeprecationWarning, {
template: template
})
}, {
separator: true
}, {
entry: translate('Unlink'),
action: () => elementTemplates.unlinkTemplate(element)
}, {
entry: jsx(RemoveTemplate, {}),
action: () => elementTemplates.removeTemplate(element)
}];
return jsx(DropdownButton, {
menuItems: menuItems,
class: "bio-properties-panel-deprecated-template-button",
children: jsxs(HeaderButton, {
children: [jsx("span", {
children: translate('Deprecated')
}), jsx(ArrowIcon, {
class: "bio-properties-panel-arrow-down"
})]
})
});
}
function DeprecationWarning({
template
}) {
const translate = useService('translate');
const {
message = translate('This template is deprecated.'),
documentationRef
} = template.deprecated;
return jsxs("div", {
class: "bio-properties-panel-deprecated-template-text",
children: [message, documentationRef && jsxs(Fragment, {
children: ["\xA0", jsx("a", {
href: documentationRef,
children: jsx(DocumentationIcon, {})
})]
})]
});
}
function DocumentationIcon() {
return jsx("svg", {
width: "12",
height: "12",
viewBox: "0 0 12 12",
fill: "none",
xmlns: "http://www.w3.org/2000/svg",
children: jsx("path", {
fillRule: "evenodd",
clipRule: "evenodd",
d: "M10.6368 10.6375V5.91761H11.9995V10.6382C11.9995 10.9973 11.8623 11.3141 11.5878 11.5885C11.3134 11.863 10.9966 12.0002 10.6375 12.0002H1.36266C0.982345 12.0002 0.660159 11.8681 0.396102 11.6041C0.132044 11.34 1.52588e-05 11.0178 1.52588e-05 10.6375V1.36267C1.52588e-05 0.98236 0.132044 0.660173 0.396102 0.396116C0.660159 0.132058 0.982345 2.95639e-05 1.36266 2.95639e-05H5.91624V1.36267H1.36266V10.6375H10.6368ZM12 0H7.2794L7.27873 1.36197H9.68701L3.06507 7.98391L4.01541 8.93425L10.6373 2.31231V4.72059H12V0Z",
fill: "#818798"
})
});
}
function IncompatibleTemplate({
element
}) {
const translate = useService('translate'),
elementTemplates = useService('elementTemplates');
const menuItems = [{
entry: jsx(IncompatibleText, {})
}, {
separator: true
}, {
entry: translate('Unlink'),
action: () => elementTemplates.unlinkTemplate(element)
}, {
entry: jsx(RemoveTemplate, {}),
action: () => elementTemplates.removeTemplate(element)
}];
return jsx(DropdownButton, {
menuItems: menuItems,
class: "bio-properties-panel-template-incompatible",
children: jsxs(HeaderButton, {
children: [jsx("span", {
children: translate('Incompatible')
}), jsx(ArrowIcon, {
class: "bio-properties-panel-arrow-down"
})]
})
});
}
function IncompatibleText() {
const translate = useService('translate');
return jsx("div", {
class: "bio-properties-panel-template-incompatible-text",
children: translate('No compatible version of this template was found for your environment. Unlink to access the template’s data.')
});
}
// helper //////
/**
* Determine template state in the current element.
*
* @param {object} elementTemplates
* @param {object} element
* @param {function} getTemplateId
* @returns {TemplateState}
*/
function getTemplateState(elementTemplates, element, getTemplateId) {
const templateId = getTemplateId(element),
template = elementTemplates.get(element);
if (!templateId) {
return {
type: 'NO_TEMPLATE'
};
}
if (!template) {
return {
type: 'UNKNOWN_TEMPLATE',
templateId
};
}
if (template.deprecated) {
return {
type: 'DEPRECATED_TEMPLATE',
template
};
}
const compatible = elementTemplates.isCompatible(template);
const latestTemplate = elementTemplates.getLatest(templateId, {
deprecated: true
})[0];
if (latestTemplate && latestTemplate !== template) {
return {
type: 'OUTDATED_TEMPLATE',
template,
newerTemplate: latestTemplate,
compatible
};
}
if (!compatible) {
return {
type: 'INCOMPATIBLE_TEMPLATE',
template
};
}
return {
type: 'KNOWN_TEMPLATE',
template
};
}
function TemplateProps({
element,
elementTemplates,
getTemplateId,
getTemplateVersion
}) {
const localTemplate = elementTemplates.get(element);
const template = localTemplate || {
id: getTemplateId(element),
version: getTemplateVersion(element)
};
// no template configured
if (!template.id) {
return [];
}
return [!localTemplate && {
id: 'template-id',
component: TemplateId,
template
}, {
id: 'template-name',
component: TemplateName,
template
}, {
id: 'template-version',
component: TemplateVersion,
template
}, {
id: 'template-description',
component: TemplateDescription,
template
}];
}
function TemplateName({
id,
template
}) {
const translate = useService('translate');
const {
name
} = template;
return name ? jsx(TextEntry, {
id: id,
label: translate('Name'),
content: name
}) : null;
}
function TemplateId({
id,
template
}) {
const translate = useService('translate');
return jsx(TextEntry, {
id: id,
label: translate('ID'),
content: template.id
});
}
function TemplateVersion({
id,
template
}) {
const translate = useService('translate');
const version = getVersionOrDateFromTemplate(template);
return version ? jsx(TextEntry, {
id: id,
label: translate('Version'),
content: version
}) : null;
}
function TemplateDescription({
id,
template
}) {
const translate = useService('translate');
const {
description
} = template;
return description ? jsx(TextEntry, {
id: id,
label: translate('Description'),
content: template.description
}) : null;
}
function TextEntry({
id,
label,
content
}) {
return jsxs("div", {
"data-entry-id": id,
class: "bio-properties-panel-entry bio-properties-panel-text-entry",
children: [jsx("span", {
class: "bio-properties-panel-label",
children: label
}), jsx("span", {
class: "bio-properties-panel-text-entry__content",
children: content
})]
});
}
var e,
o = {};
function n(r, t, e) {
if (3 === r.nodeType) {
var o = "textContent" in r ? r.textContent : r.nodeValue || "";
if (false !== n.options.trim) {
var a = 0 === t || t === e.length - 1;
if ((!(o = o.match(/^[\s\n]+$/g) && "all" !== n.options.trim ? " " : o.replace(/(^[\s\n]+|[\s\n]+$)/g, "all" === n.options.trim || a ? "" : " ")) || " " === o) && e.length > 1 && a) return null;
}
return o;
}
if (1 !== r.nodeType) return null;
var p = String(r.nodeName).toLowerCase();
if ("script" === p && !n.options.allowScripts) return null;
var l,
s,
u = n.h(p, function (r) {
var t = r && r.length;
if (!t) return null;
for (var e = {}, o = 0; o < t; o++) {
var a = r[o],
i = a.name,
p = a.value;
"on" === i.substring(0, 2) && n.options.allowEvents && (p = new Function(p)), e[i] = p;
}
return e;
}(r.attributes), (s = (l = r.childNodes) && Array.prototype.map.call(l, n).filter(i)) && s.length ? s : null);
return n.visitor && n.visitor(u), u;
}
var a,
i = function (r) {
return r;
},
p = {};
function l(r) {
var t = (r.type || "").toLowerCase(),
e = l.map;
e && e.hasOwnProperty(t) ? (r.type = e[t], r.props = Object.keys(r.props || {}).reduce(function (t, e) {
var o;
return t[o = e, o.replace(/-(.)/g, function (r, t) {
return t.toUpperCase();
})] = r.props[e], t;
}, {})) : r.type = t.replace(/[^a-z0-9-]/i, "");
}
var Markup = (function (t) {
function i() {
t.apply(this, arguments);
}
return t && (i.__proto__ = t), (i.prototype = Object.create(t && t.prototype)).constructor = i, i.setReviver = function (r) {
a = r;
}, i.prototype.shouldComponentUpdate = function (r) {
var t = this.props;
return r.wrap !== t.wrap || r.type !== t.type || r.markup !== t.markup;
}, i.prototype.setComponents = function (r) {
if (this.map = {}, r) for (var t in r) if (r.hasOwnProperty(t)) {
var e = t.replace(/([A-Z]+)([A-Z][a-z0-9])|([a-z0-9]+)([A-Z])/g, "$1$3-$2$4").toLowerCase();
this.map[e] = r[t];
}
}, i.prototype.render = function (t) {
var i = t.wrap;
void 0 === i && (i = true);
var s,
u = t.type,
c = t.markup,
m = t.components,
v = t.reviver,
f = t.onError,
d = t["allow-scripts"],
h$1 = t["allow-events"],
y = t.trim,
w = function (r, t) {
var e = {};
for (var o in r) Object.prototype.hasOwnProperty.call(r, o) && -1 === t.indexOf(o) && (e[o] = r[o]);
return e;
}(t, ["wrap", "type", "markup", "components", "reviver", "onError", "allow-scripts", "allow-events", "trim"]),
C = v || this.reviver || this.constructor.prototype.reviver || a || h;
this.setComponents(m);
var g = {
allowScripts: d,
allowEvents: h$1,
trim: y
};
try {
s = function (r, t, a, i, s) {
var u = function (r, t) {
var o,
n,
a,
i,
p = "html" === t ? "text/html" : "application/xml";
"html" === t ? (i = "body", a = "<!DOCTYPE html>\n<html><body>" + r + "</body></html>") : (i = "xml", a = '<?xml version="1.0" encoding="UTF-8"?>\n<xml>' + r + "</xml>");
try {
o = new DOMParser().parseFromString(a, p);
} catch (r) {
n = r;
}
if (o || "html" !== t || ((o = e || (e = function () {
if (document.implementation && document.implementation.createHTMLDocument) return document.implementation.createHTMLDocument("");
var r = document.createElement("iframe");
return r.style.cssText = "position:absolute; left:0; top:-999em; width:1px; height:1px; overflow:hidden;", r.setAttribute("sandbox", "allow-forms"), document.body.appendChild(r), r.contentWindow.document;
}())).open(), o.write(a), o.close()), o) {
var l = o.getElementsByTagName(i)[0],
s = l.firstChild;
return r && !s && (l.error = "Document parse failed."), s && "parsererror" === String(s.nodeName).toLowerCase() && (s.removeChild(s.firstChild), s.removeChild(s.lastChild), l.error = s.textContent || s.nodeValue || n || "Unknown error", l.removeChild(s)), l;
}
}(r, t);
if (u && u.error) throw new Error(u.error);
var c = u && u.body || u;
l.map = i || p;
var m = c && function (r, t, e, a) {
return n.visitor = t, n.h = e, n.options = a || o, n(r);
}(c, l, a, s);
return l.map = null, m && m.props && m.props.children || null;
}(c, u, C, this.map, g);
} catch (r) {
f ? f({
error: r
}) : "undefined" != typeof console && console.error && console.error("preact-markup: " + r);
}
if (false === i) return s || null;
var x = w.hasOwnProperty("className") ? "className" : "class",
b = w[x];
return b ? b.splice ? b.splice(0, 0, "markup") : "string" == typeof b ? w[x] += " markup" : "object" == typeof b && (b.markup = true) : w[x] = "markup", C("div", w, s || null);
}, i;
})(Component);
/**
* Copied from existing form-js#Sanitizer
* cf. https://github.com/bpmn-io/form-js/blob/master/packages/form-js-viewer/src/render/components/Sanitizer.js
*/
const NODE_TYPE_TEXT = 3,
NODE_TYPE_ELEMENT = 1;
const ALLOWED_NODES = ['h1', 'h2', 'h3', 'h4', 'h5', 'span', 'em', 'a', 'p', 'div', 'ul', 'ol', 'li', 'hr', 'blockquote', 'img', 'pre', 'code', 'br', 'strong'];
const ALLOWED_ATTRIBUTES = ['align', 'alt', 'class', 'href', 'id', 'name', 'rel', 'target', 'src'];
const ALLOWED_URI_PATTERN = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; // eslint-disable-line no-useless-escape
const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g; // eslint-disable-line no-control-regex
const FORM_ELEMENT = document.createElement('form');
/**
* Sanitize a HTML string and return the cleaned, safe version.
*
* @param {string} html
* @return {string}
*/
function sanitizeHTML(html) {
const doc = new DOMParser().parseFromString(`<!DOCTYPE html>\n<html><body><div>${html}`, 'text/html');
doc.normalize();
const element = doc.body.firstChild;
if (element) {
sanitizeNode(/** @type Element */element);
return new XMLSerializer().serializeToString(element);
} else {
// handle the case that document parsing
// does not work at all, due to HTML gibberish
return '';
}
}
/**
* Recursively sanitize a HTML node, potentially
* removing it, its children or attributes.
*
* Inspired by https://github.com/developit/snarkdown/issues/70
* and https://github.com/cure53/DOMPurify. Simplified
* for our use-case.
*
* @param {Element} node
*/
function sanitizeNode(node) {
// allow text nodes
if (node.nodeType === NODE_TYPE_TEXT) {
return;
}
// disallow all other nodes but Element
if (node.nodeType !== NODE_TYPE_ELEMENT) {
return node.remove();
}
const lcTag = node.tagName.toLowerCase();
// disallow non-whitelisted tags
if (!ALLOWED_NODES.includes(lcTag)) {
return node.remove();
}
const attributes = node.attributes;
// clean attributes
for (let i = attributes.length; i--;) {
const attribute = attributes[i];
const name = attribute.name;
const lcName = name.toLowerCase();
// normalize node value
const value = attribute.value.trim();
node.removeAttribute(name);
const valid = isValidAttribute(lcTag, lcName, value);
if (valid) {
node.setAttribute(name, value);
}
}
// force noopener on target="_blank" links
if (lcTag === 'a' && node.getAttribute('target') === '_blank' && node.getAttribute('rel') !== 'noopener') {
node.setAttribute('rel', 'noopener');
}
for (let i = node.childNodes.length; i--;) {
sanitizeNode(/** @type Element */node.childNodes[i]);
}
}
/**
* Validates attributes for validity.
*
* @param {string} lcTag
* @param {string} lcName
* @param {string} value
* @return {boolean}
*/
function isValidAttribute(lcTag, lcName, value) {
// disallow most attributes based on whitelist
if (!ALLOWED_ATTRIBUTES.includes(lcName)) {
return false;
}
// disallow "DOM clobbering" / polution of document and wrapping form elements
if ((lcName === 'id' || lcName === 'name') && (value in document || value in FORM_ELEMENT)) {
return false;
}
if (lcName === 'target' && value !== '_blank') {
return false;
}
// allow valid url links only
if (lcName === 'href' && !ALLOWED_URI_PATTERN.test(value.replace(ATTR_WHITESPACE_PATTERN, ''))) {
return false;
}
return true;
}
function PropertyTooltip(props) {
const {
tooltip
} = props;
return tooltip && jsx(Markup, {
markup: sanitizeHTML(tooltip),
trim: false
});
}
function usePropertyAccessors(bpmnFactory, commandStack, element, property) {
const directSet = useCallback(propertySetter$1(bpmnFactory, commandStack, element, property), [bpmnFactory, commandStack, element, property]);
const directGet = useCallback(propertyGetter$1(element, property), [element, property]);
const [isFeelEnabled, setIsFeelEnabled] = useState(feelEnabled(property, directGet()));
const handleFeelToggle = useCallback(value => {
if (!isFeelEnabled && typeof value === 'string' && value.startsWith('=')) {
setIsFeelEnabled(true);
}
if (isFeelEnabled && (typeof value !== 'string' || !value.startsWith('='))) {
setIsFeelEnabled(false);
}
}, [isFeelEnabled]);
const set = useCallback((value, error) => {
handleFeelToggle(value);
directSet(toFeelExpression(value, property.type));
}, [directSet, property, handleFeelToggle]);
const get = useCallback(() => {
if (isFeelEnabled) {
return directGet();
}
return fromFeelExpression(directGet(), property.type);
}, [directGet, property, isFeelEnabled]);
if (!shouldCastToFeel(property)) {
return [directGet, directSet];
}
return [get, set];
}
const fromFeelExpression = (value, type) => {
if (typeof value === 'undefined') {
return value;
}
if (typeof value === 'string' && value.startsWith('=')) {
value = value.slice(1);
}
if (type === 'Number') {
return Number(value);
}
if (type === 'Boolean') {
return value !== 'false';
}
return value;
};
const feelEnabled = (property, value) => {
if (!shouldCastToFeel(property)) {
return true;
}
if (property.type === 'Boolean') {
return !(value === '=true' || value === '=false');
}
if (property.type === 'Number') {
return isNaN(fromFeelExpression(value, property.type));
}
return true;
};
function propertyGetter$1(element, property) {
return function getValue() {
return getPropertyValue(element, property);
};
}
function propertySetter$1(bpmnFactory, commandStack, element, property) {
return function setValue(value) {
return setPropertyValue(bpmnFactory, commandStack, element, property, value);
};
}
function propertyValidator$1(translate, property) {
return value => validateProperty(value, property, translate);
}
function groupByGroupId$1(properties) {
return groupBy(properties, 'group');
}
function findCustomGroup$1(groups, id) {
return find(groups, g => g.id === id);
}
/**
* Is the given property executed by the engine?
*
* @param { { binding: { type: string } } } property
* @return {boolean}
*/
function isExternalProperty(property) {
return ['zeebe:property', 'zeebe:taskHeader'].includes(property.binding.type);
}
function PropertyDescription(props) {
const {
description
} = props;
const translate = useService('translate');
return description && jsx(Markup, {
markup: sanitizeHTML(translate(description)),
trim: false
});
}
function TextAreaProperty$1(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
feel,
language,
placeholder,
tooltip
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
return TextAreaEntry({
debounce,
element,
id,
label,
feel,
placeholder,
monospace: !!language,
autoResize: true,
description: PropertyDescription({
description
}),
getValue: propertyGetter$1(element, property),
setValue: propertySetter$1(bpmnFactory, commandStack, element, property),
validate: propertyValidator$1(translate, property),
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function StringProperty$1(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
feel,
placeholder,
tooltip
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
return TextFieldEntry({
debounce,
element,
getValue: propertyGetter$1(element, property),
id,
label,
feel,
placeholder,
description: PropertyDescription({
description
}),
setValue: propertySetter$1(bpmnFactory, commandStack, element, property),
validate: propertyValidator$1(translate, property),
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function useServiceIfAvailable(service, fallback) {
const resolved = useService(service, false);
if (!resolved) {
return fallback;
}
return resolved;
}
function withVariableContext(Component) {
return props => {
const {
bpmnElement,
element
} = props;
const bo = (bpmnElement || element).businessObject;
const [variables, setVariables] = useState([]);
const eventBus = useService('eventBus');
const variableResolver = useServiceIfAvailable('variableResolver', {
getVariablesForElement
});
useEffect(() => {
const extractVariables = async () => {
const variables = await variableResolver.getVariablesForElement(bo);
setVariables(variables.map(variable => {
return {
...variable,
info: variable.info || variable.origin && 'Written in ' + variable.origin.map(origin => origin.name || origin.id).join(', ')
};
}));
};
// The callback must return undefined, so the event propagation is not canceled.
// Cf. https://github.com/camunda/camunda-modeler/issues/3392
const callback = () => {
extractVariables();
};
eventBus.on('commandStack.changed', callback);
callback();
return () => {
eventBus.off('commandStack.changed', callback);
};
}, [bo]);
return jsx(Component, {
...props,
variables: variables
});
};
}
function withTooltipContainer(Component) {
return props => {
const tooltipContainer = useMemo(() => {
const config = useService('config');
return config && config.propertiesPanel && config.propertiesPanel.feelTooltipContainer;
}, []);
return jsx(Component, {
...props,
tooltipContainer: tooltipContainer
});
};
}
const FeelEntry = withTooltipContainer(FeelEntry$1);
const FeelTextAreaEntry = withTooltipContainer(FeelTextAreaEntry$1);
const FeelEntryWithVariableContext = withVariableContext(FeelEntry);
const FeelTextAreaEntryWithVariableContext = withVariableContext(FeelTextAreaEntry);
function FeelProperty(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
feel,
placeholder,
tooltip
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
const TextFieldComponent = !isExternalProperty(property) ? FeelEntryWithVariableContext : FeelEntry;
return TextFieldComponent({
debounce,
element,
getValue: propertyGetter$1(element, property),
id,
label,
feel,
placeholder,
description: PropertyDescription({
description
}),
setValue: propertySetter$1(bpmnFactory, commandStack, element, property),
validate: propertyValidator$1(translate, property),
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function FeelTextAreaProperty(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
feel,
placeholder,
tooltip
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
const TextAreaComponent = !isExternalProperty(property) ? FeelTextAreaEntryWithVariableContext : FeelTextAreaEntry;
return TextAreaComponent({
debounce,
element,
getValue: propertyGetter$1(element, property),
id,
label,
feel,
placeholder,
description: PropertyDescription({
description
}),
setValue: propertySetter$1(bpmnFactory, commandStack, element, property),
validate: propertyValidator$1(translate, property),
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function DropdownProperty$1(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
tooltip
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
translate = useService('translate');
const getOptions = () => {
const {
choices,
optional
} = property;
let dropdownOptions = [];
dropdownOptions = choices.map(({
name,
value
}) => {
return {
label: name,
value
};
});
if (optional) {
dropdownOptions = [{
label: '',
value: undefined
}, ...dropdownOptions];
}
return dropdownOptions;
};
return SelectEntry({
element,
id,
label,
getOptions,
description: PropertyDescription({
description
}),
getValue: propertyGetter$1(element, property),
setValue: propertySetter$1(bpmnFactory, commandStack, element, property),
validate: propertyValidator$1(translate, property),
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function BooleanProperty$1(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
tooltip,
feel
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
const Component = feel === 'optional' ? FeelCheckboxEntry : CheckboxEntry;
const [getValue, setValue] = usePropertyAccessors(bpmnFactory, commandStack, element, property);
return Component({
element,
debounce,
translate,
getValue,
id,
label,
description: PropertyDescription({
description
}),
setValue,
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function NumberProperty(props) {
const {
element,
id,
property
} = props;
const {
description,
editable,
label,
feel,
tooltip
} = property;
const Component = feel === 'optional' ? FeelNumberEntry : NumberFieldEntry;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
const [getValue, setValue] = usePropertyAccessors(bpmnFactory, commandStack, element, property);
const validate = useCallback(value => {
if (shouldCastToFeel(property) && isNumber(value) && value.toString().includes('e')) {
return translate('Scientific notation is disallowed in FEEL.');
}
const defaultValidator = propertyValidator$1(translate, property);
return defaultValidator(value);
}, [translate, property]);
return Component({
debounce,
element,
getValue,
step: 'any',
id,
label,
description: PropertyDescription({
description
}),
setValue,
validate: validate,
disabled: editable === false,
tooltip: PropertyTooltip({
tooltip
})
});
}
function CustomProperties$1(props) {
const {
element,
elementTemplate,
injector
} = props;
const translate = injector.get('translate');
const groups = [];
const {
id,
properties,
groups: propertyGroups
} = elementTemplate;
// (1) group properties by group id
const groupedProperties = groupByGroupId$1(properties);
const defaultProps = [];
forEach(groupedProperties, (properties, groupId) => {
const group = findCustomGroup$1(propertyGroups, groupId);
if (!group) {
return defaultProps.push(...properties);
}
addCustomGroup$1(groups, {
element,
id: `ElementTemplates__CustomProperties-${groupId}`,
label: translate(group.label),
openByDefault: group.openByDefault,
properties: properties,
templateId: `${id}-${groupId}`,
tooltip: PropertyTooltip({
tooltip: group.tooltip
})
});
});
// (2) add default custom props
if (defaultProps.length) {
addCustomGroup$1(groups, {
id: 'ElementTemplates__CustomProperties',
label: translate('Custom properties'),
element,
properties: defaultProps,
templateId: id
});
}
return groups;
}
function addCustomGroup$1(groups, props) {
const {
element,
id,
label,
openByDefault = false,
properties,
templateId,
tooltip
} = props;
const customPropertiesGroup = {
id,
label,
component: Group,
entries: [],
shouldOpen: openByDefault,
tooltip
};
properties.forEach((property, index) => {
const entry = createCustomEntry$1(`custom-entry-${templateId}-${index}`, element, property);
if (entry) {
customPropertiesGroup.entries.push(entry);
}
});
if (customPropertiesGroup.entries.length) {
groups.push(customPropertiesGroup);
}
}
function createCustomEntry$1(id, element, property) {
let {
type,
feel
} = property;
if (!type) {
type = getDefaultType$1(property);
}
if (feel === 'required') {
return {
id,
component: FeelProperty,
isEdited: createIsEdited(isFeelEntryEdited, element, property),
property
};
}
if (type === 'Number') {
return {
id,
component: NumberProperty,
isEdited: createIsEdited(isNumberFieldEntryEdited, element, property),
property
};
}
if (type === 'Boolean') {
return {
id,
component: BooleanProperty$1,
isEdited: createIsEdited(isCheckboxEntryEdited, element, property),
property
};
}
if (type === 'Dropdown') {
return {
id,
component: DropdownProperty$1,
isEdited: createIsEdited(isSelectEntryEdited, element, property),
property
};
}
if (type === 'String') {
if (feel) {
return {
id,
component: FeelProperty,
isEdited: createIsEdited(isFeelEntryEdited, element, property),
property
};
}
return {
id,
component: StringProperty$1,
isEdited: createIsEdited(isTextFieldEntryEdited, element, property),
property
};
}
if (type === 'Text') {
if (feel) {
return {
id,
component: FeelTextAreaProperty,
isEdited: createIsEdited(isFeelEntryEdited, element, property),
property
};
}
return {
id,
component: TextAreaProperty$1,
isEdited: createIsEdited(isTextAreaEntryEdited, element, property),
property
};
}
}
function getDefaultType$1(property) {
const {
binding
} = property;
const {
type
} = binding;
if ([PROPERTY_TYPE$1, ZEEBE_TASK_DEFINITION_TYPE_TYPE, ZEEBE_TASK_DEFINITION, ZEBBE_INPUT_TYPE, ZEEBE_OUTPUT_TYPE, ZEEBE_PROPERTY_TYPE, ZEEBE_TASK_HEADER_TYPE].includes(type)) {
return 'String';
}
}
function createIsEdited(isEditedEntry, element, property) {
if (property.generatedValue) return () => true;
const actualValue = getPropertyValue(element, property);
const defaultValue = getDefaultValue(property);
return !isEmpty(defaultValue) ? () => actualValue !== defaultValue : isEditedEntry;
}
/**
* Empty string is considered empty for input/output support.
*/
function isEmpty(value) {
return value === undefined || value === '';
}
function ReferenceSelectEntry(props) {
const {
autoFocusEntry,
element,
getOptions
} = props;
const options = getOptions(element);
const prevOptions = usePrevious(options);
// auto focus specifc other entry when options changed
useEffect(() => {
if (autoFocusEntry && prevOptions && options.length > prevOptions.length) {
const entry = query(`[data-entry-id="${autoFocusEntry}"]`);
const focusableInput = query('.bio-properties-panel-input', entry);
if (focusableInput) {
focusableInput.select();
}
}
}, [options]);
return jsx(SelectEntry, {
...props
});
}
function getEventDefinition(element, eventType) {
const businessObject = getBusinessObject(element);
const eventDefinitions = businessObject.get('eventDefinitions') || [];
return find(eventDefinitions, function (definition) {
return is(definition, eventType);
});
}
function isMessageSupported(element) {
return is(element, 'bpmn:ReceiveTask') || isAny(element, ['bpmn:StartEvent', 'bpmn:EndEvent', 'bpmn:IntermediateThrowEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent']) && !!getMessageEventDefinition(element);
}
function getMessageEventDefinition(element) {
if (is(element, 'bpmn:ReceiveTask')) {
return getBusinessObject(element);
}
return getEventDefinition(element, 'bpmn:MessageEventDefinition');
}
function getMessage(element) {
const messageEventDefinition = getMessageEventDefinition(element);
return messageEventDefinition && messageEventDefinition.get('messageRef');
}
function getSignalEventDefinition(element) {
return getEventDefinition(element, 'bpmn:SignalEventDefinition');
}
function isSignalSupported(element) {
return is(element, 'bpmn:Event') && !!getSignalEventDefinition(element);
}
function getSignal(element) {
const signalEventDefinition = getSignalEventDefinition(element);
return signalEventDefinition && signalEventDefinition.get('signalRef');
}
const EMPTY_OPTION$1 = '';
const CREATE_NEW_OPTION$1 = 'create-new';
/**
* @typedef { import('@bpmn-io/properties-panel').EntryDefinition } Entry
*/
/**
* @returns {Array<Entry>} entries
*/
function MessageProps(props) {
const {
element
} = props;
if (!isMessageSupported(element)) {
return [];
}
return [{
id: 'messageRef',
component: MessageRef,
isEdited: isSelectEntryEdited
}];
}
function MessageRef(props) {
const {
element
} = props;
const bpmnFactory = useService('bpmnFactory');
const modeling = useService('modeling');
const translate = useService('translate');
const messageEventDefinition = getMessageEventDefinition(element);
const getValue = () => {
const message = getMessage(element);
if (message) {
return message.get('id');
}
return EMPTY_OPTION$1;
};
const setValue = value => {
const root = getRoot(messageEventDefinition);
let message;
// (1) create new message
if (value === CREATE_NEW_OPTION$1) {
const id = nextId('Message_');
message = createElement('bpmn:Message', {
id,
name: id
}, root, bpmnFactory);
value = message.get('id');
}
// (2) update (or remove) messageRef
message = findRootElementById(messageEventDefinition, 'bpmn:Message', value) || message;
// (3) commit all updates
return modeling.updateModdleProperties(element, messageEventDefinition, {
messageRef: message
});
};
const getOptions = () => {
let options = [{
value: EMPTY_OPTION$1,
label: translate('<none>')
}, {
value: CREATE_NEW_OPTION$1,
label: translate('Create new ...')
}];
const messages = findRootElementsByType(getBusinessObject(element), 'bpmn:Message');
const filteredMessages = withoutTemplatedMessages(messages);
sortByName$1(filteredMessages).forEach(message => {
options.push({
value: message.get('id'),
label: message.get('name')
});
});
return options;
};
return ReferenceSelectEntry({
element,
id: 'messageRef',
label: translate('Global message reference'),
autoFocusEntry: 'messageName',
getValue,
setValue,
getOptions
});
}
function withoutTemplatedMessages(messages) {
return messages.filter(message => !message.get('zeebe:modelerTemplate'));
}
// helper /////////////////////////
function sortByName$1(elements) {
return sortBy(elements, e => (e.name || '').toLowerCase());
}
const EMPTY_OPTION = '';
const CREATE_NEW_OPTION = 'create-new';
/**
* @typedef { import('@bpmn-io/properties-panel').EntryDefinition } Entry
*/
/**
* @returns {Array<Entry>} entries
*/
function SignalProps(props) {
const {
element
} = props;
if (!isSignalSupported(element)) {
return [];
}
return [{
id: 'signalRef',
component: SignalRef,
isEdited: isSelectEntryEdited
}];
}
function SignalRef(props) {
const {
element
} = props;
const bpmnFactory = useService('bpmnFactory');
const modeling = useService('modeling');
const translate = useService('translate');
const signalEventDefinition = getSignalEventDefinition(element);
const getValue = () => {
const signal = getSignal(element);
if (signal) {
return signal.get('id');
}
return EMPTY_OPTION;
};
const setValue = value => {
const root = getRoot(signalEventDefinition);
let signal;
// (1) create new signal
if (value === CREATE_NEW_OPTION) {
const id = nextId('Signal_');
signal = createElement('bpmn:Signal', {
id,
name: id
}, root, bpmnFactory);
value = signal.get('id');
}
// (2) update (or remove) signalRef
signal = findRootElementById(signalEventDefinition, 'bpmn:Signal', value) || signal;
// (3) commit all updates
return modeling.updateModdleProperties(element, signalEventDefinition, {
signalRef: signal
});
};
const getOptions = () => {
let options = [{
value: EMPTY_OPTION,
label: translate('<none>')
}, {
value: CREATE_NEW_OPTION,
label: translate('Create new ...')
}];
const signals = findRootElementsByType(getBusinessObject(element), 'bpmn:Signal');
const filteredSignals = withoutTemplatedSignals(signals);
sortByName(filteredSignals).forEach(signal => {
options.push({
value: signal.get('id'),
label: signal.get('name')
});
});
return options;
};
return ReferenceSelectEntry({
element,
id: 'signalRef',
label: translate('Global signal reference'),
autoFocusEntry: 'signalName',
getValue,
setValue,
getOptions
});
}
function withoutTemplatedSignals(signals) {
return signals.filter(signal => !signal.get('zeebe:modelerTemplate'));
}
// helper /////////////////////////
function sortByName(elements) {
return sortBy(elements, e => (e.name || '').toLowerCase());
}
const LOWER_PRIORITY$1 = 300;
const ALWAYS_VISIBLE_GROUPS = ['general', 'documentation', 'multiInstance', 'Zeebe__ExecutionListeners'];
let ElementTemplatesPropertiesProvider$1 = class ElementTemplatesPropertiesProvider {
constructor(elementTemplates, propertiesPanel, injector) {
propertiesPanel.registerProvider(LOWER_PRIORITY$1, this);
this._elementTemplates = elementTemplates;
this._injector = injector;
}
getGroups(element) {
return groups => {
const injector = this._injector;
updateMessageGroup(groups, element);
updateSignalGroup(groups, element);
if (!this._shouldShowTemplateProperties(element)) {
return groups;
}
const translate = injector.get('translate');
// (0) Copy provided groups
groups = groups.slice();
const templatesGroup = {
element,
id: 'ElementTemplates__Template',
label: translate('Template'),
component: createElementTemplatesGroup({
getTemplateId: getTemplateId$1
}),
entries: TemplateProps({
element,
elementTemplates: this._elementTemplates,
getTemplateId: getTemplateId$1,
getTemplateVersion: getTemplateVersion$1
})
};
// (1) Add templates group
addGroupsAfter$1('documentation', groups, [templatesGroup]);
let elementTemplate = this._elementTemplates.get(element);
if (elementTemplate) {
elementTemplate = applyConditions(element, elementTemplate);
const templateSpecificGroups = [].concat(CustomProperties$1({
element,
elementTemplate,
injector
}));
// (2) add template-specific properties groups
addGroupsAfter$1('ElementTemplates__Template', groups, templateSpecificGroups);
}
// (3) apply entries visible
if (getTemplateId$1(element)) {
groups = getVisibleGroups(elementTemplate || {}, groups);
}
return groups;
};
}
_shouldShowTemplateProperties(element) {
return getTemplateId$1(element) || this._elementTemplates.getAll(element).length;
}
};
ElementTemplatesPropertiesProvider$1.$inject = ['elementTemplates', 'propertiesPanel', 'injector'];
// helper /////////////////////
function updateMessageGroup(groups, element) {
const messageGroup = findGroup(groups, 'message');
if (!messageGroup) {
return;
}
messageGroup.entries = overrideGenericEntries(messageGroup.entries, MessageProps({
element
}));
}
function updateSignalGroup(groups, element) {
const signalGroup = findGroup(groups, 'signal');
if (!signalGroup) {
return;
}
signalGroup.entries = overrideGenericEntries(signalGroup.entries, SignalProps({
element
}));
}
function findGroup(groups, id) {
return groups.find(g => g.id === id);
}
function overrideGenericEntries(oldEntries, newEntries) {
return oldEntries.map(oldEntry => newEntries.find(newEntry => newEntry.id === oldEntry.id) || oldEntry);
}
/**
*
* @param {string|string[]} idOrIds
* @param {Array<{ id: string }} groups
* @param {Array<{ id: string }>} groupsToAdd
*/
function addGroupsAfter$1(idOrIds, groups, groupsToAdd) {
let ids = idOrIds;
if (!Array.isArray(idOrIds)) {
ids = [idOrIds];
}
// find index of last group with provided id
const index = groups.reduce((acc, group, index) => {
return ids.includes(group.id) ? index : acc;
}, -1);
if (index !== -1) {
groups.splice(index + 1, 0, ...groupsToAdd);
} else {
// add in the beginning if group with provided id is missing
groups.unshift(...groupsToAdd);
}
}
function getVisibleGroups(template, groups) {
if (!template.entriesVisible) {
return groups.filter(group => {
return ALWAYS_VISIBLE_GROUPS.includes(group.id) || group.id.startsWith('ElementTemplates__');
});
}
return groups;
}
var propertiesProviderModule$1 = {
__depends__: [translateModule],
__init__: ['elementTemplatesPropertiesProvider'],
elementTemplatesPropertiesProvider: ['type', ElementTemplatesPropertiesProvider$1]
};
var index$1 = {
__depends__: [coreModule$1, propertiesProviderModule$1]
};
/**
* Converts legacy scopes descriptor to newer supported array structure.
*
* For example, it transforms
*
* scopes: {
* 'camunda:Connector':
* { properties: []
* }
* }
*
* to
*
* scopes: [
* {
* type: 'camunda:Connector',
* properties: []
* }
* ]
*
* @param {ScopesDescriptor} scopes
*
* @returns {Array}
*/
function handleLegacyScopes(scopes = []) {
const scopesAsArray = [];
if (!isObject(scopes)) {
return scopes;
}
forEach(keys(scopes), function (scopeName) {
scopesAsArray.push(assign({
type: scopeName
}, scopes[scopeName]));
});
return scopesAsArray;
}
/**
* Create an input parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createInputParameter(binding, value, bpmnFactory) {
const {
name,
scriptFormat
} = binding;
let parameterValue, parameterDefinition;
if (scriptFormat) {
parameterDefinition = bpmnFactory.create('camunda:Script', {
scriptFormat,
value
});
} else {
parameterValue = value;
}
return bpmnFactory.create('camunda:InputParameter', {
name,
value: parameterValue,
definition: parameterDefinition
});
}
/**
* Create an output parameter representing the given
* binding and value.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createOutputParameter(binding, value, bpmnFactory) {
const {
scriptFormat,
source
} = binding;
let parameterValue, parameterDefinition;
if (scriptFormat) {
parameterDefinition = bpmnFactory.create('camunda:Script', {
scriptFormat,
value: source
});
} else {
parameterValue = source;
}
return bpmnFactory.create('camunda:OutputParameter', {
name: value,
value: parameterValue,
definition: parameterDefinition
});
}
/**
* Create camunda property from the given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaProperty(binding, value = '', bpmnFactory) {
const {
name
} = binding;
return bpmnFactory.create('camunda:Property', {
name,
value
});
}
/**
* Create camunda:in element from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaIn(binding, value, bpmnFactory) {
const attrs = createCamundaInOutAttrs(binding, value);
return bpmnFactory.create('camunda:In', attrs);
}
/**
* Create camunda:in with businessKey element from given binding.
*
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaInWithBusinessKey(value, bpmnFactory) {
return bpmnFactory.create('camunda:In', {
businessKey: value
});
}
/**
* Create camunda:out element from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaOut(binding, value, bpmnFactory) {
const attrs = createCamundaInOutAttrs(binding, value);
return bpmnFactory.create('camunda:Out', attrs);
}
/**
* Create camunda:executionListener element containing an inline script from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaExecutionListener(binding, value, bpmnFactory) {
const {
event,
implementationType,
scriptFormat
} = binding;
// To guarantee backwards compatibility scriptFormat is taken into account and has precedence before any other type
if (implementationType === 'script' || scriptFormat) {
return bpmnFactory.create('camunda:ExecutionListener', {
event,
script: bpmnFactory.create('camunda:Script', {
scriptFormat,
value
})
});
}
return bpmnFactory.create('camunda:ExecutionListener', {
event,
[implementationType]: value
});
}
/**
* Create camunda:field element containing string or expression from given binding.
*
* @param {PropertyBinding} binding
* @param {String} value
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaFieldInjection(binding, value, bpmnFactory) {
const DEFAULT_PROPS = {
'string': undefined,
'expression': undefined,
'name': undefined
};
const props = assign({}, DEFAULT_PROPS);
const {
expression,
name
} = binding;
if (!expression) {
props.string = value;
} else {
props.expression = value;
}
props.name = name;
return bpmnFactory.create('camunda:Field', props);
}
/**
* Create camunda:errorEventDefinition element containing expression and errorRef
* from given binding.
*
* @param {String} expression
* @param {ModdleElement} errorRef
* @param {ModdleElement} parent
* @param {BpmnFactory} bpmnFactory
*
* @return {ModdleElement}
*/
function createCamundaErrorEventDefinition(expression, errorRef, parent, bpmnFactory) {
const errorEventDefinition = bpmnFactory.create('camunda:ErrorEventDefinition', {
errorRef,
expression
});
errorEventDefinition.$parent = parent;
return errorEventDefinition;
}
/**
* Create bpmn:error element containing a specific error id given by a binding.
*
* @param {String} bindingErrorRef
* @param {ModdleElement} parent
* @param {BpmnFactory} bpmnFactory
*
* @return { ModdleElement }
*/
function createError(bindingErrorRef, parent, bpmnFactory) {
const error = bpmnFactory.create('bpmn:Error', {
// we need to later retrieve the error from a binding
id: nextId('Error_' + bindingErrorRef + '_')
});
error.$parent = parent;
return error;
}
// helpers //////////
/**
* Create properties for camunda:in and camunda:out types.
*/
function createCamundaInOutAttrs(binding, value) {
const properties = {};
const {
expression,
source,
sourceExpression,
target,
type,
variables
} = binding;
// explicitly cover all conditions as specified here:
// https://github.com/camunda/camunda-modeler/blob/develop/docs/element-templates/README.md#camundain
if (type === 'camunda:in') {
if (target && !expression && !variables) {
properties.target = target;
properties.source = value;
} else if (target && expression === true && !variables) {
properties.target = target;
properties.sourceExpression = value;
} else if (!target && !expression && variables === 'local') {
properties.local = true;
properties.variables = 'all';
} else if (target && !expression && variables === 'local') {
properties.local = true;
properties.source = value;
properties.target = target;
} else if (target && expression && variables === 'local') {
properties.local = true;
properties.sourceExpression = value;
properties.target = target;
} else if (!target && !expression && variables === 'all') {
properties.variables = 'all';
} else {
throw new Error('invalid configuration for camunda:in element template binding');
}
}
// explicitly cover all conditions as specified here:
// https://github.com/camunda/camunda-modeler/blob/develop/docs/element-templates/README.md#camundaout
if (type === 'camunda:out') {
if (source && !sourceExpression && !variables) {
properties.target = value;
properties.source = source;
} else if (!source && sourceExpression && !variables) {
properties.target = value;
properties.sourceExpression = sourceExpression;
} else if (!source && !sourceExpression && variables === 'all') {
properties.variables = 'all';
} else if (source && !sourceExpression && variables === 'local') {
properties.local = true;
properties.source = source;
properties.target = value;
} else if (!source && sourceExpression && variables === 'local') {
properties.local = true;
properties.sourceExpression = sourceExpression;
properties.target = value;
} else if (!source && !sourceExpression && variables === 'local') {
properties.local = true;
properties.variables = 'all';
} else {
throw new Error('invalid configuration for camunda:out element template binding');
}
}
return properties;
}
const CAMUNDA_SERVICE_TASK_LIKE = ['camunda:class', 'camunda:delegateExpression', 'camunda:expression'];
/**
* Applies an element template to an element. Sets `camunda:modelerTemplate` and
* `camunda:modelerTemplateVersion`.
*/
class ChangeElementTemplateHandler {
constructor(bpmnFactory, bpmnReplace, commandStack, modeling) {
this._bpmnFactory = bpmnFactory;
this._bpmnReplace = bpmnReplace;
this._commandStack = commandStack;
this._modeling = modeling;
}
/**
* Change an element's template and update its properties as specified in `newTemplate`. Specify
* `oldTemplate` to update from one template to another. If `newTemplate` isn't specified the
* `camunda:modelerTemplate` and `camunda:modelerTemplateVersion` properties will be removed from
* the element.
*
* @param {Object} context
* @param {Object} context.element
* @param {Object} [context.oldTemplate]
* @param {Object} [context.newTemplate]
*/
preExecute(context) {
const newTemplate = context.newTemplate,
oldTemplate = context.oldTemplate;
let element = context.element;
// update camunda:modelerTemplate attribute
this._updateCamundaModelerTemplate(element, newTemplate);
if (newTemplate) {
element = context.element = this._updateTaskType(element, newTemplate);
// update properties
this._updateProperties(element, oldTemplate, newTemplate);
// update camunda:ExecutionListener properties
this._updateCamundaExecutionListenerProperties(element, newTemplate);
// update camunda:Field properties
this._updateCamundaFieldProperties(element, oldTemplate, newTemplate);
// update camunda:In and camunda:Out properties
this._updateCamundaInOutProperties(element, oldTemplate, newTemplate);
// update camunda:InputParameter and camunda:OutputParameter properties
this._updateCamundaInputOutputParameterProperties(element, oldTemplate, newTemplate);
// update camunda:Property properties
this._updateCamundaPropertyProperties(element, oldTemplate, newTemplate);
// update camunda:ErrorEventDefinition properties
this._updateCamundaErrorEventDefinitionProperties(element, oldTemplate, newTemplate);
// update properties for each scope
handleLegacyScopes(newTemplate.scopes).forEach(newScopeTemplate => {
this._updateScopeProperties(element, oldTemplate, newScopeTemplate, newTemplate);
});
}
}
_getOrCreateExtensionElements(element) {
const bpmnFactory = this._bpmnFactory,
modeling = this._modeling;
const businessObject = getBusinessObject(element);
let extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
values: []
});
extensionElements.$parent = businessObject;
modeling.updateProperties(element, {
extensionElements: extensionElements
});
}
return extensionElements;
}
/**
* Update `camunda:ErrorEventDefinition` properties of specified business object. Event
* definitions can only exist in `bpmn:ExtensionElements`.
*
* Ensures an bpmn:Error exists for the event definition.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateCamundaErrorEventDefinitionProperties(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:errorEventDefinition';
});
// (1) do not override if no updates
if (!newProperties.length) {
return;
}
const extensionElements = this._getOrCreateExtensionElements(element);
const oldErrorEventDefinitions = findExtensions(element, ['camunda:ErrorEventDefinition']);
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
oldEventDefinition = oldProperty && findOldBusinessObject(extensionElements, oldProperty),
newBinding = newProperty.binding;
// (2) update old event definitions
if (oldProperty && oldEventDefinition) {
if (!propertyChanged(oldEventDefinition, oldProperty)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldEventDefinition,
properties: {
expression: newProperty.value
}
});
}
remove(oldErrorEventDefinitions, oldEventDefinition);
}
// (3) create new event definition + error
else {
const rootElement = getRoot(getBusinessObject(element)),
newError = createError(newBinding.errorRef, rootElement, bpmnFactory),
newEventDefinition = createCamundaErrorEventDefinition(newProperty.value, newError, extensionElements, bpmnFactory);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: rootElement,
properties: {
rootElements: [...rootElement.get('rootElements'), newError]
}
});
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), newEventDefinition]
}
});
}
});
// (4) remove old event definitions
if (oldErrorEventDefinitions.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: without(extensionElements.get('values'), value => oldErrorEventDefinitions.includes(value))
}
});
}
}
/**
* Update `camunda:ExecutionListener` properties of specified business object. Execution listeners
* will always be overridden. Execution listeners can only exist in `bpmn:ExtensionElements`.
*
* @param {djs.model.Base} element
* @param {Object} newTemplate
*/
_updateCamundaExecutionListenerProperties(element, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:executionListener';
});
// (1) do not override old execution listeners if no new execution listeners specified
if (!newProperties.length) {
return;
}
const extensionElements = this._getOrCreateExtensionElements(element);
// (2) remove old execution listeners
const oldExecutionListeners = findExtensions(element, ['camunda:ExecutionListener']);
// (3) add new execution listeners
const newExecutionListeners = newProperties.map(newProperty => {
const newBinding = newProperty.binding,
propertyValue = newProperty.value;
return createCamundaExecutionListener(newBinding, propertyValue, bpmnFactory);
});
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...without(extensionElements.get('values'), value => oldExecutionListeners.includes(value)), ...newExecutionListeners]
}
});
}
/**
* Update `camunda:Field` properties of specified business object.
* If business object is `camunda:ExecutionListener` or `camunda:TaskListener` `fields` property
* will be updated. Otherwise `extensionElements.values` property will be updated.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
* @param {ModdleElement} businessObject
*/
_updateCamundaFieldProperties(element, oldTemplate, newTemplate, businessObject) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:field';
});
// (1) do not override old fields if no new fields specified
if (!newProperties.length) {
return;
}
if (!businessObject) {
businessObject = this._getOrCreateExtensionElements(element);
}
const propertyName = isAny(businessObject, ['camunda:ExecutionListener', 'camunda:TaskListener']) ? 'fields' : 'values';
const oldFields = findExtensions(element, ['camunda:Field']);
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
oldField = oldProperty && findOldBusinessObject(businessObject, oldProperty),
newBinding = newProperty.binding;
// (2) update old fields
if (oldProperty && oldField) {
if (!propertyChanged(oldField, oldProperty)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldField,
properties: {
string: newProperty.value
}
});
}
remove(oldFields, oldField);
}
// (3) add new fields
else {
const newCamundaFieldInjection = createCamundaFieldInjection(newBinding, newProperty.value, bpmnFactory);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
[propertyName]: [...businessObject.get(propertyName), newCamundaFieldInjection]
}
});
}
});
// (4) remove old fields
if (oldFields.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
[propertyName]: without(businessObject.get(propertyName), value => oldFields.includes(value))
}
});
}
}
/**
* Update `camunda:In` and `camunda:Out` properties of specified business object. Only
* `bpmn:CallActivity` and events with `bpmn:SignalEventDefinition` can have ins. Only
* `camunda:CallActivity` can have outs.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateCamundaInOutProperties(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:in' || newBindingType === 'camunda:in:businessKey' || newBindingType === 'camunda:out';
});
// (1) do not override old fields if no new fields specified
if (!newProperties.length) {
return;
}
// get extension elements of either signal event definition or call activity
const extensionElements = this._getOrCreateExtensionElements(getSignalEventDefinition(element) || element);
const oldInsAndOuts = findExtensions(extensionElements, ['camunda:In', 'camunda:Out']);
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
oldBinding = oldProperty && oldProperty.binding,
oldInOurOut = oldProperty && findOldBusinessObject(extensionElements, oldProperty),
newPropertyValue = newProperty.value,
newBinding = newProperty.binding,
newBindingType = newBinding.type,
properties = {};
let newInOrOut;
// (2) update old ins and outs
if (oldProperty && oldInOurOut) {
if (!propertyChanged(oldInOurOut, oldProperty)) {
if (newBindingType === 'camunda:in') {
if (newBinding.expression) {
properties['camunda:sourceExpression'] = newPropertyValue;
} else {
properties['camunda:source'] = newPropertyValue;
}
} else if (newBindingType === 'camunda:in:businessKey') {
properties['camunda:businessKey'] = newPropertyValue;
} else if (newBindingType === 'camunda:out') {
properties['camunda:target'] = newPropertyValue;
}
}
// update camunda:local property if it changed
if (oldBinding.local && !newBinding.local || !oldBinding.local && newBinding.local) {
properties.local = newBinding.local;
}
if (keys(properties)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldInOurOut,
properties
});
}
remove(oldInsAndOuts, oldInOurOut);
}
// (3) add new ins and outs
else {
if (newBindingType === 'camunda:in') {
newInOrOut = createCamundaIn(newBinding, newPropertyValue, bpmnFactory);
} else if (newBindingType === 'camunda:out') {
newInOrOut = createCamundaOut(newBinding, newPropertyValue, bpmnFactory);
} else if (newBindingType === 'camunda:in:businessKey') {
newInOrOut = createCamundaInWithBusinessKey(newPropertyValue, bpmnFactory);
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), newInOrOut]
}
});
}
});
// (4) remove old ins and outs
if (oldInsAndOuts.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: without(extensionElements.get('values'), value => oldInsAndOuts.includes(value))
}
});
}
}
/**
* Update `camunda:InputParameter` and `camunda:OutputParameter` properties of specified business
* object. Both can only exist in `camunda:InputOutput` which can exist in `bpmn:ExtensionElements`
* or `camunda:Connector`.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
*/
_updateCamundaInputOutputParameterProperties(element, oldTemplate, newTemplate, businessObject) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:inputParameter' || newBindingType === 'camunda:outputParameter';
});
// (1) do not override old inputs and outputs if no new inputs and outputs specified
if (!newProperties.length) {
return;
}
if (!businessObject) {
businessObject = this._getOrCreateExtensionElements(element);
}
let inputOutput;
if (is(businessObject, 'camunda:Connector')) {
inputOutput = businessObject.get('camunda:inputOutput');
if (!inputOutput) {
inputOutput = bpmnFactory.create('camunda:InputOutput');
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
inputOutput
}
});
}
} else {
inputOutput = findExtension(businessObject, 'camunda:InputOutput');
if (!inputOutput) {
inputOutput = bpmnFactory.create('camunda:InputOutput');
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), inputOutput]
}
});
}
}
const oldInputs = inputOutput.get('camunda:inputParameters') ? inputOutput.get('camunda:inputParameters').slice() : [];
const oldOutputs = inputOutput.get('camunda:outputParameters') ? inputOutput.get('camunda:outputParameters').slice() : [];
let propertyName;
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
oldInputOrOutput = oldProperty && findOldBusinessObject(businessObject, oldProperty),
newPropertyValue = newProperty.value,
newBinding = newProperty.binding,
newBindingType = newBinding.type;
let newInputOrOutput, properties;
// (2) update old inputs and outputs
if (oldProperty && oldInputOrOutput) {
if (!propertyChanged(oldInputOrOutput, oldProperty)) {
if (is(oldInputOrOutput, 'camunda:InputParameter')) {
properties = {
value: newPropertyValue
};
} else {
properties = {
name: newPropertyValue
};
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldInputOrOutput,
properties
});
}
if (is(oldInputOrOutput, 'camunda:InputParameter')) {
remove(oldInputs, oldInputOrOutput);
} else {
remove(oldOutputs, oldInputOrOutput);
}
}
// (3) add new inputs and outputs
else {
if (newBindingType === 'camunda:inputParameter') {
propertyName = 'inputParameters';
newInputOrOutput = createInputParameter(newBinding, newPropertyValue, bpmnFactory);
} else {
propertyName = 'outputParameters';
newInputOrOutput = createOutputParameter(newBinding, newPropertyValue, bpmnFactory);
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: inputOutput,
properties: {
[propertyName]: [...inputOutput.get(propertyName), newInputOrOutput]
}
});
}
});
// (4) remove old inputs and outputs
if (oldInputs.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: inputOutput,
properties: {
inputParameters: without(inputOutput.get('inputParameters'), inputParameter => oldInputs.includes(inputParameter))
}
});
}
if (oldOutputs.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: inputOutput,
properties: {
outputParameters: without(inputOutput.get('outputParameters'), outputParameter => oldOutputs.includes(outputParameter))
}
});
}
}
_updateCamundaModelerTemplate(element, newTemplate) {
const modeling = this._modeling;
const newId = newTemplate && newTemplate.id;
const newVersion = newTemplate && newTemplate.version;
if (getTemplateId(element) !== newId || getTemplateVersion(element) !== newVersion) {
modeling.updateProperties(element, {
'camunda:modelerTemplate': newId,
'camunda:modelerTemplateVersion': newVersion
});
}
}
/**
* Update `camunda:Property` properties of specified business object. `camunda:Property` can only
* exist in `camunda:Properties`.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newTemplate
* @param {ModdleElement} businessObject
*/
_updateCamundaPropertyProperties(element, oldTemplate, newTemplate, businessObject) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'camunda:property';
});
// (1) do not override old properties if no new properties specified
if (!newProperties.length) {
return;
}
if (businessObject) {
businessObject = this._getOrCreateExtensionElements(businessObject);
} else {
businessObject = this._getOrCreateExtensionElements(element);
}
let camundaProperties = findExtension(businessObject, 'camunda:Properties');
if (!camundaProperties) {
camundaProperties = bpmnFactory.create('camunda:Properties');
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
values: [...businessObject.get('values'), camundaProperties]
}
});
}
const oldCamundaProperties = camundaProperties.get('camunda:values') ? camundaProperties.get('camunda:values').slice() : [];
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
oldCamundaProperty = oldProperty && findOldBusinessObject(businessObject, oldProperty),
newPropertyValue = newProperty.value,
newBinding = newProperty.binding;
// (2) update old properties
if (oldProperty && oldCamundaProperty) {
if (!propertyChanged(oldCamundaProperty, oldProperty)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldCamundaProperty,
properties: {
value: newPropertyValue
}
});
}
remove(oldCamundaProperties, oldCamundaProperty);
}
// (3) add new properties
else {
const newCamundaProperty = createCamundaProperty(newBinding, newPropertyValue, bpmnFactory);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: camundaProperties,
properties: {
values: [...camundaProperties.get('values'), newCamundaProperty]
}
});
}
});
// (4) remove old properties
if (oldCamundaProperties.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: camundaProperties,
properties: {
values: without(camundaProperties.get('values'), value => oldCamundaProperties.includes(value))
}
});
}
}
/**
* Update `bpmn:conditionExpression` property of specified element. Since condition expression is
* is not primitive it needs special handling.
*
* @param {djs.model.Base} element
* @param {Object} oldProperty
* @param {Object} newProperty
*/
_updateConditionExpression(element, oldProperty, newProperty) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack,
modeling = this._modeling;
const newBinding = newProperty.binding,
newPropertyValue = newProperty.value;
if (!oldProperty) {
modeling.updateProperties(element, {
conditionExpression: bpmnFactory.create('bpmn:FormalExpression', {
body: newPropertyValue,
language: newBinding.scriptFormat
})
});
return;
}
const oldBinding = oldProperty.binding,
oldPropertyValue = oldProperty.value;
const businessObject = getBusinessObject(element),
conditionExpression = businessObject.get('bpmn:conditionExpression');
const properties = {};
if (conditionExpression.get('body') === oldPropertyValue) {
properties.body = newPropertyValue;
}
if (conditionExpression.get('language') === oldBinding.scriptFormat) {
properties.language = newBinding.scriptFormat;
}
if (!keys(properties).length) {
return;
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: conditionExpression,
properties
});
}
_updateProperties(element, oldTemplate, newTemplate, businessObject) {
const commandStack = this._commandStack;
const newProperties = newTemplate.properties.filter(newProperty => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;
return newBindingType === 'property';
});
const oldProperties = oldTemplate && oldTemplate.properties.filter(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === 'property';
});
if (!newProperties.length) {
return;
}
if (!businessObject) {
businessObject = getBusinessObject(element);
}
newProperties.forEach(newProperty => {
const oldProperty = findOldProperty(oldTemplate, newProperty),
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newPropertyValue = newProperty.value;
let changedElement, properties;
if (oldProperty) {
remove(oldProperties, oldProperty);
}
if (newBindingName === 'conditionExpression') {
this._updateConditionExpression(element, oldProperty, newProperty);
} else {
if (is(businessObject, 'bpmn:Error')) {
changedElement = businessObject;
} else {
changedElement = element;
}
if (oldProperty && propertyChanged(changedElement, oldProperty)) {
return;
}
properties = {};
properties[newBindingName] = newPropertyValue;
// only one of `camunda:class`, `camunda:delegateExpression` and `camunda:expression` can be set
// TODO(philippfromme): ensuring only one of these properties is set at a time should be
// implemented in a behavior and not in this handler and properties panel UI
if (CAMUNDA_SERVICE_TASK_LIKE.indexOf(newBindingName) !== -1) {
CAMUNDA_SERVICE_TASK_LIKE.forEach(camundaServiceTaskLikeProperty => {
if (camundaServiceTaskLikeProperty !== newBindingName) {
properties[camundaServiceTaskLikeProperty] = undefined;
}
});
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties
});
}
});
// remove old properties not present in new template
oldProperties && oldProperties.forEach(oldProperty => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: businessObject,
properties: {
[oldProperty.binding.name]: null
}
});
});
}
/**
* Update properties for a specified scope.
*
* @param {djs.model.Base} element
* @param {Object} oldTemplate
* @param {Object} newScopeTemplate
* @param {Object} newTemplate
*/
_updateScopeProperties(element, oldTemplate, newScopeTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;
const scopeName = newScopeTemplate.type;
let scopeElement;
scopeElement = findOldScopeElement(element, newScopeTemplate, newTemplate);
if (!scopeElement) {
scopeElement = bpmnFactory.create(scopeName);
}
const oldScopeTemplate = findOldScopeTemplate(newScopeTemplate, oldTemplate);
// update properties
this._updateProperties(element, oldScopeTemplate, newScopeTemplate, scopeElement);
// update camunda:ExecutionListener properties
this._updateCamundaExecutionListenerProperties(element, newScopeTemplate);
// update camunda:In and camunda:Out properties
this._updateCamundaInOutProperties(element, oldScopeTemplate, newScopeTemplate);
// update camunda:InputParameter and camunda:OutputParameter properties
this._updateCamundaInputOutputParameterProperties(element, oldScopeTemplate, newScopeTemplate, scopeElement);
// update camunda:Field properties
this._updateCamundaFieldProperties(element, oldScopeTemplate, newScopeTemplate, scopeElement);
// update camunda:Property properties
this._updateCamundaPropertyProperties(element, oldScopeTemplate, newScopeTemplate, scopeElement);
// assume that root elements were already created in root by referenced event definition binding
if (isRootElementScope(scopeName)) {
return;
}
const extensionElements = this._getOrCreateExtensionElements(element);
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), scopeElement]
}
});
}
/**
* Replaces the element with the specified elementType
*
* @param {djs.model.Base} element
* @param {Object} newTemplate
*/
_updateTaskType(element, newTemplate) {
// determine new task type
const newType = newTemplate.elementType;
if (!newType) {
return element;
}
// don't replace Task that is already the correct type
if (element.$type === newType.value) {
return element;
}
return this._bpmnReplace.replaceElement(element, {
type: newType.value
});
}
}
ChangeElementTemplateHandler.$inject = ['bpmnFactory', 'bpmnReplace', 'commandStack', 'modeling'];
// helpers //////////
/**
* Find old business object matching specified old property.
*
* @param {djs.model.Base|ModdleElement} element
* @param {Object} oldProperty
*
* @returns {ModdleElement}
*/
function findOldBusinessObject(element, oldProperty) {
let businessObject = getBusinessObject(element),
propertyName;
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType === 'camunda:field') {
if (isAny(businessObject, ['camunda:ExecutionListener', 'camunda:TaskListener'])) {
propertyName = 'camunda:fields';
} else {
propertyName = 'bpmn:values';
}
if (!businessObject || !businessObject.get(propertyName) || !businessObject.get(propertyName).length) {
return;
}
return find(businessObject.get(propertyName), function (oldBusinessObject) {
return oldBusinessObject.get('camunda:name') === oldBinding.name;
});
}
if (oldBindingType === 'camunda:in') {
return find(businessObject.get('values'), function (oldBusinessObject) {
return oldBusinessObject.get('target') === oldBinding.target;
});
}
if (oldBindingType === 'camunda:in:businessKey') {
return find(businessObject.get('values'), function (oldBusinessObject) {
return isString(oldBusinessObject.get('businessKey'));
});
}
if (oldBindingType === 'camunda:out') {
return find(businessObject.get('values'), function (oldBusinessObject) {
return oldBusinessObject.get('source') === oldBinding.source || oldBusinessObject.get('sourceExpression') || oldBinding.sourceExpression;
});
}
if (oldBindingType === 'camunda:inputParameter' || oldBindingType === 'camunda:outputParameter') {
if (is(businessObject, 'camunda:Connector')) {
businessObject = businessObject.get('camunda:inputOutput');
if (!businessObject) {
return;
}
} else {
businessObject = findExtension(businessObject, 'camunda:InputOutput');
if (!businessObject) {
return;
}
}
if (oldBindingType === 'camunda:inputParameter') {
return find(businessObject.get('camunda:inputParameters'), function (oldBusinessObject) {
return oldBusinessObject.get('camunda:name') === oldBinding.name;
});
} else {
return find(businessObject.get('camunda:outputParameters'), function (oldBusinessObject) {
if (oldBinding.scriptFormat) {
const definition = oldBusinessObject.get('camunda:definition');
return definition && definition.get('camunda:value') === oldBinding.source;
} else {
return oldBusinessObject.get('camunda:value') === oldBinding.source;
}
});
}
}
if (oldBindingType === 'camunda:property') {
if (!businessObject || !businessObject.get('values') || !businessObject.get('values').length) {
return;
}
businessObject = findExtension(businessObject, 'camunda:Properties');
if (!businessObject) {
return;
}
return find(businessObject.get('values'), function (oldBusinessObject) {
return oldBusinessObject.get('camunda:name') === oldBinding.name;
});
}
if (oldBindingType === 'camunda:errorEventDefinition') {
return findCamundaErrorEventDefinition(element, oldBinding.errorRef);
}
}
/**
* Find old property matching specified new property.
*
* @param {Object} oldTemplate
* @param {Object} newProperty
*
* @returns {Object}
*/
function findOldProperty(oldTemplate, newProperty) {
if (!oldTemplate) {
return;
}
const oldProperties = oldTemplate.properties,
newBinding = newProperty.binding,
newBindingName = newBinding.name,
newBindingType = newBinding.type;
if (newBindingType === 'property') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
return oldBindingType === 'property' && oldBindingName === newBindingName;
});
}
if (newBindingType === 'camunda:field') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
return oldBindingType === 'camunda:field' && oldBindingName === newBindingName;
});
}
if (newBindingType === 'camunda:in') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'camunda:in') {
return;
}
// always override if change from source to source expression or vice versa
if (oldBinding.expression && !newBinding.expression || !oldBinding.expression && newBinding.expression) {
return;
}
return oldBinding.target === newBinding.target;
});
}
if (newBindingType === 'camunda:in:businessKey') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === 'camunda:in:businessKey';
});
}
if (newBindingType === 'camunda:out') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
return oldBindingType === 'camunda:out' && (oldBinding.source === newBinding.source || oldBinding.sourceExpression === newBinding.sourceExpression);
});
}
if (newBindingType === 'camunda:inputParameter') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'camunda:inputParameter') {
return;
}
return oldBindingName === newBindingName && oldBinding.scriptFormat === newBinding.scriptFormat;
});
}
if (newBindingType === 'camunda:outputParameter') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;
if (oldBindingType !== 'camunda:outputParameter') {
return;
}
return oldBinding.source === newBinding.source && oldBinding.scriptFormat === newBinding.scriptFormat;
});
}
if (newBindingType === 'camunda:property') {
return find(oldProperties, function (oldProperty) {
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type;
return oldBindingType === 'camunda:property' && oldBindingName === newBindingName;
});
}
if (newBindingType === 'camunda:errorEventDefinition') {
return find(oldProperties, function (oldProperty) {
const newBindingRef = newBinding.errorRef,
oldBinding = oldProperty.binding,
oldBindingRef = oldBinding.errorRef,
oldBindingType = oldBinding.type;
return oldBindingType === 'camunda:errorEventDefinition' && oldBindingRef === newBindingRef;
});
}
}
function findOldScopeElement(element, scopeTemplate, template) {
const scopeName = scopeTemplate.type,
id = scopeTemplate.id;
if (scopeName === 'camunda:Connector') {
return findExtension(element, 'camunda:Connector');
}
if (scopeName === 'bpmn:Error') {
// (1) find by error event definition binding
const errorEventDefinitionBinding = findErrorEventDefinitionBinding(template, id);
if (!errorEventDefinitionBinding) {
return;
}
// (2) find error event definition
const errorEventDefinition = findOldBusinessObject(element, errorEventDefinitionBinding);
if (!errorEventDefinition) {
return;
}
// (3) retrieve referenced error
return errorEventDefinition.errorRef;
}
}
function isRootElementScope(scopeName) {
return ['bpmn:Error'].includes(scopeName);
}
function findOldScopeTemplate(scopeTemplate, oldTemplate) {
const scopeName = scopeTemplate.type,
scopeId = scopeTemplate.id,
scopes = oldTemplate && handleLegacyScopes(oldTemplate.scopes);
return scopes && find(scopes, function (scope) {
if (isRootElementScope(scopeName)) {
return scope.id === scopeId;
}
return scope.type === scopeName;
});
}
function findErrorEventDefinitionBinding(template, templateErrorId) {
return find(template.properties, function (property) {
return property.binding.errorRef === templateErrorId;
});
}
/**
* Check whether property was changed after being set by template.
*
* @param {djs.model.Base|ModdleElement} element
* @param {Object} oldProperty
*
* @returns {boolean}
*/
function propertyChanged(element, oldProperty) {
const businessObject = getBusinessObject(element);
const oldBinding = oldProperty.binding,
oldBindingName = oldBinding.name,
oldBindingType = oldBinding.type,
oldPropertyValue = oldProperty.value;
let conditionExpression, definition;
if (oldBindingType === 'property') {
if (oldBindingName === 'conditionExpression') {
conditionExpression = businessObject.get('bpmn:conditionExpression');
return conditionExpression.get('bpmn:body') !== oldPropertyValue;
}
return businessObject.get(oldBindingName) !== oldPropertyValue;
}
if (oldBindingType === 'camunda:field') {
return businessObject.get('camunda:string') !== oldPropertyValue;
}
if (oldBindingType === 'camunda:in') {
if (oldBinding.expression) {
return businessObject.get('sourceExpression') !== oldPropertyValue;
} else {
return businessObject.get('camunda:source') !== oldPropertyValue;
}
}
if (oldBindingType === 'camunda:in:businessKey') {
return businessObject.get('camunda:businessKey') !== oldPropertyValue;
}
if (oldBindingType === 'camunda:out') {
return businessObject.get('camunda:target') !== oldPropertyValue;
}
if (oldBindingType === 'camunda:inputParameter') {
if (oldBinding.scriptFormat) {
definition = businessObject.get('camunda:definition');
return definition && definition.get('camunda:value') !== oldPropertyValue;
} else {
return businessObject.get('camunda:value') !== oldPropertyValue;
}
}
if (oldBindingType === 'camunda:outputParameter') {
return businessObject.get('camunda:name') !== oldPropertyValue;
}
if (oldBindingType === 'camunda:property') {
return businessObject.get('camunda:value') !== oldPropertyValue;
}
if (oldBindingType === 'camunda:errorEventDefinition') {
return businessObject.get('expression') !== oldPropertyValue;
}
}
function remove(array, item) {
const index = array.indexOf(item);
if (isUndefined$1(index)) {
return array;
}
array.splice(index, 1);
return array;
}
class RemoveElementTemplateHandler {
constructor(modeling, elementFactory, elementRegistry, canvas, bpmnFactory, replace, commandStack) {
this._modeling = modeling;
this._elementFactory = elementFactory;
this._elementRegistry = elementRegistry;
this._canvas = canvas;
this._bpmnFactory = bpmnFactory;
this._replace = replace;
this._commandStack = commandStack;
}
preExecute(context) {
const {
element
} = context;
if (element.parent) {
context.newElement = this._removeTemplate(element);
} else {
context.newElement = this._removeRootTemplate(element);
}
}
_removeTemplate(element) {
const replace = this._replace;
const businessObject = getBusinessObject(element);
const type = businessObject.$type,
eventDefinitionType = this._getEventDefinitionType(businessObject);
const newBusinessObject = this._createBlankBusinessObject(element);
return replace.replaceElement(element, {
type: type,
businessObject: newBusinessObject,
eventDefinitionType: eventDefinitionType
}, {
createElementsBehavior: false
});
}
/**
* Remove template from a given element.
*
* @param {djs.model.Base} element
*
* @return {djs.model.Base} the updated element
*/
_removeRootTemplate(element) {
var modeling = this._modeling,
elementFactory = this._elementFactory,
elementRegistry = this._elementRegistry,
canvas = this._canvas;
// We are inside a collapsed subprocess, move up to the parent before replacing the collapsed object
if (isPlane(element)) {
const shapeId = getShapeIdFromPlane(element);
const shape = elementRegistry.get(shapeId);
if (shape && shape !== element) {
canvas.setRootElement(canvas.findRoot(shape));
return this._removeTemplate(shape);
}
}
const businessObject = getBusinessObject(element);
const type = businessObject.$type;
const newBusinessObject = this._createBlankBusinessObject(element);
const newRoot = elementFactory.create('root', {
type: type,
businessObject: newBusinessObject
});
this._commandStack.execute('canvas.updateRoot', {
newRoot: newRoot,
oldRoot: element
});
modeling.moveElements(element.children, {
x: 0,
y: 0
}, newRoot);
return newRoot;
}
_getEventDefinitionType(businessObject) {
if (!businessObject.eventDefinitions) {
return null;
}
const eventDefinition = businessObject.eventDefinitions[0];
if (!eventDefinition) {
return null;
}
return eventDefinition.$type;
}
_createBlankBusinessObject(element) {
const bpmnFactory = this._bpmnFactory;
const bo = getBusinessObject(element),
newBo = bpmnFactory.create(bo.$type),
label = getLabel(element);
if (!label) {
return newBo;
}
if (is(element, 'bpmn:Group')) {
newBo.categoryValueRef = bpmnFactory.create('bpmn:CategoryValue');
}
setLabel({
businessObject: newBo
}, label);
return newBo;
}
}
RemoveElementTemplateHandler.$inject = ['modeling', 'elementFactory', 'elementRegistry', 'canvas', 'bpmnFactory', 'replace', 'commandStack'];
class UnlinkElementTemplateHandler {
constructor(commandStack) {
this._commandStack = commandStack;
}
preExecute(context) {
const {
element,
oldTemplate
} = context;
this._commandStack.execute('propertiesPanel.camunda.changeTemplate', {
element,
oldTemplate,
newTemplate: null
});
}
}
UnlinkElementTemplateHandler.$inject = ['commandStack'];
class ElementTemplatesCommands {
constructor(commandStack, elementTemplates, eventBus) {
commandStack.registerHandler('element-templates.multi-command-executor', MultiCommandHandler);
commandStack.registerHandler('propertiesPanel.camunda.changeTemplate', ChangeElementTemplateHandler);
commandStack.registerHandler('propertiesPanel.removeTemplate', RemoveElementTemplateHandler);
commandStack.registerHandler('propertiesPanel.unlinkTemplate', UnlinkElementTemplateHandler);
// apply default element templates on shape creation
eventBus.on(['commandStack.shape.create.postExecuted'], function (event) {
const {
context: {
hints = {},
shape
}
} = event;
if (hints.createElementsBehavior !== false) {
applyDefaultTemplate(shape, elementTemplates, commandStack);
}
});
// apply default element templates on connection creation
eventBus.on(['commandStack.connection.create.postExecuted'], function (event) {
const {
context: {
hints = {},
connection
}
} = event;
if (hints.createElementsBehavior !== false) {
applyDefaultTemplate(connection, elementTemplates, commandStack);
}
});
}
}
ElementTemplatesCommands.$inject = ['commandStack', 'elementTemplates', 'eventBus'];
function applyDefaultTemplate(element, elementTemplates, commandStack) {
if (!elementTemplates.get(element) && elementTemplates.getDefault(element)) {
const command = 'propertiesPanel.camunda.changeTemplate';
const commandContext = {
element: element,
newTemplate: elementTemplates.getDefault(element)
};
commandStack.execute(command, commandContext);
}
}
var commandsModule = {
__init__: ['elementTemplateCommands'],
elementTemplateCommands: ['type', ElementTemplatesCommands]
};
/**
* This Behavior checks if the new element's type is in
* the list of elements the template applies to and unlinks
* it if not.
*/
class ReplaceBehavior extends CommandInterceptor {
constructor(elementTemplates, injector) {
super(injector.get('eventBus'));
this.postExecuted('shape.replace', function (e) {
var context = e.context,
oldShape = context.oldShape,
oldBo = getBusinessObject(oldShape),
newShape = context.newShape,
newBo = getBusinessObject(newShape);
if (!oldBo.modelerTemplate) {
return;
}
const template = newBo.modelerTemplate;
const version = newBo.modelerTemplateVersion;
const elementTemplate = elementTemplates.get(template, version);
if (!elementTemplate) {
elementTemplates.unlinkTemplate(newShape, injector);
return;
}
const {
appliesTo,
elementType
} = elementTemplate;
if (elementType) {
if (!is(newShape, elementType.value)) {
elementTemplates.unlinkTemplate(newShape, injector);
}
return;
}
const allowed = appliesTo.reduce((allowed, type) => {
return allowed || is(newBo, type);
}, false);
if (!allowed) {
elementTemplates.unlinkTemplate(newShape, injector);
}
});
}
}
ReplaceBehavior.$inject = ['elementTemplates', 'injector'];
var behaviorModule = {
__init__: ['elementTemplatesReplaceBehavior'],
elementTemplatesReplaceBehavior: ['type', ReplaceBehavior]
};
var coreModule = {
__depends__: [commandsModule, behaviorModule],
__init__: ['elementTemplatesLoader'],
elementTemplates: ['type', ElementTemplates$1],
elementTemplatesLoader: ['type', ElementTemplatesLoader$1]
};
const CAMUNDA_ERROR_EVENT_DEFINITION_TYPE$1 = 'camunda:errorEventDefinition';
const CAMUNDA_EXECUTION_LISTENER_TYPE = 'camunda:executionListener';
const CAMUNDA_FIELD_TYPE = 'camunda:field';
const CAMUNDA_IN_BUSINESS_KEY_TYPE = 'camunda:in:businessKey';
const CAMUNDA_IN_TYPE = 'camunda:in';
const CAMUNDA_INPUT_PARAMETER_TYPE$1 = 'camunda:inputParameter';
const CAMUNDA_OUT_TYPE = 'camunda:out';
const CAMUNDA_OUTPUT_PARAMETER_TYPE$1 = 'camunda:outputParameter';
const CAMUNDA_PROPERTY_TYPE = 'camunda:property';
const PROPERTY_TYPE = 'property';
const EXTENSION_BINDING_TYPES = [CAMUNDA_ERROR_EVENT_DEFINITION_TYPE$1, CAMUNDA_FIELD_TYPE, CAMUNDA_IN_TYPE, CAMUNDA_IN_BUSINESS_KEY_TYPE, CAMUNDA_INPUT_PARAMETER_TYPE$1, CAMUNDA_OUT_TYPE, CAMUNDA_OUTPUT_PARAMETER_TYPE$1, CAMUNDA_PROPERTY_TYPE];
const IO_BINDING_TYPES = [CAMUNDA_INPUT_PARAMETER_TYPE$1, CAMUNDA_OUTPUT_PARAMETER_TYPE$1];
const IN_OUT_BINDING_TYPES = [CAMUNDA_IN_BUSINESS_KEY_TYPE, CAMUNDA_IN_TYPE, CAMUNDA_OUT_TYPE];
const PRIMITIVE_MODDLE_TYPES = ['Boolean', 'Integer', 'String'];
function CustomProperties(props) {
const {
element,
elementTemplate,
injector
} = props;
const translate = injector.get('translate');
const groups = [];
const {
id,
properties,
groups: propertyGroups,
scopes
} = elementTemplate;
// (1) group properties by group id
const groupedProperties = groupByGroupId(properties);
const defaultProps = [];
forEach(groupedProperties, (properties, groupId) => {
const group = findCustomGroup(propertyGroups, groupId);
if (!group) {
return defaultProps.push(...properties);
}
addCustomGroup(groups, {
element,
id: `ElementTemplates__CustomProperties-${groupId}`,
label: translate(group.label),
properties: properties,
templateId: `${id}-${groupId}`
});
});
// (2) add default custom props
if (defaultProps.length) {
addCustomGroup(groups, {
id: 'ElementTemplates__CustomProperties',
label: translate('Custom properties'),
element,
properties: defaultProps,
templateId: id
});
}
// (3) add custom scopes props
if (isArray(scopes)) {
scopes.forEach(scope => {
const {
properties,
type
} = scope;
const id = type.replace(/:/g, '-');
addCustomGroup(groups, {
element,
id: `ElementTemplates__CustomGroup-${id}`,
label: translate(`Custom properties for scope <${type}>`),
properties,
templateId: id,
scope
});
});
}
return groups;
}
function addCustomGroup(groups, props) {
const {
element,
id,
label,
properties,
scope,
templateId
} = props;
const customPropertiesGroup = {
id,
label,
component: Group,
entries: [],
shouldOpen: false
};
properties.forEach((property, index) => {
const entry = createCustomEntry(`custom-entry-${templateId}-${index}`, element, property, scope);
if (entry) {
customPropertiesGroup.entries.push(entry);
}
});
if (customPropertiesGroup.entries.length) {
groups.push(customPropertiesGroup);
}
}
function createCustomEntry(id, element, property, scope) {
let {
type
} = property;
if (!type) {
type = getDefaultType(property);
}
if (type === 'Boolean') {
return {
id,
component: BooleanProperty,
isEdited: isCheckboxEntryEdited,
property,
scope
};
}
if (type === 'Dropdown') {
return {
id,
component: DropdownProperty,
isEdited: isSelectEntryEdited,
property,
scope
};
}
if (type === 'String') {
return {
id,
component: StringProperty,
isEdited: isTextFieldEntryEdited,
property,
scope
};
}
if (type === 'Text') {
return {
id,
component: TextAreaProperty,
isEdited: isTextAreaEntryEdited,
property,
scope
};
}
}
function getDefaultType(property) {
const {
binding
} = property;
const {
type
} = binding;
if ([PROPERTY_TYPE, CAMUNDA_PROPERTY_TYPE, CAMUNDA_IN_TYPE, CAMUNDA_IN_BUSINESS_KEY_TYPE, CAMUNDA_OUT_TYPE, CAMUNDA_FIELD_TYPE].includes(type)) {
return 'String';
}
if (type === CAMUNDA_EXECUTION_LISTENER_TYPE) {
return 'Hidden';
}
}
function BooleanProperty(props) {
const {
element,
id,
property,
scope
} = props;
const {
description,
editable,
label
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
translate = useService('translate');
return CheckboxEntry({
element,
getValue: propertyGetter(element, property, scope),
id,
label: label ? translate(label) : label,
description: PropertyDescription({
description
}),
setValue: propertySetter(bpmnFactory, commandStack, element, property, scope),
disabled: editable === false
});
}
function DropdownProperty(props) {
const {
element,
id,
property,
scope
} = props;
const {
description,
editable,
label
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
translate = useService('translate');
const getOptions = () => {
const {
choices
} = property;
return choices.map(({
name,
value
}) => {
return {
label: translate(name),
value
};
});
};
return SelectEntry({
element,
id,
label: label ? translate(label) : label,
getOptions,
description: PropertyDescription({
description
}),
getValue: propertyGetter(element, property, scope),
setValue: propertySetter(bpmnFactory, commandStack, element, property, scope),
disabled: editable === false,
validate: propertyValidator(translate, property)
});
}
function StringProperty(props) {
const {
element,
id,
property,
scope
} = props;
const {
description,
editable,
label,
placeholder
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
return TextFieldEntry({
debounce,
element,
getValue: propertyGetter(element, property, scope),
id,
placeholder,
label: label ? translate(label) : label,
description: PropertyDescription({
description
}),
setValue: propertySetter(bpmnFactory, commandStack, element, property, scope),
validate: propertyValidator(translate, property),
disabled: editable === false
});
}
function TextAreaProperty(props) {
const {
element,
id,
property,
scope
} = props;
const {
description,
editable,
label,
placeholder
} = property;
const bpmnFactory = useService('bpmnFactory'),
commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
return TextAreaEntry({
debounce,
element,
id,
placeholder,
label: label ? translate(label) : label,
description: PropertyDescription({
description
}),
getValue: propertyGetter(element, property, scope),
setValue: propertySetter(bpmnFactory, commandStack, element, property, scope),
disabled: editable === false,
validate: propertyValidator(translate, property)
});
}
function propertyGetter(element, property, scope) {
return function getValue() {
let businessObject = getBusinessObject(element);
const {
binding,
value: defaultValue = ''
} = property;
const {
name,
type
} = binding;
if (scope) {
businessObject = getScopeBusinessObject(businessObject, scope);
if (!businessObject) {
return defaultValue;
}
}
// property
if (type === 'property') {
const value = businessObject.get(name);
if (name === 'conditionExpression') {
if (value) {
return value.get('body');
}
return defaultValue;
} else {
if (!isUndefined$1(value)) {
return value;
}
return defaultValue;
}
}
// camunda:ErrorEventDefinition
if (type === CAMUNDA_ERROR_EVENT_DEFINITION_TYPE$1) {
const {
errorRef
} = binding;
const errorEventDefinition = findCamundaErrorEventDefinition(businessObject, errorRef);
if (errorEventDefinition) {
return errorEventDefinition.get('camunda:expression');
} else {
return '';
}
}
// camunda:Field
if (type === CAMUNDA_FIELD_TYPE) {
const camundaFields = findExtensions(businessObject, ['camunda:Field']);
const camundaField = camundaFields.find(camundaField => {
return camundaField.get('camunda:name') === name;
});
if (camundaField) {
return camundaField.get('camunda:string') || camundaField.get('camunda:expression');
} else {
return '';
}
}
// camunda:Property
if (type === CAMUNDA_PROPERTY_TYPE) {
let camundaProperties;
if (scope) {
// TODO(philippfromme): as only bpmn:Error and camunda:Connector are supported this code is practically dead
camundaProperties = businessObject.get('properties');
} else {
camundaProperties = findExtension(businessObject, 'camunda:Properties');
}
if (camundaProperties) {
const camundaProperty = findCamundaProperty(camundaProperties, binding);
if (camundaProperty) {
return camundaProperty.get('camunda:value');
}
}
return defaultValue;
}
if (IO_BINDING_TYPES.includes(type)) {
let inputOutput;
if (scope) {
inputOutput = businessObject.get('inputOutput');
} else {
inputOutput = findExtension(businessObject, 'camunda:InputOutput');
}
if (!inputOutput) {
return defaultValue;
}
// camunda:InputParameter
if (type === CAMUNDA_INPUT_PARAMETER_TYPE$1) {
const inputParameter = findInputParameter(inputOutput, binding);
if (inputParameter) {
const {
scriptFormat
} = binding;
if (scriptFormat) {
const definition = inputParameter.get('camunda:definition');
if (definition) {
return definition.get('camunda:value');
}
} else {
return inputParameter.get('value') || '';
}
}
return defaultValue;
}
// camunda:OutputParameter
if (type === CAMUNDA_OUTPUT_PARAMETER_TYPE$1) {
const outputParameter = findOutputParameter(inputOutput, binding);
if (outputParameter) {
return outputParameter.get('camunda:name');
}
return defaultValue;
}
}
// camunda:In and camunda:Out
if (IN_OUT_BINDING_TYPES.includes(type)) {
const camundaInOut = findCamundaInOut(businessObject, binding);
if (camundaInOut) {
if (type === CAMUNDA_IN_BUSINESS_KEY_TYPE) {
return camundaInOut.get('camunda:businessKey');
} else if (type === CAMUNDA_OUT_TYPE) {
return camundaInOut.get('camunda:target');
} else if (type === CAMUNDA_IN_TYPE) {
const {
expression
} = binding;
if (expression) {
return camundaInOut.get('camunda:sourceExpression');
} else {
return camundaInOut.get('camunda:source');
}
}
}
return defaultValue;
}
// should never throw as templates are validated beforehand
throw unknownBindingError(element, property);
};
}
function propertySetter(bpmnFactory, commandStack, element, property, scope) {
return function setValue(value) {
let businessObject = getBusinessObject(element);
const {
binding
} = property;
const {
name,
type
} = binding;
const rootElement = getRoot(businessObject);
let extensionElements;
let propertyValue;
const commands = [];
if (EXTENSION_BINDING_TYPES.includes(type)) {
extensionElements = businessObject.get('extensionElements');
if (!extensionElements) {
extensionElements = createElement('bpmn:ExtensionElements', null, businessObject, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: businessObject,
properties: {
extensionElements
}
}
});
}
}
if (scope) {
businessObject = getScopeBusinessObject(businessObject, scope);
if (!businessObject) {
// bpmn:Error
if (scope.type === 'bpmn:Error') {
businessObject = createError(scope.id, rootElement, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: rootElement,
properties: {
rootElements: [...rootElement.get('rootElements'), businessObject]
}
}
});
} else {
businessObject = createElement(scope.type, null, element, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), businessObject]
}
}
});
}
}
}
// property
if (type === 'property') {
if (name === 'conditionExpression') {
const {
scriptFormat
} = binding;
propertyValue = createElement('bpmn:FormalExpression', {
body: value,
language: scriptFormat
}, businessObject, bpmnFactory);
} else {
const propertyDescriptor = businessObject.$descriptor.propertiesByName[name];
const {
type: propertyType
} = propertyDescriptor;
// do not override non-primitive types
if (!PRIMITIVE_MODDLE_TYPES.includes(propertyType)) {
throw new Error(`cannot set property of type <${propertyType}>`);
}
if (propertyType === 'Boolean') {
propertyValue = !!value;
} else if (propertyType === 'Integer') {
propertyValue = parseInt(value, 10);
if (isNaN(propertyValue)) {
// do not set NaN value
propertyValue = undefined;
}
} else {
// make sure we don't remove the property
propertyValue = value || '';
}
}
if (!isUndefined$1(propertyValue)) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: businessObject,
properties: {
[name]: propertyValue
}
}
});
}
}
// camunda:ErrorEventDefinition
if (type === CAMUNDA_ERROR_EVENT_DEFINITION_TYPE$1) {
const {
errorRef
} = binding;
const oldCamundaErrorEventDefinition = findCamundaErrorEventDefinition(businessObject, errorRef);
if (oldCamundaErrorEventDefinition) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: oldCamundaErrorEventDefinition,
properties: {
'camunda:expression': value
}
}
});
} else {
const newError = createError(binding.errorRef, rootElement, bpmnFactory),
newCamundaErrorEventDefinition = createCamundaErrorEventDefinition(value, newError, extensionElements, bpmnFactory);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: rootElement,
properties: {
rootElements: [...rootElement.get('rootElements'), newError]
}
}
});
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), newCamundaErrorEventDefinition]
}
}
});
}
}
// camunda:Field
if (type === CAMUNDA_FIELD_TYPE) {
const oldCamundaFields = findExtensions(businessObject, ['camunda:Field']);
const newCamundaFields = [];
if (oldCamundaFields.length) {
oldCamundaFields.forEach(camundaField => {
if (camundaField.name === name) {
newCamundaFields.push(createCamundaFieldInjection(binding, value, bpmnFactory));
} else {
newCamundaFields.push(camundaField);
}
});
} else {
newCamundaFields.push(createCamundaFieldInjection(binding, value, bpmnFactory));
}
const values = extensionElements.get('values').filter(value => !oldCamundaFields.includes(value));
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...values, ...newCamundaFields]
}
}
});
}
// camunda:Property
if (type === CAMUNDA_PROPERTY_TYPE) {
let camundaProperties;
if (scope) {
camundaProperties = businessObject.get('properties');
} else {
camundaProperties = findExtension(extensionElements, 'camunda:Properties');
}
if (!camundaProperties) {
camundaProperties = createElement('camunda:Properties', null, businessObject, bpmnFactory);
if (scope) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: businessObject,
properties: {
properties: camundaProperties
}
}
});
} else {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), camundaProperties]
}
}
});
}
}
const oldCamundaProperty = findCamundaProperty(camundaProperties, binding);
const newCamundaProperty = createCamundaProperty(binding, value, bpmnFactory);
const values = camundaProperties.get('values').filter(value => value !== oldCamundaProperty);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: camundaProperties,
properties: {
values: [...values, newCamundaProperty]
}
}
});
}
if (IO_BINDING_TYPES.includes(type)) {
let inputOutput;
if (scope) {
inputOutput = businessObject.get('inputOutput');
} else {
inputOutput = findExtension(extensionElements, 'camunda:InputOutput');
}
if (!inputOutput) {
inputOutput = createElement('camunda:InputOutput', null, businessObject, bpmnFactory);
if (scope) {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: businessObject,
properties: {
inputOutput
}
}
});
} else {
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...extensionElements.get('values'), inputOutput]
}
}
});
}
}
// camunda:InputParameter
if (type === CAMUNDA_INPUT_PARAMETER_TYPE$1) {
const oldCamundaInputParameter = findInputParameter(inputOutput, binding);
const newCamundaInputParameter = createInputParameter(binding, value, bpmnFactory);
const values = inputOutput.get('camunda:inputParameters').filter(value => value !== oldCamundaInputParameter);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: inputOutput,
properties: {
'camunda:inputParameters': [...values, newCamundaInputParameter]
}
}
});
}
// camunda:OutputParameter
if (type === CAMUNDA_OUTPUT_PARAMETER_TYPE$1) {
const oldCamundaOutputParameter = findOutputParameter(inputOutput, binding);
const newCamundaOutputParameter = createOutputParameter(binding, value, bpmnFactory);
const values = inputOutput.get('camunda:outputParameters').filter(value => value !== oldCamundaOutputParameter);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: inputOutput,
properties: {
'camunda:outputParameters': [...values, newCamundaOutputParameter]
}
}
});
}
}
// camunda:In and camunda:Out
if (IN_OUT_BINDING_TYPES.includes(type)) {
const oldCamundaInOut = findCamundaInOut(businessObject, binding);
let newCamundaInOut;
if (type === CAMUNDA_IN_TYPE) {
newCamundaInOut = createCamundaIn(binding, value, bpmnFactory);
} else if (type === CAMUNDA_OUT_TYPE) {
newCamundaInOut = createCamundaOut(binding, value, bpmnFactory);
} else {
newCamundaInOut = createCamundaInWithBusinessKey(value, bpmnFactory);
}
const values = extensionElements.get('values').filter(value => value !== oldCamundaInOut);
commands.push({
cmd: 'element.updateModdleProperties',
context: {
element,
moddleElement: extensionElements,
properties: {
values: [...values, newCamundaInOut]
}
}
});
}
if (commands.length) {
commandStack.execute('element-templates.multi-command-executor', commands);
return;
}
// should never throw as templates are validated beforehand
throw unknownBindingError(element, property);
};
}
function propertyValidator(translate, property) {
return function validate(value) {
const {
constraints = {}
} = property;
const {
maxLength,
minLength,
notEmpty
} = constraints;
if (notEmpty && isEmptyString(value)) {
return translate('Must not be empty.');
}
if (property.feel && isFeel(value)) {
return;
}
if (maxLength && value.length > maxLength) {
return translate('Must have max length {maxLength}.', {
maxLength
});
}
if (minLength && value.length < minLength) {
return translate('Must have min length {minLength}.', {
minLength
});
}
let {
pattern
} = constraints;
if (pattern) {
let message;
if (!isString(pattern)) {
message = pattern.message;
pattern = pattern.value;
}
if (!matchesPattern(value, pattern)) {
return message || translate('Must match pattern {pattern}.', {
pattern
});
}
}
};
}
function getScopeBusinessObject(businessObject, scope) {
const {
id,
type
} = scope;
if (type === 'bpmn:Error') {
// retrieve error through referenced error event definition
const errorEventDefinition = findCamundaErrorEventDefinition(businessObject, id);
if (errorEventDefinition) {
return errorEventDefinition.get('errorRef');
}
}
return findExtension(businessObject, type);
}
function unknownBindingError(element, property) {
const businessObject = getBusinessObject(element);
const id = businessObject.get('id');
const {
binding
} = property;
const {
type
} = binding;
return new Error(`unknown binding <${type}> for element <${id}>, this should never happen`);
}
function isEmptyString(string) {
return !string || !string.trim().length;
}
function matchesPattern(string, pattern) {
return new RegExp(pattern).test(string);
}
function groupByGroupId(properties) {
return groupBy(properties, 'group');
}
function findCustomGroup(groups, id) {
return find(groups, g => g.id === id);
}
function isFeel(value) {
return isString(value) && value.trim().startsWith('=');
}
function ErrorProperties(props) {
const {
element,
index,
property,
groups
} = props;
const {
binding,
label
} = property;
const {
errorRef
} = binding;
const businessObject = getBusinessObject(element),
errorEventDefinitions = findExtensions(businessObject, ['camunda:ErrorEventDefinition']);
if (!errorEventDefinitions.length) {
return;
}
const errorEventDefinition = findCamundaErrorEventDefinition(element, errorRef);
const id = `${element.id}-error-${index}`;
let entries = [];
const errorGroup = groups.find(({
id
}) => id === 'CamundaPlatform__Errors');
const originalItem = errorGroup.items.find(({
entries
}) => entries[0].errorEventDefinition === errorEventDefinition);
entries = originalItem.entries;
// (1) remove global error referenced entry
// entries.shift();
entries = removeEntry$1(entries, '-errorRef');
// (2) remove throw expression input
// entries.pop();
entries = removeEntry$1(entries, '-expression');
// (3) add disabled throw expression input
entries.push({
id: `${id}-expression`,
component: Expression,
errorEventDefinition,
property
});
const item = {
id,
label: label || getErrorLabel(errorEventDefinition),
entries
};
return item;
}
function Expression(props) {
const {
errorEventDefinition,
id
} = props;
const translate = useService('translate');
const debounce = useService('debounceInput');
const setValue = () => {};
const getValue = () => {
return errorEventDefinition.get('camunda:expression');
};
return TextFieldEntry({
element: errorEventDefinition,
id,
label: translate('Throw expression'),
getValue,
setValue,
debounce,
disabled: true
});
}
function removeEntry$1(entries, suffix) {
const entry = entries.find(({
id
}) => id.endsWith(suffix));
return without(entries, entry);
}
function getErrorLabel(errorEventDefinition) {
const error = errorEventDefinition.get('errorRef');
if (!error) {
return '<no reference>';
}
const errorCode = error.get('errorCode'),
name = error.get('name') || '<unnamed>';
if (errorCode) {
return `${name} (code = ${errorCode})`;
}
return name;
}
function InputProperties(props) {
const {
element,
index,
property,
groups
} = props;
const {
binding,
description,
label
} = property;
const {
name
} = binding;
const businessObject = getBusinessObject(element),
inputOutput = findExtension(businessObject, 'camunda:InputOutput');
const inputParameter = inputOutput && findInputParameter(inputOutput, binding);
const id = `${element.id}-inputParameter-${index}`;
let entries = [];
if (inputParameter) {
const inputGroup = groups.find(({
id
}) => id === 'CamundaPlatform__Input');
const item = inputGroup.items.find(({
entries
}) => entries[0].parameter === inputParameter);
entries = item.entries;
// (1) remove name entry
entries = removeEntry(entries, '-name');
}
// (2) add local variable assignment entry
entries.unshift({
id: `${id}-local-variable-assignment`,
component: LocalVariableAssignment,
inputParameter,
property
});
// (3) add description entry
if (description) {
entries.unshift({
id: `${id}-description`,
component: Description$1,
text: description
});
}
// @barmac: binding#name is required so there is no third option
const item = {
id,
label: label || name,
entries
};
return item;
}
// TODO(philippfromme): add text entry to properties-panel
function Description$1(props) {
const {
id,
text
} = props;
return jsx("div", {
class: "bio-properties-panel-entry",
"data-entry-id": id,
children: jsx("div", {
class: "bio-properties-panel-description",
children: jsx(PropertyDescription, {
description: text
})
})
});
}
function LocalVariableAssignment(props) {
const {
element,
id,
property,
inputParameter
} = props;
const {
binding
} = property;
const bpmnFactory = useService('bpmnFactory'),
modeling = useService('modeling'),
translate = useService('translate');
const getValue = () => {
return inputParameter;
};
const setValue = value => {
if (value) {
addInputParameter(element, property, bpmnFactory, modeling);
} else {
removeInputParameter(element, binding, modeling);
}
};
return ToggleSwitchEntry({
id,
label: translate('Local variable assignment'),
switcherLabel: inputParameter ? translate('On') : translate('Off'),
description: inputParameter ? '' : translate('Parameter won\'t be created as local variable.'),
getValue,
setValue
});
}
function addInputParameter(element, property, bpmnFactory, modeling) {
const {
binding,
value
} = property;
const businessObject = getBusinessObject(element);
const extensionElements = businessObject.get('extensionElements');
const inputOutput = findExtension(businessObject, 'camunda:InputOutput');
let updatedBusinessObject, update;
if (!extensionElements) {
updatedBusinessObject = businessObject;
const extensionElements = createExtensionElements$1(businessObject, bpmnFactory),
inputOutput = createInputOutput$1(binding, value, bpmnFactory, extensionElements);
extensionElements.values.push(inputOutput);
update = {
extensionElements
};
} else if (!inputOutput) {
updatedBusinessObject = extensionElements;
const inputOutput = createInputOutput$1(binding, value, bpmnFactory, extensionElements);
update = {
values: extensionElements.get('values').concat(inputOutput)
};
} else {
updatedBusinessObject = inputOutput;
const inputParameter = createInputParameter(binding, value, bpmnFactory);
inputParameter.$parent = inputOutput;
update = {
inputParameters: inputOutput.get('camunda:inputParameters').concat(inputParameter)
};
}
modeling.updateModdleProperties(element, updatedBusinessObject, update);
}
function removeInputParameter(element, binding, modeling) {
const businessObject = getBusinessObject(element);
const inputOutput = findExtension(businessObject, 'camunda:InputOutput'),
inputParameters = inputOutput.get('camunda:inputParameters');
const inputParameter = findInputParameter(inputOutput, binding);
modeling.updateModdleProperties(element, inputOutput, {
inputParameters: without(inputParameters, inputParameter)
});
}
function removeEntry(entries, suffix) {
const entry = entries.find(({
id
}) => id.endsWith(suffix));
return without(entries, entry);
}
function createExtensionElements$1(businessObject, bpmnFactory) {
return createElement('bpmn:ExtensionElements', {
values: []
}, businessObject, bpmnFactory);
}
function createInputOutput$1(binding, value, bpmnFactory, extensionElements) {
const inputParameter = createInputParameter(binding, value, bpmnFactory);
const inputOutput = createElement('camunda:InputOutput', {
inputParameters: [inputParameter],
outputParameters: []
}, extensionElements, bpmnFactory);
inputParameter.$parent = inputOutput;
return inputOutput;
}
const SPACE_REGEX = /\s/;
function containsSpace(value) {
return SPACE_REGEX.test(value);
}
function OutputProperties(props) {
const {
element,
index,
injector,
property
} = props;
const {
binding,
description,
label
} = property;
const {
name
} = binding;
const businessObject = getBusinessObject(element),
inputOutput = findExtension(businessObject, 'camunda:InputOutput');
const translate = injector.get('translate');
const outputParameter = inputOutput && findOutputParameter(inputOutput, binding);
const id = `${element.id}-outputParameter-${index}`;
let entries = [];
// (1) add description entry
if (description) {
entries.push({
id: `${id}-description`,
component: Description,
text: description
});
}
// (2) add local variable assignment entry
entries.push({
id: `${id}-local-variable-assignment`,
component: ProcessVariableAssignment,
outputParameter,
property
});
if (outputParameter) {
// (3) add assign to process variable entry
entries.push({
id: `${id}-assign-to-process-variable`,
component: AssignToProcessVariable,
property
});
}
const item = {
id,
label: label || name || translate('<unnamed>'),
entries
};
return item;
}
// TODO(philippfromme): add text entry to properties-panel
function Description(props) {
const {
id,
text
} = props;
return jsx("div", {
class: "bio-properties-panel-entry",
"data-entry-id": id,
children: jsx("div", {
class: "bio-properties-panel-description",
children: jsx(PropertyDescription, {
description: text
})
})
});
}
function ProcessVariableAssignment(props) {
const {
element,
id,
property,
outputParameter
} = props;
const {
binding
} = property;
const bpmnFactory = useService('bpmnFactory'),
modeling = useService('modeling'),
translate = useService('translate');
const getValue = () => {
return outputParameter;
};
const setValue = value => {
if (value) {
addOutputParameter(element, property, bpmnFactory, modeling);
} else {
removeOutputParameter(element, binding, modeling);
}
};
return ToggleSwitchEntry({
id,
label: translate('Process variable assignment'),
switcherLabel: outputParameter ? translate('On') : translate('Off'),
description: outputParameter ? '' : translate('Parameter won\'t be available in process scope.'),
getValue,
setValue
});
}
function AssignToProcessVariable(props) {
const {
element,
id,
property
} = props;
const {
binding
} = property;
const inputOutput = findExtension(element, 'camunda:InputOutput'),
outputParameter = findOutputParameter(inputOutput, binding);
const commandStack = useService('commandStack'),
debounce = useService('debounceInput'),
translate = useService('translate');
const setValue = value => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: outputParameter,
properties: {
name: value
}
});
};
const getValue = () => {
return outputParameter.get('camunda:name');
};
const validate = value => {
if (!value) {
return translate('Process variable name must not be empty.');
} else if (containsSpace(value)) {
return translate('Process variable name must not contain spaces.');
}
};
return TextFieldEntry({
debounce,
element: outputParameter,
id,
label: translate('Assign to process variable'),
getValue,
setValue,
validate
});
}
function addOutputParameter(element, property, bpmnFactory, modeling) {
const {
binding,
value
} = property;
const businessObject = getBusinessObject(element);
const extensionElements = businessObject.get('extensionElements');
const inputOutput = findExtension(businessObject, 'camunda:InputOutput');
let updatedBusinessObject, update;
if (!extensionElements) {
updatedBusinessObject = businessObject;
const extensionElements = createExtensionElements(businessObject, bpmnFactory),
inputOutput = createInputOutput(binding, value, bpmnFactory, extensionElements);
extensionElements.values.push(inputOutput);
update = {
extensionElements
};
} else if (!inputOutput) {
updatedBusinessObject = extensionElements;
const inputOutput = createInputOutput(binding, value, bpmnFactory, extensionElements);
update = {
values: extensionElements.get('values').concat(inputOutput)
};
} else {
updatedBusinessObject = inputOutput;
const outputParameter = createOutputParameter(binding, value, bpmnFactory);
outputParameter.$parent = inputOutput;
update = {
outputParameters: inputOutput.get('camunda:outputParameters').concat(outputParameter)
};
}
modeling.updateModdleProperties(element, updatedBusinessObject, update);
}
function removeOutputParameter(element, binding, modeling) {
const businessObject = getBusinessObject(element);
const inputOutput = findExtension(businessObject, 'camunda:InputOutput'),
outputParameters = inputOutput.get('camunda:outputParameters');
const outputParameter = findOutputParameter(inputOutput, binding);
modeling.updateModdleProperties(element, inputOutput, {
outputParameters: without(outputParameters, outputParameter)
});
}
function createExtensionElements(businessObject, bpmnFactory) {
return createElement('bpmn:ExtensionElements', {
values: []
}, businessObject, bpmnFactory);
}
function createInputOutput(binding, value, bpmnFactory, extensionElements) {
const outputParameter = createOutputParameter(binding, value, bpmnFactory);
const inputOutput = createElement('camunda:InputOutput', {
inputParameters: [],
outputParameters: [outputParameter]
}, extensionElements, bpmnFactory);
outputParameter.$parent = inputOutput;
return inputOutput;
}
const CAMUNDA_ERROR_EVENT_DEFINITION_TYPE = 'camunda:errorEventDefinition',
CAMUNDA_INPUT_PARAMETER_TYPE = 'camunda:inputParameter',
CAMUNDA_OUTPUT_PARAMETER_TYPE = 'camunda:outputParameter';
const LOWER_PRIORITY = 300;
const ALWAYS_DISPLAYED_GROUPS = ['general', 'documentation', 'multiInstance'];
class ElementTemplatesPropertiesProvider {
constructor(elementTemplates, propertiesPanel, injector) {
propertiesPanel.registerProvider(LOWER_PRIORITY, this);
this._elementTemplates = elementTemplates;
this._injector = injector;
}
getGroups(element) {
return groups => {
const injector = this._injector;
if (!this._shouldShowTemplateProperties(element)) {
return groups;
}
const translate = injector.get('translate');
// (0) Copy provided groups
groups = groups.slice();
const templatesGroup = {
element,
id: 'ElementTemplates__Template',
label: translate('Template'),
component: createElementTemplatesGroup({
getTemplateId
}),
entries: TemplateProps({
element,
elementTemplates: this._elementTemplates,
getTemplateId,
getTemplateVersion
})
};
// (1) Add templates group
addGroupsAfter(ALWAYS_DISPLAYED_GROUPS, groups, [templatesGroup]);
const elementTemplate = this._elementTemplates.get(element);
if (elementTemplate) {
const templateSpecificGroups = [].concat(createInputGroup(element, elementTemplate, injector, groups) || [], createOutputGroup(element, elementTemplate, injector) || [], createErrorGroup(element, elementTemplate, injector, groups) || [], CustomProperties({
element,
elementTemplate,
injector
}));
// (2) add template-specific properties groups
addGroupsAfter('ElementTemplates__Template', groups, templateSpecificGroups);
}
// (3) apply entries visible
if (getTemplateId(element)) {
groups = filterWithEntriesVisible(elementTemplate || {}, groups);
}
return groups;
};
}
_shouldShowTemplateProperties(element) {
return getTemplateId(element) || this._elementTemplates.getAll(element).length;
}
}
ElementTemplatesPropertiesProvider.$inject = ['elementTemplates', 'propertiesPanel', 'injector'];
// helper /////////////////////
function createInputGroup(element, elementTemplate, injector, groups) {
const translate = injector.get('translate');
const group = {
label: translate('Inputs'),
id: 'ElementTemplates__Input',
component: ListGroup,
items: []
};
const properties = elementTemplate.properties.filter(({
binding,
type
}) => {
return !type && binding.type === CAMUNDA_INPUT_PARAMETER_TYPE;
});
properties.forEach((property, index) => {
const item = InputProperties({
element,
index,
property,
groups
});
if (item) {
group.items.push(item);
}
});
// remove if empty
if (!group.items.length) {
return null;
}
return group;
}
function createOutputGroup(element, elementTemplate, injector, groups) {
const translate = injector.get('translate');
const group = {
label: translate('Outputs'),
id: 'ElementTemplates__Output',
component: ListGroup,
items: []
};
const properties = elementTemplate.properties.filter(({
binding,
type
}) => {
return !type && binding.type === CAMUNDA_OUTPUT_PARAMETER_TYPE;
});
properties.forEach((property, index) => {
const item = OutputProperties({
element,
index,
property,
injector
});
if (item) {
group.items.push(item);
}
});
// remove if empty
if (!group.items.length) {
return null;
}
return group;
}
function createErrorGroup(element, elementTemplate, injector, groups) {
const translate = injector.get('translate');
const group = {
label: translate('Errors'),
id: 'ElementTemplates__Error',
component: ListGroup,
items: []
};
const properties = elementTemplate.properties.filter(({
binding,
type
}) => {
return !type && binding.type === CAMUNDA_ERROR_EVENT_DEFINITION_TYPE;
});
properties.forEach((property, index) => {
const item = ErrorProperties({
element,
index,
property,
groups
});
if (item) {
group.items.push(item);
}
});
// remove if empty
if (!group.items.length) {
return null;
}
return group;
}
/**
*
* @param {string|string[]} idOrIds
* @param {Array<{ id: string }} groups
* @param {Array<{ id: string }>} groupsToAdd
*/
function addGroupsAfter(idOrIds, groups, groupsToAdd) {
let ids = idOrIds;
if (!Array.isArray(idOrIds)) {
ids = [idOrIds];
}
// find index of last group with provided id
const index = groups.reduce((acc, group, index) => {
return ids.includes(group.id) ? index : acc;
}, -1);
if (index !== -1) {
groups.splice(index + 1, 0, ...groupsToAdd);
} else {
// add in the beginning if group with provided id is missing
groups.unshift(...groupsToAdd);
}
}
function filterWithEntriesVisible(template, groups) {
if (!template.entriesVisible) {
return groups.filter(group => {
return ALWAYS_DISPLAYED_GROUPS.includes(group.id) || group.id.startsWith('ElementTemplates__');
});
}
return groups;
}
var propertiesProviderModule = {
__depends__: [translateModule, CamundaPlatformPropertiesProviderModule],
__init__: ['elementTemplatesPropertiesProvider'],
elementTemplatesPropertiesProvider: ['type', ElementTemplatesPropertiesProvider]
};
var index = {
__depends__: [coreModule, propertiesProviderModule]
};
/**
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership.
*
* Camunda licenses this file to you under the MIT; you may not use this file
* except in compliance with the MIT License.
*/
function validate ({
templates = []
}) {
// We use the ElementTemplates Module without the required bpmn-js modules
// As we only use it to facilitate template ID and version lookup,
// access to commandstack etc. is not required
const eventBus = new EventBus();
const elementTemplates = new ElementTemplates(null, null, eventBus, null, null);
elementTemplates.set(templates);
function check(node, reporter) {
if (!is(node, 'bpmn:FlowElement')) {
return;
}
let template = elementTemplates.get(node);
const templateId = elementTemplates._getTemplateId(node);
const templateVersion = elementTemplates._getTemplateVersion(node);
// Handle missing template
if (templateId && !template) {
const versionText = templateVersion ? ` (version: ${templateVersion})` : '';
reporter.report(node.id, `Linked element template '${templateId}'${versionText} not found`, {
name: node.name
});
return;
}
if (!template) {
return;
}
template = applyConditions(node, template);
// Check attributes
template.properties.forEach(property => {
const value = getPropertyValue(node, property);
const error = validateProperty(value, property);
if (!error) {
return;
}
reporter.report(node.id, error, {
propertiesPanel: {
entryIds: [getEntryId(property, template)]
},
name: node.name
});
});
}
return {
check
};
}
// helpers //////////////////////
function getEntryId(property, template) {
const index = template.properties.filter(p => p.group === property.group).indexOf(property);
const path = ['custom-entry', template.id];
if (property.group) {
path.push(property.group);
}
path.push(index);
return path.join('-');
}
/**
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership.
*
* Camunda licenses this file to you under the MIT; you may not use this file
* except in compliance with the MIT License.
*/
function compatibility ({
templates = []
}) {
// We use the ElementTemplates Module without the required bpmn-js modules
// As we only use it to facilitate template ID and version lookup,
// access to commandstack etc. is not required
const eventBus = new EventBus();
const elementTemplates = new ElementTemplates(null, null, eventBus, null, null);
elementTemplates.set(templates);
function isUpdateAvailable(template) {
const latestTemplate = elementTemplates.getLatest(template.id, {
deprecated: true
})[0];
if (latestTemplate && latestTemplate !== template) {
return true;
}
return false;
}
function check(node, reporter) {
if (is(node, 'bpmn:Definitions')) {
elementTemplates.setEngines(getEnginesConfig(node));
}
if (!is(node, 'bpmn:FlowElement')) {
return;
}
let template = elementTemplates.get(node);
if (!template) {
return;
}
// Check compatibility
if (template.engines) {
const incomp = elementTemplates.getIncompatibleEngines(template);
Object.keys(incomp).forEach(engine => {
reporter.report(node.id, getIncompatibilityText(engine, incomp[engine], isUpdateAvailable(template)), {
name: node.name
});
});
}
}
return {
check
};
}
// helpers //////////////////////
function getEnginesConfig(definitions) {
const engines = {};
const executionPlatform = definitions.get('modeler:executionPlatform');
const executionPlatformVersion = definitions.get('modeler:executionPlatformVersion');
if (executionPlatform === 'Camunda Cloud' && executionPlatformVersion) {
engines.camunda = executionPlatformVersion;
}
return engines;
}
function getIncompatibilityText(engine, {
actual,
required
}, updateAvailable) {
const message = `Element template incompatible with current <${engine}> environment. ` + `Requires '${engine} ${required}'; found '${actual}'. ` + `${updateAvailable ? 'Update available.' : ''}`;
return message.trim();
}
/**
* Moddle base element.
*/
function Base() {}
Base.prototype.get = function (name) {
return this.$model.properties.get(this, name);
};
Base.prototype.set = function (name, value) {
this.$model.properties.set(this, name, value);
};
/**
* A model element factory.
*
* @param {Moddle} model
* @param {Properties} properties
*/
function Factory(model, properties) {
this.model = model;
this.properties = properties;
}
Factory.prototype.createType = function (descriptor) {
var model = this.model;
var props = this.properties,
prototype = Object.create(Base.prototype);
// initialize default values
forEach(descriptor.properties, function (p) {
if (!p.isMany && p.default !== undefined) {
prototype[p.name] = p.default;
}
});
props.defineModel(prototype, model);
props.defineDescriptor(prototype, descriptor);
var name = descriptor.ns.name;
/**
* The new type constructor
*/
function ModdleElement(attrs) {
props.define(this, '$type', {
value: name,
enumerable: true
});
props.define(this, '$attrs', {
value: {}
});
props.define(this, '$parent', {
writable: true
});
forEach(attrs, bind(function (val, key) {
this.set(key, val);
}, this));
}
ModdleElement.prototype = prototype;
ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;
// static links
props.defineModel(ModdleElement, model);
props.defineDescriptor(ModdleElement, descriptor);
return ModdleElement;
};
/**
* Built-in moddle types
*/
var BUILTINS = {
String: true,
Boolean: true,
Integer: true,
Real: true,
Element: true
};
/**
* Converters for built in types from string representations
*/
var TYPE_CONVERTERS = {
String: function (s) {
return s;
},
Boolean: function (s) {
return s === 'true';
},
Integer: function (s) {
return parseInt(s, 10);
},
Real: function (s) {
return parseFloat(s);
}
};
/**
* Convert a type to its real representation
*/
function coerceType(type, value) {
var converter = TYPE_CONVERTERS[type];
if (converter) {
return converter(value);
} else {
return value;
}
}
/**
* Return whether the given type is built-in
*/
function isBuiltIn(type) {
return !!BUILTINS[type];
}
/**
* Return whether the given type is simple
*/
function isSimple(type) {
return !!TYPE_CONVERTERS[type];
}
/**
* Parses a namespaced attribute name of the form (ns:)localName to an object,
* given a default prefix to assume in case no explicit namespace is given.
*
* @param {String} name
* @param {String} [defaultPrefix] the default prefix to take, if none is present.
*
* @return {Object} the parsed name
*/
function parseName(name, defaultPrefix) {
var parts = name.split(/:/),
localName,
prefix;
// no prefix (i.e. only local name)
if (parts.length === 1) {
localName = name;
prefix = defaultPrefix;
}
// prefix + local name
else if (parts.length === 2) {
localName = parts[1];
prefix = parts[0];
} else {
throw new Error('expected <prefix:localName> or <localName>, got ' + name);
}
name = (prefix ? prefix + ':' : '') + localName;
return {
name: name,
prefix: prefix,
localName: localName
};
}
/**
* A utility to build element descriptors.
*/
function DescriptorBuilder(nameNs) {
this.ns = nameNs;
this.name = nameNs.name;
this.allTypes = [];
this.allTypesByName = {};
this.properties = [];
this.propertiesByName = {};
}
DescriptorBuilder.prototype.build = function () {
return pick(this, ['ns', 'name', 'allTypes', 'allTypesByName', 'properties', 'propertiesByName', 'bodyProperty', 'idProperty']);
};
/**
* Add property at given index.
*
* @param {Object} p
* @param {Number} [idx]
* @param {Boolean} [validate=true]
*/
DescriptorBuilder.prototype.addProperty = function (p, idx, validate) {
if (typeof idx === 'boolean') {
validate = idx;
idx = undefined;
}
this.addNamedProperty(p, validate !== false);
var properties = this.properties;
if (idx !== undefined) {
properties.splice(idx, 0, p);
} else {
properties.push(p);
}
};
DescriptorBuilder.prototype.replaceProperty = function (oldProperty, newProperty, replace) {
var oldNameNs = oldProperty.ns;
var props = this.properties,
propertiesByName = this.propertiesByName,
rename = oldProperty.name !== newProperty.name;
if (oldProperty.isId) {
if (!newProperty.isId) {
throw new Error('property <' + newProperty.ns.name + '> must be id property ' + 'to refine <' + oldProperty.ns.name + '>');
}
this.setIdProperty(newProperty, false);
}
if (oldProperty.isBody) {
if (!newProperty.isBody) {
throw new Error('property <' + newProperty.ns.name + '> must be body property ' + 'to refine <' + oldProperty.ns.name + '>');
}
// TODO: Check compatibility
this.setBodyProperty(newProperty, false);
}
// validate existence and get location of old property
var idx = props.indexOf(oldProperty);
if (idx === -1) {
throw new Error('property <' + oldNameNs.name + '> not found in property list');
}
// remove old property
props.splice(idx, 1);
// replacing the named property is intentional
//
// * validate only if this is a "rename" operation
// * add at specific index unless we "replace"
//
this.addProperty(newProperty, replace ? undefined : idx, rename);
// make new property available under old name
propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
};
DescriptorBuilder.prototype.redefineProperty = function (p, targetPropertyName, replace) {
var nsPrefix = p.ns.prefix;
var parts = targetPropertyName.split('#');
var name = parseName(parts[0], nsPrefix);
var attrName = parseName(parts[1], name.prefix).name;
var redefinedProperty = this.propertiesByName[attrName];
if (!redefinedProperty) {
throw new Error('refined property <' + attrName + '> not found');
} else {
this.replaceProperty(redefinedProperty, p, replace);
}
delete p.redefines;
};
DescriptorBuilder.prototype.addNamedProperty = function (p, validate) {
var ns = p.ns,
propsByName = this.propertiesByName;
if (validate) {
this.assertNotDefined(p, ns.name);
this.assertNotDefined(p, ns.localName);
}
propsByName[ns.name] = propsByName[ns.localName] = p;
};
DescriptorBuilder.prototype.removeNamedProperty = function (p) {
var ns = p.ns,
propsByName = this.propertiesByName;
delete propsByName[ns.name];
delete propsByName[ns.localName];
};
DescriptorBuilder.prototype.setBodyProperty = function (p, validate) {
if (validate && this.bodyProperty) {
throw new Error('body property defined multiple times ' + '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
}
this.bodyProperty = p;
};
DescriptorBuilder.prototype.setIdProperty = function (p, validate) {
if (validate && this.idProperty) {
throw new Error('id property defined multiple times ' + '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)');
}
this.idProperty = p;
};
DescriptorBuilder.prototype.assertNotTrait = function (typeDescriptor) {
const _extends = typeDescriptor.extends || [];
if (_extends.length) {
throw new Error(`cannot create <${typeDescriptor.name}> extending <${typeDescriptor.extends}>`);
}
};
DescriptorBuilder.prototype.assertNotDefined = function (p, name) {
var propertyName = p.name,
definedProperty = this.propertiesByName[propertyName];
if (definedProperty) {
throw new Error('property <' + propertyName + '> already defined; ' + 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' + '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
}
};
DescriptorBuilder.prototype.hasProperty = function (name) {
return this.propertiesByName[name];
};
DescriptorBuilder.prototype.addTrait = function (t, inherited) {
if (inherited) {
this.assertNotTrait(t);
}
var typesByName = this.allTypesByName,
types = this.allTypes;
var typeName = t.name;
if (typeName in typesByName) {
return;
}
forEach(t.properties, bind(function (p) {
// clone property to allow extensions
p = assign({}, p, {
name: p.ns.localName,
inherited: inherited
});
Object.defineProperty(p, 'definedBy', {
value: t
});
var replaces = p.replaces,
redefines = p.redefines;
// add replace/redefine support
if (replaces || redefines) {
this.redefineProperty(p, replaces || redefines, replaces);
} else {
if (p.isBody) {
this.setBodyProperty(p);
}
if (p.isId) {
this.setIdProperty(p);
}
this.addProperty(p);
}
}, this));
types.push(t);
typesByName[typeName] = t;
};
/**
* A registry of Moddle packages.
*
* @param {Array<Package>} packages
* @param {Properties} properties
*/
function Registry(packages, properties) {
this.packageMap = {};
this.typeMap = {};
this.packages = [];
this.properties = properties;
forEach(packages, bind(this.registerPackage, this));
}
Registry.prototype.getPackage = function (uriOrPrefix) {
return this.packageMap[uriOrPrefix];
};
Registry.prototype.getPackages = function () {
return this.packages;
};
Registry.prototype.registerPackage = function (pkg) {
// copy package
pkg = assign({}, pkg);
var pkgMap = this.packageMap;
ensureAvailable(pkgMap, pkg, 'prefix');
ensureAvailable(pkgMap, pkg, 'uri');
// register types
forEach(pkg.types, bind(function (descriptor) {
this.registerType(descriptor, pkg);
}, this));
pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg;
this.packages.push(pkg);
};
/**
* Register a type from a specific package with us
*/
Registry.prototype.registerType = function (type, pkg) {
type = assign({}, type, {
superClass: (type.superClass || []).slice(),
extends: (type.extends || []).slice(),
properties: (type.properties || []).slice(),
meta: assign(type.meta || {})
});
var ns = parseName(type.name, pkg.prefix),
name = ns.name,
propertiesByName = {};
// parse properties
forEach(type.properties, bind(function (p) {
// namespace property names
var propertyNs = parseName(p.name, ns.prefix),
propertyName = propertyNs.name;
// namespace property types
if (!isBuiltIn(p.type)) {
p.type = parseName(p.type, propertyNs.prefix).name;
}
assign(p, {
ns: propertyNs,
name: propertyName
});
propertiesByName[propertyName] = p;
}, this));
// update ns + name
assign(type, {
ns: ns,
name: name,
propertiesByName: propertiesByName
});
forEach(type.extends, bind(function (extendsName) {
var extendsNameNs = parseName(extendsName, ns.prefix);
var extended = this.typeMap[extendsNameNs.name];
extended.traits = extended.traits || [];
extended.traits.push(name);
}, this));
// link to package
this.definePackage(type, pkg);
// register
this.typeMap[name] = type;
};
/**
* Traverse the type hierarchy from bottom to top,
* calling iterator with (type, inherited) for all elements in
* the inheritance chain.
*
* @param {Object} nsName
* @param {Function} iterator
* @param {Boolean} [trait=false]
*/
Registry.prototype.mapTypes = function (nsName, iterator, trait) {
var type = isBuiltIn(nsName.name) ? {
name: nsName.name
} : this.typeMap[nsName.name];
var self = this;
/**
* Traverse the selected super type or trait
*
* @param {String} cls
* @param {Boolean} [trait=false]
*/
function traverse(cls, trait) {
var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix);
self.mapTypes(parentNs, iterator, trait);
}
/**
* Traverse the selected trait.
*
* @param {String} cls
*/
function traverseTrait(cls) {
return traverse(cls, true);
}
/**
* Traverse the selected super type
*
* @param {String} cls
*/
function traverseSuper(cls) {
return traverse(cls, false);
}
if (!type) {
throw new Error('unknown type <' + nsName.name + '>');
}
forEach(type.superClass, trait ? traverseTrait : traverseSuper);
// call iterator with (type, inherited=!trait)
iterator(type, !trait);
forEach(type.traits, traverseTrait);
};
/**
* Returns the effective descriptor for a type.
*
* @param {String} type the namespaced name (ns:localName) of the type
*
* @return {Descriptor} the resulting effective descriptor
*/
Registry.prototype.getEffectiveDescriptor = function (name) {
var nsName = parseName(name);
var builder = new DescriptorBuilder(nsName);
this.mapTypes(nsName, function (type, inherited) {
builder.addTrait(type, inherited);
});
var descriptor = builder.build();
// define package link
this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);
return descriptor;
};
Registry.prototype.definePackage = function (target, pkg) {
this.properties.define(target, '$pkg', {
value: pkg
});
};
// helpers ////////////////////////////
function ensureAvailable(packageMap, pkg, identifierKey) {
var value = pkg[identifierKey];
if (value in packageMap) {
throw new Error('package with ' + identifierKey + ' <' + value + '> already defined');
}
}
/**
* A utility that gets and sets properties of model elements.
*
* @param {Model} model
*/
function Properties(model) {
this.model = model;
}
/**
* Sets a named property on the target element.
* If the value is undefined, the property gets deleted.
*
* @param {Object} target
* @param {String} name
* @param {Object} value
*/
Properties.prototype.set = function (target, name, value) {
if (!isString(name) || !name.length) {
throw new TypeError('property name must be a non-empty string');
}
var property = this.getProperty(target, name);
var propertyName = property && property.name;
if (isUndefined(value)) {
// unset the property, if the specified value is undefined;
// delete from $attrs (for extensions) or the target itself
if (property) {
delete target[propertyName];
} else {
delete target.$attrs[stripGlobal(name)];
}
} else {
// set the property, defining well defined properties on the fly
// or simply updating them in target.$attrs (for extensions)
if (property) {
if (propertyName in target) {
target[propertyName] = value;
} else {
defineProperty(target, property, value);
}
} else {
target.$attrs[stripGlobal(name)] = value;
}
}
};
/**
* Returns the named property of the given element
*
* @param {Object} target
* @param {String} name
*
* @return {Object}
*/
Properties.prototype.get = function (target, name) {
var property = this.getProperty(target, name);
if (!property) {
return target.$attrs[stripGlobal(name)];
}
var propertyName = property.name;
// check if access to collection property and lazily initialize it
if (!target[propertyName] && property.isMany) {
defineProperty(target, property, []);
}
return target[propertyName];
};
/**
* Define a property on the target element
*
* @param {Object} target
* @param {String} name
* @param {Object} options
*/
Properties.prototype.define = function (target, name, options) {
if (!options.writable) {
var value = options.value;
// use getters for read-only variables to support ES6 proxies
// cf. https://github.com/bpmn-io/internal-docs/issues/386
options = assign({}, options, {
get: function () {
return value;
}
});
delete options.value;
}
Object.defineProperty(target, name, options);
};
/**
* Define the descriptor for an element
*/
Properties.prototype.defineDescriptor = function (target, descriptor) {
this.define(target, '$descriptor', {
value: descriptor
});
};
/**
* Define the model for an element
*/
Properties.prototype.defineModel = function (target, model) {
this.define(target, '$model', {
value: model
});
};
/**
* Return property with the given name on the element.
*
* @param {any} target
* @param {string} name
*
* @return {object | null} property
*/
Properties.prototype.getProperty = function (target, name) {
var model = this.model;
var property = model.getPropertyDescriptor(target, name);
if (property) {
return property;
}
if (name.includes(':')) {
return null;
}
const strict = model.config.strict;
if (typeof strict !== 'undefined') {
const error = new TypeError(`unknown property <${name}> on <${target.$type}>`);
if (strict) {
throw error;
} else {
// eslint-disable-next-line no-undef
typeof console !== 'undefined' && console.warn(error);
}
}
return null;
};
function isUndefined(val) {
return typeof val === 'undefined';
}
function defineProperty(target, property, value) {
Object.defineProperty(target, property.name, {
enumerable: !property.isReference,
writable: true,
value: value,
configurable: true
});
}
function stripGlobal(name) {
return name.replace(/^:/, '');
}
// Moddle implementation /////////////////////////////////////////////////
/**
* @class Moddle
*
* A model that can be used to create elements of a specific type.
*
* @example
*
* var Moddle = require('moddle');
*
* var pkg = {
* name: 'mypackage',
* prefix: 'my',
* types: [
* { name: 'Root' }
* ]
* };
*
* var moddle = new Moddle([pkg]);
*
* @param {Array<Package>} packages the packages to contain
*
* @param { { strict?: boolean } } [config] moddle configuration
*/
function Moddle(packages, config = {}) {
this.properties = new Properties(this);
this.factory = new Factory(this, this.properties);
this.registry = new Registry(packages, this.properties);
this.typeCache = {};
this.config = config;
}
/**
* Create an instance of the specified type.
*
* @method Moddle#create
*
* @example
*
* var foo = moddle.create('my:Foo');
* var bar = moddle.create('my:Bar', { id: 'BAR_1' });
*
* @param {String|Object} descriptor the type descriptor or name know to the model
* @param {Object} attrs a number of attributes to initialize the model instance with
* @return {Object} model instance
*/
Moddle.prototype.create = function (descriptor, attrs) {
var Type = this.getType(descriptor);
if (!Type) {
throw new Error('unknown type <' + descriptor + '>');
}
return new Type(attrs);
};
/**
* Returns the type representing a given descriptor
*
* @method Moddle#getType
*
* @example
*
* var Foo = moddle.getType('my:Foo');
* var foo = new Foo({ 'id' : 'FOO_1' });
*
* @param {String|Object} descriptor the type descriptor or name know to the model
* @return {Object} the type representing the descriptor
*/
Moddle.prototype.getType = function (descriptor) {
var cache = this.typeCache;
var name = isString(descriptor) ? descriptor : descriptor.ns.name;
var type = cache[name];
if (!type) {
descriptor = this.registry.getEffectiveDescriptor(name);
type = cache[name] = this.factory.createType(descriptor);
}
return type;
};
/**
* Creates an any-element type to be used within model instances.
*
* This can be used to create custom elements that lie outside the meta-model.
* The created element contains all the meta-data required to serialize it
* as part of meta-model elements.
*
* @method Moddle#createAny
*
* @example
*
* var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
* value: 'bar'
* });
*
* var container = moddle.create('my:Container', 'http://my', {
* any: [ foo ]
* });
*
* // go ahead and serialize the stuff
*
*
* @param {String} name the name of the element
* @param {String} nsUri the namespace uri of the element
* @param {Object} [properties] a map of properties to initialize the instance with
* @return {Object} the any type instance
*/
Moddle.prototype.createAny = function (name, nsUri, properties) {
var nameNs = parseName(name);
var element = {
$type: name,
$instanceOf: function (type) {
return type === this.$type;
},
get: function (key) {
return this[key];
},
set: function (key, value) {
set(this, [key], value);
}
};
var descriptor = {
name: name,
isGeneric: true,
ns: {
prefix: nameNs.prefix,
localName: nameNs.localName,
uri: nsUri
}
};
this.properties.defineDescriptor(element, descriptor);
this.properties.defineModel(element, this);
this.properties.define(element, 'get', {
enumerable: false,
writable: true
});
this.properties.define(element, 'set', {
enumerable: false,
writable: true
});
this.properties.define(element, '$parent', {
enumerable: false,
writable: true
});
this.properties.define(element, '$instanceOf', {
enumerable: false,
writable: true
});
forEach(properties, function (a, key) {
if (isObject(a) && a.value !== undefined) {
element[a.name] = a.value;
} else {
element[key] = a;
}
});
return element;
};
/**
* Returns a registered package by uri or prefix
*
* @return {Object} the package
*/
Moddle.prototype.getPackage = function (uriOrPrefix) {
return this.registry.getPackage(uriOrPrefix);
};
/**
* Returns a snapshot of all known packages
*
* @return {Object} the package
*/
Moddle.prototype.getPackages = function () {
return this.registry.getPackages();
};
/**
* Returns the descriptor for an element
*/
Moddle.prototype.getElementDescriptor = function (element) {
return element.$descriptor;
};
/**
* Returns true if the given descriptor or instance
* represents the given type.
*
* May be applied to this, if element is omitted.
*/
Moddle.prototype.hasType = function (element, type) {
if (type === undefined) {
type = element;
element = this;
}
var descriptor = element.$model.getElementDescriptor(element);
return type in descriptor.allTypesByName;
};
/**
* Returns the descriptor of an elements named property
*/
Moddle.prototype.getPropertyDescriptor = function (element, property) {
return this.getElementDescriptor(element).propertiesByName[property];
};
/**
* Returns a mapped type's descriptor
*/
Moddle.prototype.getTypeDescriptor = function (type) {
return this.registry.typeMap[type];
};
var fromCharCode = String.fromCharCode;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig;
var ENTITY_MAPPING = {
'amp': '&',
'apos': '\'',
'gt': '>',
'lt': '<',
'quot': '"'
};
// map UPPERCASE variants of supported special chars
Object.keys(ENTITY_MAPPING).forEach(function (k) {
ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k];
});
function replaceEntities(_, d, x, z) {
// reserved names, i.e.
if (z) {
if (hasOwnProperty.call(ENTITY_MAPPING, z)) {
return ENTITY_MAPPING[z];
} else {
// fall back to original value
return '&' + z + ';';
}
}
// decimal encoded char
if (d) {
return fromCharCode(d);
}
// hex encoded char
return fromCharCode(parseInt(x, 16));
}
/**
* A basic entity decoder that can decode a minimal
* sub-set of reserved names (&) as well as
* hex (ય) and decimal (ӏ) encoded characters.
*
* @param {string} s
*
* @return {string} decoded string
*/
function decodeEntities(s) {
if (s.length > 3 && s.indexOf('&') !== -1) {
return s.replace(ENTITY_PATTERN, replaceEntities);
}
return s;
}
var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node';
function error$1(msg) {
return new Error(msg);
}
function missingNamespaceForPrefix(prefix) {
return 'missing namespace for prefix <' + prefix + '>';
}
function getter(getFn) {
return {
'get': getFn,
'enumerable': true
};
}
function cloneNsMatrix(nsMatrix) {
var clone = {},
key;
for (key in nsMatrix) {
clone[key] = nsMatrix[key];
}
return clone;
}
function uriPrefix(prefix) {
return prefix + '$uri';
}
function buildNsMatrix(nsUriToPrefix) {
var nsMatrix = {},
uri,
prefix;
for (uri in nsUriToPrefix) {
prefix = nsUriToPrefix[uri];
nsMatrix[prefix] = prefix;
nsMatrix[uriPrefix(prefix)] = uri;
}
return nsMatrix;
}
function noopGetContext() {
return {
line: 0,
column: 0
};
}
function throwFunc(err) {
throw err;
}
/**
* Creates a new parser with the given options.
*
* @constructor
*
* @param {!Object<string, ?>=} options
*/
function Parser(options) {
if (!this) {
return new Parser(options);
}
var proxy = options && options['proxy'];
var onText,
onOpenTag,
onCloseTag,
onCDATA,
onError = throwFunc,
onWarning,
onComment,
onQuestion,
onAttention;
var getContext = noopGetContext;
/**
* Do we need to parse the current elements attributes for namespaces?
*
* @type {boolean}
*/
var maybeNS = false;
/**
* Do we process namespaces at all?
*
* @type {boolean}
*/
var isNamespace = false;
/**
* The caught error returned on parse end
*
* @type {Error}
*/
var returnError = null;
/**
* Should we stop parsing?
*
* @type {boolean}
*/
var parseStop = false;
/**
* A map of { uri: prefix } used by the parser.
*
* This map will ensure we can normalize prefixes during processing;
* for each uri, only one prefix will be exposed to the handlers.
*
* @type {!Object<string, string>}}
*/
var nsUriToPrefix;
/**
* Handle parse error.
*
* @param {string|Error} err
*/
function handleError(err) {
if (!(err instanceof Error)) {
err = error$1(err);
}
returnError = err;
onError(err, getContext);
}
/**
* Handle parse error.
*
* @param {string|Error} err
*/
function handleWarning(err) {
if (!onWarning) {
return;
}
if (!(err instanceof Error)) {
err = error$1(err);
}
onWarning(err, getContext);
}
/**
* Register parse listener.
*
* @param {string} name
* @param {Function} cb
*
* @return {Parser}
*/
this['on'] = function (name, cb) {
if (typeof cb !== 'function') {
throw error$1('required args <name, cb>');
}
switch (name) {
case 'openTag':
onOpenTag = cb;
break;
case 'text':
onText = cb;
break;
case 'closeTag':
onCloseTag = cb;
break;
case 'error':
onError = cb;
break;
case 'warn':
onWarning = cb;
break;
case 'cdata':
onCDATA = cb;
break;
case 'attention':
onAttention = cb;
break;
// <!XXXXX zzzz="eeee">
case 'question':
onQuestion = cb;
break;
// <? .... ?>
case 'comment':
onComment = cb;
break;
default:
throw error$1('unsupported event: ' + name);
}
return this;
};
/**
* Set the namespace to prefix mapping.
*
* @example
*
* parser.ns({
* 'http://foo': 'foo',
* 'http://bar': 'bar'
* });
*
* @param {!Object<string, string>} nsMap
*
* @return {Parser}
*/
this['ns'] = function (nsMap) {
if (typeof nsMap === 'undefined') {
nsMap = {};
}
if (typeof nsMap !== 'object') {
throw error$1('required args <nsMap={}>');
}
var _nsUriToPrefix = {},
k;
for (k in nsMap) {
_nsUriToPrefix[k] = nsMap[k];
}
isNamespace = true;
nsUriToPrefix = _nsUriToPrefix;
return this;
};
/**
* Parse xml string.
*
* @param {string} xml
*
* @return {Error} returnError, if not thrown
*/
this['parse'] = function (xml) {
if (typeof xml !== 'string') {
throw error$1('required args <xml=string>');
}
returnError = null;
parse(xml);
getContext = noopGetContext;
parseStop = false;
return returnError;
};
/**
* Stop parsing.
*/
this['stop'] = function () {
parseStop = true;
};
/**
* Parse string, invoking configured listeners on element.
*
* @param {string} xml
*/
function parse(xml) {
var nsMatrixStack = isNamespace ? [] : null,
nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
_nsMatrix,
nodeStack = [],
anonymousNsCount = 0,
tagStart = false,
tagEnd = false,
i = 0,
j = 0,
x,
y,
q,
w,
v,
xmlns,
elementName,
_elementName,
elementProxy;
var attrsString = '',
attrsStart = 0,
cachedAttrs // false = parsed with errors, null = needs parsing
;
/**
* Parse attributes on demand and returns the parsed attributes.
*
* Return semantics: (1) `false` on attribute parse error,
* (2) object hash on extracted attrs.
*
* @return {boolean|Object}
*/
function getAttrs() {
if (cachedAttrs !== null) {
return cachedAttrs;
}
var nsUri,
nsUriPrefix,
nsName,
defaultAlias = isNamespace && nsMatrix['xmlns'],
attrList = isNamespace && maybeNS ? [] : null,
i = attrsStart,
s = attrsString,
l = s.length,
hasNewMatrix,
newalias,
value,
alias,
name,
attrs = {},
seenAttrs = {},
skipAttr,
w,
j;
parseAttr: for (; i < l; i++) {
skipAttr = false;
w = s.charCodeAt(i);
if (w === 32 || w < 14 && w > 8) {
// WHITESPACE={ \f\n\r\t\v}
continue;
}
// wait for non whitespace character
if (w < 65 || w > 122 || w > 90 && w < 97) {
if (w !== 95 && w !== 58) {
// char 95"_" 58":"
handleWarning('illegal first char attribute name');
skipAttr = true;
}
}
// parse attribute name
for (j = i + 1; j < l; j++) {
w = s.charCodeAt(j);
if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 46 ||
// '.'
w === 45 ||
// '-'
w === 95 // '_'
) {
continue;
}
// unexpected whitespace
if (w === 32 || w < 14 && w > 8) {
// WHITESPACE
handleWarning('missing attribute value');
i = j;
continue parseAttr;
}
// expected "="
if (w === 61) {
// "=" == 61
break;
}
handleWarning('illegal attribute name char');
skipAttr = true;
}
name = s.substring(i, j);
if (name === 'xmlns:xmlns') {
handleWarning('illegal declaration of xmlns');
skipAttr = true;
}
w = s.charCodeAt(j + 1);
if (w === 34) {
// '"'
j = s.indexOf('"', i = j + 2);
if (j === -1) {
j = s.indexOf('\'', i);
if (j !== -1) {
handleWarning('attribute value quote missmatch');
skipAttr = true;
}
}
} else if (w === 39) {
// "'"
j = s.indexOf('\'', i = j + 2);
if (j === -1) {
j = s.indexOf('"', i);
if (j !== -1) {
handleWarning('attribute value quote missmatch');
skipAttr = true;
}
}
} else {
handleWarning('missing attribute value quotes');
skipAttr = true;
// skip to next space
for (j = j + 1; j < l; j++) {
w = s.charCodeAt(j + 1);
if (w === 32 || w < 14 && w > 8) {
// WHITESPACE
break;
}
}
}
if (j === -1) {
handleWarning('missing closing quotes');
j = l;
skipAttr = true;
}
if (!skipAttr) {
value = s.substring(i, j);
}
i = j;
// ensure SPACE follows attribute
// skip illegal content otherwise
// example a="b"c
for (; j + 1 < l; j++) {
w = s.charCodeAt(j + 1);
if (w === 32 || w < 14 && w > 8) {
// WHITESPACE
break;
}
// FIRST ILLEGAL CHAR
if (i === j) {
handleWarning('illegal character after attribute end');
skipAttr = true;
}
}
// advance cursor to next attribute
i = j + 1;
if (skipAttr) {
continue parseAttr;
}
// check attribute re-declaration
if (name in seenAttrs) {
handleWarning('attribute <' + name + '> already defined');
continue;
}
seenAttrs[name] = true;
if (!isNamespace) {
attrs[name] = value;
continue;
}
// try to extract namespace information
if (maybeNS) {
newalias = name === 'xmlns' ? 'xmlns' : name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:' ? name.substr(6) : null;
// handle xmlns(:alias) assignment
if (newalias !== null) {
nsUri = decodeEntities(value);
nsUriPrefix = uriPrefix(newalias);
alias = nsUriToPrefix[nsUri];
if (!alias) {
// no prefix defined or prefix collision
if (newalias === 'xmlns' || nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri) {
// alocate free ns prefix
do {
alias = 'ns' + anonymousNsCount++;
} while (typeof nsMatrix[alias] !== 'undefined');
} else {
alias = newalias;
}
nsUriToPrefix[nsUri] = alias;
}
if (nsMatrix[newalias] !== alias) {
if (!hasNewMatrix) {
nsMatrix = cloneNsMatrix(nsMatrix);
hasNewMatrix = true;
}
nsMatrix[newalias] = alias;
if (newalias === 'xmlns') {
nsMatrix[uriPrefix(alias)] = nsUri;
defaultAlias = alias;
}
nsMatrix[nsUriPrefix] = nsUri;
}
// expose xmlns(:asd)="..." in attributes
attrs[name] = value;
continue;
}
// collect attributes until all namespace
// declarations are processed
attrList.push(name, value);
continue;
} /** end if (maybeNs) */
// handle attributes on element without
// namespace declarations
w = name.indexOf(':');
if (w === -1) {
attrs[name] = value;
continue;
}
// normalize ns attribute name
if (!(nsName = nsMatrix[name.substring(0, w)])) {
handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
continue;
}
name = defaultAlias === nsName ? name.substr(w + 1) : nsName + name.substr(w);
// end: normalize ns attribute name
attrs[name] = value;
}
// handle deferred, possibly namespaced attributes
if (maybeNS) {
// normalize captured attributes
for (i = 0, l = attrList.length; i < l; i++) {
name = attrList[i++];
value = attrList[i];
w = name.indexOf(':');
if (w !== -1) {
// normalize ns attribute name
if (!(nsName = nsMatrix[name.substring(0, w)])) {
handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
continue;
}
name = defaultAlias === nsName ? name.substr(w + 1) : nsName + name.substr(w);
// end: normalize ns attribute name
}
attrs[name] = value;
}
// end: normalize captured attributes
}
return cachedAttrs = attrs;
}
/**
* Extract the parse context { line, column, part }
* from the current parser position.
*
* @return {Object} parse context
*/
function getParseContext() {
var splitsRe = /(\r\n|\r|\n)/g;
var line = 0;
var column = 0;
var startOfLine = 0;
var endOfLine = j;
var match;
var data;
while (i >= startOfLine) {
match = splitsRe.exec(xml);
if (!match) {
break;
}
// end of line = (break idx + break chars)
endOfLine = match[0].length + match.index;
if (endOfLine > i) {
break;
}
// advance to next line
line += 1;
startOfLine = endOfLine;
}
// EOF errors
if (i == -1) {
column = endOfLine;
data = xml.substring(j);
} else
// start errors
if (j === 0) {
data = xml.substring(j, i);
}
// other errors
else {
column = i - startOfLine;
data = j == -1 ? xml.substring(i) : xml.substring(i, j + 1);
}
return {
'data': data,
'line': line,
'column': column
};
}
getContext = getParseContext;
if (proxy) {
elementProxy = Object.create({}, {
'name': getter(function () {
return elementName;
}),
'originalName': getter(function () {
return _elementName;
}),
'attrs': getter(getAttrs),
'ns': getter(function () {
return nsMatrix;
})
});
}
// actual parse logic
while (j !== -1) {
if (xml.charCodeAt(j) === 60) {
// "<"
i = j;
} else {
i = xml.indexOf('<', j);
}
// parse end
if (i === -1) {
if (nodeStack.length) {
return handleError('unexpected end of file');
}
if (j === 0) {
return handleError('missing start tag');
}
if (j < xml.length) {
if (xml.substring(j).trim()) {
handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
}
}
return;
}
// parse text
if (j !== i) {
if (nodeStack.length) {
if (onText) {
onText(xml.substring(j, i), decodeEntities, getContext);
if (parseStop) {
return;
}
}
} else {
if (xml.substring(j, i).trim()) {
handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
if (parseStop) {
return;
}
}
}
}
w = xml.charCodeAt(i + 1);
// parse comments + CDATA
if (w === 33) {
// "!"
q = xml.charCodeAt(i + 2);
// CDATA section
if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') {
// 91 == "["
j = xml.indexOf(']]>', i);
if (j === -1) {
return handleError('unclosed cdata');
}
if (onCDATA) {
onCDATA(xml.substring(i + 9, j), getContext);
if (parseStop) {
return;
}
}
j += 3;
continue;
}
// comment
if (q === 45 && xml.charCodeAt(i + 3) === 45) {
// 45 == "-"
j = xml.indexOf('-->', i);
if (j === -1) {
return handleError('unclosed comment');
}
if (onComment) {
onComment(xml.substring(i + 4, j), decodeEntities, getContext);
if (parseStop) {
return;
}
}
j += 3;
continue;
}
}
// parse question <? ... ?>
if (w === 63) {
// "?"
j = xml.indexOf('?>', i);
if (j === -1) {
return handleError('unclosed question');
}
if (onQuestion) {
onQuestion(xml.substring(i, j + 2), getContext);
if (parseStop) {
return;
}
}
j += 2;
continue;
}
// find matching closing tag for attention or standard tags
// for that we must skip through attribute values
// (enclosed in single or double quotes)
for (x = i + 1;; x++) {
v = xml.charCodeAt(x);
if (isNaN(v)) {
j = -1;
return handleError('unclosed tag');
}
// [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
// skips the quoted string
// (double quotes) does not appear in a literal enclosed by (double quotes)
// (single quote) does not appear in a literal enclosed by (single quote)
if (v === 34) {
// '"'
q = xml.indexOf('"', x + 1);
x = q !== -1 ? q : x;
} else if (v === 39) {
// "'"
q = xml.indexOf("'", x + 1);
x = q !== -1 ? q : x;
} else if (v === 62) {
// '>'
j = x;
break;
}
}
// parse attention <! ...>
// previously comment and CDATA have already been parsed
if (w === 33) {
// "!"
if (onAttention) {
onAttention(xml.substring(i, j + 1), decodeEntities, getContext);
if (parseStop) {
return;
}
}
j += 1;
continue;
}
// don't process attributes;
// there are none
cachedAttrs = {};
// if (xml.charCodeAt(i+1) === 47) { // </...
if (w === 47) {
// </...
tagStart = false;
tagEnd = true;
if (!nodeStack.length) {
return handleError('missing open tag');
}
// verify open <-> close tag match
x = elementName = nodeStack.pop();
q = i + 2 + x.length;
if (xml.substring(i + 2, q) !== x) {
return handleError('closing tag mismatch');
}
// verify chars in close tag
for (; q < j; q++) {
w = xml.charCodeAt(q);
if (w === 32 || w > 8 && w < 14) {
// \f\n\r\t\v space
continue;
}
return handleError('close tag');
}
} else {
if (xml.charCodeAt(j - 1) === 47) {
// .../>
x = elementName = xml.substring(i + 1, j - 1);
tagStart = true;
tagEnd = true;
} else {
x = elementName = xml.substring(i + 1, j);
tagStart = true;
tagEnd = false;
}
if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) {
// char 95"_" 58":"
return handleError('illegal first char nodeName');
}
for (q = 1, y = x.length; q < y; q++) {
w = x.charCodeAt(q);
if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) {
continue;
}
if (w === 32 || w < 14 && w > 8) {
// \f\n\r\t\v space
elementName = x.substring(0, q);
// maybe there are attributes
cachedAttrs = null;
break;
}
return handleError('invalid nodeName');
}
if (!tagEnd) {
nodeStack.push(elementName);
}
}
if (isNamespace) {
_nsMatrix = nsMatrix;
if (tagStart) {
// remember old namespace
// unless we're self-closing
if (!tagEnd) {
nsMatrixStack.push(_nsMatrix);
}
if (cachedAttrs === null) {
// quick check, whether there may be namespace
// declarations on the node; if that is the case
// we need to eagerly parse the node attributes
if (maybeNS = x.indexOf('xmlns', q) !== -1) {
attrsStart = q;
attrsString = x;
getAttrs();
maybeNS = false;
}
}
}
_elementName = elementName;
w = elementName.indexOf(':');
if (w !== -1) {
xmlns = nsMatrix[elementName.substring(0, w)];
// prefix given; namespace must exist
if (!xmlns) {
return handleError('missing namespace on <' + _elementName + '>');
}
elementName = elementName.substr(w + 1);
} else {
xmlns = nsMatrix['xmlns'];
// if no default namespace is defined,
// we'll import the element as anonymous.
//
// it is up to users to correct that to the document defined
// targetNamespace, or whatever their undersanding of the
// XML spec mandates.
}
// adjust namespace prefixs as configured
if (xmlns) {
elementName = xmlns + ':' + elementName;
}
}
if (tagStart) {
attrsStart = q;
attrsString = x;
if (onOpenTag) {
if (proxy) {
onOpenTag(elementProxy, decodeEntities, tagEnd, getContext);
} else {
onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext);
}
if (parseStop) {
return;
}
}
}
if (tagEnd) {
if (onCloseTag) {
onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext);
if (parseStop) {
return;
}
}
// restore old namespace
if (isNamespace) {
if (!tagStart) {
nsMatrix = nsMatrixStack.pop();
} else {
nsMatrix = _nsMatrix;
}
}
}
j += 1;
}
} /** end parse */
}
function hasLowerCaseAlias(pkg) {
return pkg.xml && pkg.xml.tagAlias === 'lowerCase';
}
var DEFAULT_NS_MAP = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'xml': 'http://www.w3.org/XML/1998/namespace'
};
var SERIALIZE_PROPERTY = 'property';
function getSerialization(element) {
return element.xml && element.xml.serialize;
}
function getSerializationType(element) {
const type = getSerialization(element);
return type !== SERIALIZE_PROPERTY && (type || null);
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function aliasToName(aliasNs, pkg) {
if (!hasLowerCaseAlias(pkg)) {
return aliasNs.name;
}
return aliasNs.prefix + ':' + capitalize(aliasNs.localName);
}
/**
* Un-prefix a potentially prefixed type name.
*
* @param {NsName} nameNs
* @param {Object} [pkg]
*
* @return {string}
*/
function prefixedToName(nameNs, pkg) {
var name = nameNs.name,
localName = nameNs.localName;
var typePrefix = pkg && pkg.xml && pkg.xml.typePrefix;
if (typePrefix && localName.indexOf(typePrefix) === 0) {
return nameNs.prefix + ':' + localName.slice(typePrefix.length);
} else {
return name;
}
}
function normalizeTypeName(name, nsMap, model) {
// normalize against actual NS
const nameNs = parseName(name, nsMap.xmlns);
const normalizedName = `${nsMap[nameNs.prefix] || nameNs.prefix}:${nameNs.localName}`;
const normalizedNameNs = parseName(normalizedName);
// determine actual type name, based on package-defined prefix
var pkg = model.getPackage(normalizedNameNs.prefix);
return prefixedToName(normalizedNameNs, pkg);
}
function error(message) {
return new Error(message);
}
/**
* Get the moddle descriptor for a given instance or type.
*
* @param {ModdleElement|Function} element
*
* @return {Object} the moddle descriptor
*/
function getModdleDescriptor(element) {
return element.$descriptor;
}
/**
* A parse context.
*
* @class
*
* @param {Object} options
* @param {ElementHandler} options.rootHandler the root handler for parsing a document
* @param {boolean} [options.lax=false] whether or not to ignore invalid elements
*/
function Context(options) {
/**
* @property {ElementHandler} rootHandler
*/
/**
* @property {Boolean} lax
*/
assign(this, options);
this.elementsById = {};
this.references = [];
this.warnings = [];
/**
* Add an unresolved reference.
*
* @param {Object} reference
*/
this.addReference = function (reference) {
this.references.push(reference);
};
/**
* Add a processed element.
*
* @param {ModdleElement} element
*/
this.addElement = function (element) {
if (!element) {
throw error('expected element');
}
var elementsById = this.elementsById;
var descriptor = getModdleDescriptor(element);
var idProperty = descriptor.idProperty,
id;
if (idProperty) {
id = element.get(idProperty.name);
if (id) {
// for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) {
throw new Error('illegal ID <' + id + '>');
}
if (elementsById[id]) {
throw error('duplicate ID <' + id + '>');
}
elementsById[id] = element;
}
}
};
/**
* Add an import warning.
*
* @param {Object} warning
* @param {String} warning.message
* @param {Error} [warning.error]
*/
this.addWarning = function (warning) {
this.warnings.push(warning);
};
}
function BaseHandler() {}
BaseHandler.prototype.handleEnd = function () {};
BaseHandler.prototype.handleText = function () {};
BaseHandler.prototype.handleNode = function () {};
/**
* A simple pass through handler that does nothing except for
* ignoring all input it receives.
*
* This is used to ignore unknown elements and
* attributes.
*/
function NoopHandler() {}
NoopHandler.prototype = Object.create(BaseHandler.prototype);
NoopHandler.prototype.handleNode = function () {
return this;
};
function BodyHandler() {}
BodyHandler.prototype = Object.create(BaseHandler.prototype);
BodyHandler.prototype.handleText = function (text) {
this.body = (this.body || '') + text;
};
function ReferenceHandler(property, context) {
this.property = property;
this.context = context;
}
ReferenceHandler.prototype = Object.create(BodyHandler.prototype);
ReferenceHandler.prototype.handleNode = function (node) {
if (this.element) {
throw error('expected no sub nodes');
} else {
this.element = this.createReference(node);
}
return this;
};
ReferenceHandler.prototype.handleEnd = function () {
this.element.id = this.body;
};
ReferenceHandler.prototype.createReference = function (node) {
return {
property: this.property.ns.name,
id: ''
};
};
function ValueHandler(propertyDesc, element) {
this.element = element;
this.propertyDesc = propertyDesc;
}
ValueHandler.prototype = Object.create(BodyHandler.prototype);
ValueHandler.prototype.handleEnd = function () {
var value = this.body || '',
element = this.element,
propertyDesc = this.propertyDesc;
value = coerceType(propertyDesc.type, value);
if (propertyDesc.isMany) {
element.get(propertyDesc.name).push(value);
} else {
element.set(propertyDesc.name, value);
}
};
function BaseElementHandler() {}
BaseElementHandler.prototype = Object.create(BodyHandler.prototype);
BaseElementHandler.prototype.handleNode = function (node) {
var parser = this,
element = this.element;
if (!element) {
element = this.element = this.createElement(node);
this.context.addElement(element);
} else {
parser = this.handleChild(node);
}
return parser;
};
/**
* @class Reader.ElementHandler
*
*/
function ElementHandler(model, typeName, context) {
this.model = model;
this.type = model.getType(typeName);
this.context = context;
}
ElementHandler.prototype = Object.create(BaseElementHandler.prototype);
ElementHandler.prototype.addReference = function (reference) {
this.context.addReference(reference);
};
ElementHandler.prototype.handleText = function (text) {
var element = this.element,
descriptor = getModdleDescriptor(element),
bodyProperty = descriptor.bodyProperty;
if (!bodyProperty) {
throw error('unexpected body text <' + text + '>');
}
BodyHandler.prototype.handleText.call(this, text);
};
ElementHandler.prototype.handleEnd = function () {
var value = this.body,
element = this.element,
descriptor = getModdleDescriptor(element),
bodyProperty = descriptor.bodyProperty;
if (bodyProperty && value !== undefined) {
value = coerceType(bodyProperty.type, value);
element.set(bodyProperty.name, value);
}
};
/**
* Create an instance of the model from the given node.
*
* @param {Element} node the xml node
*/
ElementHandler.prototype.createElement = function (node) {
var attributes = node.attributes,
Type = this.type,
descriptor = getModdleDescriptor(Type),
context = this.context,
instance = new Type({}),
model = this.model,
propNameNs;
forEach(attributes, function (value, name) {
var prop = descriptor.propertiesByName[name],
values;
if (prop && prop.isReference) {
if (!prop.isMany) {
context.addReference({
element: instance,
property: prop.ns.name,
id: value
});
} else {
// IDREFS: parse references as whitespace-separated list
values = value.split(' ');
forEach(values, function (v) {
context.addReference({
element: instance,
property: prop.ns.name,
id: v
});
});
}
} else {
if (prop) {
value = coerceType(prop.type, value);
} else if (name === 'xmlns') {
name = ':' + name;
} else {
propNameNs = parseName(name, descriptor.ns.prefix);
// check whether attribute is defined in a well-known namespace
// if that is the case we emit a warning to indicate potential misuse
if (model.getPackage(propNameNs.prefix)) {
context.addWarning({
message: 'unknown attribute <' + name + '>',
element: instance,
property: name,
value: value
});
}
}
instance.set(name, value);
}
});
return instance;
};
ElementHandler.prototype.getPropertyForNode = function (node) {
var name = node.name;
var nameNs = parseName(name);
var type = this.type,
model = this.model,
descriptor = getModdleDescriptor(type);
var propertyName = nameNs.name,
property = descriptor.propertiesByName[propertyName];
// search for properties by name first
if (property && !property.isAttr) {
const serializationType = getSerializationType(property);
if (serializationType) {
const elementTypeName = node.attributes[serializationType];
// type is optional, if it does not exists the
// default type is assumed
if (elementTypeName) {
// convert the prefix used to the mapped form, but also
// take possible type prefixes from XML
// into account, i.e.: xsi:type="t{ActualType}",
const normalizedTypeName = normalizeTypeName(elementTypeName, node.ns, model);
const elementType = model.getType(normalizedTypeName);
return assign({}, property, {
effectiveType: getModdleDescriptor(elementType).name
});
}
}
// search for properties by name first
return property;
}
var pkg = model.getPackage(nameNs.prefix);
if (pkg) {
const elementTypeName = aliasToName(nameNs, pkg);
const elementType = model.getType(elementTypeName);
// search for collection members later
property = find(descriptor.properties, function (p) {
return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
});
if (property) {
return assign({}, property, {
effectiveType: getModdleDescriptor(elementType).name
});
}
} else {
// parse unknown element (maybe extension)
property = find(descriptor.properties, function (p) {
return !p.isReference && !p.isAttribute && p.type === 'Element';
});
if (property) {
return property;
}
}
throw error('unrecognized element <' + nameNs.name + '>');
};
ElementHandler.prototype.toString = function () {
return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']';
};
ElementHandler.prototype.valueHandler = function (propertyDesc, element) {
return new ValueHandler(propertyDesc, element);
};
ElementHandler.prototype.referenceHandler = function (propertyDesc) {
return new ReferenceHandler(propertyDesc, this.context);
};
ElementHandler.prototype.handler = function (type) {
if (type === 'Element') {
return new GenericElementHandler(this.model, type, this.context);
} else {
return new ElementHandler(this.model, type, this.context);
}
};
/**
* Handle the child element parsing
*
* @param {Element} node the xml node
*/
ElementHandler.prototype.handleChild = function (node) {
var propertyDesc, type, element, childHandler;
propertyDesc = this.getPropertyForNode(node);
element = this.element;
type = propertyDesc.effectiveType || propertyDesc.type;
if (isSimple(type)) {
return this.valueHandler(propertyDesc, element);
}
if (propertyDesc.isReference) {
childHandler = this.referenceHandler(propertyDesc).handleNode(node);
} else {
childHandler = this.handler(type).handleNode(node);
}
var newElement = childHandler.element;
// child handles may decide to skip elements
// by not returning anything
if (newElement !== undefined) {
if (propertyDesc.isMany) {
element.get(propertyDesc.name).push(newElement);
} else {
element.set(propertyDesc.name, newElement);
}
if (propertyDesc.isReference) {
assign(newElement, {
element: element
});
this.context.addReference(newElement);
} else {
// establish child -> parent relationship
newElement.$parent = element;
}
}
return childHandler;
};
/**
* An element handler that performs special validation
* to ensure the node it gets initialized with matches
* the handlers type (namespace wise).
*
* @param {Moddle} model
* @param {String} typeName
* @param {Context} context
*/
function RootElementHandler(model, typeName, context) {
ElementHandler.call(this, model, typeName, context);
}
RootElementHandler.prototype = Object.create(ElementHandler.prototype);
RootElementHandler.prototype.createElement = function (node) {
var name = node.name,
nameNs = parseName(name),
model = this.model,
type = this.type,
pkg = model.getPackage(nameNs.prefix),
typeName = pkg && aliasToName(nameNs, pkg) || name;
// verify the correct namespace if we parse
// the first element in the handler tree
//
// this ensures we don't mistakenly import wrong namespace elements
if (!type.hasType(typeName)) {
throw error('unexpected element <' + node.originalName + '>');
}
return ElementHandler.prototype.createElement.call(this, node);
};
function GenericElementHandler(model, typeName, context) {
this.model = model;
this.context = context;
}
GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);
GenericElementHandler.prototype.createElement = function (node) {
var name = node.name,
ns = parseName(name),
prefix = ns.prefix,
uri = node.ns[prefix + '$uri'],
attributes = node.attributes;
return this.model.createAny(name, uri, attributes);
};
GenericElementHandler.prototype.handleChild = function (node) {
var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
element = this.element;
var newElement = handler.element,
children;
if (newElement !== undefined) {
children = element.$children = element.$children || [];
children.push(newElement);
// establish child -> parent relationship
newElement.$parent = element;
}
return handler;
};
GenericElementHandler.prototype.handleEnd = function () {
if (this.body) {
this.element.$body = this.body;
}
};
/**
* A reader for a meta-model
*
* @param {Object} options
* @param {Model} options.model used to read xml files
* @param {Boolean} options.lax whether to make parse errors warnings
*/
function Reader(options) {
if (options instanceof Moddle) {
options = {
model: options
};
}
assign(this, {
lax: false
}, options);
}
/**
* The fromXML result.
*
* @typedef {Object} ParseResult
*
* @property {ModdleElement} rootElement
* @property {Array<Object>} references
* @property {Array<Error>} warnings
* @property {Object} elementsById - a mapping containing each ID -> ModdleElement
*/
/**
* The fromXML result.
*
* @typedef {Error} ParseError
*
* @property {Array<Error>} warnings
*/
/**
* Parse the given XML into a moddle document tree.
*
* @param {String} xml
* @param {ElementHandler|Object} options or rootHandler
*
* @returns {Promise<ParseResult, ParseError>}
*/
Reader.prototype.fromXML = function (xml, options, done) {
var rootHandler = options.rootHandler;
if (options instanceof ElementHandler) {
// root handler passed via (xml, { rootHandler: ElementHandler }, ...)
rootHandler = options;
options = {};
} else {
if (typeof options === 'string') {
// rootHandler passed via (xml, 'someString', ...)
rootHandler = this.handler(options);
options = {};
} else if (typeof rootHandler === 'string') {
// rootHandler passed via (xml, { rootHandler: 'someString' }, ...)
rootHandler = this.handler(rootHandler);
}
}
var model = this.model,
lax = this.lax;
var context = new Context(assign({}, options, {
rootHandler: rootHandler
})),
parser = new Parser({
proxy: true
}),
stack = createStack();
rootHandler.context = context;
// push root handler
stack.push(rootHandler);
/**
* Handle error.
*
* @param {Error} err
* @param {Function} getContext
* @param {boolean} lax
*
* @return {boolean} true if handled
*/
function handleError(err, getContext, lax) {
var ctx = getContext();
var line = ctx.line,
column = ctx.column,
data = ctx.data;
// we receive the full context data here,
// for elements trim down the information
// to the tag name, only
if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) {
data = data.slice(0, data.indexOf(' ')) + '>';
}
var message = 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' + 'line: ' + line + '\n\t' + 'column: ' + column + '\n\t' + 'nested error: ' + err.message;
if (lax) {
context.addWarning({
message: message,
error: err
});
return true;
} else {
throw error(message);
}
}
function handleWarning(err, getContext) {
// just like handling errors in <lax=true> mode
return handleError(err, getContext, true);
}
/**
* Resolve collected references on parse end.
*/
function resolveReferences() {
var elementsById = context.elementsById;
var references = context.references;
var i, r;
for (i = 0; r = references[i]; i++) {
var element = r.element;
var reference = elementsById[r.id];
var property = getModdleDescriptor(element).propertiesByName[r.property];
if (!reference) {
context.addWarning({
message: 'unresolved reference <' + r.id + '>',
element: r.element,
property: r.property,
value: r.id
});
}
if (property.isMany) {
var collection = element.get(property.name),
idx = collection.indexOf(r);
// we replace an existing place holder (idx != -1) or
// append to the collection instead
if (idx === -1) {
idx = collection.length;
}
if (!reference) {
// remove unresolvable reference
collection.splice(idx, 1);
} else {
// add or update reference in collection
collection[idx] = reference;
}
} else {
element.set(property.name, reference);
}
}
}
function handleClose() {
stack.pop().handleEnd();
}
var PREAMBLE_START_PATTERN = /^<\?xml /i;
var ENCODING_PATTERN = / encoding="([^"]+)"/i;
var UTF_8_PATTERN = /^utf-8$/i;
function handleQuestion(question) {
if (!PREAMBLE_START_PATTERN.test(question)) {
return;
}
var match = ENCODING_PATTERN.exec(question);
var encoding = match && match[1];
if (!encoding || UTF_8_PATTERN.test(encoding)) {
return;
}
context.addWarning({
message: 'unsupported document encoding <' + encoding + '>, ' + 'falling back to UTF-8'
});
}
function handleOpen(node, getContext) {
var handler = stack.peek();
try {
stack.push(handler.handleNode(node));
} catch (err) {
if (handleError(err, getContext, lax)) {
stack.push(new NoopHandler());
}
}
}
function handleCData(text, getContext) {
try {
stack.peek().handleText(text);
} catch (err) {
handleWarning(err, getContext);
}
}
function handleText(text, getContext) {
// strip whitespace only nodes, i.e. before
// <!CDATA[ ... ]> sections and in between tags
if (!text.trim()) {
return;
}
handleCData(text, getContext);
}
var uriMap = model.getPackages().reduce(function (uriMap, p) {
uriMap[p.uri] = p.prefix;
return uriMap;
}, Object.entries(DEFAULT_NS_MAP).reduce(function (map, [prefix, url]) {
map[url] = prefix;
return map;
}, model.config && model.config.nsMap || {}));
parser.ns(uriMap).on('openTag', function (obj, decodeStr, selfClosing, getContext) {
// gracefully handle unparsable attributes (attrs=false)
var attrs = obj.attrs || {};
var decodedAttrs = Object.keys(attrs).reduce(function (d, key) {
var value = decodeStr(attrs[key]);
d[key] = value;
return d;
}, {});
var node = {
name: obj.name,
originalName: obj.originalName,
attributes: decodedAttrs,
ns: obj.ns
};
handleOpen(node, getContext);
}).on('question', handleQuestion).on('closeTag', handleClose).on('cdata', handleCData).on('text', function (text, decodeEntities, getContext) {
handleText(decodeEntities(text), getContext);
}).on('error', handleError).on('warn', handleWarning);
// async XML parsing to make sure the execution environment
// (node or brower) is kept responsive and that certain optimization
// strategies can kick in.
return new Promise(function (resolve, reject) {
var err;
try {
parser.parse(xml);
resolveReferences();
} catch (e) {
err = e;
}
var rootElement = rootHandler.element;
if (!err && !rootElement) {
err = error('failed to parse document as <' + rootHandler.type.$descriptor.name + '>');
}
var warnings = context.warnings;
var references = context.references;
var elementsById = context.elementsById;
if (err) {
err.warnings = warnings;
return reject(err);
} else {
return resolve({
rootElement: rootElement,
elementsById: elementsById,
references: references,
warnings: warnings
});
}
});
};
Reader.prototype.handler = function (name) {
return new RootElementHandler(this.model, name);
};
// helpers //////////////////////////
function createStack() {
var stack = [];
Object.defineProperty(stack, 'peek', {
value: function () {
return this[this.length - 1];
}
});
return stack;
}
var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\n';
var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g;
var ESCAPE_CHARS = /<|>|&/g;
function Namespaces(parent) {
this.prefixMap = {};
this.uriMap = {};
this.used = {};
this.wellknown = [];
this.custom = [];
this.parent = parent;
this.defaultPrefixMap = parent && parent.defaultPrefixMap || {};
}
Namespaces.prototype.mapDefaultPrefixes = function (defaultPrefixMap) {
this.defaultPrefixMap = defaultPrefixMap;
};
Namespaces.prototype.defaultUriByPrefix = function (prefix) {
return this.defaultPrefixMap[prefix];
};
Namespaces.prototype.byUri = function (uri) {
return this.uriMap[uri] || this.parent && this.parent.byUri(uri);
};
Namespaces.prototype.add = function (ns, isWellknown) {
this.uriMap[ns.uri] = ns;
if (isWellknown) {
this.wellknown.push(ns);
} else {
this.custom.push(ns);
}
this.mapPrefix(ns.prefix, ns.uri);
};
Namespaces.prototype.uriByPrefix = function (prefix) {
return this.prefixMap[prefix || 'xmlns'] || this.parent && this.parent.uriByPrefix(prefix);
};
Namespaces.prototype.mapPrefix = function (prefix, uri) {
this.prefixMap[prefix || 'xmlns'] = uri;
};
Namespaces.prototype.getNSKey = function (ns) {
return ns.prefix !== undefined ? ns.uri + '|' + ns.prefix : ns.uri;
};
Namespaces.prototype.logUsed = function (ns) {
var uri = ns.uri;
var nsKey = this.getNSKey(ns);
this.used[nsKey] = this.byUri(uri);
// Inform parent recursively about the usage of this NS
if (this.parent) {
this.parent.logUsed(ns);
}
};
Namespaces.prototype.getUsed = function (ns) {
var allNs = [].concat(this.wellknown, this.custom);
return allNs.filter(ns => {
var nsKey = this.getNSKey(ns);
return this.used[nsKey];
});
};
function lower(string) {
return string.charAt(0).toLowerCase() + string.slice(1);
}
function nameToAlias(name, pkg) {
if (hasLowerCaseAlias(pkg)) {
return lower(name);
} else {
return name;
}
}
function inherits(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
}
function nsName(ns) {
if (isString(ns)) {
return ns;
} else {
return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
}
}
function getNsAttrs(namespaces) {
return namespaces.getUsed().filter(function (ns) {
// do not serialize built in <xml> namespace
return ns.prefix !== 'xml';
}).map(function (ns) {
var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
return {
name: name,
value: ns.uri
};
});
}
function getElementNs(ns, descriptor) {
if (descriptor.isGeneric) {
return assign({
localName: descriptor.ns.localName
}, ns);
} else {
return assign({
localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg)
}, ns);
}
}
function getPropertyNs(ns, descriptor) {
return assign({
localName: descriptor.ns.localName
}, ns);
}
function getSerializableProperties(element) {
var descriptor = element.$descriptor;
return filter(descriptor.properties, function (p) {
var name = p.name;
if (p.isVirtual) {
return false;
}
// do not serialize defaults
if (!has(element, name)) {
return false;
}
var value = element[name];
// do not serialize default equals
if (value === p.default) {
return false;
}
// do not serialize null properties
if (value === null) {
return false;
}
return p.isMany ? value.length : true;
});
}
var ESCAPE_ATTR_MAP = {
'\n': '#10',
'\n\r': '#10',
'"': '#34',
'\'': '#39',
'<': '#60',
'>': '#62',
'&': '#38'
};
var ESCAPE_MAP = {
'<': 'lt',
'>': 'gt',
'&': 'amp'
};
function escape(str, charPattern, replaceMap) {
// ensure we are handling strings here
str = isString(str) ? str : '' + str;
return str.replace(charPattern, function (s) {
return '&' + replaceMap[s] + ';';
});
}
/**
* Escape a string attribute to not contain any bad values (line breaks, '"', ...)
*
* @param {String} str the string to escape
* @return {String} the escaped string
*/
function escapeAttr(str) {
return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP);
}
function escapeBody(str) {
return escape(str, ESCAPE_CHARS, ESCAPE_MAP);
}
function filterAttributes(props) {
return filter(props, function (p) {
return p.isAttr;
});
}
function filterContained(props) {
return filter(props, function (p) {
return !p.isAttr;
});
}
function ReferenceSerializer(tagName) {
this.tagName = tagName;
}
ReferenceSerializer.prototype.build = function (element) {
this.element = element;
return this;
};
ReferenceSerializer.prototype.serializeTo = function (writer) {
writer.appendIndent().append('<' + this.tagName + '>' + this.element.id + '</' + this.tagName + '>').appendNewLine();
};
function BodySerializer() {}
BodySerializer.prototype.serializeValue = BodySerializer.prototype.serializeTo = function (writer) {
writer.append(this.escape ? escapeBody(this.value) : this.value);
};
BodySerializer.prototype.build = function (prop, value) {
this.value = value;
if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) {
this.escape = true;
}
return this;
};
function ValueSerializer(tagName) {
this.tagName = tagName;
}
inherits(ValueSerializer, BodySerializer);
ValueSerializer.prototype.serializeTo = function (writer) {
writer.appendIndent().append('<' + this.tagName + '>');
this.serializeValue(writer);
writer.append('</' + this.tagName + '>').appendNewLine();
};
function ElementSerializer(parent, propertyDescriptor) {
this.body = [];
this.attrs = [];
this.parent = parent;
this.propertyDescriptor = propertyDescriptor;
}
ElementSerializer.prototype.build = function (element) {
this.element = element;
var elementDescriptor = element.$descriptor,
propertyDescriptor = this.propertyDescriptor;
var otherAttrs, properties;
var isGeneric = elementDescriptor.isGeneric;
if (isGeneric) {
otherAttrs = this.parseGenericNsAttributes(element);
} else {
otherAttrs = this.parseNsAttributes(element);
}
if (propertyDescriptor) {
this.ns = this.nsPropertyTagName(propertyDescriptor);
} else {
this.ns = this.nsTagName(elementDescriptor);
}
// compute tag name
this.tagName = this.addTagName(this.ns);
if (isGeneric) {
this.parseGenericContainments(element);
} else {
properties = getSerializableProperties(element);
this.parseAttributes(filterAttributes(properties));
this.parseContainments(filterContained(properties));
}
this.parseGenericAttributes(element, otherAttrs);
return this;
};
ElementSerializer.prototype.nsTagName = function (descriptor) {
var effectiveNs = this.logNamespaceUsed(descriptor.ns);
return getElementNs(effectiveNs, descriptor);
};
ElementSerializer.prototype.nsPropertyTagName = function (descriptor) {
var effectiveNs = this.logNamespaceUsed(descriptor.ns);
return getPropertyNs(effectiveNs, descriptor);
};
ElementSerializer.prototype.isLocalNs = function (ns) {
return ns.uri === this.ns.uri;
};
/**
* Get the actual ns attribute name for the given element.
*
* @param {Object} element
* @param {Boolean} [element.inherited=false]
*
* @return {Object} nsName
*/
ElementSerializer.prototype.nsAttributeName = function (element) {
var ns;
if (isString(element)) {
ns = parseName(element);
} else {
ns = element.ns;
}
// return just local name for inherited attributes
if (element.inherited) {
return {
localName: ns.localName
};
}
// parse + log effective ns
var effectiveNs = this.logNamespaceUsed(ns);
// LOG ACTUAL namespace use
this.getNamespaces().logUsed(effectiveNs);
// strip prefix if same namespace like parent
if (this.isLocalNs(effectiveNs)) {
return {
localName: ns.localName
};
} else {
return assign({
localName: ns.localName
}, effectiveNs);
}
};
ElementSerializer.prototype.parseGenericNsAttributes = function (element) {
return Object.entries(element).filter(([key, value]) => !key.startsWith('$') && this.parseNsAttribute(element, key, value)).map(([key, value]) => ({
name: key,
value: value
}));
};
ElementSerializer.prototype.parseGenericContainments = function (element) {
var body = element.$body;
if (body) {
this.body.push(new BodySerializer().build({
type: 'String'
}, body));
}
var children = element.$children;
if (children) {
forEach(children, child => {
this.body.push(new ElementSerializer(this).build(child));
});
}
};
ElementSerializer.prototype.parseNsAttribute = function (element, name, value) {
var model = element.$model;
var nameNs = parseName(name);
var ns;
// parse xmlns:foo="http://foo.bar"
if (nameNs.prefix === 'xmlns') {
ns = {
prefix: nameNs.localName,
uri: value
};
}
// parse xmlns="http://foo.bar"
if (!nameNs.prefix && nameNs.localName === 'xmlns') {
ns = {
uri: value
};
}
if (!ns) {
return {
name: name,
value: value
};
}
if (model && model.getPackage(value)) {
// register well known namespace
this.logNamespace(ns, true, true);
} else {
// log custom namespace directly as used
var actualNs = this.logNamespaceUsed(ns, true);
this.getNamespaces().logUsed(actualNs);
}
};
/**
* Parse namespaces and return a list of left over generic attributes
*
* @param {Object} element
* @return {Array<Object>}
*/
ElementSerializer.prototype.parseNsAttributes = function (element) {
var self = this;
var genericAttrs = element.$attrs;
var attributes = [];
// parse namespace attributes first
// and log them. push non namespace attributes to a list
// and process them later
forEach(genericAttrs, function (value, name) {
var nonNsAttr = self.parseNsAttribute(element, name, value);
if (nonNsAttr) {
attributes.push(nonNsAttr);
}
});
return attributes;
};
ElementSerializer.prototype.parseGenericAttributes = function (element, attributes) {
var self = this;
forEach(attributes, function (attr) {
try {
self.addAttribute(self.nsAttributeName(attr.name), attr.value);
} catch (e) {
// eslint-disable-next-line no-undef
typeof console !== 'undefined' && console.warn(`missing namespace information for <${attr.name}=${attr.value}> on`, element, e);
}
});
};
ElementSerializer.prototype.parseContainments = function (properties) {
var self = this,
body = this.body,
element = this.element;
forEach(properties, function (p) {
var value = element.get(p.name),
isReference = p.isReference,
isMany = p.isMany;
if (!isMany) {
value = [value];
}
if (p.isBody) {
body.push(new BodySerializer().build(p, value[0]));
} else if (isSimple(p.type)) {
forEach(value, function (v) {
body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v));
});
} else if (isReference) {
forEach(value, function (v) {
body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v));
});
} else {
// allow serialization via type
// rather than element name
var serialization = getSerialization(p);
forEach(value, function (v) {
var serializer;
if (serialization) {
if (serialization === SERIALIZE_PROPERTY) {
serializer = new ElementSerializer(self, p);
} else {
serializer = new TypeSerializer(self, p, serialization);
}
} else {
serializer = new ElementSerializer(self);
}
body.push(serializer.build(v));
});
}
});
};
ElementSerializer.prototype.getNamespaces = function (local) {
var namespaces = this.namespaces,
parent = this.parent,
parentNamespaces;
if (!namespaces) {
parentNamespaces = parent && parent.getNamespaces();
if (local || !parentNamespaces) {
this.namespaces = namespaces = new Namespaces(parentNamespaces);
} else {
namespaces = parentNamespaces;
}
}
return namespaces;
};
ElementSerializer.prototype.logNamespace = function (ns, wellknown, local) {
var namespaces = this.getNamespaces(local);
var nsUri = ns.uri,
nsPrefix = ns.prefix;
var existing = namespaces.byUri(nsUri);
if (!existing || local) {
namespaces.add(ns, wellknown);
}
namespaces.mapPrefix(nsPrefix, nsUri);
return ns;
};
ElementSerializer.prototype.logNamespaceUsed = function (ns, local) {
var namespaces = this.getNamespaces(local);
// ns may be
//
// * prefix only
// * prefix:uri
// * localName only
var prefix = ns.prefix,
uri = ns.uri,
newPrefix,
idx,
wellknownUri;
// handle anonymous namespaces (elementForm=unqualified), cf. #23
if (!prefix && !uri) {
return {
localName: ns.localName
};
}
wellknownUri = namespaces.defaultUriByPrefix(prefix);
uri = uri || wellknownUri || namespaces.uriByPrefix(prefix);
if (!uri) {
throw new Error('no namespace uri given for prefix <' + prefix + '>');
}
ns = namespaces.byUri(uri);
// register new default prefix <xmlns> in local scope
if (!ns && !prefix) {
ns = this.logNamespace({
uri
}, wellknownUri === uri, true);
}
if (!ns) {
newPrefix = prefix;
idx = 1;
// find a prefix that is not mapped yet
while (namespaces.uriByPrefix(newPrefix)) {
newPrefix = prefix + '_' + idx++;
}
ns = this.logNamespace({
prefix: newPrefix,
uri: uri
}, wellknownUri === uri);
}
if (prefix) {
namespaces.mapPrefix(prefix, uri);
}
return ns;
};
ElementSerializer.prototype.parseAttributes = function (properties) {
var self = this,
element = this.element;
forEach(properties, function (p) {
var value = element.get(p.name);
if (p.isReference) {
if (!p.isMany) {
value = value.id;
} else {
var values = [];
forEach(value, function (v) {
values.push(v.id);
});
// IDREFS is a whitespace-separated list of references.
value = values.join(' ');
}
}
self.addAttribute(self.nsAttributeName(p), value);
});
};
ElementSerializer.prototype.addTagName = function (nsTagName) {
var actualNs = this.logNamespaceUsed(nsTagName);
this.getNamespaces().logUsed(actualNs);
return nsName(nsTagName);
};
ElementSerializer.prototype.addAttribute = function (name, value) {
var attrs = this.attrs;
if (isString(value)) {
value = escapeAttr(value);
}
// de-duplicate attributes
// https://github.com/bpmn-io/moddle-xml/issues/66
var idx = findIndex(attrs, function (element) {
return element.name.localName === name.localName && element.name.uri === name.uri && element.name.prefix === name.prefix;
});
var attr = {
name: name,
value: value
};
if (idx !== -1) {
attrs.splice(idx, 1, attr);
} else {
attrs.push(attr);
}
};
ElementSerializer.prototype.serializeAttributes = function (writer) {
var attrs = this.attrs,
namespaces = this.namespaces;
if (namespaces) {
attrs = getNsAttrs(namespaces).concat(attrs);
}
forEach(attrs, function (a) {
writer.append(' ').append(nsName(a.name)).append('="').append(a.value).append('"');
});
};
ElementSerializer.prototype.serializeTo = function (writer) {
var firstBody = this.body[0],
indent = firstBody && firstBody.constructor !== BodySerializer;
writer.appendIndent().append('<' + this.tagName);
this.serializeAttributes(writer);
writer.append(firstBody ? '>' : ' />');
if (firstBody) {
if (indent) {
writer.appendNewLine().indent();
}
forEach(this.body, function (b) {
b.serializeTo(writer);
});
if (indent) {
writer.unindent().appendIndent();
}
writer.append('</' + this.tagName + '>');
}
writer.appendNewLine();
};
/**
* A serializer for types that handles serialization of data types
*/
function TypeSerializer(parent, propertyDescriptor, serialization) {
ElementSerializer.call(this, parent, propertyDescriptor);
this.serialization = serialization;
}
inherits(TypeSerializer, ElementSerializer);
TypeSerializer.prototype.parseNsAttributes = function (element) {
// extracted attributes with serialization attribute
// <type=typeName> stripped; it may be later
var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element).filter(attr => attr.name !== this.serialization);
var descriptor = element.$descriptor;
// only serialize <type=typeName> if necessary
if (descriptor.name === this.propertyDescriptor.type) {
return attributes;
}
var typeNs = this.typeNs = this.nsTagName(descriptor);
this.getNamespaces().logUsed(this.typeNs);
// add xsi:type attribute to represent the elements
// actual type
var pkg = element.$model.getPackage(typeNs.uri),
typePrefix = pkg.xml && pkg.xml.typePrefix || '';
this.addAttribute(this.nsAttributeName(this.serialization), (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName);
return attributes;
};
TypeSerializer.prototype.isLocalNs = function (ns) {
return ns.uri === (this.typeNs || this.ns).uri;
};
function SavingWriter() {
this.value = '';
this.write = function (str) {
this.value += str;
};
}
function FormatingWriter(out, format) {
var indent = [''];
this.append = function (str) {
out.write(str);
return this;
};
this.appendNewLine = function () {
if (format) {
out.write('\n');
}
return this;
};
this.appendIndent = function () {
if (format) {
out.write(indent.join(' '));
}
return this;
};
this.indent = function () {
indent.push('');
return this;
};
this.unindent = function () {
indent.pop();
return this;
};
}
/**
* A writer for meta-model backed document trees
*
* @param {Object} options output options to pass into the writer
*/
function Writer(options) {
options = assign({
format: false,
preamble: true
}, options || {});
function toXML(tree, writer) {
var internalWriter = writer || new SavingWriter();
var formatingWriter = new FormatingWriter(internalWriter, options.format);
if (options.preamble) {
formatingWriter.append(XML_PREAMBLE);
}
var serializer = new ElementSerializer();
var model = tree.$model;
serializer.getNamespaces().mapDefaultPrefixes(getDefaultPrefixMappings(model));
serializer.build(tree).serializeTo(formatingWriter);
if (!writer) {
return internalWriter.value;
}
}
return {
toXML: toXML
};
}
// helpers ///////////
/**
* @param {Moddle} model
*
* @return { Record<string, string> } map from prefix to URI
*/
function getDefaultPrefixMappings(model) {
const nsMap = model.config && model.config.nsMap || {};
const prefixMap = {};
// { prefix -> uri }
for (const prefix in DEFAULT_NS_MAP) {
prefixMap[prefix] = DEFAULT_NS_MAP[prefix];
}
// { uri -> prefix }
for (const uri in nsMap) {
const prefix = nsMap[uri];
prefixMap[prefix] = uri;
}
for (const pkg of model.getPackages()) {
prefixMap[pkg.prefix] = pkg.uri;
}
return prefixMap;
}
/**
* A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
*
* @class BpmnModdle
* @extends Moddle
*
* @param {Object|Array} packages to use for instantiating the model
* @param {Object} [options] additional options to pass over
*/
function BpmnModdle(packages, options) {
Moddle.call(this, packages, options);
}
BpmnModdle.prototype = Object.create(Moddle.prototype);
/**
* The fromXML result.
*
* @typedef {Object} ParseResult
*
* @property {ModdleElement} rootElement
* @property {Array<Object>} references
* @property {Array<Error>} warnings
* @property {Object} elementsById - a mapping containing each ID -> ModdleElement
*/
/**
* The fromXML error.
*
* @typedef {Error} ParseError
*
* @property {Array<Error>} warnings
*/
/**
* Instantiates a BPMN model tree from a given xml string.
*
* @param {String} xmlStr
* @param {String} [typeName='bpmn:Definitions'] name of the root element
* @param {Object} [options] options to pass to the underlying reader
*
* @returns {Promise<ParseResult, ParseError>}
*/
BpmnModdle.prototype.fromXML = function (xmlStr, typeName, options) {
if (!isString(typeName)) {
options = typeName;
typeName = 'bpmn:Definitions';
}
var reader = new Reader(assign({
model: this,
lax: true
}, options));
var rootHandler = reader.handler(typeName);
return reader.fromXML(xmlStr, rootHandler);
};
/**
* The toXML result.
*
* @typedef {Object} SerializationResult
*
* @property {String} xml
*/
/**
* Serializes a BPMN 2.0 object tree to XML.
*
* @param {String} element the root element, typically an instance of `bpmn:Definitions`
* @param {Object} [options] to pass to the underlying writer
*
* @returns {Promise<SerializationResult, Error>}
*/
BpmnModdle.prototype.toXML = function (element, options) {
var writer = new Writer(options);
return new Promise(function (resolve, reject) {
try {
var result = writer.toXML(element);
return resolve({
xml: result
});
} catch (err) {
return reject(err);
}
});
};
var name$5 = "BPMN20";
var uri$5 = "http://www.omg.org/spec/BPMN/20100524/MODEL";
var prefix$5 = "bpmn";
var associations$5 = [];
var types$5 = [{
name: "Interface",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "operations",
type: "Operation",
isMany: true
}, {
name: "implementationRef",
isAttr: true,
type: "String"
}]
}, {
name: "Operation",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "inMessageRef",
type: "Message",
isReference: true
}, {
name: "outMessageRef",
type: "Message",
isReference: true
}, {
name: "errorRef",
type: "Error",
isMany: true,
isReference: true
}, {
name: "implementationRef",
isAttr: true,
type: "String"
}]
}, {
name: "EndPoint",
superClass: ["RootElement"]
}, {
name: "Auditing",
superClass: ["BaseElement"]
}, {
name: "GlobalTask",
superClass: ["CallableElement"],
properties: [{
name: "resources",
type: "ResourceRole",
isMany: true
}]
}, {
name: "Monitoring",
superClass: ["BaseElement"]
}, {
name: "Performer",
superClass: ["ResourceRole"]
}, {
name: "Process",
superClass: ["FlowElementsContainer", "CallableElement"],
properties: [{
name: "processType",
type: "ProcessType",
isAttr: true
}, {
name: "isClosed",
isAttr: true,
type: "Boolean"
}, {
name: "auditing",
type: "Auditing"
}, {
name: "monitoring",
type: "Monitoring"
}, {
name: "properties",
type: "Property",
isMany: true
}, {
name: "laneSets",
isMany: true,
replaces: "FlowElementsContainer#laneSets",
type: "LaneSet"
}, {
name: "flowElements",
isMany: true,
replaces: "FlowElementsContainer#flowElements",
type: "FlowElement"
}, {
name: "artifacts",
type: "Artifact",
isMany: true
}, {
name: "resources",
type: "ResourceRole",
isMany: true
}, {
name: "correlationSubscriptions",
type: "CorrelationSubscription",
isMany: true
}, {
name: "supports",
type: "Process",
isMany: true,
isReference: true
}, {
name: "definitionalCollaborationRef",
type: "Collaboration",
isAttr: true,
isReference: true
}, {
name: "isExecutable",
isAttr: true,
type: "Boolean"
}]
}, {
name: "LaneSet",
superClass: ["BaseElement"],
properties: [{
name: "lanes",
type: "Lane",
isMany: true
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "Lane",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "partitionElementRef",
type: "BaseElement",
isAttr: true,
isReference: true
}, {
name: "partitionElement",
type: "BaseElement"
}, {
name: "flowNodeRef",
type: "FlowNode",
isMany: true,
isReference: true
}, {
name: "childLaneSet",
type: "LaneSet",
xml: {
serialize: "xsi:type"
}
}]
}, {
name: "GlobalManualTask",
superClass: ["GlobalTask"]
}, {
name: "ManualTask",
superClass: ["Task"]
}, {
name: "UserTask",
superClass: ["Task"],
properties: [{
name: "renderings",
type: "Rendering",
isMany: true
}, {
name: "implementation",
isAttr: true,
type: "String"
}]
}, {
name: "Rendering",
superClass: ["BaseElement"]
}, {
name: "HumanPerformer",
superClass: ["Performer"]
}, {
name: "PotentialOwner",
superClass: ["HumanPerformer"]
}, {
name: "GlobalUserTask",
superClass: ["GlobalTask"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}, {
name: "renderings",
type: "Rendering",
isMany: true
}]
}, {
name: "Gateway",
isAbstract: true,
superClass: ["FlowNode"],
properties: [{
name: "gatewayDirection",
type: "GatewayDirection",
"default": "Unspecified",
isAttr: true
}]
}, {
name: "EventBasedGateway",
superClass: ["Gateway"],
properties: [{
name: "instantiate",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "eventGatewayType",
type: "EventBasedGatewayType",
isAttr: true,
"default": "Exclusive"
}]
}, {
name: "ComplexGateway",
superClass: ["Gateway"],
properties: [{
name: "activationCondition",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "default",
type: "SequenceFlow",
isAttr: true,
isReference: true
}]
}, {
name: "ExclusiveGateway",
superClass: ["Gateway"],
properties: [{
name: "default",
type: "SequenceFlow",
isAttr: true,
isReference: true
}]
}, {
name: "InclusiveGateway",
superClass: ["Gateway"],
properties: [{
name: "default",
type: "SequenceFlow",
isAttr: true,
isReference: true
}]
}, {
name: "ParallelGateway",
superClass: ["Gateway"]
}, {
name: "RootElement",
isAbstract: true,
superClass: ["BaseElement"]
}, {
name: "Relationship",
superClass: ["BaseElement"],
properties: [{
name: "type",
isAttr: true,
type: "String"
}, {
name: "direction",
type: "RelationshipDirection",
isAttr: true
}, {
name: "source",
isMany: true,
isReference: true,
type: "Element"
}, {
name: "target",
isMany: true,
isReference: true,
type: "Element"
}]
}, {
name: "BaseElement",
isAbstract: true,
properties: [{
name: "id",
isAttr: true,
type: "String",
isId: true
}, {
name: "documentation",
type: "Documentation",
isMany: true
}, {
name: "extensionDefinitions",
type: "ExtensionDefinition",
isMany: true,
isReference: true
}, {
name: "extensionElements",
type: "ExtensionElements"
}]
}, {
name: "Extension",
properties: [{
name: "mustUnderstand",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "definition",
type: "ExtensionDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "ExtensionDefinition",
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "extensionAttributeDefinitions",
type: "ExtensionAttributeDefinition",
isMany: true
}]
}, {
name: "ExtensionAttributeDefinition",
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "type",
isAttr: true,
type: "String"
}, {
name: "isReference",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "extensionDefinition",
type: "ExtensionDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "ExtensionElements",
properties: [{
name: "valueRef",
isAttr: true,
isReference: true,
type: "Element"
}, {
name: "values",
type: "Element",
isMany: true
}, {
name: "extensionAttributeDefinition",
type: "ExtensionAttributeDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "Documentation",
superClass: ["BaseElement"],
properties: [{
name: "text",
type: "String",
isBody: true
}, {
name: "textFormat",
"default": "text/plain",
isAttr: true,
type: "String"
}]
}, {
name: "Event",
isAbstract: true,
superClass: ["FlowNode", "InteractionNode"],
properties: [{
name: "properties",
type: "Property",
isMany: true
}]
}, {
name: "IntermediateCatchEvent",
superClass: ["CatchEvent"]
}, {
name: "IntermediateThrowEvent",
superClass: ["ThrowEvent"]
}, {
name: "EndEvent",
superClass: ["ThrowEvent"]
}, {
name: "StartEvent",
superClass: ["CatchEvent"],
properties: [{
name: "isInterrupting",
"default": true,
isAttr: true,
type: "Boolean"
}]
}, {
name: "ThrowEvent",
isAbstract: true,
superClass: ["Event"],
properties: [{
name: "dataInputs",
type: "DataInput",
isMany: true
}, {
name: "dataInputAssociations",
type: "DataInputAssociation",
isMany: true
}, {
name: "inputSet",
type: "InputSet"
}, {
name: "eventDefinitions",
type: "EventDefinition",
isMany: true
}, {
name: "eventDefinitionRef",
type: "EventDefinition",
isMany: true,
isReference: true
}]
}, {
name: "CatchEvent",
isAbstract: true,
superClass: ["Event"],
properties: [{
name: "parallelMultiple",
isAttr: true,
type: "Boolean",
"default": false
}, {
name: "dataOutputs",
type: "DataOutput",
isMany: true
}, {
name: "dataOutputAssociations",
type: "DataOutputAssociation",
isMany: true
}, {
name: "outputSet",
type: "OutputSet"
}, {
name: "eventDefinitions",
type: "EventDefinition",
isMany: true
}, {
name: "eventDefinitionRef",
type: "EventDefinition",
isMany: true,
isReference: true
}]
}, {
name: "BoundaryEvent",
superClass: ["CatchEvent"],
properties: [{
name: "cancelActivity",
"default": true,
isAttr: true,
type: "Boolean"
}, {
name: "attachedToRef",
type: "Activity",
isAttr: true,
isReference: true
}]
}, {
name: "EventDefinition",
isAbstract: true,
superClass: ["RootElement"]
}, {
name: "CancelEventDefinition",
superClass: ["EventDefinition"]
}, {
name: "ErrorEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "errorRef",
type: "Error",
isAttr: true,
isReference: true
}]
}, {
name: "TerminateEventDefinition",
superClass: ["EventDefinition"]
}, {
name: "EscalationEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "escalationRef",
type: "Escalation",
isAttr: true,
isReference: true
}]
}, {
name: "Escalation",
properties: [{
name: "structureRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}, {
name: "escalationCode",
isAttr: true,
type: "String"
}],
superClass: ["RootElement"]
}, {
name: "CompensateEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "waitForCompletion",
isAttr: true,
type: "Boolean",
"default": true
}, {
name: "activityRef",
type: "Activity",
isAttr: true,
isReference: true
}]
}, {
name: "TimerEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "timeDate",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "timeCycle",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "timeDuration",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}]
}, {
name: "LinkEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "target",
type: "LinkEventDefinition",
isReference: true
}, {
name: "source",
type: "LinkEventDefinition",
isMany: true,
isReference: true
}]
}, {
name: "MessageEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "messageRef",
type: "Message",
isAttr: true,
isReference: true
}, {
name: "operationRef",
type: "Operation",
isReference: true
}]
}, {
name: "ConditionalEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "condition",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}]
}, {
name: "SignalEventDefinition",
superClass: ["EventDefinition"],
properties: [{
name: "signalRef",
type: "Signal",
isAttr: true,
isReference: true
}]
}, {
name: "Signal",
superClass: ["RootElement"],
properties: [{
name: "structureRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "ImplicitThrowEvent",
superClass: ["ThrowEvent"]
}, {
name: "DataState",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "ItemAwareElement",
superClass: ["BaseElement"],
properties: [{
name: "itemSubjectRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}, {
name: "dataState",
type: "DataState"
}]
}, {
name: "DataAssociation",
superClass: ["BaseElement"],
properties: [{
name: "sourceRef",
type: "ItemAwareElement",
isMany: true,
isReference: true
}, {
name: "targetRef",
type: "ItemAwareElement",
isReference: true
}, {
name: "transformation",
type: "FormalExpression",
xml: {
serialize: "property"
}
}, {
name: "assignment",
type: "Assignment",
isMany: true
}]
}, {
name: "DataInput",
superClass: ["ItemAwareElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "isCollection",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "inputSetRef",
type: "InputSet",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "inputSetWithOptional",
type: "InputSet",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "inputSetWithWhileExecuting",
type: "InputSet",
isMany: true,
isVirtual: true,
isReference: true
}]
}, {
name: "DataOutput",
superClass: ["ItemAwareElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "isCollection",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "outputSetRef",
type: "OutputSet",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "outputSetWithOptional",
type: "OutputSet",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "outputSetWithWhileExecuting",
type: "OutputSet",
isMany: true,
isVirtual: true,
isReference: true
}]
}, {
name: "InputSet",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "dataInputRefs",
type: "DataInput",
isMany: true,
isReference: true
}, {
name: "optionalInputRefs",
type: "DataInput",
isMany: true,
isReference: true
}, {
name: "whileExecutingInputRefs",
type: "DataInput",
isMany: true,
isReference: true
}, {
name: "outputSetRefs",
type: "OutputSet",
isMany: true,
isReference: true
}]
}, {
name: "OutputSet",
superClass: ["BaseElement"],
properties: [{
name: "dataOutputRefs",
type: "DataOutput",
isMany: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}, {
name: "inputSetRefs",
type: "InputSet",
isMany: true,
isReference: true
}, {
name: "optionalOutputRefs",
type: "DataOutput",
isMany: true,
isReference: true
}, {
name: "whileExecutingOutputRefs",
type: "DataOutput",
isMany: true,
isReference: true
}]
}, {
name: "Property",
superClass: ["ItemAwareElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "DataInputAssociation",
superClass: ["DataAssociation"]
}, {
name: "DataOutputAssociation",
superClass: ["DataAssociation"]
}, {
name: "InputOutputSpecification",
superClass: ["BaseElement"],
properties: [{
name: "dataInputs",
type: "DataInput",
isMany: true
}, {
name: "dataOutputs",
type: "DataOutput",
isMany: true
}, {
name: "inputSets",
type: "InputSet",
isMany: true
}, {
name: "outputSets",
type: "OutputSet",
isMany: true
}]
}, {
name: "DataObject",
superClass: ["FlowElement", "ItemAwareElement"],
properties: [{
name: "isCollection",
"default": false,
isAttr: true,
type: "Boolean"
}]
}, {
name: "InputOutputBinding",
properties: [{
name: "inputDataRef",
type: "InputSet",
isAttr: true,
isReference: true
}, {
name: "outputDataRef",
type: "OutputSet",
isAttr: true,
isReference: true
}, {
name: "operationRef",
type: "Operation",
isAttr: true,
isReference: true
}]
}, {
name: "Assignment",
superClass: ["BaseElement"],
properties: [{
name: "from",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "to",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}]
}, {
name: "DataStore",
superClass: ["RootElement", "ItemAwareElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "capacity",
isAttr: true,
type: "Integer"
}, {
name: "isUnlimited",
"default": true,
isAttr: true,
type: "Boolean"
}]
}, {
name: "DataStoreReference",
superClass: ["ItemAwareElement", "FlowElement"],
properties: [{
name: "dataStoreRef",
type: "DataStore",
isAttr: true,
isReference: true
}]
}, {
name: "DataObjectReference",
superClass: ["ItemAwareElement", "FlowElement"],
properties: [{
name: "dataObjectRef",
type: "DataObject",
isAttr: true,
isReference: true
}]
}, {
name: "ConversationLink",
superClass: ["BaseElement"],
properties: [{
name: "sourceRef",
type: "InteractionNode",
isAttr: true,
isReference: true
}, {
name: "targetRef",
type: "InteractionNode",
isAttr: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "ConversationAssociation",
superClass: ["BaseElement"],
properties: [{
name: "innerConversationNodeRef",
type: "ConversationNode",
isAttr: true,
isReference: true
}, {
name: "outerConversationNodeRef",
type: "ConversationNode",
isAttr: true,
isReference: true
}]
}, {
name: "CallConversation",
superClass: ["ConversationNode"],
properties: [{
name: "calledCollaborationRef",
type: "Collaboration",
isAttr: true,
isReference: true
}, {
name: "participantAssociations",
type: "ParticipantAssociation",
isMany: true
}]
}, {
name: "Conversation",
superClass: ["ConversationNode"]
}, {
name: "SubConversation",
superClass: ["ConversationNode"],
properties: [{
name: "conversationNodes",
type: "ConversationNode",
isMany: true
}]
}, {
name: "ConversationNode",
isAbstract: true,
superClass: ["InteractionNode", "BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "participantRef",
type: "Participant",
isMany: true,
isReference: true
}, {
name: "messageFlowRefs",
type: "MessageFlow",
isMany: true,
isReference: true
}, {
name: "correlationKeys",
type: "CorrelationKey",
isMany: true
}]
}, {
name: "GlobalConversation",
superClass: ["Collaboration"]
}, {
name: "PartnerEntity",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "participantRef",
type: "Participant",
isMany: true,
isReference: true
}]
}, {
name: "PartnerRole",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "participantRef",
type: "Participant",
isMany: true,
isReference: true
}]
}, {
name: "CorrelationProperty",
superClass: ["RootElement"],
properties: [{
name: "correlationPropertyRetrievalExpression",
type: "CorrelationPropertyRetrievalExpression",
isMany: true
}, {
name: "name",
isAttr: true,
type: "String"
}, {
name: "type",
type: "ItemDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "Error",
superClass: ["RootElement"],
properties: [{
name: "structureRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}, {
name: "errorCode",
isAttr: true,
type: "String"
}]
}, {
name: "CorrelationKey",
superClass: ["BaseElement"],
properties: [{
name: "correlationPropertyRef",
type: "CorrelationProperty",
isMany: true,
isReference: true
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "Expression",
superClass: ["BaseElement"],
isAbstract: false,
properties: [{
name: "body",
isBody: true,
type: "String"
}]
}, {
name: "FormalExpression",
superClass: ["Expression"],
properties: [{
name: "language",
isAttr: true,
type: "String"
}, {
name: "evaluatesToTypeRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "Message",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "itemRef",
type: "ItemDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "ItemDefinition",
superClass: ["RootElement"],
properties: [{
name: "itemKind",
type: "ItemKind",
isAttr: true
}, {
name: "structureRef",
isAttr: true,
type: "String"
}, {
name: "isCollection",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "import",
type: "Import",
isAttr: true,
isReference: true
}]
}, {
name: "FlowElement",
isAbstract: true,
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "auditing",
type: "Auditing"
}, {
name: "monitoring",
type: "Monitoring"
}, {
name: "categoryValueRef",
type: "CategoryValue",
isMany: true,
isReference: true
}]
}, {
name: "SequenceFlow",
superClass: ["FlowElement"],
properties: [{
name: "isImmediate",
isAttr: true,
type: "Boolean"
}, {
name: "conditionExpression",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "sourceRef",
type: "FlowNode",
isAttr: true,
isReference: true
}, {
name: "targetRef",
type: "FlowNode",
isAttr: true,
isReference: true
}]
}, {
name: "FlowElementsContainer",
isAbstract: true,
superClass: ["BaseElement"],
properties: [{
name: "laneSets",
type: "LaneSet",
isMany: true
}, {
name: "flowElements",
type: "FlowElement",
isMany: true
}]
}, {
name: "CallableElement",
isAbstract: true,
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "ioSpecification",
type: "InputOutputSpecification",
xml: {
serialize: "property"
}
}, {
name: "supportedInterfaceRef",
type: "Interface",
isMany: true,
isReference: true
}, {
name: "ioBinding",
type: "InputOutputBinding",
isMany: true,
xml: {
serialize: "property"
}
}]
}, {
name: "FlowNode",
isAbstract: true,
superClass: ["FlowElement"],
properties: [{
name: "incoming",
type: "SequenceFlow",
isMany: true,
isReference: true
}, {
name: "outgoing",
type: "SequenceFlow",
isMany: true,
isReference: true
}, {
name: "lanes",
type: "Lane",
isMany: true,
isVirtual: true,
isReference: true
}]
}, {
name: "CorrelationPropertyRetrievalExpression",
superClass: ["BaseElement"],
properties: [{
name: "messagePath",
type: "FormalExpression"
}, {
name: "messageRef",
type: "Message",
isAttr: true,
isReference: true
}]
}, {
name: "CorrelationPropertyBinding",
superClass: ["BaseElement"],
properties: [{
name: "dataPath",
type: "FormalExpression"
}, {
name: "correlationPropertyRef",
type: "CorrelationProperty",
isAttr: true,
isReference: true
}]
}, {
name: "Resource",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "resourceParameters",
type: "ResourceParameter",
isMany: true
}]
}, {
name: "ResourceParameter",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "isRequired",
isAttr: true,
type: "Boolean"
}, {
name: "type",
type: "ItemDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "CorrelationSubscription",
superClass: ["BaseElement"],
properties: [{
name: "correlationKeyRef",
type: "CorrelationKey",
isAttr: true,
isReference: true
}, {
name: "correlationPropertyBinding",
type: "CorrelationPropertyBinding",
isMany: true
}]
}, {
name: "MessageFlow",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "sourceRef",
type: "InteractionNode",
isAttr: true,
isReference: true
}, {
name: "targetRef",
type: "InteractionNode",
isAttr: true,
isReference: true
}, {
name: "messageRef",
type: "Message",
isAttr: true,
isReference: true
}]
}, {
name: "MessageFlowAssociation",
superClass: ["BaseElement"],
properties: [{
name: "innerMessageFlowRef",
type: "MessageFlow",
isAttr: true,
isReference: true
}, {
name: "outerMessageFlowRef",
type: "MessageFlow",
isAttr: true,
isReference: true
}]
}, {
name: "InteractionNode",
isAbstract: true,
properties: [{
name: "incomingConversationLinks",
type: "ConversationLink",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "outgoingConversationLinks",
type: "ConversationLink",
isMany: true,
isVirtual: true,
isReference: true
}]
}, {
name: "Participant",
superClass: ["InteractionNode", "BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "interfaceRef",
type: "Interface",
isMany: true,
isReference: true
}, {
name: "participantMultiplicity",
type: "ParticipantMultiplicity"
}, {
name: "endPointRefs",
type: "EndPoint",
isMany: true,
isReference: true
}, {
name: "processRef",
type: "Process",
isAttr: true,
isReference: true
}]
}, {
name: "ParticipantAssociation",
superClass: ["BaseElement"],
properties: [{
name: "innerParticipantRef",
type: "Participant",
isAttr: true,
isReference: true
}, {
name: "outerParticipantRef",
type: "Participant",
isAttr: true,
isReference: true
}]
}, {
name: "ParticipantMultiplicity",
properties: [{
name: "minimum",
"default": 0,
isAttr: true,
type: "Integer"
}, {
name: "maximum",
"default": 1,
isAttr: true,
type: "Integer"
}],
superClass: ["BaseElement"]
}, {
name: "Collaboration",
superClass: ["RootElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "isClosed",
isAttr: true,
type: "Boolean"
}, {
name: "participants",
type: "Participant",
isMany: true
}, {
name: "messageFlows",
type: "MessageFlow",
isMany: true
}, {
name: "artifacts",
type: "Artifact",
isMany: true
}, {
name: "conversations",
type: "ConversationNode",
isMany: true
}, {
name: "conversationAssociations",
type: "ConversationAssociation"
}, {
name: "participantAssociations",
type: "ParticipantAssociation",
isMany: true
}, {
name: "messageFlowAssociations",
type: "MessageFlowAssociation",
isMany: true
}, {
name: "correlationKeys",
type: "CorrelationKey",
isMany: true
}, {
name: "choreographyRef",
type: "Choreography",
isMany: true,
isReference: true
}, {
name: "conversationLinks",
type: "ConversationLink",
isMany: true
}]
}, {
name: "ChoreographyActivity",
isAbstract: true,
superClass: ["FlowNode"],
properties: [{
name: "participantRef",
type: "Participant",
isMany: true,
isReference: true
}, {
name: "initiatingParticipantRef",
type: "Participant",
isAttr: true,
isReference: true
}, {
name: "correlationKeys",
type: "CorrelationKey",
isMany: true
}, {
name: "loopType",
type: "ChoreographyLoopType",
"default": "None",
isAttr: true
}]
}, {
name: "CallChoreography",
superClass: ["ChoreographyActivity"],
properties: [{
name: "calledChoreographyRef",
type: "Choreography",
isAttr: true,
isReference: true
}, {
name: "participantAssociations",
type: "ParticipantAssociation",
isMany: true
}]
}, {
name: "SubChoreography",
superClass: ["ChoreographyActivity", "FlowElementsContainer"],
properties: [{
name: "artifacts",
type: "Artifact",
isMany: true
}]
}, {
name: "ChoreographyTask",
superClass: ["ChoreographyActivity"],
properties: [{
name: "messageFlowRef",
type: "MessageFlow",
isMany: true,
isReference: true
}]
}, {
name: "Choreography",
superClass: ["Collaboration", "FlowElementsContainer"]
}, {
name: "GlobalChoreographyTask",
superClass: ["Choreography"],
properties: [{
name: "initiatingParticipantRef",
type: "Participant",
isAttr: true,
isReference: true
}]
}, {
name: "TextAnnotation",
superClass: ["Artifact"],
properties: [{
name: "text",
type: "String"
}, {
name: "textFormat",
"default": "text/plain",
isAttr: true,
type: "String"
}]
}, {
name: "Group",
superClass: ["Artifact"],
properties: [{
name: "categoryValueRef",
type: "CategoryValue",
isAttr: true,
isReference: true
}]
}, {
name: "Association",
superClass: ["Artifact"],
properties: [{
name: "associationDirection",
type: "AssociationDirection",
isAttr: true
}, {
name: "sourceRef",
type: "BaseElement",
isAttr: true,
isReference: true
}, {
name: "targetRef",
type: "BaseElement",
isAttr: true,
isReference: true
}]
}, {
name: "Category",
superClass: ["RootElement"],
properties: [{
name: "categoryValue",
type: "CategoryValue",
isMany: true
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "Artifact",
isAbstract: true,
superClass: ["BaseElement"]
}, {
name: "CategoryValue",
superClass: ["BaseElement"],
properties: [{
name: "categorizedFlowElements",
type: "FlowElement",
isMany: true,
isVirtual: true,
isReference: true
}, {
name: "value",
isAttr: true,
type: "String"
}]
}, {
name: "Activity",
isAbstract: true,
superClass: ["FlowNode"],
properties: [{
name: "isForCompensation",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "default",
type: "SequenceFlow",
isAttr: true,
isReference: true
}, {
name: "ioSpecification",
type: "InputOutputSpecification",
xml: {
serialize: "property"
}
}, {
name: "boundaryEventRefs",
type: "BoundaryEvent",
isMany: true,
isReference: true
}, {
name: "properties",
type: "Property",
isMany: true
}, {
name: "dataInputAssociations",
type: "DataInputAssociation",
isMany: true
}, {
name: "dataOutputAssociations",
type: "DataOutputAssociation",
isMany: true
}, {
name: "startQuantity",
"default": 1,
isAttr: true,
type: "Integer"
}, {
name: "resources",
type: "ResourceRole",
isMany: true
}, {
name: "completionQuantity",
"default": 1,
isAttr: true,
type: "Integer"
}, {
name: "loopCharacteristics",
type: "LoopCharacteristics"
}]
}, {
name: "ServiceTask",
superClass: ["Task"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}, {
name: "operationRef",
type: "Operation",
isAttr: true,
isReference: true
}]
}, {
name: "SubProcess",
superClass: ["Activity", "FlowElementsContainer", "InteractionNode"],
properties: [{
name: "triggeredByEvent",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "artifacts",
type: "Artifact",
isMany: true
}]
}, {
name: "LoopCharacteristics",
isAbstract: true,
superClass: ["BaseElement"]
}, {
name: "MultiInstanceLoopCharacteristics",
superClass: ["LoopCharacteristics"],
properties: [{
name: "isSequential",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "behavior",
type: "MultiInstanceBehavior",
"default": "All",
isAttr: true
}, {
name: "loopCardinality",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "loopDataInputRef",
type: "ItemAwareElement",
isReference: true
}, {
name: "loopDataOutputRef",
type: "ItemAwareElement",
isReference: true
}, {
name: "inputDataItem",
type: "DataInput",
xml: {
serialize: "property"
}
}, {
name: "outputDataItem",
type: "DataOutput",
xml: {
serialize: "property"
}
}, {
name: "complexBehaviorDefinition",
type: "ComplexBehaviorDefinition",
isMany: true
}, {
name: "completionCondition",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "oneBehaviorEventRef",
type: "EventDefinition",
isAttr: true,
isReference: true
}, {
name: "noneBehaviorEventRef",
type: "EventDefinition",
isAttr: true,
isReference: true
}]
}, {
name: "StandardLoopCharacteristics",
superClass: ["LoopCharacteristics"],
properties: [{
name: "testBefore",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "loopCondition",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "loopMaximum",
type: "Integer",
isAttr: true
}]
}, {
name: "CallActivity",
superClass: ["Activity", "InteractionNode"],
properties: [{
name: "calledElement",
type: "String",
isAttr: true
}]
}, {
name: "Task",
superClass: ["Activity", "InteractionNode"]
}, {
name: "SendTask",
superClass: ["Task"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}, {
name: "operationRef",
type: "Operation",
isAttr: true,
isReference: true
}, {
name: "messageRef",
type: "Message",
isAttr: true,
isReference: true
}]
}, {
name: "ReceiveTask",
superClass: ["Task"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}, {
name: "instantiate",
"default": false,
isAttr: true,
type: "Boolean"
}, {
name: "operationRef",
type: "Operation",
isAttr: true,
isReference: true
}, {
name: "messageRef",
type: "Message",
isAttr: true,
isReference: true
}]
}, {
name: "ScriptTask",
superClass: ["Task"],
properties: [{
name: "scriptFormat",
isAttr: true,
type: "String"
}, {
name: "script",
type: "String"
}]
}, {
name: "BusinessRuleTask",
superClass: ["Task"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}]
}, {
name: "AdHocSubProcess",
superClass: ["SubProcess"],
properties: [{
name: "completionCondition",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "ordering",
type: "AdHocOrdering",
isAttr: true
}, {
name: "cancelRemainingInstances",
"default": true,
isAttr: true,
type: "Boolean"
}]
}, {
name: "Transaction",
superClass: ["SubProcess"],
properties: [{
name: "protocol",
isAttr: true,
type: "String"
}, {
name: "method",
isAttr: true,
type: "String"
}]
}, {
name: "GlobalScriptTask",
superClass: ["GlobalTask"],
properties: [{
name: "scriptLanguage",
isAttr: true,
type: "String"
}, {
name: "script",
isAttr: true,
type: "String"
}]
}, {
name: "GlobalBusinessRuleTask",
superClass: ["GlobalTask"],
properties: [{
name: "implementation",
isAttr: true,
type: "String"
}]
}, {
name: "ComplexBehaviorDefinition",
superClass: ["BaseElement"],
properties: [{
name: "condition",
type: "FormalExpression"
}, {
name: "event",
type: "ImplicitThrowEvent"
}]
}, {
name: "ResourceRole",
superClass: ["BaseElement"],
properties: [{
name: "resourceRef",
type: "Resource",
isReference: true
}, {
name: "resourceParameterBindings",
type: "ResourceParameterBinding",
isMany: true
}, {
name: "resourceAssignmentExpression",
type: "ResourceAssignmentExpression"
}, {
name: "name",
isAttr: true,
type: "String"
}]
}, {
name: "ResourceParameterBinding",
properties: [{
name: "expression",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}, {
name: "parameterRef",
type: "ResourceParameter",
isAttr: true,
isReference: true
}],
superClass: ["BaseElement"]
}, {
name: "ResourceAssignmentExpression",
properties: [{
name: "expression",
type: "Expression",
xml: {
serialize: "xsi:type"
}
}],
superClass: ["BaseElement"]
}, {
name: "Import",
properties: [{
name: "importType",
isAttr: true,
type: "String"
}, {
name: "location",
isAttr: true,
type: "String"
}, {
name: "namespace",
isAttr: true,
type: "String"
}]
}, {
name: "Definitions",
superClass: ["BaseElement"],
properties: [{
name: "name",
isAttr: true,
type: "String"
}, {
name: "targetNamespace",
isAttr: true,
type: "String"
}, {
name: "expressionLanguage",
"default": "http://www.w3.org/1999/XPath",
isAttr: true,
type: "String"
}, {
name: "typeLanguage",
"default": "http://www.w3.org/2001/XMLSchema",
isAttr: true,
type: "String"
}, {
name: "imports",
type: "Import",
isMany: true
}, {
name: "extensions",
type: "Extension",
isMany: true
}, {
name: "rootElements",
type: "RootElement",
isMany: true
}, {
name: "diagrams",
isMany: true,
type: "bpmndi:BPMNDiagram"
}, {
name: "exporter",
isAttr: true,
type: "String"
}, {
name: "relationships",
type: "Relationship",
isMany: true
}, {
name: "exporterVersion",
isAttr: true,
type: "String"
}]
}];
var enumerations$3 = [{
name: "ProcessType",
literalValues: [{
name: "None"
}, {
name: "Public"
}, {
name: "Private"
}]
}, {
name: "GatewayDirection",
literalValues: [{
name: "Unspecified"
}, {
name: "Converging"
}, {
name: "Diverging"
}, {
name: "Mixed"
}]
}, {
name: "EventBasedGatewayType",
literalValues: [{
name: "Parallel"
}, {
name: "Exclusive"
}]
}, {
name: "RelationshipDirection",
literalValues: [{
name: "None"
}, {
name: "Forward"
}, {
name: "Backward"
}, {
name: "Both"
}]
}, {
name: "ItemKind",
literalValues: [{
name: "Physical"
}, {
name: "Information"
}]
}, {
name: "ChoreographyLoopType",
literalValues: [{
name: "None"
}, {
name: "Standard"
}, {
name: "MultiInstanceSequential"
}, {
name: "MultiInstanceParallel"
}]
}, {
name: "AssociationDirection",
literalValues: [{
name: "None"
}, {
name: "One"
}, {
name: "Both"
}]
}, {
name: "MultiInstanceBehavior",
literalValues: [{
name: "None"
}, {
name: "One"
}, {
name: "All"
}, {
name: "Complex"
}]
}, {
name: "AdHocOrdering",
literalValues: [{
name: "Parallel"
}, {
name: "Sequential"
}]
}];
var xml$1 = {
tagAlias: "lowerCase",
typePrefix: "t"
};
var BpmnPackage = {
name: name$5,
uri: uri$5,
prefix: prefix$5,
associations: associations$5,
types: types$5,
enumerations: enumerations$3,
xml: xml$1
};
var name$4 = "BPMNDI";
var uri$4 = "http://www.omg.org/spec/BPMN/20100524/DI";
var prefix$4 = "bpmndi";
var types$4 = [{
name: "BPMNDiagram",
properties: [{
name: "plane",
type: "BPMNPlane",
redefines: "di:Diagram#rootElement"
}, {
name: "labelStyle",
type: "BPMNLabelStyle",
isMany: true
}],
superClass: ["di:Diagram"]
}, {
name: "BPMNPlane",
properties: [{
name: "bpmnElement",
isAttr: true,
isReference: true,
type: "bpmn:BaseElement",
redefines: "di:DiagramElement#modelElement"
}],
superClass: ["di:Plane"]
}, {
name: "BPMNShape",
properties: [{
name: "bpmnElement",
isAttr: true,
isReference: true,
type: "bpmn:BaseElement",
redefines: "di:DiagramElement#modelElement"
}, {
name: "isHorizontal",
isAttr: true,
type: "Boolean"
}, {
name: "isExpanded",
isAttr: true,
type: "Boolean"
}, {
name: "isMarkerVisible",
isAttr: true,
type: "Boolean"
}, {
name: "label",
type: "BPMNLabel"
}, {
name: "isMessageVisible",
isAttr: true,
type: "Boolean"
}, {
name: "participantBandKind",
type: "ParticipantBandKind",
isAttr: true
}, {
name: "choreographyActivityShape",
type: "BPMNShape",
isAttr: true,
isReference: true
}],
superClass: ["di:LabeledShape"]
}, {
name: "BPMNEdge",
properties: [{
name: "label",
type: "BPMNLabel"
}, {
name: "bpmnElement",
isAttr: true,
isReference: true,
type: "bpmn:BaseElement",
redefines: "di:DiagramElement#modelElement"
}, {
name: "sourceElement",
isAttr: true,
isReference: true,
type: "di:DiagramElement",
redefines: "di:Edge#source"
}, {
name: "targetElement",
isAttr: true,
isReference: true,
type: "di:DiagramElement",
redefines: "di:Edge#target"
}, {
name: "messageVisibleKind",
type: "MessageVisibleKind",
isAttr: true,
"default": "initiating"
}],
superClass: ["di:LabeledEdge"]
}, {
name: "BPMNLabel",
properties: [{
name: "labelStyle",
type: "BPMNLabelStyle",
isAttr: true,
isReference: true,
redefines: "di:DiagramElement#style"
}],
superClass: ["di:Label"]
}, {
name: "BPMNLabelStyle",
properties: [{
name: "font",
type: "dc:Font"
}],
superClass: ["di:Style"]
}];
var enumerations$2 = [{
name: "ParticipantBandKind",
literalValues: [{
name: "top_initiating"
}, {
name: "middle_initiating"
}, {
name: "bottom_initiating"
}, {
name: "top_non_initiating"
}, {
name: "middle_non_initiating"
}, {
name: "bottom_non_initiating"
}]
}, {
name: "MessageVisibleKind",
literalValues: [{
name: "initiating"
}, {
name: "non_initiating"
}]
}];
var associations$4 = [];
var BpmnDiPackage = {
name: name$4,
uri: uri$4,
prefix: prefix$4,
types: types$4,
enumerations: enumerations$2,
associations: associations$4
};
var name$3 = "DC";
var uri$3 = "http://www.omg.org/spec/DD/20100524/DC";
var prefix$3 = "dc";
var types$3 = [{
name: "Boolean"
}, {
name: "Integer"
}, {
name: "Real"
}, {
name: "String"
}, {
name: "Font",
properties: [{
name: "name",
type: "String",
isAttr: true
}, {
name: "size",
type: "Real",
isAttr: true
}, {
name: "isBold",
type: "Boolean",
isAttr: true
}, {
name: "isItalic",
type: "Boolean",
isAttr: true
}, {
name: "isUnderline",
type: "Boolean",
isAttr: true
}, {
name: "isStrikeThrough",
type: "Boolean",
isAttr: true
}]
}, {
name: "Point",
properties: [{
name: "x",
type: "Real",
"default": "0",
isAttr: true
}, {
name: "y",
type: "Real",
"default": "0",
isAttr: true
}]
}, {
name: "Bounds",
properties: [{
name: "x",
type: "Real",
"default": "0",
isAttr: true
}, {
name: "y",
type: "Real",
"default": "0",
isAttr: true
}, {
name: "width",
type: "Real",
isAttr: true
}, {
name: "height",
type: "Real",
isAttr: true
}]
}];
var associations$3 = [];
var DcPackage = {
name: name$3,
uri: uri$3,
prefix: prefix$3,
types: types$3,
associations: associations$3
};
var name$2 = "DI";
var uri$2 = "http://www.omg.org/spec/DD/20100524/DI";
var prefix$2 = "di";
var types$2 = [{
name: "DiagramElement",
isAbstract: true,
properties: [{
name: "id",
isAttr: true,
isId: true,
type: "String"
}, {
name: "extension",
type: "Extension"
}, {
name: "owningDiagram",
type: "Diagram",
isReadOnly: true,
isVirtual: true,
isReference: true
}, {
name: "owningElement",
type: "DiagramElement",
isReadOnly: true,
isVirtual: true,
isReference: true
}, {
name: "modelElement",
isReadOnly: true,
isVirtual: true,
isReference: true,
type: "Element"
}, {
name: "style",
type: "Style",
isReadOnly: true,
isVirtual: true,
isReference: true
}, {
name: "ownedElement",
type: "DiagramElement",
isReadOnly: true,
isMany: true,
isVirtual: true
}]
}, {
name: "Node",
isAbstract: true,
superClass: ["DiagramElement"]
}, {
name: "Edge",
isAbstract: true,
superClass: ["DiagramElement"],
properties: [{
name: "source",
type: "DiagramElement",
isReadOnly: true,
isVirtual: true,
isReference: true
}, {
name: "target",
type: "DiagramElement",
isReadOnly: true,
isVirtual: true,
isReference: true
}, {
name: "waypoint",
isUnique: false,
isMany: true,
type: "dc:Point",
xml: {
serialize: "xsi:type"
}
}]
}, {
name: "Diagram",
isAbstract: true,
properties: [{
name: "id",
isAttr: true,
isId: true,
type: "String"
}, {
name: "rootElement",
type: "DiagramElement",
isReadOnly: true,
isVirtual: true
}, {
name: "name",
isAttr: true,
type: "String"
}, {
name: "documentation",
isAttr: true,
type: "String"
}, {
name: "resolution",
isAttr: true,
type: "Real"
}, {
name: "ownedStyle",
type: "Style",
isReadOnly: true,
isMany: true,
isVirtual: true
}]
}, {
name: "Shape",
isAbstract: true,
superClass: ["Node"],
properties: [{
name: "bounds",
type: "dc:Bounds"
}]
}, {
name: "Plane",
isAbstract: true,
superClass: ["Node"],
properties: [{
name: "planeElement",
type: "DiagramElement",
subsettedProperty: "DiagramElement-ownedElement",
isMany: true
}]
}, {
name: "LabeledEdge",
isAbstract: true,
superClass: ["Edge"],
properties: [{
name: "ownedLabel",
type: "Label",
isReadOnly: true,
subsettedProperty: "DiagramElement-ownedElement",
isMany: true,
isVirtual: true
}]
}, {
name: "LabeledShape",
isAbstract: true,
superClass: ["Shape"],
properties: [{
name: "ownedLabel",
type: "Label",
isReadOnly: true,
subsettedProperty: "DiagramElement-ownedElement",
isMany: true,
isVirtual: true
}]
}, {
name: "Label",
isAbstract: true,
superClass: ["Node"],
properties: [{
name: "bounds",
type: "dc:Bounds"
}]
}, {
name: "Style",
isAbstract: true,
properties: [{
name: "id",
isAttr: true,
isId: true,
type: "String"
}]
}, {
name: "Extension",
properties: [{
name: "values",
isMany: true,
type: "Element"
}]
}];
var associations$2 = [];
var xml$2 = {
tagAlias: "lowerCase"
};
var DiPackage = {
name: name$2,
uri: uri$2,
prefix: prefix$2,
types: types$2,
associations: associations$2,
xml: xml$2
};
var name$1 = "bpmn.io colors for BPMN";
var uri$1 = "http://bpmn.io/schema/bpmn/biocolor/1.0";
var prefix$1 = "bioc";
var types$1 = [{
name: "ColoredShape",
"extends": ["bpmndi:BPMNShape"],
properties: [{
name: "stroke",
isAttr: true,
type: "String"
}, {
name: "fill",
isAttr: true,
type: "String"
}]
}, {
name: "ColoredEdge",
"extends": ["bpmndi:BPMNEdge"],
properties: [{
name: "stroke",
isAttr: true,
type: "String"
}, {
name: "fill",
isAttr: true,
type: "String"
}]
}];
var enumerations$1 = [];
var associations$1 = [];
var BiocPackage = {
name: name$1,
uri: uri$1,
prefix: prefix$1,
types: types$1,
enumerations: enumerations$1,
associations: associations$1
};
var name$6 = "BPMN in Color";
var uri$6 = "http://www.omg.org/spec/BPMN/non-normative/color/1.0";
var prefix$6 = "color";
var types$6 = [{
name: "ColoredLabel",
"extends": ["bpmndi:BPMNLabel"],
properties: [{
name: "color",
isAttr: true,
type: "String"
}]
}, {
name: "ColoredShape",
"extends": ["bpmndi:BPMNShape"],
properties: [{
name: "background-color",
isAttr: true,
type: "String"
}, {
name: "border-color",
isAttr: true,
type: "String"
}]
}, {
name: "ColoredEdge",
"extends": ["bpmndi:BPMNEdge"],
properties: [{
name: "border-color",
isAttr: true,
type: "String"
}]
}];
var enumerations = [];
var associations$6 = [];
var BpmnInColorPackage = {
name: name$6,
uri: uri$6,
prefix: prefix$6,
types: types$6,
enumerations: enumerations,
associations: associations$6
};
const packages = {
bpmn: BpmnPackage,
bpmndi: BpmnDiPackage,
dc: DcPackage,
di: DiPackage,
bioc: BiocPackage,
color: BpmnInColorPackage
};
function SimpleBpmnModdle(additionalPackages, options) {
const pks = assign({}, packages, additionalPackages);
return new BpmnModdle(pks, options);
}
var name = "zeebe";
var prefix = "zeebe";
var uri = "http://camunda.org/schema/zeebe/1.0";
var xml = {
tagAlias: "lowerCase"
};
var associations = [
];
var types = [
{
name: "ZeebeServiceTask",
"extends": [
"bpmn:ServiceTask",
"bpmn:BusinessRuleTask",
"bpmn:ScriptTask",
"bpmn:SendTask",
"bpmn:EndEvent",
"bpmn:IntermediateThrowEvent",
"bpmn:AdHocSubProcess"
],
properties: [
{
name: "retryCounter",
type: "String",
isAttr: true
}
]
},
{
name: "IoMapping",
superClass: [
"Element"
],
properties: [
{
name: "ioMapping",
type: "IoMapping"
},
{
name: "inputParameters",
isMany: true,
type: "Input"
},
{
name: "outputParameters",
isMany: true,
type: "Output"
}
],
meta: {
allowedIn: [
"bpmn:CallActivity",
"bpmn:Event",
"bpmn:ReceiveTask",
"zeebe:ZeebeServiceTask",
"bpmn:SubProcess",
"bpmn:UserTask"
]
}
},
{
name: "InputOutputParameter",
properties: [
{
name: "source",
isAttr: true,
type: "String"
},
{
name: "target",
isAttr: true,
type: "String"
}
]
},
{
name: "Subscription",
superClass: [
"Element"
],
properties: [
{
name: "correlationKey",
isAttr: true,
type: "String"
}
]
},
{
name: "Input",
superClass: [
"InputOutputParameter"
],
meta: {
allowedIn: [
"bpmn:CallActivity",
"zeebe:ZeebeServiceTask",
"bpmn:SubProcess",
"bpmn:UserTask"
]
}
},
{
name: "Output",
superClass: [
"InputOutputParameter"
],
meta: {
allowedIn: [
"bpmn:CallActivity",
"bpmn:Event",
"bpmn:ReceiveTask",
"zeebe:ZeebeServiceTask",
"bpmn:SubProcess",
"bpmn:UserTask"
]
}
},
{
name: "TaskHeaders",
superClass: [
"Element"
],
meta: {
allowedIn: [
"zeebe:ZeebeServiceTask",
"bpmn:UserTask"
]
},
properties: [
{
name: "values",
type: "Header",
isMany: true
}
]
},
{
name: "Header",
superClass: [
"Element"
],
properties: [
{
name: "id",
type: "String",
isAttr: true
},
{
name: "key",
type: "String",
isAttr: true
},
{
name: "value",
type: "String",
isAttr: true
}
]
},
{
name: "TaskDefinition",
superClass: [
"Element"
],
meta: {
allowedIn: [
"zeebe:ZeebeServiceTask"
]
},
properties: [
{
name: "type",
type: "String",
isAttr: true
},
{
name: "retries",
type: "String",
isAttr: true
}
]
},
{
name: "LoopCharacteristics",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:MultiInstanceLoopCharacteristics"
]
},
properties: [
{
name: "inputCollection",
type: "String",
isAttr: true
},
{
name: "inputElement",
type: "String",
isAttr: true
},
{
name: "outputCollection",
type: "String",
isAttr: true
},
{
name: "outputElement",
type: "String",
isAttr: true
}
]
},
{
name: "CalledElement",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:CallActivity"
]
},
properties: [
{
name: "processId",
type: "String",
isAttr: true
},
{
name: "processIdExpression",
type: "String",
isAttr: true
},
{
name: "propagateAllChildVariables",
isAttr: true,
type: "Boolean"
},
{
name: "propagateAllParentVariables",
isAttr: true,
type: "Boolean",
"default": true
}
]
},
{
name: "UserTaskForm",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:Process"
]
},
properties: [
{
name: "id",
type: "String",
isAttr: true
},
{
name: "body",
type: "String",
isBody: true
}
]
},
{
name: "FormDefinition",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
{
name: "formKey",
type: "String",
isAttr: true
},
{
name: "formId",
type: "String",
isAttr: true
},
{
name: "externalReference",
type: "String",
isAttr: true
}
]
},
{
name: "LinkedResource",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:ServiceTask"
]
},
properties: [
{
name: "resourceId",
type: "String",
isAttr: true
},
{
name: "resourceType",
type: "String",
isAttr: true
},
{
name: "linkName",
type: "String",
isAttr: true
}
]
},
{
name: "LinkedResources",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:ServiceTask"
]
},
properties: [
{
name: "values",
type: "LinkedResource",
isMany: true
}
]
},
{
name: "UserTask",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
]
},
{
name: "CalledDecision",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:BusinessRuleTask"
]
},
properties: [
{
name: "decisionId",
type: "String",
isAttr: true
},
{
name: "resultVariable",
type: "String",
isAttr: true
}
]
},
{
name: "AssignmentDefinition",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
{
name: "assignee",
type: "String",
isAttr: true
},
{
name: "candidateGroups",
type: "String",
isAttr: true
},
{
name: "candidateUsers",
type: "String",
isAttr: true
}
]
},
{
name: "PriorityDefinition",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
{
name: "priority",
type: "String",
isAttr: true
}
]
},
{
name: "TaskSchedule",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
{
name: "dueDate",
type: "String",
isAttr: true
},
{
name: "followUpDate",
type: "String",
isAttr: true
}
]
},
{
name: "Properties",
superClass: [
"Element"
],
properties: [
{
name: "properties",
type: "Property",
isMany: true
}
]
},
{
name: "Property",
properties: [
{
name: "name",
type: "String",
isAttr: true
},
{
name: "value",
type: "String",
isAttr: true
}
]
},
{
name: "TemplateSupported",
isAbstract: true,
"extends": [
"bpmn:Collaboration",
"bpmn:Process",
"bpmn:FlowElement"
],
properties: [
{
name: "modelerTemplate",
isAttr: true,
type: "String"
},
{
name: "modelerTemplateVersion",
isAttr: true,
type: "Integer"
},
{
name: "modelerTemplateIcon",
isAttr: true,
type: "String"
}
]
},
{
name: "TemplatedRootElement",
isAbstract: true,
"extends": [
"bpmn:Error",
"bpmn:Escalation",
"bpmn:Message",
"bpmn:Signal"
],
properties: [
{
name: "modelerTemplate",
isAttr: true,
type: "String"
}
]
},
{
name: "Script",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:ScriptTask"
]
},
properties: [
{
name: "expression",
type: "String",
isAttr: true
},
{
name: "resultVariable",
type: "String",
isAttr: true
}
]
},
{
name: "ExecutionListeners",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:Event",
"bpmn:Activity",
"bpmn:Process",
"bpmn:ExclusiveGateway",
"bpmn:InclusiveGateway",
"bpmn:ParallelGateway",
"bpmn:EventBasedGateway"
]
},
properties: [
{
name: "listeners",
type: "ExecutionListener",
isMany: true
}
]
},
{
name: "ExecutionListener",
superClass: [
"Element"
],
meta: {
allowedIn: [
"zeebe:ExecutionListeners"
]
},
properties: [
{
name: "eventType",
type: "String",
isAttr: true
},
{
name: "retries",
type: "String",
isAttr: true
},
{
name: "type",
type: "String",
isAttr: true
}
]
},
{
name: "TaskListeners",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:UserTask"
]
},
properties: [
{
name: "listeners",
type: "TaskListener",
isMany: true
}
]
},
{
name: "TaskListener",
superClass: [
"Element"
],
meta: {
allowedIn: [
"zeebe:TaskListeners"
]
},
properties: [
{
name: "eventType",
type: "String",
isAttr: true
},
{
name: "retries",
type: "String",
isAttr: true
},
{
name: "type",
type: "String",
isAttr: true
}
]
},
{
name: "VersionTag",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:Process"
]
},
properties: [
{
name: "value",
type: "String",
isAttr: true
}
]
},
{
name: "BindingTypeSupported",
isAbstract: true,
"extends": [
"zeebe:CalledDecision",
"zeebe:CalledElement",
"zeebe:FormDefinition",
"zeebe:LinkedResource"
],
properties: [
{
name: "bindingType",
isAttr: true,
type: "String",
"default": "latest"
},
{
name: "versionTag",
isAttr: true,
type: "String"
}
]
},
{
name: "AdHoc",
superClass: [
"Element"
],
meta: {
allowedIn: [
"bpmn:AdHocSubProcess"
]
},
properties: [
{
name: "activeElementsCollection",
isAttr: true,
type: "String"
},
{
name: "outputCollection",
isAttr: true,
type: "String"
},
{
name: "outputElement",
isAttr: true,
type: "String"
}
]
}
];
var zeebeModdle = {
name: name,
prefix: prefix,
uri: uri,
xml: xml,
associations: associations,
types: types
};
/**
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership.
*
* Camunda licenses this file to you under the MIT; you may not use this file
* except in compliance with the MIT License.
*/
let lastTemplates = [],
lastValidTemplates = [];
const ElementTemplateLinterPlugin = function (templates) {
if (!templatesEqual(lastTemplates, templates)) {
const moddle = new SimpleBpmnModdle({
zeebe: zeebeModdle
});
const validator = new Validator(moddle).addAll(templates);
const validTemplates = validator.getValidTemplates();
lastTemplates = templates;
lastValidTemplates = validTemplates;
}
return {
config: {
rules: {
'element-templates/validate': ['error', {
templates: lastValidTemplates
}],
'element-templates/compatibility': ['warn', {
templates: lastValidTemplates
}]
}
},
resolver: new StaticResolver({
'rule:bpmnlint-plugin-element-templates/validate': validate,
'rule:bpmnlint-plugin-element-templates/compatibility': compatibility
})
};
};
function templatesEqual(a, b) {
if (a.length !== b.length) {
return false;
}
return JSON.stringify(a) === JSON.stringify(b);
}
export { coreModule$1 as CloudElementTemplatesCoreModule, ElementTemplateLinterPlugin as CloudElementTemplatesLinterPlugin, index$1 as CloudElementTemplatesPropertiesProviderModule, Validator as CloudElementTemplatesValidator, coreModule as ElementTemplatesCoreModule, index as ElementTemplatesPropertiesProviderModule };
//# sourceMappingURL=index.esm.js.map