From 3a9f2c09b1a4d3b32767445dd5d2283d64754797 Mon Sep 17 00:00:00 2001 From: Benjamin Weder Date: Thu, 15 Jun 2023 13:37:25 +0200 Subject: [PATCH 01/29] Add linter script and fix errors --- components/bpmn-q/bpmnlint-plugin-custom/index.js | 2 +- .../rules/subprocess-connected-end-event.js | 2 +- .../editor/ui/notifications/__tests__/NotificationSpec.js | 2 +- .../editor/ui/notifications/__tests__/NotificationsSpec.js | 1 + .../modeler-component/extensions/planqk/PlanQKPlugin.js | 2 +- .../extensions/quantme/modeling/BpmnKeyboardBindings.js | 2 +- .../modeler-component/extensions/quantme/modeling/index.js | 2 +- .../service-task/ImplementationTypeProps.js | 2 +- .../modeler-component/extensions/quantme/qrm-manager/index.js | 2 +- .../extensions/quantme/ui/adaptation/AdaptationPlugin.js | 2 +- components/bpmn-q/package.json | 4 +++- 11 files changed, 13 insertions(+), 10 deletions(-) diff --git a/components/bpmn-q/bpmnlint-plugin-custom/index.js b/components/bpmn-q/bpmnlint-plugin-custom/index.js index 450b234d..494e1df0 100644 --- a/components/bpmn-q/bpmnlint-plugin-custom/index.js +++ b/components/bpmn-q/bpmnlint-plugin-custom/index.js @@ -12,4 +12,4 @@ module.exports = { } } } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-connected-end-event.js b/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-connected-end-event.js index d7f2740e..9dd14e52 100644 --- a/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-connected-end-event.js +++ b/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-connected-end-event.js @@ -27,7 +27,7 @@ module.exports = function () { } function check(node, reporter) { - console.log(node) + console.log(node); if (!isAny(node, ['bpmn:SubProcess'])) { return; } diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js index 647f922d..d02450ca 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js @@ -20,7 +20,7 @@ import { import Notification from '../Notification'; import {describe, it, after, before} from "mocha"; -const { expect } = require('chai') +const { expect } = require('chai'); describe('', function() { diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js index fb25bb12..cf538d47 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js @@ -14,6 +14,7 @@ import { shallow } from 'enzyme'; import Notifications from '..'; import Notification from '../Notification'; +import {expect} from "chai"; describe('', function() { diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js index a95dc46c..35cd2c0d 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js @@ -20,4 +20,4 @@ export default { return await startPlanqkReplacementProcess(xml); } }/>, -} \ No newline at end of file +}; \ No newline at end of file diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js index cebb4a1f..c186b41f 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js @@ -183,7 +183,7 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // put into clipboard getModeler().get('clipboard').set(parsedCopy); - }) + }); /** * A factory function that returns a reviver to be diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js index f5ca811e..1da7fb14 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js @@ -15,7 +15,7 @@ import QuantMEPathMap from './QuantMEPathMap'; import QuantMEPropertiesProvider from './properties-provider/QuantMEPropertiesProvider'; import BpmnKeyboardBinding from './BpmnKeyboardBindings'; import BpmnEditorActions from './BpmnEditorActions'; -import BpmnKeyboard from './BpmnKeyboard' +import BpmnKeyboard from './BpmnKeyboard'; export default { __init__: ['quantMERenderer', 'quantMEReplaceMenu', 'bpmnFactory', 'quantMEPathMap', 'propertiesProvider', 'keyboardBindings', 'editorActions', 'keyboard'], diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/service-task/ImplementationTypeProps.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/service-task/ImplementationTypeProps.js index cc7ea824..c54e549e 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/service-task/ImplementationTypeProps.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/service-task/ImplementationTypeProps.js @@ -5,7 +5,7 @@ import { import {SelectEntry, isSelectEntryEdited} from '@bpmn-io/properties-panel'; import {useService} from "bpmn-js-properties-panel"; import {getImplementationType} from "../../../utilities/ImplementationTypeHelperExtension"; -import {createElement} from "../../../../../editor/util/camunda-utils/ElementUtil" +import {createElement} from "../../../../../editor/util/camunda-utils/ElementUtil"; import { getServiceTaskLikeBusinessObject, isDeploymentCapable, isDmnCapable, diff --git a/components/bpmn-q/modeler-component/extensions/quantme/qrm-manager/index.js b/components/bpmn-q/modeler-component/extensions/quantme/qrm-manager/index.js index 6d77ca82..5bd0f9fd 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/qrm-manager/index.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/qrm-manager/index.js @@ -28,7 +28,7 @@ export const updateQRMs = async function () { console.log('Updating QRMs in backend.'); try { QRMs = await qrmHandler.getCurrentQRMs(); - console.log('Found ' + QRMs.length + ' QRMs.') + console.log('Found ' + QRMs.length + ' QRMs.'); return QRMs; } catch (error) { console.log('Error while updating QRMs in backend: ' + error); diff --git a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/AdaptationPlugin.js b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/AdaptationPlugin.js index dca3f4f9..ef294d4e 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/AdaptationPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/ui/adaptation/AdaptationPlugin.js @@ -113,7 +113,7 @@ export default class AdaptationPlugin extends PureComponent { content: 'Error during workflow analysis. Aborting rewriting modal!', duration: 20000 }); - console.log('Error during workflow analysis. Aborting rewriting modal!') + console.log('Error during workflow analysis. Aborting rewriting modal!'); this.setState({rewriteOpen: false}); return; diff --git a/components/bpmn-q/package.json b/components/bpmn-q/package.json index 388a747f..550ca650 100644 --- a/components/bpmn-q/package.json +++ b/components/bpmn-q/package.json @@ -13,7 +13,9 @@ "test:watch": "npm test -- --auto-watch --no-single-run", "build": "webpack", "dev": "webpack-dev-server", - "dev-open": "webpack-dev-server --open" + "dev-open": "webpack-dev-server --open", + "lint": "eslint --ext .js .", + "lint-fix": "eslint --fix --ext .js ." }, "author": "", "license": "Apache-2.0", From 9f734359391d814b62205faa0a4a24c9c2a75612 Mon Sep 17 00:00:00 2001 From: Benjamin Weder Date: Thu, 15 Jun 2023 14:03:41 +0200 Subject: [PATCH 02/29] Apply prettier --- components/bpmn-q/.eslintrc.js | 30 - components/bpmn-q/.eslintrc.json | 24 + components/bpmn-q/.prettierignore | Bin 0 -> 62 bytes components/bpmn-q/.prettierrc.json | 1 + components/bpmn-q/README.md | 25 +- components/bpmn-q/babel.config.js | 56 +- .../bpmn-q/bpmnlint-plugin-custom/index.js | 17 +- .../rules/quantme-tasks.js | 16 +- .../rules/subprocess-connected-end-event.js | 31 +- .../rules/subprocess-required-start-event.js | 21 +- components/bpmn-q/client/index.js | 10 +- components/bpmn-q/karma.conf.js | 119 +- .../QuantumWorkflowModeler.js | 542 +-- .../editor/EditorConstants.js | 15 +- .../editor/ModelerHandler.js | 169 +- .../editor/config/ConfigModal.js | 138 +- .../editor/config/ConfigPlugin.js | 95 +- .../editor/config/EditorConfigManager.js | 87 +- .../editor/config/EditorTab.js | 134 +- .../editor/config/config-modal.css | 66 +- .../configurations/ConfigurationEndpoint.js | 106 +- .../ConfigurationsProperties.js | 239 +- .../configurations/ConfigurationsUtil.js | 458 +- .../editor/configurations/Constants.js | 5 +- .../editor/events/EditorEventHandler.js | 26 +- .../editor/plugin/PluginConfigHandler.js | 15 +- .../editor/plugin/PluginHandler.js | 191 +- .../editor/popup/CustomPopupMenu.js | 344 +- .../editor/popup/HiddenTextFieldEntry.js | 41 +- .../popup/SearchablePopupMenuComponent.js | 605 +-- .../modeler-component/editor/popup/index.js | 6 +- .../editor/resources/styling/bpmn-fonts.css | 46 +- .../styling/camunda-styles/_buttons.css | 70 +- .../styling/camunda-styles/_colors.css | 70 +- .../styling/camunda-styles/_modal.css | 191 +- .../styling/camunda-styles/style.css | 2 +- .../editor/resources/styling/editor-ui.css | 258 +- .../editor/resources/styling/modeler.css | 151 +- .../editor/shortcut/ShortcutModal.js | 140 +- .../editor/shortcut/ShortcutPlugin.js | 72 +- .../editor/ui/ButtonToolbar.js | 60 +- .../editor/ui/DeploymentButton.js | 112 +- .../editor/ui/ExtensibleButton.js | 141 +- .../editor/ui/NewDiagramButton.js | 27 +- .../modeler-component/editor/ui/OpenButton.js | 121 +- .../modeler-component/editor/ui/SaveButton.js | 26 +- .../editor/ui/TransformationButton.js | 65 +- .../editor/ui/TransformationToolbarButton.js | 307 +- .../editor/ui/modal/EscapeTrap.js | 33 +- .../editor/ui/modal/FocusTrap.js | 154 +- .../ui/modal/KeyboardInteractionTrap.js | 119 +- .../editor/ui/modal/Modal.js | 198 +- .../editor/ui/modal/index.js | 2 +- .../editor/ui/notifications/Notification.css | 44 +- .../editor/ui/notifications/Notification.js | 104 +- .../ui/notifications/NotificationHandler.js | 108 +- .../editor/ui/notifications/Notifications.css | 24 +- .../editor/ui/notifications/Notifications.js | 208 +- .../__tests__/NotificationSpec.js | 63 +- .../__tests__/NotificationsSpec.js | 32 +- .../editor/ui/notifications/index.js | 4 +- .../editor/util/IoUtilities.js | 325 +- .../editor/util/ModellingUtilities.js | 797 ++-- .../editor/util/PopupMenuUtilities.js | 234 +- .../editor/util/RenderUtilities.js | 91 +- .../editor/util/TransformationUtilities.js | 444 +- .../util/camunda-utils/ConnectorUtil.js | 24 +- .../editor/util/camunda-utils/ElementUtil.js | 42 +- .../util/camunda-utils/EventDefinitionUtil.js | 164 +- .../camunda-utils/ExtensionElementsUtil.js | 171 +- .../util/camunda-utils/FormTypeUtils.js | 32 +- .../camunda-utils/ImplementationTypeUtils.js | 56 +- .../util/camunda-utils/InputOutputUtil.js | 245 +- .../util/camunda-utils/ValidationUtil.js | 40 +- .../util/camunda-utils/generateImage.js | 63 +- .../extensions/data-extension/Constants.js | 30 +- .../data-extension/DataFlowPlugin.js | 68 +- .../config/DataConfigManager.js | 28 +- .../extensions/data-extension/index.js | 42 +- .../menu/DataFlowReplaceMenuProvider.js | 498 ++- .../menu/DataFlowReplaceOptions.js | 46 +- .../palette/DataFlowPaletteProvider.js | 174 +- .../DataFlowPropertiesProvider.js | 213 +- .../properties-panel/KeyValueEntry.js | 150 +- .../properties-panel/KeyValueMap.js | 129 +- .../rendering/DataFlowRenderer.js | 264 +- .../rendering/DataFlowSVGMap.js | 39 +- .../resources/data-flow-extension.json | 14 +- .../resources/data-flow-styles.css | 158 +- .../rules/DataFlowRulesProvider.js | 404 +- .../rules/DataReplaceConnectionBehaviour.js | 308 +- .../TransformationTaskConfigurations.js | 22 +- .../TransformationTaskConfigurationsTab.js | 63 +- .../transformation/TransformationManager.js | 1062 +++-- .../ui/UpdateTransformationConfigurations.js | 26 +- .../planqk/PlanQKPaletteProvider.js | 122 +- .../extensions/planqk/PlanQKPlugin.js | 34 +- .../extensions/planqk/PlanQKRenderer.js | 127 +- .../planqk/PlanQKReplaceMenuProvider.js | 492 +- .../extensions/planqk/PlanQKReplaceOptions.js | 32 +- .../extensions/planqk/SVGMap.js | 30 +- .../PlanQKServiceTaskCompletion.js | 480 +- .../extensions/planqk/index.js | 51 +- .../DataPoolProperties.js | 199 +- .../DataPoolPropertiesProvider.js | 65 +- .../InputOutputProperties.js | 198 +- .../ServiceTaskPropertiesProvider.js | 82 +- .../SubscriptionProperties.js | 165 +- .../planqk/resources/css/planqk-icons.css | 72 +- .../resources/planqk-service-task-ext.json | 8 +- .../extensions/planqk/utilities/Constants.js | 10 +- .../extensions/qhana/QHAnaConstants.js | 19 +- .../extensions/qhana/QHAnaPlugin.js | 68 +- .../qhana/config/QHAnaConfigManager.js | 48 +- .../configurations/QHAnaConfigurations.js | 385 +- .../configurations/QHAnaConfigurationsTab.js | 84 +- .../extensions/qhana/index.js | 20 +- .../qhana/menu/QHAnaReplaceMenuProvider.js | 428 +- .../qhana/menu/QHAnaReplaceOptions.js | 20 +- .../properties/QHAnaPropertiesProvider.js | 126 +- .../properties/QHAnaServiceStepProperties.js | 69 +- .../qhana/rendering/QHAnaRenderer.js | 107 +- .../extensions/qhana/rendering/QHAnaSVGMap.js | 35 +- .../qhana/resources/qhana-extension.json | 6 +- .../qhana/resources/qhana-icons.css | 56 +- .../QHAnaTransformationHandler.js | 328 +- .../ui/UpdateQHAnaConfigurationsButton.js | 25 +- .../extensions/quantme/Constants.js | 155 +- .../extensions/quantme/QuantMEPlugin.js | 113 +- .../quantme/configTabs/BPMNConfigTab.js | 202 +- .../quantme/configTabs/HybridRuntimeTab.js | 216 +- .../quantme/configTabs/NisqAnalyzerTab.js | 92 +- .../quantme/configTabs/OpenToscaTab.js | 131 +- .../quantme/configTabs/QrmDataTab.js | 176 +- .../DataObjectConfigurations.js | 72 +- .../DataObjectConfigurationsTab.js | 61 +- .../UpdateDataObjectConfigurationsButton.js | 27 +- .../quantme/deployment/BindingUtils.js | 119 +- .../quantme/deployment/DeploymentUtils.js | 72 +- .../quantme/deployment/OpenTOSCAUtils.js | 589 +-- .../framework-config/config-manager.js | 286 +- .../quantme/framework-config/config.js | 31 +- .../quantme/framework-config/index.js | 2 +- .../quantme/modeling/BpmnEditorActions.js | 114 +- .../quantme/modeling/BpmnKeyboard.js | 68 +- .../quantme/modeling/BpmnKeyboardBindings.js | 149 +- .../quantme/modeling/QuantMEFactory.js | 97 +- .../quantme/modeling/QuantMEPathMap.js | 786 ++-- .../quantme/modeling/QuantMERenderer.js | 286 +- .../modeling/QuantMEReplaceMenuProvider.js | 280 +- .../quantme/modeling/QuantMEReplaceOptions.js | 146 +- .../quantme/modeling/QuantMESVGMap.js | 48 +- .../extensions/quantme/modeling/index.js | 43 +- .../QuantMEPropertiesProvider.js | 276 +- .../QuantMEPropertyEntries.js | 2515 ++++++----- .../QuantMETaskProperties.js | 803 ++-- .../service-task/Deployment.js | 166 +- .../service-task/DmnImplementationProps.js | 570 +-- .../service-task/ImplementationProps.js | 528 ++- .../service-task/ImplementationTypeProps.js | 489 +- .../quantme/qrm-manager/git-handler.js | 84 +- .../extensions/quantme/qrm-manager/index.js | 26 +- .../quantme/qrm-manager/qrm-handler.js | 126 +- .../quantme/replacement/InputOutputHandler.js | 46 +- .../replacement/QuantMEAttributeChecker.js | 95 +- .../quantme/replacement/QuantMEMatcher.js | 436 +- .../replacement/QuantMETransformator.js | 354 +- .../circuit-cutting/QuantMECuttingHandler.js | 360 +- .../HardwareSelectionScripts.js | 418 +- .../QuantMEHardwareSelectionHandler.js | 717 +-- .../quantme/replacement/layouter/Layouter.js | 610 +-- .../quantme/resources/quantum4bpmn.json | 1142 +++-- .../extensions/quantme/styling/quantme.css | 362 +- .../quantme/ui/QuantMEPluginButton.js | 48 +- .../quantme/ui/adaptation/AdaptationModal.js | 102 +- .../quantme/ui/adaptation/AdaptationPlugin.js | 435 +- .../ui/adaptation/CandidateDetector.js | 639 +-- .../quantme/ui/adaptation/RewriteModal.js | 298 +- .../quantme/ui/adaptation/WorkflowRewriter.js | 324 +- .../adaptation/runtimes/AwsRuntimeHandler.js | 390 +- .../runtimes/QiskitRuntimeHandler.js | 408 +- .../runtimes/RuntimeHandlerUtils.js | 293 +- .../quantme/ui/control/QuantMEController.js | 422 +- .../deployment/services/DeploymentPlugin.js | 612 +-- .../services/ServiceDeploymentBindingModal.js | 160 +- .../services/ServiceDeploymentInputModal.js | 251 +- .../ServiceDeploymentOverviewModal.js | 130 +- .../ImplementationTypeHelperExtension.js | 99 +- .../extensions/quantme/utilities/Utilities.js | 41 +- .../bpmn-q/modeler-component/modeler.css | 18 +- components/bpmn-q/package-lock.json | 26 +- components/bpmn-q/package.json | 21 +- components/bpmn-q/postcss.config.js | 6 +- components/bpmn-q/public/index.html | 359 +- .../bpmn-q/test/test-setup/QHAnaMockServer.js | 3952 ++++++++--------- .../QuantMEDataConfigurationsServer.js | 145 +- .../ServiceTaskConfigurationsServer.js | 540 +-- .../test/tests/dataflow/DataFlowWorkflows.js | 36 +- .../TransformationTaskConfigurations.js | 274 +- .../data-flow-configurations-endpoint.spec.js | 103 +- .../tests/dataflow/data-flow-palette.spec.js | 27 +- .../dataflow/data-flow-plugin-config.spec.js | 66 +- .../dataflow/data-flow-replace-menu.spec.js | 148 +- .../dataflow/data-flow-transformation.spec.js | 1065 +++-- .../test/tests/editor/configurations.spec.js | 113 +- .../bpmn-q/test/tests/editor/editor.spec.js | 145 +- .../bpmn-q/test/tests/editor/plugin.spec.js | 269 +- .../tests/editor/utils/modelling-util.spec.js | 266 +- .../test/tests/helpers/BPMNWorkflowHelper.js | 8 +- .../tests/helpers/ConfigurationsHelper.js | 112 +- .../test/tests/helpers/DiagramHelper.js | 574 +-- .../test/tests/helpers/ModelerHelper.js | 150 +- .../test/tests/helpers/PropertiesHelper.js | 65 +- .../planqk/planqk-transformation.spec.js | 168 +- .../tests/qhana/QHAnaServiceDataHelper.js | 394 +- .../tests/qhana/qhana-plugin-config.spec.js | 42 +- .../tests/qhana/qhana-service-configs.spec.js | 122 +- .../tests/quantme/data-object-configs.spec.js | 184 +- .../test/tests/quantme/quantme-config.spec.js | 110 +- .../quantme/quantme-transformation.spec.js | 126 +- components/bpmn-q/webpack.config.js | 163 +- 221 files changed, 24554 insertions(+), 21420 deletions(-) delete mode 100644 components/bpmn-q/.eslintrc.js create mode 100644 components/bpmn-q/.eslintrc.json create mode 100644 components/bpmn-q/.prettierignore create mode 100644 components/bpmn-q/.prettierrc.json diff --git a/components/bpmn-q/.eslintrc.js b/components/bpmn-q/.eslintrc.js deleted file mode 100644 index ee100eb0..00000000 --- a/components/bpmn-q/.eslintrc.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2021": true, - "commonjs": true, - "node": true, - "mocha": true - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended" - ], - "overrides": [ - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module", - }, - "plugins": [ - "react" - ], - "rules": { - "semi": [2, "always"], - "react/prop-types": "off", - "no-prototype-builtins": "off", - "react/jsx-key": "off", - "no-unused-vars": "off", - "no-useless-escape": "off", - } -}; diff --git a/components/bpmn-q/.eslintrc.json b/components/bpmn-q/.eslintrc.json new file mode 100644 index 00000000..355de0a2 --- /dev/null +++ b/components/bpmn-q/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "env": { + "browser": true, + "es2021": true, + "commonjs": true, + "node": true, + "mocha": true + }, + "extends": ["eslint:recommended", "plugin:react/recommended"], + "overrides": [], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["react"], + "rules": { + "semi": [2, "always"], + "react/prop-types": "off", + "no-prototype-builtins": "off", + "react/jsx-key": "off", + "no-unused-vars": "off", + "no-useless-escape": "off" + } +} diff --git a/components/bpmn-q/.prettierignore b/components/bpmn-q/.prettierignore new file mode 100644 index 0000000000000000000000000000000000000000..29cab7f4da15a55beb4e97ed6febb9cedaaaed7e GIT binary patch literal 62 zcmezWPnki1!IL4KA&()Sp@< is(node, 'bpmn:EndEvent')) - ); + return flowElements.some((node) => is(node, "bpmn:EndEvent")); } function hasConnectedEndEvent(node) { const flowElements = node.flowElements || []; - return ( - flowElements.some(node => { const incomingflow = node.incoming || []; return is(node, 'bpmn:EndEvent') && incomingflow.length === 0; }) - ); - + return flowElements.some((node) => { + const incomingflow = node.incoming || []; + return is(node, "bpmn:EndEvent") && incomingflow.length === 0; + }); } function check(node, reporter) { console.log(node); - if (!isAny(node, ['bpmn:SubProcess'])) { + if (!isAny(node, ["bpmn:SubProcess"])) { return; } if (!hasEndEvent(node)) { - - reporter.report(node.id, 'Subprocess is missing end event'); + reporter.report(node.id, "Subprocess is missing end event"); } if (hasConnectedEndEvent(node)) { - reporter.report(node.id, 'Each end event must have at least one incoming flow'); + reporter.report( + node.id, + "Each end event must have at least one incoming flow" + ); } } return { check }; -}; \ No newline at end of file +}; diff --git a/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-required-start-event.js b/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-required-start-event.js index bbb06d17..5d83043a 100644 --- a/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-required-start-event.js +++ b/components/bpmn-q/bpmnlint-plugin-custom/rules/subprocess-required-start-event.js @@ -1,38 +1,33 @@ -const { - is -} = require('bpmnlint-utils'); - +const { is } = require("bpmnlint-utils"); /** * A rule that checks that start events inside a normal sub-processes * are blank (do not have an event definition). */ module.exports = function () { - function check(node, reporter) { - - if (!is(node, 'bpmn:SubProcess') || node.triggeredByEvent) { + if (!is(node, "bpmn:SubProcess") || node.triggeredByEvent) { return; } const flowElements = node.flowElements || []; flowElements.forEach(function (flowElement) { - - if (!is(flowElement, 'bpmn:StartEvent')) { + if (!is(flowElement, "bpmn:StartEvent")) { return false; } const eventDefinitions = flowElement.eventDefinitions || []; if (eventDefinitions.length > 0) { - reporter.report(flowElement.id, 'Start event must be blank', ['eventDefinitions']); + reporter.report(flowElement.id, "Start event must be blank", [ + "eventDefinitions", + ]); } }); } return { - check + check, }; - -}; \ No newline at end of file +}; diff --git a/components/bpmn-q/client/index.js b/components/bpmn-q/client/index.js index 01847a26..69dd72b7 100644 --- a/components/bpmn-q/client/index.js +++ b/components/bpmn-q/client/index.js @@ -9,14 +9,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { registerBpmnJSPlugin } from 'camunda-modeler-plugin-helpers'; +import { registerBpmnJSPlugin } from "camunda-modeler-plugin-helpers"; -import customLinterConfig from '../.bpmnlintrc'; +import customLinterConfig from "../.bpmnlintrc"; registerBpmnJSPlugin({ __init__: [ - function(linting) { + function (linting) { linting.setLinterConfig(customLinterConfig); - } - ] + }, + ], }); diff --git a/components/bpmn-q/karma.conf.js b/components/bpmn-q/karma.conf.js index eb1c219d..d4e053ee 100644 --- a/components/bpmn-q/karma.conf.js +++ b/components/bpmn-q/karma.conf.js @@ -1,63 +1,62 @@ // Karma configuration -const webpackConfig = require('./webpack.config.js'); +const webpackConfig = require("./webpack.config.js"); module.exports = function (config) { - config.set({ - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - // frameworks to use - // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter - frameworks: ['mocha', 'webpack'], - - // list of files / patterns to load in the browser - files: [ - 'test/tests/editor/configurations.spec.js', - 'test/tests/editor/editor.spec.js', - 'test/tests/editor/plugin.spec.js', - 'test/tests/planqk/planqk-transformation.spec.js', - 'test/tests/quantme/quantme-transformation.spec.js', - 'test/tests/editor/utils/modelling-util.spec.js', - 'test/tests/qhana/qhana-plugin-config.spec.js', - 'test/tests/qhana/qhana-service-configs.spec.js', - 'test/tests/quantme/data-object-configs.spec.js', - 'test/tests/quantme/quantme-config.spec.js', - 'test/tests/dataflow/data-flow-transformation.spec.js', - 'test/tests/dataflow/data-flow-plugin-config.spec.js', - 'test/tests/dataflow/data-flow-configurations-endpoint.spec.js', - 'test/tests/dataflow/data-flow-palette.spec.js', - 'test/tests/dataflow/data-flow-replace-menu.spec.js', - ], - - // list of files / patterns to exclude - exclude: [], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor - preprocessors: { - 'test/**/*.spec.js': ['webpack'] - }, - - webpack: webpackConfig, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - // start these browsers - // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher - browsers: ['ChromeHeadless'], - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true, - - // Concurrency level - // how many browser instances should be started simultaneously - concurrency: 1, - - mochaReporter: { - output: "minimal" - }, - }); -}; \ No newline at end of file + config.set({ + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: "", + + // frameworks to use + // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter + frameworks: ["mocha", "webpack"], + + // list of files / patterns to load in the browser + files: [ + "test/tests/editor/configurations.spec.js", + "test/tests/editor/editor.spec.js", + "test/tests/editor/plugin.spec.js", + "test/tests/planqk/planqk-transformation.spec.js", + "test/tests/quantme/quantme-transformation.spec.js", + "test/tests/editor/utils/modelling-util.spec.js", + "test/tests/qhana/qhana-plugin-config.spec.js", + "test/tests/qhana/qhana-service-configs.spec.js", + "test/tests/quantme/data-object-configs.spec.js", + "test/tests/quantme/quantme-config.spec.js", + "test/tests/dataflow/data-flow-transformation.spec.js", + "test/tests/dataflow/data-flow-plugin-config.spec.js", + "test/tests/dataflow/data-flow-configurations-endpoint.spec.js", + "test/tests/dataflow/data-flow-palette.spec.js", + "test/tests/dataflow/data-flow-replace-menu.spec.js", + ], + + // list of files / patterns to exclude + exclude: [], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor + preprocessors: { + "test/**/*.spec.js": ["webpack"], + }, + + webpack: webpackConfig, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // start these browsers + // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher + browsers: ["ChromeHeadless"], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: true, + + // Concurrency level + // how many browser instances should be started simultaneously + concurrency: 1, + + mochaReporter: { + output: "minimal", + }, + }); +}; diff --git a/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js b/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js index daec9ec6..bc39d896 100644 --- a/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js +++ b/components/bpmn-q/modeler-component/QuantumWorkflowModeler.js @@ -1,27 +1,33 @@ -import 'bpmn-js/dist/assets/diagram-js.css'; -import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'; -import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'; -import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'; -import 'bpmn-js-properties-panel/dist/assets/element-templates.css'; -import 'bpmn-js-properties-panel/dist/assets/properties-panel.css'; -import './editor/resources/styling/modeler.css'; -import './editor/resources/styling/editor-ui.css'; -import './editor/ui/notifications/Notifications.css'; -import './editor/ui/notifications/Notification.css'; -import './editor/resources/styling/camunda-styles/style.css'; -import 'bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css'; -import './modeler.css'; - -import React from 'react'; -import { createRoot } from 'react-dom/client'; +import "bpmn-js/dist/assets/diagram-js.css"; +import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css"; +import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css"; +import "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css"; +import "bpmn-js-properties-panel/dist/assets/element-templates.css"; +import "bpmn-js-properties-panel/dist/assets/properties-panel.css"; +import "./editor/resources/styling/modeler.css"; +import "./editor/resources/styling/editor-ui.css"; +import "./editor/ui/notifications/Notifications.css"; +import "./editor/ui/notifications/Notification.css"; +import "./editor/resources/styling/camunda-styles/style.css"; +import "bpmn-js-bpmnlint/dist/assets/css/bpmn-js-bpmnlint.css"; +import "./modeler.css"; + +import React from "react"; +import { createRoot } from "react-dom/client"; import ButtonToolbar from "./editor/ui/ButtonToolbar"; import { createNewDiagram, loadDiagram } from "./editor/util/IoUtilities"; import NotificationHandler from "./editor/ui/notifications/NotificationHandler"; import { createModeler, getModeler } from "./editor/ModelerHandler"; -import { getPluginButtons, getTransformationButtons } from "./editor/plugin/PluginHandler"; -import { getPluginConfig, setPluginConfig } from "./editor/plugin/PluginConfigHandler"; -import * as editorConfig from './editor/config/EditorConfigManager'; -import { initEditorEventHandler } from './editor/events/EditorEventHandler'; +import { + getPluginButtons, + getTransformationButtons, +} from "./editor/plugin/PluginHandler"; +import { + getPluginConfig, + setPluginConfig, +} from "./editor/plugin/PluginConfigHandler"; +import * as editorConfig from "./editor/config/EditorConfigManager"; +import { initEditorEventHandler } from "./editor/events/EditorEventHandler"; /** * The Quantum Workflow modeler HTML web component which contains the bpmn-js modeler to model BPMN diagrams, an editor @@ -29,54 +35,53 @@ import { initEditorEventHandler } from './editor/events/EditorEventHandler'; * the modelling of quantum workflows. */ export class QuantumWorkflowModeler extends HTMLElement { - - workflowModel; - constructor() { - super(); - } - - connectedCallback() { - - // create the HTML structure of the component - this.setInnerHtml(); - - // add listener for post messages containing a workflow to load into the modeler - const self = this; - window.addEventListener("message", function (event) { - - // check if the message contains a correctly formatted workflow - if (event.origin === window.location.href.replace(/\/$/, '') - && event.data && event.data.workflow && typeof event.data.workflow === 'string' && event.data.workflow.startsWith('')) { - - const xmlString = event.data.workflow; - self.workflowModel = xmlString; - - // open sent workflow and save its file name - editorConfig.setFileName(event.data.name); - loadDiagram(xmlString, getModeler()).then(); - } - }); - - // wait until shadow dom is loaded - requestAnimationFrame(() => { - - // start the bpmn-js modeler and render the React components - this.startModeler(); - }); - - const beforeUnloadListener = (event) => { - event.preventDefault(); - return event.returnValue = ''; - }; - addEventListener("beforeunload", beforeUnloadListener, { capture: true }); - } - - - /** - * Set up the inner structure of the component - */ - setInnerHtml() { - this.innerHTML = ` + workflowModel; + constructor() { + super(); + } + + connectedCallback() { + // create the HTML structure of the component + this.setInnerHtml(); + + // add listener for post messages containing a workflow to load into the modeler + const self = this; + window.addEventListener("message", function (event) { + // check if the message contains a correctly formatted workflow + if ( + event.origin === window.location.href.replace(/\/$/, "") && + event.data && + event.data.workflow && + typeof event.data.workflow === "string" && + event.data.workflow.startsWith('') + ) { + const xmlString = event.data.workflow; + self.workflowModel = xmlString; + + // open sent workflow and save its file name + editorConfig.setFileName(event.data.name); + loadDiagram(xmlString, getModeler()).then(); + } + }); + + // wait until shadow dom is loaded + requestAnimationFrame(() => { + // start the bpmn-js modeler and render the React components + this.startModeler(); + }); + + const beforeUnloadListener = (event) => { + event.preventDefault(); + return (event.returnValue = ""); + }; + addEventListener("beforeunload", beforeUnloadListener, { capture: true }); + } + + /** + * Set up the inner structure of the component + */ + setInnerHtml() { + this.innerHTML = `

@@ -87,207 +92,218 @@ export class QuantumWorkflowModeler extends HTMLElement {
`; - let panel = document.getElementById("properties"); - let maindiv = document.getElementById("main-div"); - - let isResizing = false; - let startX; - let startWidth; - let width = panel.style.width; - var propertiesElement = document.getElementById("properties"); - - propertiesElement.addEventListener("mousemove", function (e) { - var rect = this.getBoundingClientRect(); - var x = e.clientX - rect.left; - var y = e.clientY - rect.top; - - var borderSize = 5; - - if ( - x < borderSize || - x > rect.width - borderSize || - y < borderSize || - y > rect.height - borderSize - ) { - this.style.cursor = "w-resize"; - } else { - this.style.cursor = "default"; - - } - }); - - - // Mouse down event listener - panel.addEventListener('mousedown', handleMouseDown); - - panel.addEventListener("mouseup", function () { - this.style.cursor = "default"; - }); - - // Mouse move event listener - document.addEventListener('mousemove', handleMouseMove); - - // Mouse up event listener - document.addEventListener('mouseup', handleMouseUp); - - // Mouse down handler - function handleMouseDown(event) { - var rect = panel.getBoundingClientRect(); - var x = event.clientX - rect.left; - - var borderSize = 5; - - if ( - x < borderSize || - x > rect.width - borderSize - ) { - - isResizing = true; - } - startX = event.clientX; - startWidth = parseFloat(panel.style.width); - } - let isCollapsed = false; - const resizeButton = document.createElement('button'); - resizeButton.className = "fa fa-angle-right resize"; - maindiv.appendChild(resizeButton); - - // Mouse move handler - function handleMouseMove(event) { - if (!isResizing) { maindiv.style.cursor = "default"; return; } - maindiv.style.cursor = "w-resize"; - panel.style.cursor = "w-resize"; - const deltaX = event.clientX - startX; - let newWidth = startWidth - deltaX; - - // enable to completely hide the panel - if (newWidth < 20) { - newWidth = 0; - isCollapsed = true; - resizeButton.className = "fa fa-angle-left resize"; - } - panel.style.width = `${newWidth}px`; - } - - // Mouse up handler - function handleMouseUp() { - panel.style.cursor = "default"; - isResizing = false; - } - - - resizeButton.addEventListener('click', function () { - let offsetWidth = panel.offsetWidth; - if (isCollapsed) { - panel.style.display = 'block'; - panel.style.width = offsetWidth; - if (panel.offsetWidth < parseInt(width, 10)) { - panel.style.width = width; - } - resizeButton.className = "fa fa-angle-right resize"; - } else { - panel.style.display = 'none'; - resizeButton.className = "fa fa-angle-left resize"; - } - - isCollapsed = !isCollapsed; - }); + let panel = document.getElementById("properties"); + let maindiv = document.getElementById("main-div"); + + let isResizing = false; + let startX; + let startWidth; + let width = panel.style.width; + var propertiesElement = document.getElementById("properties"); + + propertiesElement.addEventListener("mousemove", function (e) { + var rect = this.getBoundingClientRect(); + var x = e.clientX - rect.left; + var y = e.clientY - rect.top; + + var borderSize = 5; + + if ( + x < borderSize || + x > rect.width - borderSize || + y < borderSize || + y > rect.height - borderSize + ) { + this.style.cursor = "w-resize"; + } else { + this.style.cursor = "default"; + } + }); + + // Mouse down event listener + panel.addEventListener("mousedown", handleMouseDown); + + panel.addEventListener("mouseup", function () { + this.style.cursor = "default"; + }); + + // Mouse move event listener + document.addEventListener("mousemove", handleMouseMove); + + // Mouse up event listener + document.addEventListener("mouseup", handleMouseUp); + + // Mouse down handler + function handleMouseDown(event) { + var rect = panel.getBoundingClientRect(); + var x = event.clientX - rect.left; + + var borderSize = 5; + + if (x < borderSize || x > rect.width - borderSize) { + isResizing = true; + } + startX = event.clientX; + startWidth = parseFloat(panel.style.width); } - - /** - * Initializes the modeler component by creating the bpmn-js modeler instance and rendering the React components of - * the editor into the DOM. - */ - startModeler() { - console.log('Start Modeler'); - - // initialize event handler for workflow events with the instance of the component to dispatch the events correctly - initEditorEventHandler(this); - - // get and reset the container in which the bpmn-js modeler and its properties panel should be rendered - const bpmnContainer = document.getElementById('canvas'); - const propertiesPanelContainer = document.getElementById('properties'); - bpmnContainer.innerHTML = ''; - propertiesPanelContainer.innerHTML = ''; - - // create a new bpmn-js modeler instance with all additional modules and extensions defined by the plugins - const modeler = createModeler(bpmnContainer, propertiesPanelContainer); - console.log('Created Modeler'); - - // set up the notification handler and render it into the DOM - const notificationsContainer = document.getElementById('qwm-notification-container'); - const handler = NotificationHandler.getInstance(); - const notificationComponent = handler.createNotificationsComponent([], notificationsContainer); - - const notificationRoot = createRoot(notificationsContainer); - notificationRoot.render(
{notificationComponent}
); - console.log('Rendered Notifications React Component'); - - // create a transformation button for each transformation method of an active plugin - const transformationButtons = getTransformationButtons(); - - // integrate the React ButtonToolbar into its DOM container - const root = createRoot(document.getElementById('button-container')); - root.render(); - - // load initial workflow - this.workflowModel = this.workflowModel || getPluginConfig('editor').defaultWorkflow; - if (this.workflowModel) { - loadDiagram(this.workflowModel, getModeler()).then(); - } else { - createNewDiagram(modeler); - } + let isCollapsed = false; + const resizeButton = document.createElement("button"); + resizeButton.className = "fa fa-angle-right resize"; + maindiv.appendChild(resizeButton); + + // Mouse move handler + function handleMouseMove(event) { + if (!isResizing) { + maindiv.style.cursor = "default"; + return; + } + maindiv.style.cursor = "w-resize"; + panel.style.cursor = "w-resize"; + const deltaX = event.clientX - startX; + let newWidth = startWidth - deltaX; + + // enable to completely hide the panel + if (newWidth < 20) { + newWidth = 0; + isCollapsed = true; + resizeButton.className = "fa fa-angle-left resize"; + } + panel.style.width = `${newWidth}px`; } - /** - * Load the given xml string as a workflow into the modeler. - * - * @param xmlDiagram The workflow to load as xml string - * @return {Promise<*|undefined>} - */ - async loadWorkflowDiagram(xmlDiagram) { - const modeler = getModeler(); - - if (modeler) { - return await loadDiagram(xmlDiagram, getModeler()); - } else { - console.log('Loading of Workflow via external interface not possible until modeler is loaded.'); - } - + // Mouse up handler + function handleMouseUp() { + panel.style.cursor = "default"; + isResizing = false; } - /** - * Getter for the plugin config of the Quantum Workflow Modeler - * - * @return {*[]} The plugin config as an array of {name: string, (optional) config: {}} - */ - get pluginConfigs() { - return this.pluginConfigsList || []; + resizeButton.addEventListener("click", function () { + let offsetWidth = panel.offsetWidth; + if (isCollapsed) { + panel.style.display = "block"; + panel.style.width = offsetWidth; + if (panel.offsetWidth < parseInt(width, 10)) { + panel.style.width = width; + } + resizeButton.className = "fa fa-angle-right resize"; + } else { + panel.style.display = "none"; + resizeButton.className = "fa fa-angle-left resize"; + } + + isCollapsed = !isCollapsed; + }); + } + + /** + * Initializes the modeler component by creating the bpmn-js modeler instance and rendering the React components of + * the editor into the DOM. + */ + startModeler() { + console.log("Start Modeler"); + + // initialize event handler for workflow events with the instance of the component to dispatch the events correctly + initEditorEventHandler(this); + + // get and reset the container in which the bpmn-js modeler and its properties panel should be rendered + const bpmnContainer = document.getElementById("canvas"); + const propertiesPanelContainer = document.getElementById("properties"); + bpmnContainer.innerHTML = ""; + propertiesPanelContainer.innerHTML = ""; + + // create a new bpmn-js modeler instance with all additional modules and extensions defined by the plugins + const modeler = createModeler(bpmnContainer, propertiesPanelContainer); + console.log("Created Modeler"); + + // set up the notification handler and render it into the DOM + const notificationsContainer = document.getElementById( + "qwm-notification-container" + ); + const handler = NotificationHandler.getInstance(); + const notificationComponent = handler.createNotificationsComponent( + [], + notificationsContainer + ); + + const notificationRoot = createRoot(notificationsContainer); + notificationRoot.render(
{notificationComponent}
); + console.log("Rendered Notifications React Component"); + + // create a transformation button for each transformation method of an active plugin + const transformationButtons = getTransformationButtons(); + + // integrate the React ButtonToolbar into its DOM container + const root = createRoot(document.getElementById("button-container")); + root.render( + + ); + + // load initial workflow + this.workflowModel = + this.workflowModel || getPluginConfig("editor").defaultWorkflow; + if (this.workflowModel) { + loadDiagram(this.workflowModel, getModeler()).then(); + } else { + createNewDiagram(modeler); } - - /** - * Setter for the plugin config of the Quantum Workflow Modeler - * - * @param pluginConfigs The plugin config as an array of {name: string, (optional) config: {}} - */ - set pluginConfigs(pluginConfigs) { - console.log(pluginConfigs); - this.pluginConfigsList = pluginConfigs; - const configs = this.pluginConfigsList; - console.log(configs); - - // add plugin config to the PluginConfigHandler - setPluginConfig(configs); - - // rerender shadow dom to add plugin elements - this.setInnerHtml(); - - // restart modeler to apply plugin config when shadow dom is rendered - requestAnimationFrame(() => { - this.startModeler(); - }); + } + + /** + * Load the given xml string as a workflow into the modeler. + * + * @param xmlDiagram The workflow to load as xml string + * @return {Promise<*|undefined>} + */ + async loadWorkflowDiagram(xmlDiagram) { + const modeler = getModeler(); + + if (modeler) { + return await loadDiagram(xmlDiagram, getModeler()); + } else { + console.log( + "Loading of Workflow via external interface not possible until modeler is loaded." + ); } + } + + /** + * Getter for the plugin config of the Quantum Workflow Modeler + * + * @return {*[]} The plugin config as an array of {name: string, (optional) config: {}} + */ + get pluginConfigs() { + return this.pluginConfigsList || []; + } + + /** + * Setter for the plugin config of the Quantum Workflow Modeler + * + * @param pluginConfigs The plugin config as an array of {name: string, (optional) config: {}} + */ + set pluginConfigs(pluginConfigs) { + console.log(pluginConfigs); + this.pluginConfigsList = pluginConfigs; + const configs = this.pluginConfigsList; + console.log(configs); + + // add plugin config to the PluginConfigHandler + setPluginConfig(configs); + + // rerender shadow dom to add plugin elements + this.setInnerHtml(); + + // restart modeler to apply plugin config when shadow dom is rendered + requestAnimationFrame(() => { + this.startModeler(); + }); + } } -window.customElements.define('quantum-workflow-modeler', QuantumWorkflowModeler); +window.customElements.define( + "quantum-workflow-modeler", + QuantumWorkflowModeler +); diff --git a/components/bpmn-q/modeler-component/editor/EditorConstants.js b/components/bpmn-q/modeler-component/editor/EditorConstants.js index 85155ba1..89b406e9 100644 --- a/components/bpmn-q/modeler-component/editor/EditorConstants.js +++ b/components/bpmn-q/modeler-component/editor/EditorConstants.js @@ -1,14 +1,13 @@ - // supported options to handle a transformed workflow export const transformedWorkflowHandlers = { - NEW_TAB: 'Open in new Tab', - SAVE_AS_FILE: 'Save as File', + NEW_TAB: "Open in new Tab", + SAVE_AS_FILE: "Save as File", }; // workflow event types dispatched by the EditorEventHandler export const workflowEventTypes = { - LOADED: 'quantum-workflow-loaded', // New Workflow loaded in modeler - SAVED: 'quantum-workflow-saved', // Workflow saved - TRANSFORMED: 'quantum-workflow-transformed', // Workflow transformed - DEPLOYED: 'quantum-workflow-deployed', // Workflow deployed to workflow engine -}; \ No newline at end of file + LOADED: "quantum-workflow-loaded", // New Workflow loaded in modeler + SAVED: "quantum-workflow-saved", // Workflow saved + TRANSFORMED: "quantum-workflow-transformed", // Workflow transformed + DEPLOYED: "quantum-workflow-deployed", // Workflow deployed to workflow engine +}; diff --git a/components/bpmn-q/modeler-component/editor/ModelerHandler.js b/components/bpmn-q/modeler-component/editor/ModelerHandler.js index c9879e7c..f89922f8 100644 --- a/components/bpmn-q/modeler-component/editor/ModelerHandler.js +++ b/components/bpmn-q/modeler-component/editor/ModelerHandler.js @@ -1,18 +1,21 @@ import BpmnModeler from "bpmn-js/lib/Modeler"; import BpmnPalletteModule from "bpmn-js/lib/features/palette"; import { - BpmnPropertiesPanelModule, - BpmnPropertiesProviderModule, - CamundaPlatformPropertiesProviderModule + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, + CamundaPlatformPropertiesProviderModule, } from "bpmn-js-properties-panel"; -import CamundaExtensionModule from 'camunda-bpmn-moddle/resources/camunda.json'; +import CamundaExtensionModule from "camunda-bpmn-moddle/resources/camunda.json"; import CustomPopupMenuModule from "./popup/"; -import {getAdditionalModules, getModdleExtension} from "./plugin/PluginHandler"; -import LintModule from 'bpmn-js-bpmnlint'; -import bpmnlintConfig from '../../.bpmnlintrc'; +import { + getAdditionalModules, + getModdleExtension, +} from "./plugin/PluginHandler"; +import LintModule from "bpmn-js-bpmnlint"; +import bpmnlintConfig from "../../.bpmnlintrc"; -import Clipboard from 'diagram-js/lib/features/clipboard/Clipboard'; -let camundaModdleDescriptor = require('camunda-bpmn-moddle/resources/camunda.json'); +import Clipboard from "diagram-js/lib/features/clipboard/Clipboard"; +let camundaModdleDescriptor = require("camunda-bpmn-moddle/resources/camunda.json"); /** * Handler which manages the creation of bpmn-js modeler instances. It controls the access to the modeler instance currently @@ -30,22 +33,21 @@ let modeler = undefined; * @returns {Modeler} The created bpmn-js modeler instance */ export function createModeler(containerId, propertiesParentId) { - - modeler = new BpmnModeler({ - container: containerId, - propertiesPanel: { - parent: propertiesParentId - }, - additionalModules: getModules(), - keyboard: { - bindTo: document - }, - linting: { - bpmnlint: bpmnlintConfig - }, - moddleExtensions: getExtensions(), - }); - return modeler; + modeler = new BpmnModeler({ + container: containerId, + propertiesPanel: { + parent: propertiesParentId, + }, + additionalModules: getModules(), + keyboard: { + bindTo: document, + }, + linting: { + bpmnlint: bpmnlintConfig, + }, + moddleExtensions: getExtensions(), + }); + return modeler; } /** @@ -54,17 +56,15 @@ export function createModeler(containerId, propertiesParentId) { * @return the created modeler */ export function createPlainModeler() { - return new BpmnModeler({ - additionalModules: [ - CamundaExtensionModule, - ], - keyboard: { - bindTo: document - }, - moddleExtensions: { - camunda: camundaModdleDescriptor, - }, - }); + return new BpmnModeler({ + additionalModules: [CamundaExtensionModule], + keyboard: { + bindTo: document, + }, + moddleExtensions: { + camunda: camundaModdleDescriptor, + }, + }); } /** @@ -74,13 +74,13 @@ export function createPlainModeler() { * @returns the created modeler */ export function createTempModeler() { - return new BpmnModeler({ - additionalModules: getModules(), - keyboard: { - bindTo: document - }, - moddleExtensions: getExtensions(), - }); + return new BpmnModeler({ + additionalModules: getModules(), + keyboard: { + bindTo: document, + }, + moddleExtensions: getExtensions(), + }); } /** @@ -89,9 +89,9 @@ export function createTempModeler() { * @returns the created bpmn-js modeler */ export function createLightweightModeler() { - return new BpmnModeler({ - moddleExtensions: getExtensions(), - }); + return new BpmnModeler({ + moddleExtensions: getExtensions(), + }); } /** @@ -103,61 +103,64 @@ export function createLightweightModeler() { * @returns the created modeler */ export async function createTempModelerFromXml(xml) { - // create new modeler with the custom QuantME extensions - const bpmnModeler = createTempModeler(); - - // import the xml containing the definitions - try { - await bpmnModeler.importXML(xml); - return bpmnModeler; - } catch (err) { - console.error(err); - } - return undefined; + // create new modeler with the custom QuantME extensions + const bpmnModeler = createTempModeler(); + + // import the xml containing the definitions + try { + await bpmnModeler.importXML(xml); + return bpmnModeler; + } catch (err) { + console.error(err); + } + return undefined; } /** * Returns the current modeler instance rendered into the UI of the Quantum Workflow Modeler */ export function getModeler() { - return modeler; + return modeler; } /** * Returns all additional modules for the bpmn-js modeler necessary to use all modelling extensions of the active plugins. */ function getModules() { - const pluginModules = getAdditionalModules(); - var clipboardModule = { - 'clipboard': [ 'value', new Clipboard() ] - }; - let additionalModules = [ - BpmnPalletteModule, - BpmnPropertiesPanelModule, - BpmnPropertiesProviderModule, - CamundaPlatformPropertiesProviderModule, - CamundaExtensionModule, - CustomPopupMenuModule, - LintModule, - clipboardModule - ].concat(pluginModules); - - console.log('\n Additional modules of the modeler: '); - console.log(additionalModules); - - return additionalModules; + const pluginModules = getAdditionalModules(); + var clipboardModule = { + clipboard: ["value", new Clipboard()], + }; + let additionalModules = [ + BpmnPalletteModule, + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, + CamundaPlatformPropertiesProviderModule, + CamundaExtensionModule, + CustomPopupMenuModule, + LintModule, + clipboardModule, + ].concat(pluginModules); + + console.log("\n Additional modules of the modeler: "); + console.log(additionalModules); + + return additionalModules; } /** * Returns all moddle extensions for the bpmn-js modeler necessary to use all modelling extensions of the active plugins. */ function getExtensions() { - let moddleExtension = Object.assign({ - camunda: camundaModdleDescriptor, - }, getModdleExtension()); + let moddleExtension = Object.assign( + { + camunda: camundaModdleDescriptor, + }, + getModdleExtension() + ); - console.log('\n Moddle extensions of the modeler: '); - console.log(moddleExtension); + console.log("\n Moddle extensions of the modeler: "); + console.log(moddleExtension); - return moddleExtension; + return moddleExtension; } diff --git a/components/bpmn-q/modeler-component/editor/config/ConfigModal.js b/components/bpmn-q/modeler-component/editor/config/ConfigModal.js index 4de98bed..9ad4d758 100644 --- a/components/bpmn-q/modeler-component/editor/config/ConfigModal.js +++ b/components/bpmn-q/modeler-component/editor/config/ConfigModal.js @@ -10,14 +10,14 @@ */ /* eslint-disable no-unused-vars */ -import React from 'react'; -import Modal from '../ui/modal/Modal'; -import './config-modal.css'; +import React from "react"; +import Modal from "../ui/modal/Modal"; +import "./config-modal.css"; // polyfill upcoming structural components -const Title = Modal.Title || (({children}) =>

{children}

); -const Body = Modal.Body || (({children}) =>
{children}
); -const Footer = Modal.Footer || (({children}) =>
{children}
); +const Title = Modal.Title || (({ children }) =>

{children}

); +const Body = Modal.Body || (({ children }) =>
{children}
); +const Footer = Modal.Footer || (({ children }) =>
{children}
); /** * Configuration modal of the editor which displays a set of given configTabs. used to display customized tabs of the @@ -28,64 +28,88 @@ const Footer = Modal.Footer || (({children}) =>
{children}
); * @returns {JSX.Element} The modal as React component * @constructor */ -export default function ConfigModal({onClose, configTabs}) { +export default function ConfigModal({ onClose, configTabs }) { + // return the new values to the config plugin + const onSubmit = () => { + // call close callback + onClose(); - // return the new values to the config plugin - const onSubmit = () => { - - // call close callback - onClose(); - - for (let tab of configTabs) { - - // call close callback for each tab to allow custom cleanups - tab.configTab.prototype.onClose(); - } - }; + for (let tab of configTabs) { + // call close callback for each tab to allow custom cleanups + tab.configTab.prototype.onClose(); + } + }; - // refs to enable changing the state through the plugin - let elementsRootRef = React.createRef(); + // refs to enable changing the state through the plugin + let elementsRootRef = React.createRef(); - // method to enable button functionality by hiding and displaying different div elements - function openTab(tabName, id) { - console.log(id); - const elements = elementsRootRef.current.children; + // method to enable button functionality by hiding and displaying different div elements + function openTab(tabName, id) { + console.log(id); + const elements = elementsRootRef.current.children; - for (let i = 0; i < elements.length; i++) { - elements[i].hidden = true; - } - elements[id].hidden = false; + for (let i = 0; i < elements.length; i++) { + elements[i].hidden = true; } + elements[id].hidden = false; + } - return - - Modeler Configuration - + return ( + + Modeler Configuration - -
-
-
- {React.Children.toArray(configTabs.map((tab, index) => ))} -
- -
- {React.Children.toArray(configTabs.map((tab, index) => ))} -
-
-
- + +
+
+
+ {React.Children.toArray( + configTabs.map((tab, index) => ( + + )) + )} +
-
-
- - +
+ {React.Children.toArray( + configTabs.map((tab, index) => ( + + )) + )}
-
- ; -} +
+
+ +
+
+ + +
+
+
+ ); +} diff --git a/components/bpmn-q/modeler-component/editor/config/ConfigPlugin.js b/components/bpmn-q/modeler-component/editor/config/ConfigPlugin.js index bde3f03a..d0be2867 100644 --- a/components/bpmn-q/modeler-component/editor/config/ConfigPlugin.js +++ b/components/bpmn-q/modeler-component/editor/config/ConfigPlugin.js @@ -9,60 +9,65 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, {PureComponent, Fragment} from 'react'; +import React, { PureComponent, Fragment } from "react"; -import ConfigModal from './ConfigModal'; -import {getModeler} from "../ModelerHandler"; -import {getConfigTabs} from "../plugin/PluginHandler"; +import ConfigModal from "./ConfigModal"; +import { getModeler } from "../ModelerHandler"; +import { getConfigTabs } from "../plugin/PluginHandler"; export default class ConfigPlugin extends PureComponent { + constructor(props) { + super(props); - constructor(props) { - super(props); + this.state = { + configOpen: false, + }; - this.state = { - configOpen: false, - }; + this.handleConfigClosed = this.handleConfigClosed.bind(this); + } - this.handleConfigClosed = this.handleConfigClosed.bind(this); - } - - componentDidMount() { - - // get current modeler instance - this.modeler = getModeler(); + componentDidMount() { + // get current modeler instance + this.modeler = getModeler(); - // set up config of the modeler - if (!this.modeler.config) { - this.modeler.config = {}; + // set up config of the modeler + if (!this.modeler.config) { + this.modeler.config = {}; - for (let tab of getConfigTabs()) { - tab.configTab.prototype.config(); - } - } + for (let tab of getConfigTabs()) { + tab.configTab.prototype.config(); + } } + } - // callback function to close the config modal - handleConfigClosed() { - this.setState({configOpen: false}); - } - - render() { + // callback function to close the config modal + handleConfigClosed() { + this.setState({ configOpen: false }); + } - // render config button and pop-up menu - return ( -
- -
- {this.state.configOpen && ( - - )} -
); - } + render() { + // render config button and pop-up menu + return ( + +
+ +
+ {this.state.configOpen && ( + + )} +
+ ); + } } diff --git a/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js b/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js index c7cd1b1f..250526a1 100644 --- a/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js +++ b/components/bpmn-q/modeler-component/editor/config/EditorConfigManager.js @@ -1,11 +1,11 @@ -import {getPluginConfig} from '../plugin/PluginConfigHandler'; -import {transformedWorkflowHandlers} from '../EditorConstants'; +import { getPluginConfig } from "../plugin/PluginConfigHandler"; +import { transformedWorkflowHandlers } from "../EditorConstants"; // default configurations of the editor const defaultConfig = { - camundaEndpoint: process.env.CAMUNDA_ENDPOINT, - fileName: 'quantum-workflow-model.bpmn', - transformedWorkflowHandler: transformedWorkflowHandlers.NEW_TAB, + camundaEndpoint: process.env.CAMUNDA_ENDPOINT, + fileName: "quantum-workflow-model.bpmn", + transformedWorkflowHandler: transformedWorkflowHandlers.NEW_TAB, }; let config = {}; @@ -16,10 +16,12 @@ let config = {}; * @return {string} the currently specified endpoint of the Camunda engine */ export function getCamundaEndpoint() { - if (config.camundaEndpoint === undefined) { - setCamundaEndpoint(getPluginConfig('editor').camundaEndpoint || defaultConfig.camundaEndpoint); - } - return config.camundaEndpoint; + if (config.camundaEndpoint === undefined) { + setCamundaEndpoint( + getPluginConfig("editor").camundaEndpoint || defaultConfig.camundaEndpoint + ); + } + return config.camundaEndpoint; } /** @@ -28,11 +30,10 @@ export function getCamundaEndpoint() { * @param camundaEndpoint the endpoint of the Camunda engine */ export function setCamundaEndpoint(camundaEndpoint) { - if (camundaEndpoint !== null && camundaEndpoint !== undefined) { - - // remove trailing slashes - config.camundaEndpoint = camundaEndpoint.replace(/\/$/, ''); - } + if (camundaEndpoint !== null && camundaEndpoint !== undefined) { + // remove trailing slashes + config.camundaEndpoint = camundaEndpoint.replace(/\/$/, ""); + } } /** @@ -41,10 +42,10 @@ export function setCamundaEndpoint(camundaEndpoint) { * @return {string} the file name */ export function getFileName() { - if (config.fileName === undefined) { - setFileName(getPluginConfig('editor').fileName || defaultConfig.fileName); - } - return config.fileName; + if (config.fileName === undefined) { + setFileName(getPluginConfig("editor").fileName || defaultConfig.fileName); + } + return config.fileName; } /** @@ -53,11 +54,14 @@ export function getFileName() { * @param fileName the new file name */ export function setFileName(fileName) { - if (fileName !== null && fileName !== undefined && /^[a-zA-Z0-9-_]+\.bpmn$/.test(fileName)) { - - // remove trailing slashes - config.fileName = fileName; - } + if ( + fileName !== null && + fileName !== undefined && + /^[a-zA-Z0-9-_]+\.bpmn$/.test(fileName) + ) { + // remove trailing slashes + config.fileName = fileName; + } } /** @@ -66,11 +70,16 @@ export function setFileName(fileName) { * @return {string} the currently specified handler id */ export function getTransformedWorkflowHandler() { - if (config.transformedWorkflowHandler === undefined) { - const workflowHandler = transformedWorkflowHandlers[getPluginConfig('editor').transformedWorkflowHandler]; - setTransformedWorkflowHandler(workflowHandler || defaultConfig.transformedWorkflowHandler); - } - return config.transformedWorkflowHandler; + if (config.transformedWorkflowHandler === undefined) { + const workflowHandler = + transformedWorkflowHandlers[ + getPluginConfig("editor").transformedWorkflowHandler + ]; + setTransformedWorkflowHandler( + workflowHandler || defaultConfig.transformedWorkflowHandler + ); + } + return config.transformedWorkflowHandler; } /** @@ -79,18 +88,22 @@ export function getTransformedWorkflowHandler() { * @param transformedWorkflowHandler the id of the transformed workflow handler */ export function setTransformedWorkflowHandler(transformedWorkflowHandler) { - if (transformedWorkflowHandler !== null && transformedWorkflowHandler !== undefined - // check that the new value is a valid handler id - && Object.values(transformedWorkflowHandlers).includes(transformedWorkflowHandler)) { - - // remove trailing slashes - config.transformedWorkflowHandler = transformedWorkflowHandler; - } + if ( + transformedWorkflowHandler !== null && + transformedWorkflowHandler !== undefined && + // check that the new value is a valid handler id + Object.values(transformedWorkflowHandlers).includes( + transformedWorkflowHandler + ) + ) { + // remove trailing slashes + config.transformedWorkflowHandler = transformedWorkflowHandler; + } } /** * Resets the current editor configs */ export function reset() { - config = {}; -} \ No newline at end of file + config = {}; +} diff --git a/components/bpmn-q/modeler-component/editor/config/EditorTab.js b/components/bpmn-q/modeler-component/editor/config/EditorTab.js index fe8c9d42..ce67c91a 100644 --- a/components/bpmn-q/modeler-component/editor/config/EditorTab.js +++ b/components/bpmn-q/modeler-component/editor/config/EditorTab.js @@ -1,7 +1,7 @@ -import React, {useState} from 'react'; -import {getModeler} from "../ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../ModelerHandler"; import * as editorConfig from "./EditorConfigManager"; -import {transformedWorkflowHandlers} from '../EditorConstants'; +import { transformedWorkflowHandlers } from "../EditorConstants"; /** * Tab for the ConfigModal. Used to allow the configurations of the editor configs, namely the camunda endpoint and the @@ -11,73 +11,81 @@ import {transformedWorkflowHandlers} from '../EditorConstants'; * @constructor */ export default function EditorTab() { + const [camundaEndpoint, setCamundaEndpoint] = useState( + editorConfig.getCamundaEndpoint() + ); + const [workflowHandler, setWorkflowHandler] = useState( + editorConfig.getTransformedWorkflowHandler() + ); - const [camundaEndpoint, setCamundaEndpoint] = useState(editorConfig.getCamundaEndpoint()); - const [workflowHandler, setWorkflowHandler] = useState(editorConfig.getTransformedWorkflowHandler()); + const modeler = getModeler(); - const modeler = getModeler(); + const editorActions = modeler.get("editorActions"); - const editorActions = modeler.get('editorActions'); - - // register listener for editor action to get changes on the camunda endpoint - if (!editorActions._actions.hasOwnProperty('camundaEndpointChanged')) { - editorActions.register({ - camundaEndpointChanged: function (camundaEndpoint) { - modeler.config.camundaEndpoint = camundaEndpoint; - } - }); - } - - // save values of the tab entries in the editor config - EditorTab.prototype.onClose = () => { + // register listener for editor action to get changes on the camunda endpoint + if (!editorActions._actions.hasOwnProperty("camundaEndpointChanged")) { + editorActions.register({ + camundaEndpointChanged: function (camundaEndpoint) { modeler.config.camundaEndpoint = camundaEndpoint; - editorConfig.setCamundaEndpoint(camundaEndpoint); - editorConfig.setTransformedWorkflowHandler(workflowHandler); - }; + }, + }); + } - // return tab which contains entries to change the camunda endpoint and the workflow handler - return (<> -

Workflow Engine configuration:

- - - - - - - -
Camunda Engine Endpoint - setCamundaEndpoint(event.target.value)}/> -
-

Handle for transformed workflows:

- - - - - - - -
Transformed Workflow Handler - + // save values of the tab entries in the editor config + EditorTab.prototype.onClose = () => { + modeler.config.camundaEndpoint = camundaEndpoint; + editorConfig.setCamundaEndpoint(camundaEndpoint); + editorConfig.setTransformedWorkflowHandler(workflowHandler); + }; -
- ); + // return tab which contains entries to change the camunda endpoint and the workflow handler + return ( + <> +

Workflow Engine configuration:

+ + + + + + + +
Camunda Engine Endpoint + setCamundaEndpoint(event.target.value)} + /> +
+

Handle for transformed workflows:

+ + + + + + + +
Transformed Workflow Handler + +
+ + ); } EditorTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.camundaEndpoint = editorConfig.getCamundaEndpoint(); -}; \ No newline at end of file + modeler.config.camundaEndpoint = editorConfig.getCamundaEndpoint(); +}; diff --git a/components/bpmn-q/modeler-component/editor/config/config-modal.css b/components/bpmn-q/modeler-component/editor/config/config-modal.css index cada1cf4..da914a5b 100644 --- a/components/bpmn-q/modeler-component/editor/config/config-modal.css +++ b/components/bpmn-q/modeler-component/editor/config/config-modal.css @@ -1,53 +1,53 @@ .qwm-innerConfig { - min-width: 100px; - max-width: 100px; - min-height: auto; - height: auto; - border-radius: 3px; - font-size: 13px; - font-weight: bold; - font-stretch: normal; - font-style: normal; - line-height: normal; - letter-spacing: normal; - text-align: center; - padding: 5px; + min-width: 100px; + max-width: 100px; + min-height: auto; + height: auto; + border-radius: 3px; + font-size: 13px; + font-weight: bold; + font-stretch: normal; + font-style: normal; + line-height: normal; + letter-spacing: normal; + text-align: center; + padding: 5px; } .qwm-innerConfig + .qwm-innerConfig { - margin-top: 5px; + margin-top: 5px; } .qwm-indent { - margin-left: 5px; + margin-left: 5px; } .qwm-btn-config:before { - content: ""; - display: block; - width: 15px; - height: 15px; - background-size: contain; - background: url("../resources/icons/config-icon.png") no-repeat center center; - float: left; + content: ""; + display: block; + width: 15px; + height: 15px; + background-size: contain; + background: url("../resources/icons/config-icon.png") no-repeat center center; + float: left; } .qwm-btn-primary:focus { - border: solid 1px var(--blue-darken-48); - background-color: var(--blue-darken-55); + border: solid 1px var(--blue-darken-48); + background-color: var(--blue-darken-55); } .qwm-spaceAbove { - padding-top: 1em; - margin-left: 10px; + padding-top: 1em; + margin-left: 10px; } .qwm-tabButtonsContainer { - display: flex; - flex-direction: column; - align-items: flex-start; - overflow: auto; - min-width: 120px; - max-height: 263px; - direction: rtl; + display: flex; + flex-direction: column; + align-items: flex-start; + overflow: auto; + min-width: 120px; + max-height: 263px; + direction: rtl; } diff --git a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js index 17e56ecb..48aaa008 100644 --- a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js +++ b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationEndpoint.js @@ -2,55 +2,57 @@ * Class to fetch and store Configurations from an external repository. The used repository can be configured in the constructor. */ export default class ConfigurationsEndpoint { - - // array containing the fetched configurations - _configurations = []; - - constructor(endpointUrl) { - this._endpointUrl = endpointUrl; - - // initial fetch for configurations - this.fetchConfigurations(); - } - - /** - * Fetch the configured endpoint and store the result in this._configurations - */ - fetchConfigurations() { - - fetch(this._endpointUrl) - .then(response => response.json()) - .then(data => { - this._configurations = data; - console.log('Received ' + data.length + ' configurations: '); - console.log(data); - }) - .catch(error => { - console.error('Error fetching configurations from ' + this._endpointUrl + ': \n' + error); - }); - } - - /** - * Returns all stored configurations which apply to the given type. - * - * @param type The type the wanted configurations are applied to. - * @returns {*[]} All configurations of this._configurations which apply to the given type. - */ - getConfigurations(type) { - - // return all configurations which apply to the given type - return this._configurations.filter(function (configuration) { - return configuration.appliesTo === type; - }); - } - - /** - * Returns the configurations which has the given id. - * - * @param id The id of the searched configuration. - * @returns {*} The configuration with the given id. - */ - getConfiguration(id) { - return this._configurations.find(config => config.id === id); - } -} \ No newline at end of file + // array containing the fetched configurations + _configurations = []; + + constructor(endpointUrl) { + this._endpointUrl = endpointUrl; + + // initial fetch for configurations + this.fetchConfigurations(); + } + + /** + * Fetch the configured endpoint and store the result in this._configurations + */ + fetchConfigurations() { + fetch(this._endpointUrl) + .then((response) => response.json()) + .then((data) => { + this._configurations = data; + console.log("Received " + data.length + " configurations: "); + console.log(data); + }) + .catch((error) => { + console.error( + "Error fetching configurations from " + + this._endpointUrl + + ": \n" + + error + ); + }); + } + + /** + * Returns all stored configurations which apply to the given type. + * + * @param type The type the wanted configurations are applied to. + * @returns {*[]} All configurations of this._configurations which apply to the given type. + */ + getConfigurations(type) { + // return all configurations which apply to the given type + return this._configurations.filter(function (configuration) { + return configuration.appliesTo === type; + }); + } + + /** + * Returns the configurations which has the given id. + * + * @param id The id of the searched configuration. + * @returns {*} The configuration with the given id. + */ + getConfiguration(id) { + return this._configurations.find((config) => config.id === id); + } +} diff --git a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsProperties.js b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsProperties.js index af2ad42c..9a1fc2e4 100644 --- a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsProperties.js +++ b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsProperties.js @@ -1,11 +1,19 @@ -import {isTextFieldEntryEdited, TextFieldEntry, CheckboxEntry, isCheckboxEntryEdited} from "@bpmn-io/properties-panel"; -import {useService} from 'bpmn-js-properties-panel'; -import {nextId} from '../util/camunda-utils/ElementUtil'; import { - addAttributeValueToCamundaIO, addAttributeValueToKeyValueMap, getAttributeValue, - getAttributeValueFromCamundaIO, - getAttributeValueFromKeyValueMap, setAttributeValue -} from './ConfigurationsUtil'; + isTextFieldEntryEdited, + TextFieldEntry, + CheckboxEntry, + isCheckboxEntryEdited, +} from "@bpmn-io/properties-panel"; +import { useService } from "bpmn-js-properties-panel"; +import { nextId } from "../util/camunda-utils/ElementUtil"; +import { + addAttributeValueToCamundaIO, + addAttributeValueToKeyValueMap, + getAttributeValue, + getAttributeValueFromCamundaIO, + getAttributeValueFromKeyValueMap, + setAttributeValue, +} from "./ConfigurationsUtil"; /** * Creates entries for the properties panel based on the attributes of the given configuration. @@ -16,62 +24,80 @@ import { * @param configuration The configuration for which the properties should be generated. * @returns {*} The created properties panel entries. */ -export default function ConfigurationsProperties(element, injector, translate, configuration) { - - const bpmnFactory = injector.get('bpmnFactory'); - const modeling = injector.get('modeling'); - const commandStack = injector.get('commandStack'); - - // generate entries based on the attributes of the configuration and their definitions - return configuration.attributes.map(function (attribute) { - - // do not display hidden attributes - if (attribute.hide) { - return {}; - } - - let component; - let isEdited; - switch (attribute.type) { - case 'Boolean': - component = BooleanEntry; - isEdited = isCheckboxEntryEdited; - break; - default: // String - component = TextEntry; - isEdited = isTextFieldEntryEdited; - break; - } - - // set setter and getter depending on the type of the bindTo attribute - let setValue; - let getValue; - switch (attribute.bindTo.type) { - case 'camunda:InputParameter': - case 'camunda:OutputParameter': - setValue = addAttributeValueToCamundaIO(element, bpmnFactory, attribute.bindTo.type, attribute, modeling); - getValue = getAttributeValueFromCamundaIO(element, bpmnFactory, attribute.bindTo.type); - break; - case 'KeyValueMap': - setValue = addAttributeValueToKeyValueMap(element, attribute, bpmnFactory, commandStack); - getValue = getAttributeValueFromKeyValueMap(element); - break; - default: - setValue = setAttributeValue(element, attribute, modeling); - getValue = getAttributeValue(element); - break; - } - - return { - id: nextId(attribute.name), - attribute, - setValue: setValue, - getValue: getValue, - component: component, - isEdited: isEdited, - disabled: true, - }; - }); +export default function ConfigurationsProperties( + element, + injector, + translate, + configuration +) { + const bpmnFactory = injector.get("bpmnFactory"); + const modeling = injector.get("modeling"); + const commandStack = injector.get("commandStack"); + + // generate entries based on the attributes of the configuration and their definitions + return configuration.attributes.map(function (attribute) { + // do not display hidden attributes + if (attribute.hide) { + return {}; + } + + let component; + let isEdited; + switch (attribute.type) { + case "Boolean": + component = BooleanEntry; + isEdited = isCheckboxEntryEdited; + break; + default: // String + component = TextEntry; + isEdited = isTextFieldEntryEdited; + break; + } + + // set setter and getter depending on the type of the bindTo attribute + let setValue; + let getValue; + switch (attribute.bindTo.type) { + case "camunda:InputParameter": + case "camunda:OutputParameter": + setValue = addAttributeValueToCamundaIO( + element, + bpmnFactory, + attribute.bindTo.type, + attribute, + modeling + ); + getValue = getAttributeValueFromCamundaIO( + element, + bpmnFactory, + attribute.bindTo.type + ); + break; + case "KeyValueMap": + setValue = addAttributeValueToKeyValueMap( + element, + attribute, + bpmnFactory, + commandStack + ); + getValue = getAttributeValueFromKeyValueMap(element); + break; + default: + setValue = setAttributeValue(element, attribute, modeling); + getValue = getAttributeValue(element); + break; + } + + return { + id: nextId(attribute.name), + attribute, + setValue: setValue, + getValue: getValue, + component: component, + isEdited: isEdited, + disabled: true, + }; + }); } /** @@ -81,25 +107,20 @@ export default function ConfigurationsProperties(element, injector, translate, c * @returns {preact.VNode} */ function TextEntry(props) { - const { - idPrefix, - attribute, - setValue, - getValue, - } = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - return TextFieldEntry({ - element: attribute, - id: idPrefix + '-value', - label: translate(attribute.label), - disabled: attribute.disable, - getValue, - setValue, - debounce - }); + const { idPrefix, attribute, setValue, getValue } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + return TextFieldEntry({ + element: attribute, + id: idPrefix + "-value", + label: translate(attribute.label), + disabled: attribute.disable, + getValue, + setValue, + debounce, + }); } /** @@ -109,33 +130,27 @@ function TextEntry(props) { * @returns {preact.VNode} */ function BooleanEntry(props) { - const { - idPrefix, - attribute, - setValue, - getValue, - } = props; - - console.log(attribute.label); - - const translate = useService('translate'); - - const getBoolValue = (attribute) => { - const boolStr = getValue(attribute); - try { - return JSON.parse(boolStr); - } catch (error) { - console.log(`Failed to parse ${boolStr} to boolean.`); - } - - }; - - return CheckboxEntry({ - element: attribute, - id: idPrefix + '-value', - label: translate(attribute.label), - disabled: attribute.disable, - getValue: getBoolValue, - setValue, - }); -} \ No newline at end of file + const { idPrefix, attribute, setValue, getValue } = props; + + console.log(attribute.label); + + const translate = useService("translate"); + + const getBoolValue = (attribute) => { + const boolStr = getValue(attribute); + try { + return JSON.parse(boolStr); + } catch (error) { + console.log(`Failed to parse ${boolStr} to boolean.`); + } + }; + + return CheckboxEntry({ + element: attribute, + id: idPrefix + "-value", + label: translate(attribute.label), + disabled: attribute.disable, + getValue: getBoolValue, + setValue, + }); +} diff --git a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsUtil.js b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsUtil.js index 7b2c0043..72124964 100644 --- a/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsUtil.js +++ b/components/bpmn-q/modeler-component/editor/configurations/ConfigurationsUtil.js @@ -1,12 +1,12 @@ -import * as configConsts from './Constants'; -import {getBusinessObject} from 'bpmn-js/lib/util/ModelUtil'; -import * as dataConsts from '../../extensions/data-extension/Constants'; +import * as configConsts from "./Constants"; +import { getBusinessObject } from "bpmn-js/lib/util/ModelUtil"; +import * as dataConsts from "../../extensions/data-extension/Constants"; import { - addCamundaInputMapParameter, - addCamundaOutputMapParameter, - getCamundaInputOutput -} from '../util/ModellingUtilities'; -import * as configsConsts from './Constants'; + addCamundaInputMapParameter, + addCamundaOutputMapParameter, + getCamundaInputOutput, +} from "../util/ModellingUtilities"; +import * as configsConsts from "./Constants"; /** * Create popup menu entries for a given array of configurations. Per default each entry applies its configuration to the @@ -22,43 +22,55 @@ import * as configsConsts from './Constants'; * @param action Optional action which will be triggered when an entry is selected. * @returns {{}} The list of popup menu entries. */ -export function createConfigurationsEntries(element, className, configurations, bpmnFactory, modeling, commandStack, replaceElement, action = undefined) { - - const menuEntries = {}; - let updateAction; - - console.log('Create entries for configurations:'); - console.log(configurations); - - configurations.map(function (config) { - - // define action for the entry - if (action) { - updateAction = function (event) { - action(event, config); - }; - } else { - updateAction = function () { - - // replace element with configuration type if types mismatch - let newElement; - if (element.type !== config.appliesTo) { - newElement = replaceElement(element, {type: config.appliesTo}); - } - - handleConfigurationsAction(newElement || element, config, bpmnFactory, modeling, commandStack); - }; +export function createConfigurationsEntries( + element, + className, + configurations, + bpmnFactory, + modeling, + commandStack, + replaceElement, + action = undefined +) { + const menuEntries = {}; + let updateAction; + + console.log("Create entries for configurations:"); + console.log(configurations); + + configurations.map(function (config) { + // define action for the entry + if (action) { + updateAction = function (event) { + action(event, config); + }; + } else { + updateAction = function () { + // replace element with configuration type if types mismatch + let newElement; + if (element.type !== config.appliesTo) { + newElement = replaceElement(element, { type: config.appliesTo }); } - // create popup menu entry - menuEntries[config.id] = { - label: config.name, - className: className, - action: updateAction, - }; - }); + handleConfigurationsAction( + newElement || element, + config, + bpmnFactory, + modeling, + commandStack + ); + }; + } - return menuEntries; + // create popup menu entry + menuEntries[config.id] = { + label: config.name, + className: className, + action: updateAction, + }; + }); + + return menuEntries; } /** @@ -70,47 +82,72 @@ export function createConfigurationsEntries(element, className, configurations, * @param modeling modeling dependency of the modeler instance. * @param commandStack dependency of the modeler instance. */ -export function handleConfigurationsAction(element, config, bpmnFactory, modeling, commandStack) { - - // save id of selected configuration in the element +export function handleConfigurationsAction( + element, + config, + bpmnFactory, + modeling, + commandStack +) { + // save id of selected configuration in the element + modeling.updateProperties(element, { + [configConsts.SELECT_CONFIGURATIONS_ID]: config.id, + }); + + // save icon property if defined of the selected configuration in the element to allow customized rendering + if (config.icon) { modeling.updateProperties(element, { - [configConsts.SELECT_CONFIGURATIONS_ID]: config.id, + [configsConsts.CONFIGURATIONS_ICON]: JSON.stringify(config.icon), }); - - // save icon property if defined of the selected configuration in the element to allow customized rendering - if (config.icon) { - modeling.updateProperties(element, { - [configsConsts.CONFIGURATIONS_ICON]: JSON.stringify(config.icon), - }); + } + + // set name of the element to configuration name + modeling.updateProperties(element, { + name: config.name, + }); + + config.attributes.forEach(function (attribute) { + // set properties based on the type of the bindTo value + switch (attribute.bindTo.type) { + case "camunda:InputParameter": + case "camunda:OutputParameter": + addAttributeValueToCamundaIO( + element, + bpmnFactory, + attribute.bindTo.type, + attribute, + modeling + )(attribute.value); + break; + case "camunda:InputMapParameter": + addCamundaInputMapParameter( + element.businessObject, + attribute.name, + attribute.value, + bpmnFactory + ); + break; + case "camunda:OutputMapParameter": + addCamundaOutputMapParameter( + element.businessObject, + attribute.name, + attribute.value, + bpmnFactory + ); + break; + case "KeyValueMap": + addAttributeValueToKeyValueMap( + element, + attribute, + bpmnFactory, + commandStack + )(attribute.value); + break; + default: + setAttributeValue(element, attribute, modeling)(attribute.value); + break; } - - // set name of the element to configuration name - modeling.updateProperties(element, { - name: config.name, - }); - - config.attributes.forEach(function (attribute) { - - // set properties based on the type of the bindTo value - switch (attribute.bindTo.type) { - case 'camunda:InputParameter': - case 'camunda:OutputParameter': - addAttributeValueToCamundaIO(element, bpmnFactory, attribute.bindTo.type, attribute, modeling)(attribute.value); - break; - case 'camunda:InputMapParameter': - addCamundaInputMapParameter(element.businessObject, attribute.name, attribute.value, bpmnFactory); - break; - case 'camunda:OutputMapParameter': - addCamundaOutputMapParameter(element.businessObject, attribute.name, attribute.value, bpmnFactory); - break; - case 'KeyValueMap': - addAttributeValueToKeyValueMap(element, attribute, bpmnFactory, commandStack)(attribute.value); - break; - default: - setAttributeValue(element, attribute, modeling)(attribute.value); - break; - } - }); + }); } /** @@ -122,11 +159,11 @@ export function handleConfigurationsAction(element, config, bpmnFactory, modelin * @returns {function(*): *} */ export function setAttributeValue(element, attribute, modeling) { - return (newValue) => { - return modeling.updateProperties(element, { - [attribute.bindTo.name]: newValue - }); - }; + return (newValue) => { + return modeling.updateProperties(element, { + [attribute.bindTo.name]: newValue, + }); + }; } /** @@ -137,17 +174,17 @@ export function setAttributeValue(element, attribute, modeling) { * @returns {(function(*): (*))|*} */ export function getAttributeValue(element) { - return (attribute) => { - const businessObject = getBusinessObject(element); - const realValue = businessObject.get(attribute.bindTo.name) || ''; - - // return the value of the property if defined or the value defined in the attribute - if (realValue && realValue !== '') { - return realValue; - } else { - return attribute.value; - } - }; + return (attribute) => { + const businessObject = getBusinessObject(element); + const realValue = businessObject.get(attribute.bindTo.name) || ""; + + // return the value of the property if defined or the value defined in the attribute + if (realValue && realValue !== "") { + return realValue; + } else { + return attribute.value; + } + }; } /** @@ -160,37 +197,44 @@ export function getAttributeValue(element) { * @param commandStack The commandStack of the current bpmn-js modeler. * @returns {(function(*): void)|*} */ -export function addAttributeValueToKeyValueMap(element, attribute, bpmnFactory, commandStack) { - return (value) => { - - const bo = element.businessObject; - - const businessObject = getBusinessObject(element); - const keyValueMap = bo.get(attribute.bindTo.name) || []; - - // add the value to the key value map - const existingEntry = keyValueMap.find((entry) => entry.name === attribute.name); - if (existingEntry) { - - // overwrite value of existing key value entry - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: existingEntry, - properties: {value: value}, - }); - } else { - - // create new key value entry - const param = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, {name: attribute.name, value: value}); - - // add new entry to the key value map and save changes - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: businessObject, - properties: {[attribute.bindTo.name]: keyValueMap.concat(param)}, - }); - } - }; +export function addAttributeValueToKeyValueMap( + element, + attribute, + bpmnFactory, + commandStack +) { + return (value) => { + const bo = element.businessObject; + + const businessObject = getBusinessObject(element); + const keyValueMap = bo.get(attribute.bindTo.name) || []; + + // add the value to the key value map + const existingEntry = keyValueMap.find( + (entry) => entry.name === attribute.name + ); + if (existingEntry) { + // overwrite value of existing key value entry + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: existingEntry, + properties: { value: value }, + }); + } else { + // create new key value entry + const param = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, { + name: attribute.name, + value: value, + }); + + // add new entry to the key value map and save changes + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: businessObject, + properties: { [attribute.bindTo.name]: keyValueMap.concat(param) }, + }); + } + }; } /** @@ -201,18 +245,20 @@ export function addAttributeValueToKeyValueMap(element, attribute, bpmnFactory, * @returns {(function(*): (*))|*} */ export function getAttributeValueFromKeyValueMap(element) { - return (attribute) => { - const businessObject = getBusinessObject(element); - const keyValueMap = businessObject.get(attribute.bindTo.name) || []; - - // return value of respective key value entry or return the value of ConfigurationAttribute - const existingEntry = keyValueMap.find((entry) => entry.name === attribute.name); - if (existingEntry) { - return existingEntry.value; - } else { - return attribute.value; - } - }; + return (attribute) => { + const businessObject = getBusinessObject(element); + const keyValueMap = businessObject.get(attribute.bindTo.name) || []; + + // return value of respective key value entry or return the value of ConfigurationAttribute + const existingEntry = keyValueMap.find( + (entry) => entry.name === attribute.name + ); + if (existingEntry) { + return existingEntry.value; + } else { + return attribute.value; + } + }; } /** @@ -226,40 +272,48 @@ export function getAttributeValueFromKeyValueMap(element) { * @param modeling The modeling module of the current bpmn-js modeler. * @returns {(function(*): void)|*} */ -export function addAttributeValueToCamundaIO(element, bpmnFactory, camundaType, attribute, modeling) { - return (value) => { - - const businessObject = getBusinessObject(element); - const inputOutput = getCamundaInputOutput(businessObject, bpmnFactory); - - // create new io parameter with new value - const newParameter = bpmnFactory.create(camundaType, { - name: attribute.name, - value: value, - }); - - // Update existing or create a new io parameter - const parameters = camundaType === 'camunda:InputParameter' ? inputOutput.inputParameters : inputOutput.outputParameters; - let existingIoParameter = parameters.find((entry) => entry.name === attribute.name); - if (existingIoParameter) { - - // update existing value of io parameter - existingIoParameter.value = newParameter.value; - } else { +export function addAttributeValueToCamundaIO( + element, + bpmnFactory, + camundaType, + attribute, + modeling +) { + return (value) => { + const businessObject = getBusinessObject(element); + const inputOutput = getCamundaInputOutput(businessObject, bpmnFactory); + + // create new io parameter with new value + const newParameter = bpmnFactory.create(camundaType, { + name: attribute.name, + value: value, + }); - // create a new io parameter - existingIoParameter = bpmnFactory.create(camundaType, { - name: attribute.name, - value: attribute.value, - }); - parameters.push(existingIoParameter); - } + // Update existing or create a new io parameter + const parameters = + camundaType === "camunda:InputParameter" + ? inputOutput.inputParameters + : inputOutput.outputParameters; + let existingIoParameter = parameters.find( + (entry) => entry.name === attribute.name + ); + if (existingIoParameter) { + // update existing value of io parameter + existingIoParameter.value = newParameter.value; + } else { + // create a new io parameter + existingIoParameter = bpmnFactory.create(camundaType, { + name: attribute.name, + value: attribute.value, + }); + parameters.push(existingIoParameter); + } - // update model to propagate the new value - modeling.updateProperties(element, { - [camundaType]: existingIoParameter - }); - }; + // update model to propagate the new value + modeling.updateProperties(element, { + [camundaType]: existingIoParameter, + }); + }; } /** @@ -271,23 +325,30 @@ export function addAttributeValueToCamundaIO(element, bpmnFactory, camundaType, * @param camundaType The type of the camunda property, either camunda:InputMapParameter or camunda:OutputMapParameter. * @returns {(function(*): (*))|*} */ -export function getAttributeValueFromCamundaIO(element, bpmnFactory, camundaType) { - - return (attribute) => { - - const businessObject = getBusinessObject(element); - const inputOutput = getCamundaInputOutput(businessObject, bpmnFactory); - - const parameters = camundaType === 'camunda:InputParameter' ? inputOutput.inputParameters : inputOutput.outputParameters; - let existingInputParameter = parameters.find((entry) => entry.name === attribute.name); - - // return value of existing io parameter or the default value of the ConfigurationAttribute - if (existingInputParameter) { - return existingInputParameter.value; - } else { - return attribute.value; - } - }; +export function getAttributeValueFromCamundaIO( + element, + bpmnFactory, + camundaType +) { + return (attribute) => { + const businessObject = getBusinessObject(element); + const inputOutput = getCamundaInputOutput(businessObject, bpmnFactory); + + const parameters = + camundaType === "camunda:InputParameter" + ? inputOutput.inputParameters + : inputOutput.outputParameters; + let existingInputParameter = parameters.find( + (entry) => entry.name === attribute.name + ); + + // return value of existing io parameter or the default value of the ConfigurationAttribute + if (existingInputParameter) { + return existingInputParameter.value; + } else { + return attribute.value; + } + }; } /** @@ -304,18 +365,13 @@ export function getAttributeValueFromCamundaIO(element, bpmnFactory, camundaType * @returns {undefined|any} The icon object or undefined if no such property exists or the saved icon string is not correct formatted */ export function extractConfigSVG(element) { - const svgStr = element.businessObject.get(configsConsts.CONFIGURATIONS_ICON); - if (svgStr) { - try { - return JSON.parse(svgStr); - } catch (err) { - return undefined; - } + const svgStr = element.businessObject.get(configsConsts.CONFIGURATIONS_ICON); + if (svgStr) { + try { + return JSON.parse(svgStr); + } catch (err) { + return undefined; } - return undefined; + } + return undefined; } - - - - - diff --git a/components/bpmn-q/modeler-component/editor/configurations/Constants.js b/components/bpmn-q/modeler-component/editor/configurations/Constants.js index f192aad1..c9cc4740 100644 --- a/components/bpmn-q/modeler-component/editor/configurations/Constants.js +++ b/components/bpmn-q/modeler-component/editor/configurations/Constants.js @@ -1,6 +1,5 @@ - // constant used to save the id of the selected configuration in an element -export const SELECT_CONFIGURATIONS_ID = 'selectedConfigurationId'; +export const SELECT_CONFIGURATIONS_ID = "selectedConfigurationId"; // constant used to save the icon of a selected configuration in an element -export const CONFIGURATIONS_ICON = 'configsIcon'; \ No newline at end of file +export const CONFIGURATIONS_ICON = "configsIcon"; diff --git a/components/bpmn-q/modeler-component/editor/events/EditorEventHandler.js b/components/bpmn-q/modeler-component/editor/events/EditorEventHandler.js index 782ebbd1..710c9d52 100644 --- a/components/bpmn-q/modeler-component/editor/events/EditorEventHandler.js +++ b/components/bpmn-q/modeler-component/editor/events/EditorEventHandler.js @@ -11,7 +11,7 @@ let modelerComponent; * @param newModelerComponent The quantum workflow modeler component. */ export function initEditorEventHandler(newModelerComponent) { - modelerComponent = newModelerComponent; + modelerComponent = newModelerComponent; } /** @@ -24,14 +24,14 @@ export function initEditorEventHandler(newModelerComponent) { * not invoked, and false otherwise. */ export function dispatchWorkflowEvent(type, workflowXml, workflowName) { - const newEvent = new CustomEvent(type, { - detail: { - workflowName: workflowName, - workflow: workflowXml - }, - cancelable: true - }); - return modelerComponent.dispatchEvent(newEvent); + const newEvent = new CustomEvent(type, { + detail: { + workflowName: workflowName, + workflow: workflowXml, + }, + cancelable: true, + }); + return modelerComponent.dispatchEvent(newEvent); } /** @@ -42,5 +42,9 @@ export function dispatchWorkflowEvent(type, workflowXml, workflowName) { * @param callBckFunction The function defining the action executed when the event occurs */ export function addWorkflowEventListener(type, callBckFunction) { - modelerComponent.addEventListener(type, (event) => callBckFunction(event), false); -} \ No newline at end of file + modelerComponent.addEventListener( + type, + (event) => callBckFunction(event), + false + ); +} diff --git a/components/bpmn-q/modeler-component/editor/plugin/PluginConfigHandler.js b/components/bpmn-q/modeler-component/editor/plugin/PluginConfigHandler.js index a9b78c5f..55c9e550 100644 --- a/components/bpmn-q/modeler-component/editor/plugin/PluginConfigHandler.js +++ b/components/bpmn-q/modeler-component/editor/plugin/PluginConfigHandler.js @@ -25,9 +25,9 @@ let pluginConfigList = []; * @param pluginConfig List of plugin configurations. */ export function setPluginConfig(pluginConfig) { - pluginConfigList = pluginConfig || []; - console.log('New plugin config set: '); - console.log(pluginConfig); + pluginConfigList = pluginConfig || []; + console.log("New plugin config set: "); + console.log(pluginConfig); } /** @@ -38,8 +38,9 @@ export function setPluginConfig(pluginConfig) { * was defined for the plugin. */ export function getPluginConfig(pluginName) { - const plugin = pluginConfigList.find(element => element.name === pluginName) || {}; - return plugin.config || {}; + const plugin = + pluginConfigList.find((element) => element.name === pluginName) || {}; + return plugin.config || {}; } /** @@ -48,5 +49,5 @@ export function getPluginConfig(pluginName) { * @returns {*[]} The plugin configurations as an array. */ export function getAllConfigs() { - return pluginConfigList; -} \ No newline at end of file + return pluginConfigList; +} diff --git a/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js b/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js index 2b45efb1..e7b66ab6 100644 --- a/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js +++ b/components/bpmn-q/modeler-component/editor/plugin/PluginHandler.js @@ -1,8 +1,8 @@ import PlanQKPlugin from "../../extensions/planqk/PlanQKPlugin"; import QuantMEPlugin from "../../extensions/quantme/QuantMEPlugin"; -import DataFlowPlugin from '../../extensions/data-extension/DataFlowPlugin'; -import QHAnaPlugin from '../../extensions/qhana/QHAnaPlugin'; -import {getAllConfigs} from "./PluginConfigHandler"; +import DataFlowPlugin from "../../extensions/data-extension/DataFlowPlugin"; +import QHAnaPlugin from "../../extensions/qhana/QHAnaPlugin"; +import { getAllConfigs } from "./PluginConfigHandler"; import EditorTab from "../config/EditorTab"; /** @@ -11,12 +11,7 @@ import EditorTab from "../config/EditorTab"; */ // list of plugins integrated in the modeler, register new plugins here -const PLUGINS = [ - DataFlowPlugin, - QHAnaPlugin, - PlanQKPlugin, - QuantMEPlugin, -]; +const PLUGINS = [DataFlowPlugin, QHAnaPlugin, PlanQKPlugin, QuantMEPlugin]; // list of currently active plugins in the current running instance of the modeler, defined based on the plugin configuration let activePlugins = []; @@ -27,43 +22,42 @@ let activePlugins = []; * @returns {*[]} Array of active plugins. */ export function getActivePlugins() { - - // return saved active plugins array - if (activePlugins.length > 0) { - return activePlugins; + // return saved active plugins array + if (activePlugins.length > 0) { + return activePlugins; // determine active plugins - } else { - - activePlugins = []; - + } else { + activePlugins = []; - let plugin; + let plugin; - // add all plugins of PLUGINS to active plugins which have a config entry for them - for (let pluginConfig of getAllConfigs()) { + // add all plugins of PLUGINS to active plugins which have a config entry for them + for (let pluginConfig of getAllConfigs()) { + plugin = PLUGINS.find( + (plugin) => + plugin.name === pluginConfig.name && checkEnabledStatus(plugin.name) + ); - plugin = PLUGINS.find(plugin => plugin.name === pluginConfig.name && checkEnabledStatus(plugin.name)); - - if (plugin) { - activePlugins.push(plugin); - } - } - return activePlugins; + if (plugin) { + activePlugins.push(plugin); + } } + return activePlugins; + } } export function checkEnabledStatus(pluginName) { - switch(pluginName) { - case 'dataflow': - return process.env.ENABLE_DATA_FLOW_PLUGIN; - case 'planqk': - return process.env.ENABLE_PLANQK_PLUGIN; - case 'qhana': - return process.env.ENABLE_QHANA_PLUGIN; - case 'quantme': - return process.env.ENABLE_QUANTME_PLUGIN; - } + switch (pluginName) { + case "dataflow": + return process.env.ENABLE_DATA_FLOW_PLUGIN; + case "planqk": + return process.env.ENABLE_PLANQK_PLUGIN; + case "qhana": + return process.env.ENABLE_QHANA_PLUGIN; + case "quantme": + return process.env.ENABLE_QUANTME_PLUGIN; + } } /** * Returns all additional modules for the bpmn-js modeler the active plugins define in their extensionModule @@ -72,19 +66,18 @@ export function checkEnabledStatus(pluginName) { * @returns {*[]} Array of additional modules defined by the active plugins. */ export function getAdditionalModules() { + const modules = []; - const modules = []; - - // load all additional modules of the active plugins - for (let plugin of getActivePlugins()) { - if (plugin.extensionModule) { - modules.push(plugin.extensionModule); - } + // load all additional modules of the active plugins + for (let plugin of getActivePlugins()) { + if (plugin.extensionModule) { + modules.push(plugin.extensionModule); } + } - console.log('\n Get Additional Modules'); - console.log(modules); - return modules; + console.log("\n Get Additional Modules"); + console.log(modules); + return modules; } /** @@ -93,19 +86,18 @@ export function getAdditionalModules() { * @returns {*[]} Array of css style modules defined by the active plugins. */ export function getStyles() { + let styles = []; - let styles = []; - - // load css styles of the active plugins - for (let plugin of getActivePlugins()) { - if (plugin.styling) { - styles = styles.concat(plugin.styling); - } + // load css styles of the active plugins + for (let plugin of getActivePlugins()) { + if (plugin.styling) { + styles = styles.concat(plugin.styling); } + } - console.log('\n Get Plugin Styling'); - console.log(styles); - return styles; + console.log("\n Get Plugin Styling"); + console.log(styles); + return styles; } /** @@ -115,18 +107,18 @@ export function getStyles() { * @returns {*[]} Object containing the moddle extensions defined by the active plugins. */ export function getModdleExtension() { - const extensions = {}; + const extensions = {}; - // load all moddle extensions defined by the active plugins - for (let plugin of getActivePlugins()) { - if (plugin.moddleDescription) { - extensions[plugin.name] = plugin.moddleDescription; - } + // load all moddle extensions defined by the active plugins + for (let plugin of getActivePlugins()) { + if (plugin.moddleDescription) { + extensions[plugin.name] = plugin.moddleDescription; } + } - console.log('\n Get Moddle Extensions: '); - console.log(extensions); - return extensions; + console.log("\n Get Moddle Extensions: "); + console.log(extensions); + return extensions; } /** @@ -135,17 +127,17 @@ export function getModdleExtension() { * @returns {*[]} Array of css style modules defined by the active plugins. */ export function getTransformationButtons() { - const transformationButtons = []; + const transformationButtons = []; - // load all transformation buttons of the active plugins - for (let plugin of getActivePlugins()) { - if (plugin.transformExtensionButton) { - transformationButtons.push(plugin.transformExtensionButton); - } + // load all transformation buttons of the active plugins + for (let plugin of getActivePlugins()) { + if (plugin.transformExtensionButton) { + transformationButtons.push(plugin.transformExtensionButton); } + } - console.log('\n Got ' + transformationButtons.length + ' Transformations'); - return transformationButtons; + console.log("\n Got " + transformationButtons.length + " Transformations"); + return transformationButtons; } /** @@ -154,18 +146,18 @@ export function getTransformationButtons() { * @returns {*[]} Array of buttons defined by the active plugins. */ export function getPluginButtons() { - const pluginButtons = []; + const pluginButtons = []; - for (let plugin of getActivePlugins()) { - if (plugin.buttons) { - pluginButtons.push(plugin.buttons); - } + for (let plugin of getActivePlugins()) { + if (plugin.buttons) { + pluginButtons.push(plugin.buttons); } + } - console.log('\n Got ' + pluginButtons.length + ' Plugin Buttons'); - console.log(pluginButtons); + console.log("\n Got " + pluginButtons.length + " Plugin Buttons"); + console.log(pluginButtons); - return pluginButtons; + return pluginButtons; } /** @@ -175,23 +167,24 @@ export function getPluginButtons() { * @returns {*[]} Array of config tabs defined by the active plugins. */ export function getConfigTabs() { - - // add default editor tab to configure editor configs - let configTabs = [{ - tabId: 'EditorTab', - tabTitle: 'Editor', - configTab: EditorTab, - }]; - - // load the config tabs of the active plugins into one array - for (let plugin of getActivePlugins()) { - if (plugin.configTabs) { - configTabs = configTabs.concat(plugin.configTabs); - } + // add default editor tab to configure editor configs + let configTabs = [ + { + tabId: "EditorTab", + tabTitle: "Editor", + configTab: EditorTab, + }, + ]; + + // load the config tabs of the active plugins into one array + for (let plugin of getActivePlugins()) { + if (plugin.configTabs) { + configTabs = configTabs.concat(plugin.configTabs); } + } - console.log('\n Got ' + configTabs.length + ' Config Tabs'); - console.log(configTabs); + console.log("\n Got " + configTabs.length + " Config Tabs"); + console.log(configTabs); - return configTabs; -} \ No newline at end of file + return configTabs; +} diff --git a/components/bpmn-q/modeler-component/editor/popup/CustomPopupMenu.js b/components/bpmn-q/modeler-component/editor/popup/CustomPopupMenu.js index 319a79ad..9a2209a5 100644 --- a/components/bpmn-q/modeler-component/editor/popup/CustomPopupMenu.js +++ b/components/bpmn-q/modeler-component/editor/popup/CustomPopupMenu.js @@ -1,197 +1,193 @@ import PopupMenu from "diagram-js/lib/features/popup-menu/PopupMenu"; -import {html, render} from 'diagram-js/lib/ui'; -import SearchablePopupMenuComponent from './SearchablePopupMenuComponent'; -import {getMoreOptions} from "../util/PopupMenuUtilities"; +import { html, render } from "diagram-js/lib/ui"; +import SearchablePopupMenuComponent from "./SearchablePopupMenuComponent"; +import { getMoreOptions } from "../util/PopupMenuUtilities"; /** * PopupMenu with a search bar that searches though all entries loaded in the menu including entries of MoreOptionEntries. * Extends the PopupMenu of diagram-js. */ export default class CustomPopupMenu extends PopupMenu { + constructor(config, eventBus, canvas) { + super(config, eventBus, canvas); - constructor(config, eventBus, canvas) { - super(config, eventBus, canvas); + // rerender if the selected element in the modeler changes + eventBus.on("element.changed", (event) => { + const element = this.isOpen() && this._current.element; - // rerender if the selected element in the modeler changes - eventBus.on('element.changed', event => { + if (event.element === element) { + this._render(); + } + }); + } + + /** + * Open a new popup menu at the given position to display menu entries based on the type of the given element. + * The entries of the menu will be determined by the providers registered for the given ID and the type of the + * given element. + * + * @param element The given element the popup menu is opened for. + * @param providerId Id string of the type of the providers defining the entries of the popup menu, e.g. 'bpmn-replace' + * @param position The position the menu should be displayed at + * @param options Options to configure the opened menu. + */ + open(element, providerId, position, options) { + this.openWithEntries(element, providerId, undefined, options, position); + } + + /** + * Open a new popup menu at the given position to display menu entries based on the type of the given element + * with the given menu entries in it. If newEntries is undefined, the menu entries be determined by the providers + * registered for the given ID and the type of the given element. + * + * @param element The element the popup menu is opened for. + * @param providerId Id string of the type of the providers defining the entries of the popup menu, e.g. 'bpmn-replace' + * @param newEntries Menu entries which should be displayed in this menu. + * @param position The position the menu should be displayed at + * @param options Options to configure the opened menu. + */ + openWithEntries( + element, + providerId, + newEntries, + options, + position = this._current.position + ) { + if (!element) { + throw new Error("Element is missing"); + } - const element = this.isOpen() && this._current.element; + if (!providerId) { + throw new Error("No registered providers for: " + providerId); + } - if (event.element === element) { - this._render(); - } - }); + if (!position) { + throw new Error("the position argument is missing"); } - /** - * Open a new popup menu at the given position to display menu entries based on the type of the given element. - * The entries of the menu will be determined by the providers registered for the given ID and the type of the - * given element. - * - * @param element The given element the popup menu is opened for. - * @param providerId Id string of the type of the providers defining the entries of the popup menu, e.g. 'bpmn-replace' - * @param position The position the menu should be displayed at - * @param options Options to configure the opened menu. - */ - open(element, providerId, position, options) { - this.openWithEntries(element, providerId, undefined, options, position); + if (this.isOpen()) { + this.close(); } - /** - * Open a new popup menu at the given position to display menu entries based on the type of the given element - * with the given menu entries in it. If newEntries is undefined, the menu entries be determined by the providers - * registered for the given ID and the type of the given element. - * - * @param element The element the popup menu is opened for. - * @param providerId Id string of the type of the providers defining the entries of the popup menu, e.g. 'bpmn-replace' - * @param newEntries Menu entries which should be displayed in this menu. - * @param position The position the menu should be displayed at - * @param options Options to configure the opened menu. - */ - openWithEntries(element, providerId, newEntries, options, position = this._current.position) { - if (!element) { - throw new Error('Element is missing'); - } - - if (!providerId) { - throw new Error('No registered providers for: ' + providerId); - } - - if (!position) { - throw new Error('the position argument is missing'); - } - - if (this.isOpen()) { - this.close(); - } - - // load entries from providers registered for providerId for the type of element - let { - entries, - headerEntries - } = this._getContext(element, providerId); - - // use predefined entries to fill the menu if defined - if (newEntries) { - entries = newEntries; - } - - // update state of the menu - this._current = { - position, - className: providerId, - element, - entries, - headerEntries, - container: this._createContainer({provider: providerId}), - options - }; - - this._emit('open'); - - this._bindAutoClose(); + // load entries from providers registered for providerId for the type of element + let { entries, headerEntries } = this._getContext(element, providerId); - this._render(); + // use predefined entries to fill the menu if defined + if (newEntries) { + entries = newEntries; } - /** - * Get the menu entry with the given ID of the current menu entries. The current menu entries also contain the elements - * defined in MoreOptionEntries - * - * @param entryId The ID of the searched menu entry - * @returns {*} The menu entry or an error if no menu entry with this ID exists. - * @private - */ - _getEntry = function(entryId) { - - const extendedOptionsEntries = []; - - // create list of all entries of the menu including the entries of MoreOptionEntries - for (let [key, value] of Object.entries(this._current.entries)) { - value.id = key; - extendedOptionsEntries.push(value); - - // get entries of the MoreOptionsEntry - const moreOptions = getMoreOptions(value); - - if (Array.isArray(moreOptions)) { - extendedOptionsEntries.push(...moreOptions); - } else { - extendedOptionsEntries.push(moreOptions); - } - } - - // convert list to object with properties for each entry - const entries = {}; - for (let entry of extendedOptionsEntries) { - entries[entry.id] = entry; - } - - const entry = entries[entryId] || this._current.headerEntries[entryId]; - - if (!entry) { - throw new Error('entry not found'); - } - - return entry; + // update state of the menu + this._current = { + position, + className: providerId, + element, + entries, + headerEntries, + container: this._createContainer({ provider: providerId }), + options, }; - /** - * Render the popup menu - * - * @private - */ - _render() { - - const { - position: _position, - className, - entries, - headerEntries, - options - } = this._current; - - // load current entries to display them in the menu - const entriesArray = Object.entries(entries).map( - ([key, value]) => ({id: key, ...value}) - ); - - // load current header entries to display them in the menu - const headerEntriesArray = Object.entries(headerEntries).map( - ([key, value]) => ({id: key, ...value}) - ); - - const position = _position && ( - (container) => this._ensureVisible(container, _position) - ); - - const scale = this._updateScale(this._current.container); - - const onClose = result => this.close(result); - const onSelect = (event, entry, action) => this.trigger(event, entry, action); - - render( - html` - <${SearchablePopupMenuComponent} - onClose=${onClose} - onSelect=${onSelect} - position=${position} - className=${className} - entries=${entriesArray} - headerEntries=${headerEntriesArray} - scale=${scale} - onOpened=${this._onOpened.bind(this)} - onClosed=${this._onClosed.bind(this)} - ...${{...options}} - /> - `, - this._current.container - ); + this._emit("open"); + + this._bindAutoClose(); + + this._render(); + } + + /** + * Get the menu entry with the given ID of the current menu entries. The current menu entries also contain the elements + * defined in MoreOptionEntries + * + * @param entryId The ID of the searched menu entry + * @returns {*} The menu entry or an error if no menu entry with this ID exists. + * @private + */ + _getEntry = function (entryId) { + const extendedOptionsEntries = []; + + // create list of all entries of the menu including the entries of MoreOptionEntries + for (let [key, value] of Object.entries(this._current.entries)) { + value.id = key; + extendedOptionsEntries.push(value); + + // get entries of the MoreOptionsEntry + const moreOptions = getMoreOptions(value); + + if (Array.isArray(moreOptions)) { + extendedOptionsEntries.push(...moreOptions); + } else { + extendedOptionsEntries.push(moreOptions); + } + } + + // convert list to object with properties for each entry + const entries = {}; + for (let entry of extendedOptionsEntries) { + entries[entry.id] = entry; } + + const entry = entries[entryId] || this._current.headerEntries[entryId]; + + if (!entry) { + throw new Error("entry not found"); + } + + return entry; + }; + + /** + * Render the popup menu + * + * @private + */ + _render() { + const { + position: _position, + className, + entries, + headerEntries, + options, + } = this._current; + + // load current entries to display them in the menu + const entriesArray = Object.entries(entries).map(([key, value]) => ({ + id: key, + ...value, + })); + + // load current header entries to display them in the menu + const headerEntriesArray = Object.entries(headerEntries).map( + ([key, value]) => ({ id: key, ...value }) + ); + + const position = + _position && ((container) => this._ensureVisible(container, _position)); + + const scale = this._updateScale(this._current.container); + + const onClose = (result) => this.close(result); + const onSelect = (event, entry, action) => + this.trigger(event, entry, action); + + render( + html` + <${SearchablePopupMenuComponent} + onClose=${onClose} + onSelect=${onSelect} + position=${position} + className=${className} + entries=${entriesArray} + headerEntries=${headerEntriesArray} + scale=${scale} + onOpened=${this._onOpened.bind(this)} + onClosed=${this._onClosed.bind(this)} + ...${{ ...options }} + /> + `, + this._current.container + ); + } } -CustomPopupMenu.$inject = [ - 'config.popupMenu', - 'eventBus', - 'canvas', -]; \ No newline at end of file +CustomPopupMenu.$inject = ["config.popupMenu", "eventBus", "canvas"]; diff --git a/components/bpmn-q/modeler-component/editor/popup/HiddenTextFieldEntry.js b/components/bpmn-q/modeler-component/editor/popup/HiddenTextFieldEntry.js index 21d5ec32..61b4d3d8 100644 --- a/components/bpmn-q/modeler-component/editor/popup/HiddenTextFieldEntry.js +++ b/components/bpmn-q/modeler-component/editor/popup/HiddenTextFieldEntry.js @@ -1,5 +1,5 @@ -import {TextFieldEntry} from '@bpmn-io/properties-panel'; -import React from '@bpmn-io/properties-panel/preact/compat'; +import { TextFieldEntry } from "@bpmn-io/properties-panel"; +import React from "@bpmn-io/properties-panel/preact/compat"; /** * Entry which can be used in a properties group of the properties panel. Allows the definition of a TextFieldEntry @@ -16,16 +16,27 @@ import React from '@bpmn-io/properties-panel/preact/compat'; * @return {JSX.Element} * @constructor */ -export function HiddenTextFieldEntry({id, element, label, getValue, setValue, debounce, hidden}) { - - return <> - {!hidden() && ()} - ; -} \ No newline at end of file +export function HiddenTextFieldEntry({ + id, + element, + label, + getValue, + setValue, + debounce, + hidden, +}) { + return ( + <> + {!hidden() && ( + + )} + + ); +} diff --git a/components/bpmn-q/modeler-component/editor/popup/SearchablePopupMenuComponent.js b/components/bpmn-q/modeler-component/editor/popup/SearchablePopupMenuComponent.js index e97d9435..a03b20bf 100644 --- a/components/bpmn-q/modeler-component/editor/popup/SearchablePopupMenuComponent.js +++ b/components/bpmn-q/modeler-component/editor/popup/SearchablePopupMenuComponent.js @@ -1,22 +1,19 @@ import { - useEffect, - useRef, - useState, - useLayoutEffect, - useMemo, - useCallback, - html -} from 'diagram-js/lib/ui'; - -import { - closest as domClosest, - matches as domMatches -} from 'min-dom'; - -import PopupMenuList from 'diagram-js/lib/features/popup-menu/PopupMenuList'; -import classNames from 'clsx'; -import {isDefined} from 'min-dash'; -import {getMoreOptions} from "../util/PopupMenuUtilities"; + useEffect, + useRef, + useState, + useLayoutEffect, + useMemo, + useCallback, + html, +} from "diagram-js/lib/ui"; + +import { closest as domClosest, matches as domMatches } from "min-dom"; + +import PopupMenuList from "diagram-js/lib/features/popup-menu/PopupMenuList"; +import classNames from "clsx"; +import { isDefined } from "min-dash"; +import { getMoreOptions } from "../util/PopupMenuUtilities"; /** * A component that renders the popup menus with a search bar that allows to search MoreOptionsEntries. @@ -25,161 +22,177 @@ import {getMoreOptions} from "../util/PopupMenuUtilities"; * */ export default function SearchablePopupMenuComponent(props) { - const { - onClose, - onSelect, - className, - headerEntries, - position, - title, - width, - scale, - search, - entries: originalEntries, - onOpened, - onClosed - } = props; - - const searchable = useMemo(() => { - if (!isDefined(search)) { - return false; - } - - return originalEntries.length > 5; - }, [search, originalEntries]); - - const inputRef = useRef(); - - const [value, setValue] = useState(''); - - // collect MoreOptionEntries - const extendedOptionsEntries = originalEntries.flatMap(function (entry) { - return getMoreOptions(entry); - }); - - // filter menu entries based on the value - const filterEntries = useCallback((entriesToFilter, value) => { - - if (!searchable || !value || value === '') { - return originalEntries; - } - - const filter = entry => { - if (!value) { - return (entry.rank || 0) >= 0; - } - - const search = [ - entry.description || '', - entry.label || '', - entry.search || '' - ] - .join('---') - .toLowerCase(); - - return value - .toLowerCase() - .split(/\s/g) - .every(term => search.includes(term)); - }; - - return entriesToFilter.filter(filter); - }, [searchable]); - - const [entries, setEntries] = useState(filterEntries(originalEntries, value)); - const [selectedEntry, setSelectedEntry] = useState(entries[0]); - - const updateEntries = useCallback((newEntries) => { - - // select first entry if non is selected - if (!selectedEntry || !newEntries.includes(selectedEntry)) { - setSelectedEntry(newEntries[0]); - } - - setEntries(newEntries); - }, [selectedEntry, setEntries, setSelectedEntry]); - - // filter entries on value change - useEffect(() => { - updateEntries(filterEntries(extendedOptionsEntries, value)); - }, [value, originalEntries]); - - // register global handler - useEffect(() => { - const handleKeyDown = event => { - if (event.key === 'Escape') { - event.preventDefault(); - - return onClose(); - } - }; - - document.documentElement.addEventListener('keydown', handleKeyDown); - - return () => { - document.documentElement.removeEventListener('keydown', handleKeyDown); - }; - }, []); - - // focus input on initial mount - useLayoutEffect(() => { - inputRef.current && inputRef.current.focus(); - }, []); - - // handle keyboard selection - const keyboardSelect = useCallback(direction => { - const idx = entries.indexOf(selectedEntry); - - let nextIdx = idx + direction; - - if (nextIdx < 0) { - nextIdx = entries.length - 1; - } - - if (nextIdx >= entries.length) { - nextIdx = 0; + const { + onClose, + onSelect, + className, + headerEntries, + position, + title, + width, + scale, + search, + entries: originalEntries, + onOpened, + onClosed, + } = props; + + const searchable = useMemo(() => { + if (!isDefined(search)) { + return false; + } + + return originalEntries.length > 5; + }, [search, originalEntries]); + + const inputRef = useRef(); + + const [value, setValue] = useState(""); + + // collect MoreOptionEntries + const extendedOptionsEntries = originalEntries.flatMap(function (entry) { + return getMoreOptions(entry); + }); + + // filter menu entries based on the value + const filterEntries = useCallback( + (entriesToFilter, value) => { + if (!searchable || !value || value === "") { + return originalEntries; + } + + const filter = (entry) => { + if (!value) { + return (entry.rank || 0) >= 0; } - setSelectedEntry(entries[nextIdx]); - }, [entries, selectedEntry, setSelectedEntry]); - - const handleKeyDown = useCallback(event => { - if (event.key === 'Enter' && selectedEntry) { - return onSelect(event, selectedEntry); - } - - // ARROW_UP or SHIFT + TAB navigation - if (event.key === 'ArrowUp' || (event.key === 'Tab' && event.shiftKey)) { - keyboardSelect(-1); - - return event.preventDefault(); - } - - // ARROW_DOWN or TAB navigation - if (event.key === 'ArrowDown' || event.key === 'Tab') { - keyboardSelect(1); - - return event.preventDefault(); - } - }, [onSelect, onClose, selectedEntry, keyboardSelect]); - - const handleKey = useCallback(event => { - if (domMatches(event.target, 'input')) { - setValue(() => event.target.value); - } - }, [setValue]); + const search = [ + entry.description || "", + entry.label || "", + entry.search || "", + ] + .join("---") + .toLowerCase(); + + return value + .toLowerCase() + .split(/\s/g) + .every((term) => search.includes(term)); + }; + + return entriesToFilter.filter(filter); + }, + [searchable] + ); + + const [entries, setEntries] = useState(filterEntries(originalEntries, value)); + const [selectedEntry, setSelectedEntry] = useState(entries[0]); + + const updateEntries = useCallback( + (newEntries) => { + // select first entry if non is selected + if (!selectedEntry || !newEntries.includes(selectedEntry)) { + setSelectedEntry(newEntries[0]); + } + + setEntries(newEntries); + }, + [selectedEntry, setEntries, setSelectedEntry] + ); + + // filter entries on value change + useEffect(() => { + updateEntries(filterEntries(extendedOptionsEntries, value)); + }, [value, originalEntries]); + + // register global handler + useEffect(() => { + const handleKeyDown = (event) => { + if (event.key === "Escape") { + event.preventDefault(); + + return onClose(); + } + }; - useEffect(() => { - onOpened(); + document.documentElement.addEventListener("keydown", handleKeyDown); - return () => { - onClosed(); - }; - }, []); + return () => { + document.documentElement.removeEventListener("keydown", handleKeyDown); + }; + }, []); + + // focus input on initial mount + useLayoutEffect(() => { + inputRef.current && inputRef.current.focus(); + }, []); + + // handle keyboard selection + const keyboardSelect = useCallback( + (direction) => { + const idx = entries.indexOf(selectedEntry); + + let nextIdx = idx + direction; + + if (nextIdx < 0) { + nextIdx = entries.length - 1; + } + + if (nextIdx >= entries.length) { + nextIdx = 0; + } + + setSelectedEntry(entries[nextIdx]); + }, + [entries, selectedEntry, setSelectedEntry] + ); + + const handleKeyDown = useCallback( + (event) => { + if (event.key === "Enter" && selectedEntry) { + return onSelect(event, selectedEntry); + } + + // ARROW_UP or SHIFT + TAB navigation + if (event.key === "ArrowUp" || (event.key === "Tab" && event.shiftKey)) { + keyboardSelect(-1); + + return event.preventDefault(); + } + + // ARROW_DOWN or TAB navigation + if (event.key === "ArrowDown" || event.key === "Tab") { + keyboardSelect(1); + + return event.preventDefault(); + } + }, + [onSelect, onClose, selectedEntry, keyboardSelect] + ); + + const handleKey = useCallback( + (event) => { + if (domMatches(event.target, "input")) { + setValue(() => event.target.value); + } + }, + [setValue] + ); + + useEffect(() => { + onOpened(); + + return () => { + onClosed(); + }; + }, []); - const displayHeader = useMemo(() => title || headerEntries.length > 0, [title, headerEntries]); + const displayHeader = useMemo( + () => title || headerEntries.length > 0, + [title, headerEntries] + ); - return html` + return html` <${PopupMenuWrapper} onClose=${onClose} onKeyup=${handleKey} @@ -189,58 +202,87 @@ export default function SearchablePopupMenuComponent(props) { width=${width} scale=${scale} > - ${displayHeader && html` + ${ + displayHeader && + html`
-

${title}

- ${headerEntries.map(entry => html` - onSelect(event, entry)} - title=${entry.title || entry.label} - data-id=${entry.id} - onMouseEnter=${() => setSelectedEntry(entry)} - onMouseLeave=${() => setSelectedEntry(null)} - > - ${entry.imageUrl ? html` - - ` : null} - - ${entry.label ? html` - ${entry.label} - ` : null} - - `)} +

${title}

+ ${headerEntries.map( + (entry) => html` + onSelect(event, entry)} + title=${entry.title || entry.label} + data-id=${entry.id} + onMouseEnter=${() => setSelectedEntry(entry)} + onMouseLeave=${() => setSelectedEntry(null)} + > + ${entry.imageUrl + ? html` + + ` + : null} + ${entry.label + ? html` + ${entry.label} + ` + : null} + + ` + )}
- `} - ${originalEntries.length > 0 && html` + ` + } + ${ + originalEntries.length > 0 && + html`
- - ${searchable && html` - - `} - - <${PopupMenuList} - entries=${entries} - selectedEntry=${selectedEntry} - setSelectedEntry=${setSelectedEntry} - onAction=${onSelect} - /> + ${searchable && + html` + + `} + + <${PopupMenuList} + entries=${entries} + selectedEntry=${selectedEntry} + setSelectedEntry=${setSelectedEntry} + onAction=${onSelect} + />
- ${entries.length === 0 && html` -
No matching entries found.
+ ${entries.length === 0 && + html` +
+ No matching entries found. +
`} - `} + ` + } `; } @@ -251,79 +293,78 @@ export default function SearchablePopupMenuComponent(props) { * @param {*} props */ function PopupMenuWrapper(props) { - const { - onClose, - onKeydown, - onKeyup, - className, - children, - position: positionGetter - } = props; - - const popupRef = useRef(); - - const checkClose = useCallback((event) => { - - const popup = domClosest(event.target, '.djs-popup', true); - - if (popup) { - return; - } - - onClose(); - }, [onClose]); - - useLayoutEffect(() => { - if (typeof positionGetter !== 'function') { - return; - } - - const popupEl = popupRef.current; - const position = positionGetter(popupEl); - - popupEl.style.left = `${position.x}px`; - popupEl.style.top = `${position.y}px`; - }, [popupRef.current, positionGetter]); - - // focus popup initially, on mount - useLayoutEffect(() => { - popupRef.current && popupRef.current.focus(); - }, []); - - return html` -
-
- ${children} -
-
- `; + const { + onClose, + onKeydown, + onKeyup, + className, + children, + position: positionGetter, + } = props; + + const popupRef = useRef(); + + const checkClose = useCallback( + (event) => { + const popup = domClosest(event.target, ".djs-popup", true); + + if (popup) { + return; + } + + onClose(); + }, + [onClose] + ); + + useLayoutEffect(() => { + if (typeof positionGetter !== "function") { + return; + } + + const popupEl = popupRef.current; + const position = positionGetter(popupEl); + + popupEl.style.left = `${position.x}px`; + popupEl.style.top = `${position.y}px`; + }, [popupRef.current, positionGetter]); + + // focus popup initially, on mount + useLayoutEffect(() => { + popupRef.current && popupRef.current.focus(); + }, []); + + return html` +
+
+ ${children} +
+
+ `; } // helpers ////////////////////// function getPopupStyle(props) { - return { - transform: `scale(${props.scale})`, - width: `${props.width}px` - }; + return { + transform: `scale(${props.scale})`, + width: `${props.width}px`, + }; } function getHeaderClasses(entry, selected) { - return classNames( - 'entry', - entry.className, - entry.active ? 'active' : '', - entry.disabled ? 'disabled' : '', - selected ? 'selected' : '' - ); -} \ No newline at end of file + return classNames( + "entry", + entry.className, + entry.active ? "active" : "", + entry.disabled ? "disabled" : "", + selected ? "selected" : "" + ); +} diff --git a/components/bpmn-q/modeler-component/editor/popup/index.js b/components/bpmn-q/modeler-component/editor/popup/index.js index da86ab88..6c023892 100644 --- a/components/bpmn-q/modeler-component/editor/popup/index.js +++ b/components/bpmn-q/modeler-component/editor/popup/index.js @@ -1,6 +1,6 @@ import CustomPopupMenu from "./CustomPopupMenu"; export default { - __init__: ["popupMenu"], - popupMenu: ["type", CustomPopupMenu], -}; \ No newline at end of file + __init__: ["popupMenu"], + popupMenu: ["type", CustomPopupMenu], +}; diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/bpmn-fonts.css b/components/bpmn-q/modeler-component/editor/resources/styling/bpmn-fonts.css index 954c3124..e6bdbb89 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/bpmn-fonts.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/bpmn-fonts.css @@ -1,26 +1,34 @@ @font-face { - font-family: 'bpmn'; - src: url('bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?16406289'); - src: url('bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?16406289#iefix') format('embedded-opentype'), - url('bpmn-js/dist/assets/bpmn-font/font/bpmn.svg?16406289#bpmn') format('svg'); - font-weight: normal; - font-style: normal; + font-family: "bpmn"; + src: url("bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?16406289"); + src: url("bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?16406289#iefix") + format("embedded-opentype"), + url("bpmn-js/dist/assets/bpmn-font/font/bpmn.svg?16406289#bpmn") + format("svg"); + font-weight: normal; + font-style: normal; } @font-face { - font-family: 'bpmn'; - src: url('data:application/octet-stream;base64,d09GRgABAAAAAD6EAAsAAAAAukAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAARAAAAGA+JEs0Y21hcAAAAYgAAAJyAAAHdFN1l/hnbHlmAAAD/AAANDcAAKHsuTD6FGhlYWQAADg0AAAAMAAAADYjByJvaGhlYQAAOGQAAAAbAAAAJAc8A79obXR4AAA4gAAAABEAAAGwpeAAAGxvY2EAADiUAAAA2gAAANrvU9ESbWF4cAAAOXAAAAAfAAAAIAGWBHZuYW1lAAA5kAAAAVIAAAI9ejh1lXBvc3QAADrkAAADngAACigQ+Ny7eJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGF+wTiBgZWBgamKaQ8DA0MPhGZ8wGDIyAQUZWBlZsAKAtJcUxgOvGB88ZA56H8WQxTzGoZpQGFGFEVMALXcDdZ4nOXVR1JUUQCF4b+hQaJIzkmUJEEyAgJKRslRspLtbbIEywkMYHZ3off0caRLEOqj4N3zN93FA4AsIDPqipKQcUYifkbiIF5NpK9nkpe+nkz0xK9LeBGvZIREKAq94Takwl14CE+/fkG8lgzFoS997T486tpfb4nYl9DOBT/S7z/T1zLiIybjM8nmGTnkxu+XTwGFPKcofrfiWJRSRjkVVFJFNTXUUkc9DTTSRDMvaeEVr2mlLT52B528ic+7mx56eUsf/QwwyBDDjDDKO8YYZ4L3TDLFNB/4yAyzzDHPAossscwnPrPCKmuss8EmW2yzwy577POFAw454pgTTjnjK984j6/okiuuueGW76Tiy8r+59X/f28F+pBV9uerlO4R070VkhZ//oQs070Ysk33Y3hmuk9DjqHzXEPneYbO8w2dF5ju31Bo6Py56dmFIkPnxYbOSwydl1q8FwllhrblhrYVhraVhrZVhrbVhrY1hra1hrZ1hrb1hrYNhraNhrZNhrbNhrYvDW1bDG1fGdq+NrRtNbRtM7RtN7TtMLTtNLR9Y2jbZWjbbWjbY2jba/qbE/oMbfsNbQcMbQct/i4Thgx1w4a6EUPdqKHunaFuzFA3bqibMNS9N9RNGuqmDHXThroPhrqPhroZQ92soW7OUDdvqFsw1C0a6pYMdcuGuk+Gus+GuhVD3aqhbs1Qt26o2zDUbRrqtgx124a6HUPdrqFuz1C3b6j7Yqg7MNQdGuqODHXHhroTQ92poe7MUPfVUPfNUHduqLsw1F0a6q4MddeGuhtD3a2hLmWouzPU3RvqHgx1jxb/HxCejNRvRMJDLAAAeJztfQmcHMV1d1dV393TPVd3z8zu3Lszs+fcM3tIu6t7tbpvrS7QwSWwMLc5hImNwQgZ8AF2uMRhrhh8AHEc4xgHvjiYYDt2YuPY5LNjx3bwFTBy8n182uF71T2zu5JW14LEz46mp6/q6uqqV69e/d+r19UMxzBvfY30418yTUwnk2eGmCUMg/iEoKEwgo1ZjBcqqSwql1LpVDKRTnIJ3vJFEA2u+iqpZJxrQyTuSyQ1RMMHUJp0o9YELxA+guwo6AmllZeFx2VBkPF7JUGQHuUk3vCqC2o/lXVUQC4ZfUN24SGkKbWf1sYQQaS/tm0RwqqOO7HXdMHWhTTZjWBveeBMx79EmBhBqQfrLpeOq3LITzB6SVfGvie5VBm3K+58TUCfR1619jVFw34FDco0jdrfKn6sKWhI9TIEyn4n+RJJMG7Gz8yCcicExPstZBaqqFJKo5Qg4daETowkrFFSpOsgKdM1S5Kwov26Utsqy2ifEqUbOFF0dEPtqtpV6GqMWYKeY2GDCMs+Rzcme1gQSdAUogclM2AncMPPxyMZ3FGSYngox+3kq6QF6jDPLGTOZK5k9kBpUgJPF9MyrQiqDuBqJd2N0ql0MiVULBpq8lDDqXQ3XKkOIKse37kEIRjidiMnrmDa14AJ0qkKRK/AmcULplW1KlUIhKBUmtcRsAoNEywhzQtp+xJ644Kzts6arajVGTNXLfurteuvX8bzkeYrli1fRwz39s2jfTNEaebA0MNPL1m+I2ZYvT0PnLFxzYpCGbEtLbPmrlxy7+CseWwkNiN3zezlIws6OhEaHhhatGBvseI3/On0zJGmcN97eyrtnbsu6oukCsHWfKC9M5hrDRZSvT27ShdeVL6oZaQ00gqXSAJXyhs2X7DpiwtHMhmlOXzFp0c3rlp1Q6aNw3mSz61au33940Nz+t0dnbc+tWZ09tylHSSXXbZy46rlSys9bndrrrD3gYWLCx25HO5snz+yfNHNnVlByhfL82bf1j8z0XFhpO+iSk9n+6739oVpVnLBzvZAHrJCenovLO2CrLRCVlrgEsN4oN4etPmPQB0qzFLmYeYAGkSfQK/jGfha/BzhyCbyGPkDOwi16Yl7uKJQNeJly5NPCrAYsOqCEWWL1fHFGl8GuXKWS6aTZVjTVlLnosFBlvJtugxLMovgD/t0uX4Ex/SUHkOIE4VL6tiAhyCD7mHRUf3EPqDHThg2olxxMFjOsvF80mku7CH5ifJ2lnAyK9Ec0ZxBythOg6YFydulggRJ0rCfAsE02TA0uCzhoAQmTRZKAZkW6YZN1gtAywQZHy8ShoJC5jkjaWebbi2jCCtdohJNA+Kkx5fq+JJlOZp9wbJvtMss2u2eh4uDAmyKdry08wRSTxNSpY+IYkdIQPLKeOJ2khxNSQcyDeLyICoDbcqDuDgol6twGYqGaSKUTLgYRfWc0iMIo+kiSJqmTknYDMmJSWF80VlK/QYHDCp0g+yHlGlGaPI0AqRhPwCSgmQpJccrxYwndQiBE5Hm3U6obKdhF4VFaXRn7W///eK7ay/crfAKp3Icn+JlkcUgkTDPSmwHJ3Hwk7kIrPSADUggtFgWIdgKKY7n4EZ+P8dyEIixiFVBFOASLCBKdCxh4oETSQKRxoJwx87qRRLLg7yDPkGkG1hEFlJlRcTKY69zrMjBCcuqsIoE2emxEtLp7YTD8HBCj0RW4gSOc/OYxzo8z4knQFICxMSEJud1yZzAw4kosvCQFKQHiQqwCxFREAiUx84bZBgResxCuVMsDeZcT7AiD8GQBQNyQZCEVCRJxC4GPB1Bke0DjpUgEvQMuL4Qe7V/PElimzhQKnQ9B+WFhHgyHlUwESQEIdCzQEY4yckPpIzsBfYIibDCdYSFBgl5Avkg7NjzkCwkRqPaP4xEekRoKSEtQpPiyPhFmnEJ/w5u4p0wqDM7ZZNWqQp38FDBMhadRwt2HMiPeOARDihI7wFiw+Mgx1iGxAnfyDJG3kaOBTttyCyHXwPi0IsQBmeUB1S4DTscxEoiJxCoVp6jJeQb90tO7Y0B/YGmQDpe5YH0UMO0FqDWYKVcA5dtHmFpDoAvgGeIQVhZCEIR6vQHStmEFtHdkBMkyM2K4GUVYAHKBazbfgKySa9yLsgtJ3IpO0GbI23uBC6EEGyTneVqcyEWjx1mhCguCbkQy9FD2laCQBs/D61FkSWuzvfAlT6ORoF/awRyLanEhb203SA/JYPZaV+EtYnlWbuaaLEINDAI5f3IboGUUViu2UW3pMHib9ZeQD35DQNf/vJu9HtKd4FiFJsoHJRdatQ9h8Z5lRC7YlfRmhckSUbSpNYj+FkXy8YoU9ncLUErn/Rk0W6ZInII47ROxMN9EOw0aXVyi6ary3ISkKAMKlunCSeoEUpV/ySJAStnV6bdADjKjqTBp8BAmGCH6iIniVQONNiFRknX+UunhJFUCVHmp+xCFACnUkNqqYTlx1snnmiGLDFpWSUgQ79NfRenTMSSk0QgAqspsKPNDRoYZkVgiTqjcA41EHHbMo0gGdUfTmkjIZrGYzaT0bqHKnCxTZxLomVFbILl6KVGhcLlSfwZgdU+ZJ1W1oVoEixbl8gSlwaekWS2AHQVKVfy4mQW5Bsy2uSBZIR1w3VR4j0QChI/DisnA8BjUZCTEcs3CiaiNodbJS7R6AMsDsQq7rXpL0CGQkRR7Crz68BOIm04Ds9A0zSA6khw11unLVI5EH2Q//uo8IYOQbIbtMpztNyUOUA+2qKOtQVBXVYBO9jyDWqYJ27Cj1cX79SNMALxCK9CQ7RlnJ22IxFtppEaYhTVBSlNHQQczSwIbOgBqQCz+wCQklBRvNQQsGRCDDqdDdQald1sne2QG/odwklEpknrcMir/volEJG8xNo9C2k8GwQz8N7kHtDubhwZzTAM1dnuIH9DkowJeL+PGWDmUrSvEcD5GIA8hec20p+A+faSTjkLIHsf1KQFQFJI+410K6wA0/evf/jP5qKhmf33f37F6hvSbaDARS5fuuX8tSuX9/VbltXROXf+hvVr18+Zk04Rkoxc/InBygXZ77z4YuBWNNo+J0ESaP619z/18MDgQikSvmrFqvXr1qy6MZXNo1TrnLmrlz0yPJItuXXT7K0sX7Zuw9KF7x14i0ktiX/i1ltR5tYzd5y146yzzorkLEaC8j0AuDgGmNhiVjLPoLnofvQfuIDvhXICmCsaRUBhgB/hAE4B4paLvg5UR4822BkkBwFiQDxUaWPraFjnjagM4IciqqjLxjgUOGb5pA0iFRsdC0kHYnIU4LIG/BvglEJV57wOhjEFghQ1GrpsUEQ+aMPUwRTVESFDE2Dy4MVG74OQW+QgNht3NRDm5CVdh3F1WOiAOXtx4CFLi+Bq4E14puXkgUyoBmVnBdBZLZZtYti4kFA0SMmA7QMHHBbrSJSCz2JUoM8AUlsOGKyj96ychGNQMdBnd+/eu3v3ZVt274Z/UXDxLpeQdmkOooAWydJO2QLJAr82uuG5KHShVATwRAY9kXcJr1JZTyj7UwRGkYWINEAcPBXUdksgFE4SGToqyZH7GIBhHUcBNkEcDYGugEp/5OACgdRxJAAQbuwNTkB4QorTCJh2O/ShdrPD9H7a7GnvYbdVKmzqXQeVuxoFGfTyBOayU6lDI4iDbbBDiwUSiAoalhc5/CRI/XrnZIsruMLRBs5TccE7mNeWp5RcGHEI+sXx6LQ7Eg88bONmO44tBWWn6HZeaMfsEARCBNYBRxQlIRBd+At29y6zOsh4nYIEEOUAZTi763dJPO/nkJcXOOQszUiBHfwVKuttkUszMPa/qYiya4J1NeQeUUBUgUBtkIJWIF2gKyYuEsbExRKXBCjJSYRiAD/2AJD2YJDm0FVgAOoctq96KCyLU0bau9sCRrp2M/o4SEdEC2X3dHWoyzqE4aCzhISgfCztApZN4FhaesBeWNSgd+WderYxA+2PvbJIKxlktAy4UBWdPoeFHohQ1QCqhfIMSHbKRdgRyCDvVRu/OzxiV7OdqNMZNXiMinKK+gimcMPGFrR2G+AeQd8h2nvIKJTeBll1FQDuIFhtoAtKPWAf4EAeU7kPHSCLal+HR9rZLUL2eFxPl7dBOpQUAQjheFmwKeDAMbgKLQ1xgp6C9KBghK3/BaKRTgBGlF98DtZr4gWf4PLx0HqjrgYv0IXiMjs5SRIhOd6tUEjAc1k4hmYtUJWp9iRVAlmqB0I3R8ugIpvkEkUSlGv4OrYmOm9jHVZQKQSkeNaG7RRLsHY/CICo3h4BnXJUPxnvl5FT/xKyCc9Cj28XAQEcpJSgR5iiWAcmuBCGvhLBypAhfANTpPY9DVF7WCotmBY1WAndiFq+aL8paDid4iPIKlQrRWorox0o9gxcttZrWJnODeoDy2N6i9WcsIKJxDkXZrsGTVWZt63k8iJDEz0qP/P8eaIUX1yagz9U3jJj1t6q6O2P6fctF9iPZi44P9PeRZqs1efki6tzCPmbIHdYGj5/oP+yM5tbaBZpPvHv8B44EqDfY3ye4vjy5g76w3vGHsXrGytEd/rJr5A43CMyCcACC5hNzEXM9czt1IYrxAEQxCs+Pkl7f4ABFBdYFBJUKAoQUlbd1GeZFYoLOIimI9sAWLVNfINAnIoTYDiWxAFUHkAALTgn0L63XKKplbsRcmBGgl6z76fx6k+geCSZyiK0/8wz0XVf21Gsfbd4rtrc5o3Iol9t9oiaFCyGNVORmwOG+KuYwkc0rj3hN1itb97Y6+Emd5PmUgAVhV2mW9bdriAn4j/3U9kB+DGG3QGf5eOx68CnLRZEoCK6fAm3CMztwp/1tUT0kOVpJaxVLZc6Bewa+5cQx5oe37K5FktaPct7eFx4/AmUenrDBvQ9kL+i5ccIJCLtjUCh11SqZy2TDVdI5bIDLl9GOaNDVsOelM8VCOk+3t3S7DJ11VT9te9wfiXsFl3SrBkejfVphfdUBMnLBbWEJXUFUiEuqLf6hcGZolJ7SrK8kos3XLoihwIpwach2at5XXKnpOiqEchTnpiwEepMFGr4UuZFlEGXoC9iDW/E9+L/IovJLeSf2Ch7HsWAQPs6BKQ1XImAaOjGtnEXqlYDRocq7AeIQ5m/Ax23oU5nj9tMlzxRO50NQ07cTMdxRzWpJU/QphbmpjCplY9oUksSB26eiAl2OhZYzJ2wATaJjtd8CTRij8t8mfQ59DiKBdHijmpATKL9QxdawcDaa/tmrkDs3N6OQmq46NM6Zi1f1BOSzMDy7ZI/DDBGpArv6Kg3pQ2y2tsxyH0BINc7aZDj0PW2kvYOWuSksb8/cYsciz8yfTMZN/ZZB8a9M3YyDT10DDuZ57jMZOwjgCXehlX5P99Jo7I4tv8dMyqz+HMnbD5lAeuz07Cfsp94R0yNLhx6B0yNLC6hRf29141GDOM9QwMXhYw0SsxcuHgwI4R6lqyIt3YWvMkK0j20PQUTc8XfnrgVDxrKO2TGm2G3uXfcjIc/847Y8dAVb9OQdj+VdO+cIY0seXuWNPytd8/0DErVO2p7rv3d2zSyAuHfnpUVdK+3b5d0LC9v1zJ5uB/Ce5irTrYfguWfbBAsTLIInny/g8C+fVft27e1tWXfvoX33XfSfQs2L116zd13X3N34J6Re+4ZYUBPPJTe5wHFLznpFJ/CdnzSST3vEFPzSSb1g1PZpQ/2F5o9tb9QNaGzUWhKUXYQR/Egm8WDOMvqGJD0UbyF9oOM06l3z0030a0ObYq96Sba+qYIPYrL0BPHunci1DaP2GW6gzwLZWpiCswiu0yTeadiK5MO9xw8nkA5pGJfP5IrUBr9+oLtZw4OyUrvzIHVS59etW5oznJRCDdduWLVqEtfNkwrmgz1zhief1u1t+BOZYaSKxccyZeHJKqVjZvP3/RXC0bSKajy/t77129ctebmtnYW3YLbMvMWLFt4U3de1dpy+Rs/OTAraZi7juSMwzAylPtj5G9JkmlmqswKZidzIbScK5irmfczH6RUSE5FhzoZCsVJhNBQfVTFbkkH3Wc69HEaU6FYmES9bmpGcahn2J49nqQPhJgwaUVvILx9y8ah2aYR6c4ODz85umnRyLmG2bRleOHann6O27R2dW+f7m4vlUcWP71qTf/McLMkd5e/tIvjVi9bVChJSqWnb/HwPfPmd+UifqNSvGUZIYvmzeno5MVquWfe3FsvjyVwNN6X2b4Dddd+VauFXnR+JNHdtXT51rVPLVlaLLdreq77o2vXPXFXperX8qirY/GSjSsfnTs/3S7H4kOD61aPbhxZd15HR/vwwtXL7umf0dTkak0NDj24bGV/35xcOjU0a9GCmwslr79pzcDQHbPndUQTV6q129A1gdrTKB0MjgQCawKBJYEA8KN73N5Bx342ME+C+rQI3YfG8DDeg79FPORc8pfQSy86ykjQae+o095Rp9Q76qABsOxpB6nTDlKnHaT+GB2kDhl/vP20i9RpF6nTLlJ/jC5SjDiuq/mZNqYbdJuK7Sk1u6Hh+CkUL1B9tuRgenrup1cS9Lxkv+NwRGi+/6qdZ88YGJhx9s79jYOrdm3ZWK5Wyxu3vNI4aDsUVR90g32QP+gG++B7R8LG8rhflDMWeAHzPbQFPQP0WIy/dIKjf+S0u9S75S51wiNh/addqk67VE3XpeoEx2HQ8Gmnq9NOV2/P6cr2ZSIxvH7cl8mIl7n6+sKECxNev2PHC/bf8Wn+J/LP+FWmmVnLXM7sYaivb4IXehHtqW3XJLjbNrVVyr2oRK1wtMuO0m6tmC8MItOg3TcNM+CQdniJVBalyqVqifoypbsRHBaqxYqQy/O2Va9g27PTubxpUdNRfa34EhoyPH77JdcBVPaUulGSixciNFBDyXiimwYOIDTcWkAyLyWsiI7QYqRHrLiVX0hNuSADAALxsioD2/FezMuUllKkJSJRisoArmUNkC2WEpR/1VR7SqWcXPDm/HcbWZ9WHi3DH/eVN5RKG8pjX8d9rQMtLQOtY1+393h7Puk1NUXV/c34Qx/CzX5dtdqT+ayj29qKqqqbLl3xgrwEHoH6lOFHvc7oOfITMaF53Ih8QOVBV4MfBwdm7ILF8LsggRaVRsu1XeXREuzRx2A/o2Vma21X68wW2KOPwd6uM6jnW/HH6/WsMz7qt0Yhk5Gsxo24UN+PksqB3+7b9xzuHvvuc/U9/vjYJ/F533niiSeSDz30UJ1nVuKvMCxgNz/lGZpQ3BOfbOa7A39l7L14x9jdiLvrrg133YW/EhzbjL/SCQFv0vMNd9Fk2Lp/fIKxmCIz6Fi1D/eOT1qTDbIHucdbwhFsi+i1dQ+9fyoneXHN8qW9feZUXvLL5k82D5HE/N1TOcmnU7PnrpzkI19eUfeRR/84WSmdXD7H/7//xL3/KRxME4hoERqjKqSFaXj/H/Akitddag0nb7hJ0OYkIuFrCtN4BeCfkvPOe7j2F5GN3XeCvuBd0z7n0o8uYaYYS9zBnHfyR7bqPpMnfzxrzYQf5EkfzNpe922kvHM4XXcyl51sunKH8xsvnHwaI3wYh2pB5aST+/IpONps8R061tY/rbG28cHX6YytBZ1B1GmMpD0wPh5alz+vkc/gl0DedzBZZgZI66N2mR305fVko9v0gVCvhtGEgP1B+4J2+OPe9gVtbQvax/4e94bzzc358KW1z15iH30O+Wu/uWTJku4lS/BLEKW23omKHoV9DmLW1kM8VKq9BMfoUTiu/XntN7+1Kt30HmaKPBenkWfI7svOc3FfPavQL49n9dJwrrk593ma1dZ/OP5s/vqRbJZh8KT8pY+VtwkIcsT8jH3dzs1RstHIwqF8OWPafOk08ekw5vfHG+o0ePOyifY2lXw7n7ny5PcbWKAHFp39IorS9mV64RT0I4ElltWe9u8O5guqCQpc0MvLSPGbJ3+mi9m1N2ZZuQ7jxqX3rmhZuGtECPsFhY/PWUzbm3DY+PrVzF4H05+ykXUrcZBBDPDehEUsig4ziZ3K8fbs4AUH24DY1vmFyUagzuqhVqCTPia/deRgQ0FyxvAkOwHKH2woOAyHzmBGThyHHqndnDgaXXbkhnDCmPSlI7P2wWUuM30nXuYGxpxGGSeDxhMu1dcaMFAY17VpX9MBfWEV0Mjg8fc6lq2agTqetNUkW8cjcPryMbRWhN6sNaFv1x4zt23b9j60rvbY5eib+NVjqprDtevRNR8895xzjLPPPhstveACu7+Ejms36J4jzFN2j9WL+PGVDyO/RdV6s2hWe0HPL5fSRWrsz1fSA6gEVdSBbCsCfS2Jc950sl9syqK8kCsYBasXmXZSVEgYfisfQZbZj8xqseAME3QgI04PjbyfT8adY4hPqWTYcYxe5AcJluTTvYiOKJRTM5E9qAB5gH+xVEQvubwzv5T1iRiJ7q7MrFd+FApz7pDm4nm1qUkTA5rB67CtPc/xrObjddbFe2UOIWrlE8SwasoYcwLB6qI7LuJFVhRY/fdoAI4EWa89h3RBomM3ilJ7/ZscT3ge8/yPftQbibsEhHlPy/N5gUX443LmuthgIOZrceUTsyIhhJLNukdTAhGLxPv9skvzhH2KiuBBmiH4ur2yilESiUJIksOq4YKEOd42qnJqVOKTLCd7fWqS512GKiUJK3p0OjaE/GYJRTrlFm/YKAZvjLGoYSfag3fW5z9ybAdxrpVr9cTPIB1jGXx97au1W9DF6JKxJrxz7FVsJf/5xf37a702XmrwcPKInGtCGFRPIgUhlV/2bu2FPw45+7Ff4FBHX1/H2C9gi1/tOaO3tqf3jB7Yo8thv6u3rbanrbe3DV3e1uvYpybGk/qYRczmExlJOtGu6PjGl3ZOpxc5rjGovznRvuDgOQkqzBAz/0Qlo+A3oPoLRedKquqnU11NQ0peS+cquvhiPRZwGe6hn9VeuHsawvL/3H036slLzaak4xK3efOXdzv9HcWWScCWRZA7W48TV9ax4jgqoQFGA8Y0kKXNNYL/EGSJXrtgx9bBwwDkZUuXrSVN3PaN63v6eHHW0OzVK76weGmuGDPNvr5HAAkszxfw4tnzVi6+a2BoLhuNzShcV16xcEFbO1k4c2ikgR8zM5cAaShMPP9gmLgSYCKP86hYWL1u+/rPDM1qava1dy5Y8NTa0dnzl3YXcstXbVx1V7mq6elc3oaJndl8V8fwyIqRGzu7xlHiQLxrapvDucxFp8gvvJoGYCGcGnfwG3/604duAk3m7B//+KSj70sLha/ueP4qf2bmlZUPHebHO3OaOlwDjk3Lc7dtAoFNx1d3EuaqY8ud+AGyEkrmZyIgc5lW6GaRp1ioQEcKvTjdppICdLbQ2UN/LFAtwDnED9Q+jTbVPv35NWtXJGOpzOre3mgolshx5Va3tzk8p+1Z8h9jldUr+65OeOdXm5dn2ne0ZFJtyV1ef24gFW/YCyZ8val38NSzQ+Z1YmQxdfmuzxHpeH7XZ4p0HMCLg5h6djp+4HAk5HpR7mhTR+5mWYy205EaF3Xc2L7XHsvEhN27ne5ddNxwO8Is6z08InWqOCTazUdxGP/6cSZ76PPZbRAxdPjTc4fRLsREj0A7HVnxatoSkkfzjEc3CP+4EskrX35m+BfDRynIT7hPjo1de+Mrrzi2lDvr70zTEYmDHpwQJNJ/0JNoep9AN9DHofcdkrhSW+Bk43CeiDPdR5oxVEOARACHlKhcjyDAzoCc6YDR0YrZu7WnZ+vFdNNbXFsorD2bbo5S4O/WY8PGqseGzeE+/SPTkwUpgc6V0LhhXNmelmCoFLKzJcWrZVrPK/d0doeCrGl2RhZNQ0qswKHgaKmnWMq0tvJCLFzIXpgrxQKhQ8pcmab8E+LTKh9Vy6ZRmG2PP2HzqmMnCUHfOJ5rUDcOy3Rhcp84DifQazu2bJw12/DHzly44HPrNywcOc+0NNe2kcWjpfTI3Fkd0B3niqUbbpsxkEgJgATazyaJbNfyFdvWPnVXoZjR9GzXR9esX7vh3kqPV7mltQVA0vybi+UOCg+G7pw33BmLH8RTISbL9B6Vvof03mjiraE4aFqpCRLPGFi97GASNzco3Nd/CyWwns4MDa9tWXABiACrw72AJHoqG7YAWnGo3NzfN07ljzSInM2FKI3vGJyV7FgbKi0dm/vhfGli3KzBI9Vp9pL21CEcxarTssFfu3ZNz8W1D6E7vzENjrlz3rzuTbG9e5H3wcPaed/0eL4Ok6ZTlI2Ad6ZRiEts6MJM9f7aWdDPnvTRqKn1jJOODt83hVpykkHiXYcrMdwU7wyeqvcFT9lbgqfw9cCpxuNnT2c83pGNaZPCoWnZBieE5G3XDAJWGpyG1vt0Q1yObOh65ZUuR3cDQTMP3w4IfBmzilnHbGTOgPLZEwUJ1PSRbxRKR/Fq0YYJ1O0mTR1voEhc3NZ2xyPahY9TmjTiUfcc61AH22tYvLothZvcmRa3RZCkNg2PoBc5tGQex1p60pVw+8MtSxbW5qJQ2kijRjyAosF5tZ9H3UmODegtakI3UHNcfuXnP9/9r1/9Ob4do0KuuzuTCbS1GhHDFXQnOzpI17queMLfoiWsSDAcb+/u6A6HJkVSNT3eZpmdnYmEv1VNmjRSOmEYl5jmpfa2wQMTNtXzmRdOwJqacAyN46bLPLVdwm2TTJeVas42XVYrpdSE6bIDNYCZkMtPtlnyQOwoTYDqRH6BTybShgmakpGnNskORE2UtrWSHuYLjrESqiVPObIXpcr5Un0CKPgXj2XW/QtZtM4Lhl3UNKmU2m66yeMhstvDEkHUWNHn0ziZdYtfl/ychAWZ+m0TzGIjCFqFgHF+x1wJcxwRzy7O+ZBAXy3gXviMoND3Uj7zAZ46mdV+l/CZAvWKU8xZwzp1Xz22zTjD68MeI637VUtoDnS0Y2y6ZcmjA+klXm1tVlWvwguSn1fDioZMjtN5YgR5wX6Xg+MsjkcmcimKSIjp1gTBFFSBOpe6fTEjFFT9WszdWZARnV/MsUXfgM+EI+jTfFwrRw2Z29FH0UcP/JgkarvwmT/72eraEPraTxim4TfVia9gUsxS5gbmVuZT0KL8Bi+k6Yu00B+bVlkoF22vtapFd+WiUaR1A41jPFYVZIUFG2guoLNX05VqsZxMpatpATZCUaC+cXQmfsoJhk7ZwqKOFzoykmXa9ASISncHPchK8H6zUKHudaafT6RKFXSPaWZGMirf0yP683nCe8REMKGGYlrLFt70aHyTJzOSVvjeXkHJLGrzBc8L+tpG2uz4SnpR2jTRghakR5qVRDAuennipNESSqpNUS2pR5uUllALvVAo0AvJYFINJDRvLJsdymZjvmCwNRTCVzQ382p6JOML7AxGNqIWLRpSk8GE6BZJLidIIls9qyrC8zN+aycyLDvDvX28CiGmeV7Qmx5Jq3w1mWtkvymiJZGdiPPsIjzbK7YE7UxB8pFmNeEkvzw7q7t7VjYZaqU5qdffQeMn3cf2Jpho7YJt4xbSVtUTP+Z4CdZqGfTAd1577fVa9Dh4flXtIbTRuumD995b+5fGnHLEg+9hYsx65mobH0Imj+hR2dAxbJdKK2+PijhOlVFsGvy4R2W6NEhSdpdWLkEcIWfUkUDBmTcOZHu57lEZdzwq0WsdLYm0W3H3Z8ohjGfjYCnTl0knWs6VuJBblViRl91BDaSCLKkRtpl6jCN3ta/qtkcWmqhbq5t36bxaIhgrZiI2HEuY9AU+wq6NrUrWXkuuiuG7Dd/cVKI15PGHknn2l79kc8mQv20kNddnrKIvqtjOxgiJGCN/KG02eWOSX9RZFru9XjdmWV30SwkWc5YrGooGMfdvSPX6FVNVFNXkJV52eVCq95nr4fdML+O0+0l+hifsYzgtX8LjdR8c79IRMwXmO//UeUScgrkh1kw2PJ5Cr4cj+dOdfNv2YXjx5NN4Cnh5kkm97lAwSucGuI88A+1NZgLQd5aZBSDXzmOuYK5nPs7cz3yBeZb5LvNvzH/R2S+P4Fngo2Q3gYT2nJdWyhmGscd+0ymu3kqh3grOBJbFhEBlJlWm4Y5S2q4mG/s6ZrFSiqsnUbABLQcRoMIr1GmFXqJ4l9a/PWlmiq8/K1WlZw1LD5UXcJ6ojwjZ1cxR45vgjAvS6wMoZUegw9D1GON5FepxILto/5G8INSRrIp86pazVY1T8y+qOlEWdwlBnQ0s7GbH/qDIvCJys+KsJyC0reCR6y1W9gouSfJLSxcAUpr9K5cmueWZQc4T5NuWixp6v6phuRQUgm42NJcdez7IKU18wItDbqThV3UXQTnWHRDCTSzWDnxa1wjOw+O4LEYu/Bsd0JyrwOtBMZyCh409qqhYHFytepCyrMqjfWqH4gvqEY/K6Wp2Y1pErtpMxUXkju0lmv8dy3gckCRO1sRIKtquYI+rclabgF04dUTPDXQpdvNBDxs0kS/IqquQL8D1bJSBJoX1MSwjgVNEdmA5lHXzEJ2KE7OGRyR8lIUCR9NY5FQhl1N9SBnqFnx6P/JafHm14lM3zyKP4Y61SxTiUZevlrUuFNLO2qJ6kVy5ckCU3XB6/gYZ+9SNZ4tKD3LJnsE5qp8oqXVdknafx+QXhok3wEdm80uIQQQbGwf8CAUDgqGnfSaXyMrIH2CN2YQgjkgK8euqzAe9RMknIaPMVPL93FM1unZqBtVO0Wgaleeu8Xcs6XsIqm3pD4De+xU0Dz2AXsVFvI/OPHKE90JPv1X5rr1Vufngd223nn5n8vQ7k9N9Z7J5yjewcdfpVyNPvxr5Nuejp2M23yVJ/CvotVU6uzdKJKEzSfuNIu1TAKsm8cXffxlv+fmXxg6gK432gd34Vwf+A2+7afPmsQP4q6rRMasxv8GEDWIpczFzOXMVcy3zgROwOwpJDU1YcB0D7mTDrGPAFehAieFvmAWoATd5aKxDzbfHMmy8H9Bhpy5ahgRUcfla24Cs3W4/wS7RL/pE1e3Jtv3fe03F0iQT++1IWvpen+DDWBMN3icpbj979XXXzb9m53XHYRfZhJsrWdMVMBSPquumEcTNZjDg9SmG6Fc9LrfPCAdCHi0UNE2XhSEIYvkst2yFfBBH8LloHMuvqkOqOsv+H2LvbWeyJ2ABsqrUPigci0jcm2/WXGj//zuOAp7xkY+Uaq8ia2/dtjiMH6uP/W+zZ+BMOMqEx67AmG2j8ZQcS0R9WJkf91dMO4NkBbu6C5QDjEkXjaOHxw4NR/t3b8lFqZdESyy7DonrsrGkgIRENLdl99WjHSEPz3uCnZte29QZpIehjtEvQoNsKm15c0upSWD99Wt+9iihNNl68JraGhqMH/Mrbk+IrbWG/B7J75c8vib0I4JCHrfiTwJDSbIsaZLPBxs4ckn+84GzVL9f1dg2RXauKHLu6IFvTATa32LYR/4GcKOjm1aYxcwmZidzDXPbCWujjo7oqHjOOAtff9+BKpyIqpH8lGplevwjDPV07W8w2Iol1E6xUGm02SPrie7lWQV51a3bXRqvdvydqrNKa6wpJAVcfM8qduw1WeEVJdsGQi0YZFu7WJMVXfgJUPdIIMjqQSEaEuhXGEDf47JUveyi+t5nJWo9EytrVUh61xr7CwxUnZtzJu8VdZdH29oP+IM/mvZ2MdZB56GaEehCyjKqvYXa4zmZM5Tz+4mCRFZRuhfpiu7R9blZzsOJPq32AmrSCzu6QGuTK7sHBMmLQvq5m6jOt/Ec+gEG6JclmW2xQG8lwWGq9XktrjeEuWAABfJ0BqQp7DtnnXzr2WQXg5OuYK2c5JBwssejD3JfmGTPERidCTOdzACzDCi8i7ma2cN8inmU+SLzDeYHzG8pvdOCFa9UoekkQUCZ5E/ajIP/YcFtL9vWNjWQ+8Xw4DXXXPM/wnyDHiide/fY3A9nRj72SteGDWjTn6zJZhLv02/3mEwrU2WGmVHmHOZS5oPMR5n7mc8xzzHfYX5x0Jd8/pSZfvKngXb8j2D3iY8Ovfwny+jvir+RUHc0PPmjIrctGHdMPNmjIeeWGo6M8jjWZBmN8TMJQPrDzHrmbOZ9zF7bk+Bw/7Z3DWmmD3N7Y99FiInePMwlDr32LkHL+rfpVmEf9ST3ObxrNpb659eqxuRx4+5xot8Rakp2JbpSqUCTO9YfD7fEA0bUZSnNoZaOVEcm2VyIe8OVGUZTUG9yi2hkIF1q8gt8QDe0ppLhkaWmQMIbCT9UTXT53aIQ9HVHtUCT4A+0WN3NGnPIO6JLmMuYJwCNPXsC1oVD/FmSh/mzQIuf7M5SPNSdBWIJdXcWOgfPJHeWqpEUjuTOkjyGO8uxtO6FLi3YHRBJsoVo0SgmCmfopqgbotXPhgRW0wLZoEhaWjghlA2p+myXHsyGRDaRJGIgG5C9naYEBDXcBqewOBLBROUstym6/ZIp+Tyi6bZ4mcOQMidzptsUPD5J8YfDbeGwobrdltt9HOp+weNlxWA24JVnu8xeU/J64YEmp3AoGmEVnBxK0MtBlzZbVyF3AtfSSmiA5kKzkewNdAd4LmFEEWsXToC8IQtyLUB+IFc4FoNcsybNtU+qF8fkVIIL4UwkkgmbNJuW+6B3ME9kzopjzW11bAI0fFu24GUg/VKgOVCUpDleiabFURlVdkSVp5RKCNwMKr8E53sVqXQF/ZcE1Ofm753vAgYqy5aGP/gp2dSwLyDWfoOwN/1z3Lnz6qvxUnum1OyWrJwUvs3zfmXfhh10hx5QdfHL4YuaFW3dnfZv4YoVTP2bjI5sdnSaLmYOs4rZylzC3Hi8WszJF8qH6xfvezdl8iGof8O7JY8Pxyo7TsWMU6dosqlTMc0UQ2duq3+/YfL46UbmKSShxeh+VMML8c3428RLziNfZDG7+Gijqac/5HD6Qw6n9EMOhwwin3X6Uw6nP+Vw+lMOf5SfcjjCGH729DcdTn/T4fQ3Hf4ov+ng6Hx/jW8Zn1eaDqPTaRoBPGbJnvzYtr1btuBbxm7FF+XGfo2NsV8zMcCjvyOP428DHpUYDTTVABMHrazCzGKWMmuYDcwZ9syyu0BDex+zm/kwcwvzSeZ+5mHmM8znmb9kvsw8y/wv5hvMtwCnForlpDX1pmJVUx0onhCsCjWmV8opjo7h0S/hcfTTeKb9aTxOQ6AtmIVioVqCcKOYqqY5msLxbCYlY1r1dKMIzukpfRDoHEfJYTkplMpxj69Q9MSrfvqKHag++W2wbq3v7WNWCEBjMPxjv3WpEtF425Vqm9fFyR7OHxgrBnyc4hbcmizj1xW7qmWXV5APPCjkEd9NypDGgedhQ2Ye9aifk3jFI+qc2zzwLO/JKCx+xQOaHGmyImO5piDHYZ9b0LZPzl4gkNsC+7NgLdc+G92zx55n8578+A/hHCY52vwNVpgpuaE7ESwX0U0qTbyC27sWyQoolygSEgNhn6Hfni/nb8lXYC3nb4V11ISGqpBgE+FczcEmr2+WyqlBSfCl272C1y/xqmS0TTwvn3j8M7XHUE/t149YgSxDOfKtbwF//h40SYlxMR7GYIJMlEnabwlBBIuaveikp1OtsG2FNU731CMI1u68O781v81Zc678gcvypC1/4HJg9QPfL4xmJmUlnxndswl+N2N97PUceqq2xHkn8W7yPInZ7UWD3DDIU7QXJFh06tVWQOvUi3dX6vlUbfmnP715M161ZcuDJHbgGnI9XWv/9uCDF5636wK088IHqb3yrV+QT0EztCAl6H6jyBQQD9sqqkTpHBi+SorcXtuO0ZkY19ow7gKJ/zJLRkH2PwJt/K0H4OAMQmoZQjpA8H+fkA3AX7cjXH+H7q/x2XZe7ZZNJ2CFVn3gIdKOzx57DbtrYadc1MbyCHmSROpyoEHnVjpLDAJacnWaWja1ufpqz+ltHyXplTLKnlO7/4f3/+gDV2fGdp+D9mcyV+wbu3ffFT9ZSSI//OF/nQO/SOaye8794Q/3LVny1Uszd6N7Mpddlskcu5599fqccq3nIU73dfl11HrGvx9ty036tY3eZFc0pUiutgQ9BVo289abkJ/noMYDTDPIvBYmw3QyOabE9DAzmKHx2dSd6foMm8uoRbacnCqT3BRh9YpQ4Xfg+x74zd7WyPLY6vFD/NwPam78Qufj+S/mH3sMNhO/X04cNuYHbeTZsRE4+WZ8QWTP+560HeKOkMPx3NC8zJ70+NrTtevh+bWf5Go/Q+FJVGu8t9fwvVOgPZhMeNz/znG/s5JpZ2JiOi0NJdBkX7zdu3+2c+dHrty0aefOukvel22PvD+7IDBv+abr165dOztjt7nJzzAOf4LjOX94yockuhnSdN4x+z65GP8ncyHz784XEWAB5TaVRfnG22TUil43OEXrc6YK9pvAE8MJUerb57zPO/5yGu8M05ZL5Ypto3LS1nG2Pr+IUBQKeTqZYQRFqd/SIHbMkP2o4DgDCkYU1d/4s3oRHd8dH2LWUTyVHl8GsJOH6sT7b6hacbJG31SGXMFDnflMfq8FQAYT+mFsBAJcUTw+KxwEcMm5VU508S63lG4xfZY7zOGg1tJlNfem3RxaDAqO6m9SArJbkHk24NGaZa/OS9RjV+SNoCm4aOYknnCCQPsFinkAwUEwRXzxEHUdFQDQUX9cRPUYFJO9blFzyaokAozlXaLfFwgCsK79WIv3RQPdSdUHMT2mHk00h62wy63xLh3AM4hGl6EqqqjLfk+srl4KgB1/p6n+ePNgZo7LIlZMD7qDkqIgJeJLmvmUr6lgqbqY0ABSaU2RQMzT2t0cX99vCKqru7plQXN/c1e0M6RbdEpHT1IOuBd5WitWwqPoiuRpMvMt2WJsdiTpVjUUZ3m1d0Gwp6cT3Z7M+y0JVAG4rTk4lL5PMLLt3ohLBjVI8iUDKbNYnLHLSrd6S6Ot4e5WPaCJqhtxrogVa9IrgwWzrcsb0kG75IEcEW8mlOvgC/3tkfnNQY0f/9bWj8h78E/sdtxuf21rMbOVOavBr36TmvNopVv0FWWOIpIKZZ9iqVxKd2Ng1ySiGIeu/vrwF41RrFTLiSRP57qhV+h0mh3IKBRNaoYFvrXvQLhjRKE6eAGquhL7wcgOJGezN/c3R1kIaemNSj5p7OlCAX+1UBib3ZX0N7njQZVVAH9id7TaFE0juGamzOSCpL9t5sxKApKq3dHdjX/c2+G1tKo2P7c8k+jCeVTI59lwZOhTTT0LW2O9LVC1tQX5Rfl/Ti/wyLjhKg8aCBHK8XWjVnsTyiOutTemhQ3lm/mGDHqU3Er8k6yjDAKZV06CsKO+oTqaWPFO1PqrX9Uumzkw80uTVuKvjdRGZuD9M2ovD8wcGGis8J+Y6/xx/E17XKbKLDiRuQh4CDEhVgXOU4fNofndtrmZzNw2PC8zLwP/sWfwvGgpCv+xZ6LlaLQ8I1WtprCSqlRSY3/ASiCRCIz9IZhI4JfgrtpS5y70JKSSgPi1pfZNUfQkbPKVVO0D9EZ0baoC99U+QO9G1wYSjXdtnyU34/+GMjUDxTQSIdQYL6QHUTcCtSSCqnCAH+hc3h/mM3NWd+XW9Cv3fqDv4cT62n/W7iLRvIU2R7P4DZKZt3bDiv4wIdklueqivX2xWO2e2sfNfJSgHd4qY4/LvvVT8mH8Op09zB5RoXMIOnB7EAEEJx9esfzpjf504tm/WC1oyuiTT67Pb8Cvt2U2dHhp4BoI3PDkU+vPdOZ3+leyB79ho4Tx1HQ8SCAhpKMq2UMTMzuaXvx25r9x7BuQVC3TdF8Iv0HT83c2vZT474z+dy9CarWL7w/d79Dip5Dm6wxPfQEQZyFY6Md1yJ7aio21eTH0ldGxl9BO/Pr3N9RmxdDXRu/t6GD+PxpRDaMAeJxjYGRgYADi92LmJvH8Nl8ZuJlfAEUY7v9/n46g/2cxv2AOAnI5GJhAogBohg0jeJxjYGRgYA76nwUkXzAwgElGBlSQAwBdYAQDAHicY37BwMA8iocMBgARZGMlAAAAAAAAAAB+AOIBvgbaB0oKcArECt4L0BEmEd4SlhMAE3wUSBlsGdYdKh1EHh4eRB5qHtofTB/4ILYhGiF0IcQiAiJoIywkHiSYJPolXCY+JlwmlCckJ5YoMijkKUwpjCoqKmoqlirqK2QrwCwULHYs3i0+Lfguoi8QL6QwojC6MZox7jKeMvIzoDRWNeI2iDm4Odw6nDroO6Q8kD0+PrxAMEDaQb5CDEMeQ1xDqESGRShKVkpwS6ZL7kwWTERMWkyaTOJNPE12TahN0E8iT7hP6lBUUIpQsFDaUPYAAHicY2BkYGDIYcliUGAAASYg5gJCBob/YD4DACFQAhEAeJxdj71OwzAUhU/atEArMYBAYvOAEAIp/WFA9AGazq3UPT9O2iqxo8St1Kdh5AkYGXkKJBZehJPUdCCW4+9+91xHAXCBbzg4PFfcB3bgsjpwCye4sdymF5ZdrlvLHfRxb7lL/2S5h0c8W+7jEiFvcNwzVg/YWnZwilfLLZzjzXKb/t2yS/6w3ME1Pi136b8s97DEj+U+7pyXKMi3Kg7EQpa7dSQr4efhLCxyNZfpNgvKGuu9lGW11kqMvGFd+lLJMjAyFuFeVLt0bEwiklLnYqqVkVmmRVHqjYyMtzKmmAwGifVepHNECJDz5xRiksACEiV2WLMjUdH47IeYcRckhTl9yomM+fJo/85lM19xXrMSGMHD8Nj12VVNIoDhGTMRYs93xW+mGNMaJKwTZjRnBKbNTXU649I0RdPb0ET0HlbNVIEJBlzJv7zHFG/6BWb9Yn4AAHiclVb5e9pGEOWlPmrAAYPjJG2dw46TNq1y90zb9Ejv+76PRRrDxsuuursC57/vSiuQCIjP4Qc+NLMz896b2RG1UzX/qdcWfwRO4RmsYBVrWMez2EAdDTSxidNooY0tdNDFNs5gB2dxDufxHJ7HC9jFBVzEJVzGHvZxBQe4imt4ES/hOl7GKwhwAzdxC7dxB3dxD6/iNbyON/Am3sJ9vI138C4e4D28jw/wIR7iI3yMT/ApPsPn+AJf4it8jW/wLb7D9/gBP+In/Ixf8Ct+w+/4A3/iL/yNf/AvGHoIEYFwiD4G4HiEIwgMa6tWMzNo95mlMXscxEwzIUhc5NKSHlLEnSOgEUkbhMyGA/ctQxK3K/1SySBz6iS2XPaDIRnD+nTOWKbt5KgaxiQNs1zJG2XHXPQEUDBMhOWxoIZQKg6GTB+R7hRenpv2l2YzvC+ZuHly8JYPSV+vPD+H7qBatxLlxkTuY6XbJKPpwVTavSUpZMTT+PsnJzCH8MxsH/KMe0tly1TYNvRf4tycleTeSAzpwDJztNlLDJeu14FOBHVM0gtircLU4I/ulkvMwdoqe11lpatH0HfxQqU/C98p6VqSftsBm+AKlRAsNhR1S0Y6jpmMKFpJSbWKJFnS/eqaJmQiq1CNK5NxZ4boNKpTNnuCnRlFM5HXhkwmTKxrComPaNNFi4CFlo+4fTwjYVarW7bk9/DsAniu4bRbCVtwebRdEmKKeb0XD92kqNZknFOhBR13Js9ZQI85hZsTU1qqPnlQulvkTatz6TylG+GFOF0Y0vBO8TidnpLJ0yzFpPgvVbLLzy+6uHag1fiJ6SkLmhdfM6HmsW343mR9WjGu/Lq7GiMeUjPvlvekF6b9xG2jRXPlqxdqX66mkAO5d/K1UKRd1HdfOtXt6tK9MF0fjYhZFqjeIwptw8uRsd1Idch+1bMTxipN15bmLJAt6plHlvfszlO8hHKJbp08xM/eItVzEHnKK0vp5FA3WRQMVJivwmY+GV6j9Cubgm551PKjB8uz5xgW7UqP0rO4e3LaRU/djrY85G4d2vP5dZxfk1uCSXIZHCMb9Eiocd3ELKWmlGi7ZNKNREYnhbqSnq4LZozKDsxEs54akbdEbqFFlFIgas1YxspPEpdxYvOxS6z7vTFg6ai5pKt9rZK4ZenY5ZRSWf/OdX90nLQZmKYbURoHY00yHNQLkO0peXePDx2XZkSHzAHPHmq1/wGvxJ0SAAA=') format('woff'), - url('data:application/octet-stream;base64,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AADoGQAA6BkAAAAWAADoGgAA6BoAAAAXAADoGwAA6BsAAAAYAADoHAAA6BwAAAAZAADoHQAA6B0AAAAaAADoHgAA6B4AAAAbAADoHwAA6B8AAAAcAADoIAAA6CAAAAAdAADoIQAA6CEAAAAeAADoIgAA6CIAAAAfAADoIwAA6CMAAAAgAADoJAAA6CQAAAAhAADoJQAA6CUAAAAiAADoJgAA6CYAAAAjAADoJwAA6CcAAAAkAADoKAAA6CgAAAAlAADoKQAA6CkAAAAmAADoKgAA6CoAAAAnAADoKwAA6CsAAAAoAADoLAAA6CwAAAApAADoLQAA6C0AAAAqAADoLgAA6C4AAAArAADoLwAA6C8AAAAsAADoMQAA6DEAAAAtAADoMgAA6DIAAAAuAADoMwAA6DMAAAAvAADoNAAA6DQAAAAwAADoNQAA6DUAAAAxAADoNgAA6DYAAAAyAADoNwAA6DcAAAAzAADoOAAA6DgAAAA0AADoOQAA6DkAAAA1AADoOgAA6DoAAAA2AADoOwAA6DsAAAA3AADoPAAA6DwAAAA4AADoPQAA6D0AAAA5AADoPgAA6D4AAAA6AADoPwAA6D8AAAA7AADoQAAA6EAAAAA8AADoQQAA6EEAAAA9AADoQgAA6EIAAAA+AADoQwAA6EMAAAA/AADoRAAA6EQAAABAAADoRQAA6EUAAABBAADoRgAA6EYAAABCAADoRwAA6EcAAABDAADoSAAA6EgAAABEAADoSQAA6EkAAABFAADoSgAA6EoAAABGAADoSwAA6EsAAABHAADoTAAA6EwAAABIAADoTQAA6E0AAABJAADoTgAA6E4AAABKAADoTwAA6E8AAABLAADoUAAA6FAAAABMAADoUQAA6FEAAABNAADoUgAA6FIAAABOAADoUwAA6FMAAABPAADoVAAA6FQAAABQAADoVQAA6FUAAABRAADoVgAA6FYAAABSAADoVwAA6FcAAABTAADoWAAA6FgAAABUAADoWQAA6FkAAABVAADoWgAA6FoAAABWAADoWwAA6FsAAABXAADoXAAA6FwAAABYAADoXQAA6F0AAABZAADoXgAA6F4AAABaAADoXwAA6F8AAABbAADoYAAA6GAAAABcAADoYQAA6GEAAABdAADoYgAA6GIAAABeAADoYwAA6GMAAABfAADoZAAA6GQAAABg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EAgEAwICAQQEAwQFDAUNBAUEBQgFJgQJBAQEBAQEAQQDAQMBAwQECAQJBAICAQIGAwQF/kQEBQQGAgIBAgMDBQQBBA0JAQ0BBAUFBAEEBQUKBRkBBAEUBgUFBQsKCQUEBAMEAQIDAwQHEwUEBQUEBQUEJR4IBAQJDAMNAhIFBAUDARQEBAkIFiwFBQQFBQUEBRsEBgQBAQIDAwQEBAkDBgULBQUFBhQBHgUKBQUEAQQBBAUcDQQBBAUDAgECAgMECAH6/sgBODFaPrq6fQHyAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAMEAQICBQwFCQQDBgIBAQEBAQICBwICAwICAgECAwMIBQkIAwUCAQIBAgECAgIJAgEBAScCAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEOwECAwMEDQULAgMDAwMDAwIDAwojAwcDBwQOCwMHAwgDAwIBAQMCBAgJBAIECQQEBAQEBQMBAwEDBAEDEAQDAQMIAwEKAQMDAQMDAQMDAQYECQEDAgMDqQEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBC0BBAgEBAQEHgUKBQUFBQkFJwUEBQkKBDIEBQQIBAYCAgYICgUZAQQBFAYFBQULBiYFBgULBgsGFgYJCAMDBBACBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwICOQECAwMIBwQHBBoDCwsHBgQDBxQPAwcBCAUEBQgDAwICAgIHAQMVAQMLAQcQBAQMBAQEBAQECQQmBQUIBAQDBKABBAMDBAkJCQYBDAEGDAYFAwMCAQQBBAECBQQCAgEBAQQBAQEGAQEBAgEBAgMHBQQFBAgDAwkCAQYDEAMGAwIDAgMCAwIDBgIDAwMDAwdKAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgEnAQEDBgQJBQQEBAIFAwQJAQYJAwIBAgMCAQIDBAMKAggBAgECAgMBAgMDCAUECQQDAwMEAgECAQIBAgEQEAYCAwUJAwoBAgICDwIDBQYMFAECAQIBAgECBgIGCQQFBAQEAwMCAQEBAgICAQIBCAwDBAMCAQIDAgECEgEJBAMFBAQEBQkEAwMEAAAABQAA/5YDvQMjABYAMQA6AD4ARAAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhMGDwEXNRcRBycUFSclFBUnJicB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpQ/Nmwv0crKGI8BWSpDIgMiAUZ+obOmPj9JCR4delFTWFdVU4cmLzEBJiVDRFRPp0hKLzQQDxYWOTZQT1daTklwPv8AJk0hlI+PASiPYGRlZGVlZR4wFwAJAAD/ogO4AyAACwAXAFIAuwFEAaEB6AIyAp4AAAEVIxUzFTM1MzUjNQczFTMVIxUjNSM1MxMrAQcjByMHFQ8FFR8FMz8DMzczNzM3MxczFzMXMx8BMz8DNS8EIzUjJyMnFw8GFR8KMx8GFR8CMx8NMz8ENS8BNScjLwYjLwE1Iy8LIy8BIy8HIzUnNS8EIwUPASMPBBUPBBUHIwcVDwMjFQcVByMVDwQVByMPARUPAhUPAiMPAxUPAxUHFQ8KFQcVHwQ/BDM/BDU/JjUvAwEPBRUXFQcVBxUHFQcVBxUHFQcVBxUHFQcVBxUPBxUfBD8FMz8BMz8BNT8BMzczNzU3NTM1NzU3NTc1NzU3NTc1NzU3NSc1LwQFDwUVFxUXFRczHwUzHwEVFxUXFRcVMxcVFxUfBD8ENS8NNSc1JzUnNS8DAQ8XHwQ/BDM/AzM3MzczNzM3Mzc1NzM3NTczPwIzNzM1PwM1LwQFDwUVHwMVHwEzHwIzHwIVHwEzHwEVHwEzFzMXMxczHwIzFzMXFTMfBzMXFRczFTMXMz8ENS8EIycjJyMnIy8KIy8EIy8FIwGufX2MfX11Xn19Xn19MwcNBg0NBycNDgQDAwQBAgMDCQQFBgwGFwYLBgYGBikGBgYGBgUfBAUMAwMCAgYDCicGBwYNB+kFBAQDAwECAgIGAgIDBgUEAwgBDgIIAgYCBAECAgECAwIDBQQDBAMKBQYDCQQJBAQDBAIEBgEEAwIDAgMCAQIIAQUDBAIEAgIFAgcCAgECCQECAgMHAwIDAgECAwIGAgoF/fMFBwECAwIDAgMCAwIDBAECAgMCBAEEBAECAgICAgIBAgICBAICBAEBAQIDAgIKAQICAgEEAQIBAgECAQICAQQEAw4ECAQDAQECAwECAgIBAgECAQIBAgIDAgECCAIBAgICAQQCBAEGAgoCBgMKAwYDBQMCAQQGCAUCsgQFAwYCAQEBAQEBAQECAQMFAwUDBAMCBgMBAgMGBAkJBAQDAgEBAwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCIX2MfX2MfRd9Xn1+XQGTAQIGAQIEAgMECAkFBAQDBAECAgIEAgEBAQEBBQEGBAMJBQgIAgQGAQEBTwEBAwMEBAQFCQQGAQIBBgMEAggOAwgDBgMEAQICAwQDBAMHBwMIAxIKCAIEAgIDAwgKBwcBDAgEBAQEBAQECwEHAwYCBgIDBQMHAwIDCQMCAgcCAgICAQEBAgIEAgUMAQQCAgICAgECAgICAgEEAgECAgMEAQQBBAECAwIDAgECAwIBAgYCAQIGAwMCBQIBAg8DAgECAQIDBgMDAwMDAwMDAQQJBQgDAwMBBAMDBAYFAwIFAQIDAgMCAwIDAwIFAgMCDAIDAgMCAwQDBAMGAwoDBgIKAQYCBQQEBAkJBgQB/sYBAgIIBAQEBDMDCQMG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BAQDBAEEAwcBAwUEBAQEBAkEJgQJBQQEBQ2xBQMGBwEDCAMBFQMBAwMBCQEMAQkJAwMCAQIDAwgEAQQCAgIDAwgEBQQJAwgDAgQDAgMCAwIDAgMDAwIDAwMDAwMDAwYDIwIDBAQCAgUECAUBhAQEBAMDAwEGAwMCAwIDAgMCAwcWAQQBBAEGAwICBAQDBAUJBAQEAwQBAgICAQIBAgECAgIBAQEIAQEBAgIEAQIHAwIBAgMCAQIGAwkDAwMECf3HBAgDAwICAQIBAQEBAQECAQgCAQEBAwMEBAQFCQQDAwQBAgUDAgEBAQEBAQIBAQIDAwkEAo0FBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQIBf2uBAQEAwICAgUGDAYBDAEGBwMBAwMBBwcDAQcHAQMBAwQIBAQMBAQEBAkEBAQFAgEBBAQDFQMECgcZAwoCAwMDAwMDAwIGAwIDAgMCCA4BpQQEAQcKHAsHEgQLBAcEBAcECAQQCAQDAgIBBAQDBAURDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBQSnBQYGCwUMBQUGJgYKCAQCAwEBAgYECQQrBQkFBQUFCgUeBQoFBQUFCgQYCQUEAwQEAQEEAwQHJgUGBQsGCwbvBAUDBAQCAggCDAcIBwQDBAMEAwcGBw8CCQIDEQQDBAkJBAQDBAECAwIBAgMCAQISAQkEBgEDBAMBAwgEAwEDAQMBCAQEBAEECAEECgj99AQIBQQBBAQEDAQEBAgDAQMEAwQDBAkBDwIBAgMDBQIBBAMDAQIDAwgECQUHBQUQBgIGAg8HBgcDBAMEAwQHCAQEBwIBAgIDBAgEAq8ECAYCAQEBAQECCAIBAgIEARYBAQQDDAUJBAYFBAMKAggBAgECAgIHAQEBAQEEAwQIBPymBAQIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUElAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQ0CGgQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgEAjQBSzs5gFkeFRVtQD5xGhUnASI9SUw/KAcaOE1RISUsMhIjNgEPEQMDAQYZIkQI7QECAgUMBQkEAwYCAQEBAQECAgcCAgMCAgIBAgMDCAUJCAMFAgECAQIBAgICCQIBAQEnAQEDBQIEAQIPAwIBAgkMCQEJBAMDAQMDAQMLBwcJCQQDBAQBAQQDDAMEBgMDAgMDAwMDAwIDAwIDAgMCAwIDBAMWAwMICQkHAwQ8AQEDAwQNBQsCAwMDAwMDAgMDCiMDBwMHBA4LAwcDCAMDAgEBAwIECAkEAgQJBAQEBAQFAwEDAQMEAQMQBAMBAwgDAQoBAwMBAwMBAwMBBgQJAQMCAwMCqwEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBAF3AQIDAwgHBAcEGgMLCwcGBAMHFA8DBwEIBQQFCAMDAgICAgcBAxUBAwsBBxAEBAwEBAQEBAQJBCYFBQgEBAMEoQICAwMECQkJBgEMAQYMBgUDAwIBBAEEAQIFBAICAQEBBAEBAQYBAQECAQECAwcFBAUECAMDCQIBBgMQAwYDAgMCAwIDAgMGAgMDAwMDBwNNAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgECzgEBAQEHAgIEAwQEBAkEBQYDAgkCAQEBAQEBAgUBAgMDCAQFBAkDAwMHAQEBAVMBAgIEBwkJCgEJBwYHAwQDBAMEBwgHFAQNBAQgAwMCAgICAwMICQUGBQUEAQQBBAUcDQQBCAQBBAQECAEDAQMBAwQIAwEDBAMBBgQHAgoBBAQBAwQDAQMMAwEDAQMBCAQEBAEEBAQBBA0XBQUEAQQBCQUKBQwEBQQEAwQBAgUHDB4IBAkEFAcIBwQDBAMEAwcGBAMHBQQECQQEAwT+xQEECAQEBAQeBQoFBQUFCQUnBQQFCQoEMgQFBAgEBgICBggKBRkBBAEUBgUFBQsGJgUGBQsGCwYWBgkIAwMEEAEBBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwIC/rIBAwYE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AQQDBwEDBQQEBAQECQQmBAkFBAQFDa0ECAYHAQMIAwEVAwEDAwEJAQwBCQkDAwIBAgMDCAQBBAICAgMDCAQFBAkDCAMCBAMCAwIDAgMCAwMDAgMDAwMDAwMDBgMjAgMEBAICBQQIAYQFCAQDAwMBBgMDAgMCAwIDAgMHFgEEAQQBBgMCAgQEAwQFCQQEBAMEAQICAgECAQIBAgICAQEBCAEBAQICBAECBwMCAQIDAgECBgMJAwMDBP3DBQQIAwMCAgECAQEBAQEBAgEIAgEBAQMDBAQEBQkEAwMEAQIFAwIBAQEBAQECAQECAwMJAu4FBAgGAgEBAQEBAggCAQICBAEWAQEEAwwFCQQGBQQDCgIIAQIBAgICBwEBAQEBBAMECPynBQgIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUC8AQFBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQI/a0ECAQDAgICBQYMBgEMAQYHAwEDAwEHBwMBBwcBAwEDBAgEBAwEBAQECQQEBAUCAQEEBAMVAwQKBxkDCgIDAwMDAwMDAgYDAgMCAwIIAZwFBAQBBwocCwcSBAsEBwQEBwQIBBAIBAMCAgEEBAMEBQwFDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBf5EBAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQISBQQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgCIX2MfX2MfRd9Xn1+XQGVAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQD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AwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCNAFLOzmAWR4VFW1APnEaFScBIj1JTD8oBxo4TVEhJSwyEiM2AQ8RAwMBBhkiRAgBSAECBgECBAIDBAgJBQQEAwQBAgICBAIBAQEBAQUBBgQDCQUICAIEBgEBAU8BAQMDBAQEBQkEBgECAQYDBAIIDgMIAwYDBAECAgMEAwQDBwcDCAMSCggCBAICAwMICgcHAQwIBAQEBAQEBAsBBwMGAgYCAwUDBwMCAwkDAgIHAgICAgEBAQICBAIFDAEEAgICAgIBAgICAgIBBAIBAgIDBAEEAQQBAgMCAwIBAgMCAQIGAgECBgMDAgUCAQIPAwIBAgECAwYDAwMDAwMDAwEECQUIAwMDAQQDAwQGBQMCBQECAwIDAgMCAwMCBQIDAgwCAwIDAgMEAwQDBgMKAwYCCgEGAgUEBAQJCQYEAf7GAQICCAQEBAQzAwkDBgIGAwMDAwMDBgIDAwkCDgMIAQ0FCwUGCgcEBQkEBgICAgIDAwMDBgYGBQEFBw8mAwoDAwcDAwQDAwQDAwQDBwMOAywFCQgEAgMCEwECAwMIBBsGBxMHDRMGBw0GHw0FAQUBBQEFAQUBBQEBBAQEAgICAwMJCQgGBgUGEAsGEQUGBgUvBgUGBgwGBwUHBgL+sgECBgQEBgQJBAMFCQIDBAMMAQQDBAMHCQkIAwMDAgIBBgIGAwIDAgMDDwYBAwMBAwQHDAQBAwQEBAkIBAMCAgIBAwIEBAkEBQQEAwEKAgICAgICAQEHAgUBAQEDAgIBAgECAQEBAQECAgIEAgkBAQQBBAEEBwUECQgEAgMBAwcGAwMGAgIDAQMCAwIJAgMCAQQBBA0BAgAAAwAAAAADIAJYAAMABwAL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BAMIAQ4CCAIGAgQBAgIBAgMCAwUEAwQDCgUGAwkECQQEAwQCBAYBBAMCAwIDAgECCAEFAwQCBAICBQIHAgIBAgkBAgIDBwMCAwIBAgMCBgIKBf3zBQcBAgMCAwIDAgMCAwQBAgIDAgQBBAQBAgICAgICAQICAgQCAgQBAQECAwICCgECAgIBBAECAQIBAgECAgEEBAMOBAgEAwEBAgMBAgICAQIBAgECAQICAwIBAggCAQICAgEEAgQBBgIKAgYDCgMGAwUDAgEEBggFArIEBQMGAgEBAQEBAQEBAgEDBQMFAwQDAgYDAQIDBgQJCQQEAwIBAQMCAQIDAwIBBQEMAwECAQEBAQEBAQEEAwQECPymBAUDBAQBAQMCAQMCAQQBCgEEAwIDAgECAwECBAcJCQQEAwQCBAIDAgcEAQYBAgECCAEBAQEFCAgCsQQECQQFCgQPBQULDwYFCwUhBgsQCAQFAgIEAwQNCQYGFAUBEgYHBQEFAQUBBQEcAQsFAQUFAQULDwEEAQMDAgEEAwQECP3eBAQEAwICAQIDBA0CAQIDAgECAwMLAgEIAwMFAQIBAgECAQIDAwECAQIBAgQDBgMNAx0CAw0EAw0JBQgFAgEEAwQEAwQIAxQCEQMJBRECBgUDBQUFAgEPAgUCAgEEAwQRBAQFAhwM/pABfBj+tAEnGRlKGBhVGBhNGBgCLQECBgECBAIDBAgJBQQEAwQBAgICBAIBAQEBAQUBBgQDCQUICAIEBgEBAU8BAQMDBAQEBQkEBgECAQYDBAIIDgMIAwYDBAECAgMEAwQDBwcDCAMSCggCBAICAwMICgcHAQwIBAQEBAQEBAsBBwMGAgYCAwUDBwMCAwkDAgIHAgICAgEBAQICBAIFDAEEAgICAgIBAgICAgIBBAIBAgIDBAEEAQQBAgMCAwIBAgMCAQIGAgECBgMDAgUCAQIPAwIBAgECAwYDAwMDAwMDAwEECQUIAwMDAQQDAwQGBQMCBQECAwIDAgMCAwMCBQIDAgwCAwIDAgMEAwQDBgMKAwYCCgEGAgUEBAQJCQYEAf7GAQICCAQEBAQzAwkDBgIGAwMDAwMDBgIDAwkCDgMIAQ0FCwUGCgcEBQkEBgICAgIDAwMDBgYGBQEFBw8mAwoDAwcDAwQDAwQDAwQDBwMOAywFCQgEAgMCEwECAwMIBBsGBxMHDRMGBw0G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BAECBwMCAQIDAgECBgMJAwMDBP3DBQQIAwMCAgECAQEBAQEBAgEIAgEBAQMDBAQEBQkEAwMEAQIFAwIBAQEBAQECAQECAwMJAu4FBAgGAgEBAQEBAggCAQICBAEWAQEEAwwFCQQGBQQDCgIIAQIBAgICBwEBAQEBBAMECPynBQgIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUC8AQFBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQI/a0ECAQDAgICBQYMBgEMAQYHAwEDAwEHBwMBBwcBAwEDBAgEBAwEBAQECQQEBAUCAQEEBAMVAwQKBxkDCgIDAwMDAwMDAgYDAgMCAwIIAZwFBAQBBwocCwcSBAsEBwQEBwQIBBAIBAMCAgEEBAMEBQwFDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBf5EBAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQISBQQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgCHAz+kAF8GP60AScZGUoYGFUYGE0YGAIvAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAMEAQICBQwFCQQDBgIBAQEBAQICBwICAwICAgECAwMIBQkIAwUCAQIBAgECAgIJAgEBAScCAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEOwECAwMEDQULAgMDAwMDAwIDAwojAwcDBwQOCwMHAwgDAwIBAQMCBAgJBAIECQQEBAQEBQMBAwEDBAEDEAQDAQMIAwEKAQMDAQMDAQMDAQYECQEDAgMDqQEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBC0BBAgEBAQEHgUKBQUFBQkFJwUEBQkKBDIEBQQIBAYCAgYICgUZAQQBFAYFBQULBiYFBgULBgsGFgYJCAMDBBACBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwICOQECAwMIBwQHBBoDCwsHBgQDBxQPAwcBCAUEBQgDAwICAgIHAQMVAQMLAQcQBAQMBAQEBAQECQQmBQUIBAQDBKABBAMDBAkJCQYBDAEGDAYFAwMCAQQBBAECBQQCAgEBAQQBAQEGAQEBAgEBAgMHBQQFBAgDAwkCAQYDEAMGAwIDAgMCAwIDBgIDAwMDAwdKAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgEnAQEDBgQJBQQEBAIFAwQJAQYJAwIBAgMCAQIDBAMKAggBAgECAgMBAgMDCAUECQQDAwMEAgECAQIBAgEQEAYCAwUJAwoBAgICDwIDBQYMFAECAQIBAgECBgIGCQQFBAQEAwMCAQEBAgICAQIBCAwDBAMCAQIDAgECEgEJBAMFBAQEBQkEAwMEAAAAAAMAAAAAA7kCjgADAAcACwAAExEhEQcRIREjMxEjLwOKMf1ijF5eAo79jwJxMP3rAhX96wAgAAD/7gOsAs8ABAAJAA4AEwAYACEALQA2AEEATgBVAFoAXwBkAGkAbgBzAHgAfQCIAI4AlwChAKYAqwCwALUAugC/AMQAyQDOAAABMjM1IxcyMzUjFzIzNSMXMjM1IxcyMzUjFzIzNhc3JisBISIHFzYyNicmNjUmBRYXNjc2NyYnBQYHFhcWFzY3JyYFDgEXFgYWMjMyNzQnBQYVMyY3JwUyMzUjBTIzNSMFMjM1IwUyMzUjBTIzNSMFMjM1IwUyMzUjBTIzNSMFBgcWFxYXNjcnJgUWFzcmJwUGBxYfATY3JwUWFzc2NyYnBwYXMjM1IxcyMzUjFzIzNSMXMjM1IxcyMzUjFzIzNSMHNDUhERMyMxEhNxQVMzUBBRkYMWIZGDFhGRgxYhkYMWIZGDFhBAcYCwYLFRT97Q0MCQMOBgMBAgECYhINBQoRBRQY/TMYEwULEAcQDgoKAvELAwQCAQIKDRIHCvyjBzEBBi4DNRkYMfzDGRgxAz0ZGDH8wxkYMQM9GRgx/MMZGDEDPRkYMfzDGRgxAzsFCQYLEQgPBRAW/L8GESgLBALcEREFCAMbFx79MBsZBQUCExAHDmMZGDFhGRgxYhkYMWIYGDBeGRgxZRkYMTX+rh+Kiv7sJcsCnTExMTExMTExMTEBAjACAzABBAgEFQQHPQkQBAkMBxcNAw8WBAkNBBIHEBJWAQoLAg8GAR4aCBgdExUPlTE1MY4xNjGOMTUxjzE1MVkWDwMHCwMZGwMFDRwZGxITQQwFDBkJBxMnKhIHEhQJBgwJFSkxMTExMTExMTExMSKsq/6pATj+66cXGC8AAAsAAP/OA7kC8gAFAAkADQARABUAGQAfACMAJwArAC8AABc1MxUzFTM1MxUzNTMVMzUzFTM1MxUzNTMVMzUzNTMVJTUzFSE1MxUlESERJxEhES4xEDFhMWIxYTFiMWEwDTH8dTEDKTH8dgOKMfzWMlkoMTExMTExMTExMTExKFmKXFxcXIsCD/3xMAGz/k0AAAQAAP+cA8MDIAADAAcADgAVAAABETMRMxEzEQEHFzUzNSMlFQcVMxU3AW4mwyb+UKSkXV0CU15eowMg/HwDhPx8A4T+4aOjb2lubAFqb6MAAQAA/+YDmAMAABcAAAEFBhQfARYHAQYfARY3ATYfARYyNxM2JgOV/mMCAWACAv4pAgItAwQB1wQDWQEDAacBAgL/ogEDAV8DA/4oAwMrAwMB1gMDWgECAZUBAgACAAAAAAO5AmYAAwAHAAATESERAREhES8DivylAyoCZv3wAhD+HQGz/k0AAAkAAP+nA7IDHgADAAcACwARABUAGQAfACUAKQAAARUzNQUVMzUzFTM1FxUzFTM1BRUzNQUVMzUHFSMVMzUFFTM1IzUXFTM1AS9n/qHaoduCeyj9fWcB9Cgod5/9np934FIDHtra9mdnZ2ceKHWdaNran01NvnQonAGdKHV1KCgACwAA/84DuQLyAAUACQANABEAFQAZAB8AIwAnACsALwAAExUzNTM1MxUzNTMVMzUzFTM1MxUzNTMVMzUzFTMVMzUFFTM1IRUzNQURIREHESERLjEQMWExYjFhMWIxYTANMfx1MQMpMfx2A4ox/NYC8lkpMDAwMDAwMDAwMDAwKVmJXFxcXIv98AIQMP5NAbMADAAA//oDuQLCAAQAGAAcACAAJAAoACwAMAA0ADgAPABAAAATFREhEQUhFSMVMxUjFTMVITUzNSM1MzUjMxUzNTMVMzUzFTM1MxUzNTMVMzUFFTM1MxUzNTMVMzUzFTM1MxUzNS8DivylAyoMDAwM/NYRERERQmIxYTFiMWExYv1UYjFhMWIxYTFiAsLY/hACyCysMbYxqakxtjExMTExMTExMTEx5zExMTExMTExMTEAAAAABwAA//oDuQLCAAQAEAAUABgAHAAgACQAABMZASERBSERIxUzESERMzUjMxUzNTMVMzUzFTM1MxUzNTMVMzUvA4r8pQMqDAz81hERQmIxYTFiMWExYgLC/rT+hALILP7gMP7jAR0wMDAwMDAwMDAwMAAAAAAFAAD/0gMjAuoABQALAA4AFgAdAAABIiMRIREnFTMRIREFFyMnHQEjFTMVNycXBzUjNTMCctbXAl7luP38AXl9feNqao15XFxqagLq/OgCYom6/fwCvgyBbBhFUFyEVlZWQigABAAA/9IDIwLqAAUACwAOABUAAAEiIxEhEScVMxEhEQUXIycVIxUzFTcCctbXAl7luP38AXl9feNqao0C6vzoAmKJuv38Ar4MgWxdUFyEAAACAAD/1gNyAu8AbwDkAAABIgYHBgcGHwEVJi8BMScmJyYnJicmBxUGBwYXFhcWFxYfAScmJyYnJgcGBzkBBhcWFxYXFhcWFxYfASE3Nj8BNj8BNjc2NzYnLgEnJgYHBgcGDwE1NDU2JyYnLgEiBgcGBwYPAi8BJicmJy4BBzMHMjEzMhYXHgEfARYfAT8CNjc2Nz4BOwEyFh8BFhcWBxUfATY3Njc2NzYXOQEeARcWBwYHBg8BBgcGDwEhJicmJyYnJicmJyY+AhcWFxYfATcnJicmJyYnJjc+ATc2FhcWFxYXMRYfAT8BNi8BJjc2Nz4BAfIOGAcLAwMBAQYIAQkMBwsLERMXHRkHAwcFEAwFCA0GDRAJJyQWExcQHQUCGQ4kLRccOScQBQFMAwQKDBQbCxgKEAcKBgQYEQ4cChIPBgkGAQIECAYVGRYHDQcGBwYJBgMFBwcMBxcNAQIBAQUFBAcNBwIGCAghGgYHBQcHBAUFAgQCAgEGAgIBASAKEhAIDg0KDAkIAgQJBg0IFBMYGQUJCP7fDiE6HxguIwwTAgEGERYPHyIcHRcdDRAOBg0PBAYDAQUGDRUMCwwIDwoUESABAgEBAgMDBwQGAu4ODBQhHD8oQw0XAxcgDxkQGQkLCwELHhMjFjEmExsyFwwPCCIOCAECDhseGCARJS4cIVg7FQcMDS43XkccOxwtHywaDxcDAwsLESMKGBBLESU2FyIRCw8LCREbFjEkLzMgQh4jEAwOASEEBgw5Rxk4OCwBlSMxFBcJBQIBAwMLHBlAJ6AHFS8qEh4NCgEBBwkTIxgmFjMzPG4XJyUSNFklHS4lDxgOCAwQAQUNHhcgGw82PzIWKS0SGg8HBwMGChIQHhIoGjArBjI7Kh5GHBkOBgQAAAAACAAA/9sDbQLgABQAGAAqAC4AMgBMAGEAZQAAASIGBwYHFBYHFTM1Jjc+ARczNSMmBTM1Ixc2FxYHFTM0NTQnLgInJgcjATM1IwUzNSMFFBYXFhcWNzYXNhcWMzY3NSIjBicuATc1IwUUDgEjBisBFTIzFj4CNzY1NDUjBTM1IwECK0oLBAIBATIBAgQ2INhKZAEKLy+LOxwfBDIBAiQ5HwkTCf20MjICvjIy/UItIxQbECEZDAQLCQQFAhAfNxsfJwEyAr4WJhYjRyMUKT09NiIBATL+li4uAt85KxIXDjcORjBQKCItAjEBMjExBB0eQJgbOEklIDkkAQIB/kcxSzHUJ0cRCgICAQEBAQEBAQQtAQMHNSFXWRcqGwExAQUlOSAOHRULzTEAAAAABQAA/6gDjwMUAAgADAAQABQAGAAAARkBITUjETM1AQcXNw8BFzcPARc3DwEXNwJqASXq6v51PT49uD0+Pbg9Pj24PT49AxT+Sv5KPAL0PP7XPj0+Pj49Pj4+PT4+PT4+AAAAAAQAAP/wA6wCzQATACcANwBHAAATIg4BFREUHgEzITI+ATURNC4BIwUhMh4BFREUDgEjISIuATURND4BFyIGFREUFjMhMjY1ETQmIwUhMhYVERQGIyEiJjURNDbSKUQoKEQpAkUoRSgoRSj9uwJFHzQfHzQf/bsfNR8fNTwmNzcmAgsmNjYm/fUCCxgiIhj99RkiIgLMKEQp/k4oRSgoRSgBsilEKCIfNR/+Th81Hx81HwGyHzUfMTYm/oImNjYmAX4mNiIiGP6CGCIiGAF+GCIAAAAAAgAA/78DiwL3ABMAHAAAAQ4DHgM3PgI3Byc/AS4BCQEGHgE3AS4BAqIsUDsdBihDVC0wVTsLnoI6piJY/u/+mwMfMRcBXR8vAvMDKEVWWlE7HQMDL00wN0uMOiAg/p3+kxYxHwMBZBI3AAAAAQAA/+IDiALxABMAAAEGBwYHFh8BBwYHFh8BNj8BFzY3A4hRULRbFCciv6pUBw4LWbKyWDFaAvEpKForEiciv6pVBw4LWrKzWGCtAAIAAP/dA4oC8wARABUAAAEGBwYHFh8BDwI/Axc2NwEPATcDilFQtFsWKxvKzyj3AiDJWDFa/igboBoC8ykoWisULBvMIvcoD8XKWGCt/nKhGqEAAAIAAP/iA4oC8QAGAAoAAAEFFwEXARcFFSE1A4r+UVv+RSABvFn9zAFqAvHWWv5BIAHAWZ4rKwAAAAASAN4AAQAAAAAAAAAVAAAAAQAAAAAAAQAEABUAAQAAAAAAAgAHABkAAQAAAAAAAwAEACAAAQAAAAAABAAEACQAAQAAAAAABQALACgAAQAAAAAABgAEADMAAQAAAAAACgArADcAAQAAAAAACwATAGIAAwABBAkAAAAqAHUAAwABBAkAAQAIAJ8AAwABBAkAAgAOAKcAAwABBAkAAwAIALUAAwABBAkABAAIAL0AAwABBAkABQAWAMUAAwABBAkABgAIANsAAwABBAkACgBWAOMAAwABBAkACwAmATljYW11bmRhIFNlcnZpY2VzIEdtYkhicG1uUmVndWxhcmJwbW5icG1uVmVyc2lvbiAxLjBicG1uR2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20AYwBhAG0AdQBuAGQAYQAgAFMAZQByAHYAaQBjAGUAcwAgAEcAbQBiAEgAYgBwAG0AbgBSAGUAZwB1AGwAYQByAGIAcABtAG4AYgBwAG0AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAYgBwAG0AbgBHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAAV0cmFzaBBnYXRld2F5LXBhcmFsbGVsH2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1jYW5jZWwxaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctbWVzc2FnZRhzdGFydC1ldmVudC1jb21wZW5zYXRpb24uc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1wYXJhbGxlbC1tdWx0aXBsZQtsb29wLW1hcmtlchJwYXJhbGxlbC1taS1tYXJrZXIjc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1zaWduYWwvaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctdGltZXIqaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLXBhcmFsbGVsLW11bHRpcGxlJWludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1jb21wZW5zYXRpb24LZ2F0ZXdheS14b3IQZW5kLWV2ZW50LWNhbmNlbCJpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtY29uZGl0aW9uO2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLXBhcmFsbGVsLW11bHRpcGxlFXN0YXJ0LWV2ZW50LWNvbmRpdGlvbiJzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLXRpbWVyFHNlcXVlbnRpYWwtbWktbWFya2VyCXVzZXItdGFzaw1idXNpbmVzcy1ydWxlEnN1Yi1wcm9jZXNzLW1hcmtlch1zdGFydC1ldmVudC1wYXJhbGxlbC1tdWx0aXBsZRFzdGFydC1ldmVudC1lcnJvch9pbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtc2lnbmFsHmludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1lcnJvchZlbmQtZXZlbnQtY29tcGVuc2F0aW9uFHN1YnByb2Nlc3MtY29sbGFwc2VkE3N1YnByb2Nlc3MtZXhwYW5kZWQEdGFzaw9lbmQtZXZlbnQtZXJyb3IjaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWVzY2FsYXRpb24eaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLXRpbWVyFnN0YXJ0LWV2ZW50LWVzY2FsYXRpb24Sc3RhcnQtZXZlbnQtc2lnbmFsEmJ1c2luZXNzLXJ1bGUtdGFzawZtYW51YWwHcmVjZWl2ZQ1jYWxsLWFjdGl2aXR5EXN0YXJ0LWV2ZW50LXRpbWVyE3N0YXJ0LWV2ZW50LW1lc3NhZ2UXaW50ZXJtZWRpYXRlLWV2ZW50LW5vbmUdaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWxpbmsUZW5kLWV2ZW50LWVzY2FsYXRpb24HYnBtbi1pbw9nYXRld2F5LWNvbXBsZXgSZ2F0ZXdheS1ldmVudGJhc2VkDGdhdGV3YXktbm9uZQpnYXRld2F5LW9yE2VuZC1ldmVudC10ZXJtaW5hdGUQZW5kLWV2ZW50LXNpZ25hbA5lbmQtZXZlbnQtbm9uZRJlbmQtZXZlbnQtbXVsdGlwbGURZW5kLWV2ZW50LW1lc3NhZ2UOZW5kLWV2ZW50LWxpbmsgaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW1lc3NhZ2UlaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWNvbXBlbnNhdGlvbhRzdGFydC1ldmVudC1tdWx0aXBsZQZzY3JpcHQLbWFudWFsLXRhc2sEc2VuZAdzZXJ2aWNlDHJlY2VpdmUtdGFzawR1c2VyEHN0YXJ0LWV2ZW50LW5vbmUjaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWVzY2FsYXRpb24haW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW11bHRpcGxlNGludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLWVzY2FsYXRpb24daW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWxpbmsmc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1jb25kaXRpb24LZGF0YS1vYmplY3QLc2NyaXB0LXRhc2sJc2VuZC10YXNrCmRhdGEtc3RvcmUnc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1lc2NhbGF0aW9uIGludGVybWVkaWF0ZS1ldmVudC10aHJvdy1tZXNzYWdlMmludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLW11bHRpcGxlMGludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLXNpZ25hbCFpbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctbXVsdGlwbGUkc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1tZXNzYWdlDWFkLWhvYy1tYXJrZXIMc2VydmljZS10YXNrCXRhc2stbm9uZRNjb21wZW5zYXRpb24tbWFya2VyJXN0YXJ0LWV2ZW50LW5vbi1pbnRlcnJ1cHRpbmctbXVsdGlwbGUfaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LXNpZ25hbDNpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbm9uLWludGVycnVwdGluZy1jb25kaXRpb24LcGFydGljaXBhbnQZZXZlbnQtc3VicHJvY2Vzcy1leHBhbmRlZBFsYW5lLWluc2VydC1iZWxvdwpzcGFjZS10b29sEGNvbm5lY3Rpb24tbXVsdGkEbGFuZQpsYXNzby10b29sEWxhbmUtaW5zZXJ0LWFib3ZlEWxhbmUtZGl2aWRlLXRocmVlD2xhbmUtZGl2aWRlLXR3bwpkYXRhLWlucHV0C2RhdGEtb3V0cHV0CWhhbmQtdG9vbAVncm91cA90ZXh0LWFubm90YXRpb24LdHJhbnNhY3Rpb24Mc2NyZXctd3JlbmNoCmNvbm5lY3Rpb24QY29uZGl0aW9uYWwtZmxvdwxkZWZhdWx0LWZsb3cAAA==') format('truetype'); + font-family: "bpmn"; + src: url("data:application/octet-stream;base64,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") + format("woff"), + url("data:application/octet-stream;base64,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AADoGQAA6BkAAAAWAADoGgAA6BoAAAAXAADoGwAA6BsAAAAYAADoHAAA6BwAAAAZAADoHQAA6B0AAAAaAADoHgAA6B4AAAAbAADoHwAA6B8AAAAcAADoIAAA6CAAAAAdAADoIQAA6CEAAAAeAADoIgAA6CIAAAAfAADoIwAA6CMAAAAgAADoJAAA6CQAAAAhAADoJQAA6CUAAAAiAADoJgAA6CYAAAAjAADoJwAA6CcAAAAkAADoKAAA6CgAAAAlAADoKQAA6CkAAAAmAADoKgAA6CoAAAAnAADoKwAA6CsAAAAoAADoLAAA6CwAAAApAADoLQAA6C0AAAAqAADoLgAA6C4AAAArAADoLwAA6C8AAAAsAADoMQAA6DEAAAAtAADoMgAA6DIAAAAuAADoMwAA6DMAAAAvAADoNAAA6DQAAAAwAADoNQAA6DUAAAAxAADoNgAA6DYAAAAyAADoNwAA6DcAAAAzAADoOAAA6DgAAAA0AADoOQAA6DkAAAA1AADoOgAA6DoAAAA2AADoOwAA6DsAAAA3AADoPAAA6DwAAAA4AADoPQAA6D0AAAA5AADoPgAA6D4AAAA6AADoPwAA6D8AAAA7AADoQAAA6EAAAAA8AADoQQAA6EEAAAA9AADoQgAA6EIAAAA+AADoQwAA6EMAAAA/AADoRAAA6EQAAABAAADoRQAA6EUAAABBAADoRgAA6EYAAABCAADoRwAA6EcAAABDAADoSAAA6EgAAABEAADoSQAA6EkAAABFAADoSgAA6EoAAABGAADoSwAA6EsAAABHAADoTAAA6EwAAABIAADoTQAA6E0AAABJAADoTgAA6E4AAABKAADoTwAA6E8AAABLAADoUAAA6FAAAABMAADoUQAA6FEAAABNAADoUgAA6FIAAABOAADoUwAA6FMAAABPAADoVAAA6FQAAABQAADoVQAA6FUAAABRAADoVgAA6FYAAABSAADoVwAA6FcAAABTAADoWAAA6FgAAABUAADoWQAA6FkAAABVAADoWgAA6FoAAABWAADoWwAA6FsAAABXAADoXAAA6FwAAABYAADoXQAA6F0AAABZAADoXgAA6F4AAABaAADoXwAA6F8AAABbAADoYAAA6GAAAABcAADoYQAA6GEAAABdAADoYgAA6GIAAABeAADoYwAA6GMAAABfAADoZAAA6GQAAABg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EAgEAwICAQQEAwQFDAUNBAUEBQgFJgQJBAQEBAQEAQQDAQMBAwQECAQJBAICAQIGAwQF/kQEBQQGAgIBAgMDBQQBBA0JAQ0BBAUFBAEEBQUKBRkBBAEUBgUFBQsKCQUEBAMEAQIDAwQHEwUEBQUEBQUEJR4IBAQJDAMNAhIFBAUDARQEBAkIFiwFBQQFBQUEBRsEBgQBAQIDAwQEBAkDBgULBQUFBhQBHgUKBQUEAQQBBAUcDQQBBAUDAgECAgMECAH6/sgBODFaPrq6fQHyAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAMEAQICBQwFCQQDBgIBAQEBAQICBwICAwICAgECAwMIBQkIAwUCAQIBAgECAgIJAgEBAScCAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEOwECAwMEDQULAgMDAwMDAwIDAwojAwcDBwQOCwMHAwgDAwIBAQMCBAgJBAIECQQEBAQEBQMBAwEDBAEDEAQDAQMIAwEKAQMDAQMDAQMDAQYECQEDAgMDqQEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBC0BBAgEBAQEHgUKBQUFBQkFJwUEBQkKBDIEBQQIBAYCAgYICgUZAQQBFAYFBQULBiYFBgULBgsGFgYJCAMDBBACBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwICOQECAwMIBwQHBBoDCwsHBgQDBxQPAwcBCAUEBQgDAwICAgIHAQMVAQMLAQcQBAQMBAQEBAQECQQmBQUIBAQDBKABBAMDBAkJCQYBDAEGDAYFAwMCAQQBBAECBQQCAgEBAQQBAQEGAQEBAgEBAgMHBQQFBAgDAwkCAQYDEAMGAwIDAgMCAwIDBgIDAwMDAwdKAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgEnAQEDBgQJBQQEBAIFAwQJAQYJAwIBAgMCAQIDBAMKAggBAgECAgMBAgMDCAUECQQDAwMEAgECAQIBAgEQEAYCAwUJAwoBAgICDwIDBQYMFAECAQIBAgECBgIGCQQFBAQEAwMCAQEBAgICAQIBCAwDBAMCAQIDAgECEgEJBAMFBAQEBQkEAwMEAAAABQAA/5YDvQMjABYAMQA6AD4ARAAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhMGDwEXNRcRBycUFSclFBUnJicB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpQ/Nmwv0crKGI8BWSpDIgMiAUZ+obOmPj9JCR4delFTWFdVU4cmLzEBJiVDRFRPp0hKLzQQDxYWOTZQT1daTklwPv8AJk0hlI+PASiPYGRlZGVlZR4wFwAJAAD/ogO4AyAACwAXAFIAuwFEAaEB6AIyAp4AAAEVIxUzFTM1MzUjNQczFTMVIxUjNSM1MxMrAQcjByMHFQ8FFR8FMz8DMzczNzM3MxczFzMXMx8BMz8DNS8EIzUjJyMnFw8GFR8KMx8GFR8CMx8NMz8ENS8BNScjLwYjLwE1Iy8LIy8BIy8HIzUnNS8EIwUPASMPBBUPBBUHIwcVDwMjFQcVByMVDwQVByMPARUPAhUPAiMPAxUPAxUHFQ8KFQcVHwQ/BDM/BDU/JjUvAwEPBRUXFQcVBxUHFQcVBxUHFQcVBxUHFQcVBxUPBxUfBD8FMz8BMz8BNT8BMzczNzU3NTM1NzU3NTc1NzU3NTc1NzU3NSc1LwQFDwUVFxUXFRczHwUzHwEVFxUXFRcVMxcVFxUfBD8ENS8NNSc1JzUnNS8DAQ8XHwQ/BDM/AzM3MzczNzM3Mzc1NzM3NTczPwIzNzM1PwM1LwQFDwUVHwMVHwEzHwIzHwIVHwEzHwEVHwEzFzMXMxczHwIzFzMXFTMfBzMXFRczFTMXMz8ENS8EIycjJyMnIy8KIy8EIy8FIwGufX2MfX11Xn19Xn19MwcNBg0NBycNDgQDAwQBAgMDCQQFBgwGFwYLBgYGBikGBgYGBgUfBAUMAwMCAgYDCicGBwYNB+kFBAQDAwECAgIGAgIDBgUEAwgBDgIIAgYCBAECAgECAwIDBQQDBAMKBQYDCQQJBAQDBAIEBgEEAwIDAgMCAQIIAQUDBAIEAgIFAgcCAgECCQECAgMHAwIDAgECAwIGAgoF/fMFBwECAwIDAgMCAwIDBAECAgMCBAEEBAECAgICAgIBAgICBAICBAEBAQIDAgIKAQICAgEEAQIBAgECAQICAQQEAw4ECAQDAQECAwECAgIBAgECAQIBAgIDAgECCAIBAgICAQQCBAEGAgoCBgMKAwYDBQMCAQQGCAUCsgQFAwYCAQEBAQEBAQECAQMFAwUDBAMCBgMBAgMGBAkJBAQDAgEBAwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCIX2MfX2MfRd9Xn1+XQGTAQIGAQIEAgMECAkFBAQDBAECAgIEAgEBAQEBBQEGBAMJBQgIAgQGAQEBTwEBAwMEBAQFCQQGAQIBBgMEAggOAwgDBgMEAQICAwQDBAMHBwMIAxIKCAIEAgIDAwgKBwcBDAgEBAQEBAQECwEHAwYCBgIDBQMHAwIDCQMCAgcCAgICAQEBAgIEAgUMAQQCAgICAgECAgICAgEEAgECAgMEAQQBBAECAwIDAgECAwIBAgYCAQIGAwMCBQIBAg8DAgECAQIDBgMDAwMDAwMDAQQJBQgDAwMBBAMDBAYFAwIFAQIDAgMCAwIDAwIFAgMCDAIDAgMCAwQDBAMGAwoDBgIKAQYCBQQEBAkJBgQB/sYBAgIIBAQEBDMDCQMG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BAQDBAEEAwcBAwUEBAQEBAkEJgQJBQQEBQ2xBQMGBwEDCAMBFQMBAwMBCQEMAQkJAwMCAQIDAwgEAQQCAgIDAwgEBQQJAwgDAgQDAgMCAwIDAgMDAwIDAwMDAwMDAwYDIwIDBAQCAgUECAUBhAQEBAMDAwEGAwMCAwIDAgMCAwcWAQQBBAEGAwICBAQDBAUJBAQEAwQBAgICAQIBAgECAgIBAQEIAQEBAgIEAQIHAwIBAgMCAQIGAwkDAwMECf3HBAgDAwICAQIBAQEBAQECAQgCAQEBAwMEBAQFCQQDAwQBAgUDAgEBAQEBAQIBAQIDAwkEAo0FBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQIBf2uBAQEAwICAgUGDAYBDAEGBwMBAwMBBwcDAQcHAQMBAwQIBAQMBAQEBAkEBAQFAgEBBAQDFQMECgcZAwoCAwMDAwMDAwIGAwIDAgMCCA4BpQQEAQcKHAsHEgQLBAcEBAcECAQQCAQDAgIBBAQDBAURDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBQSnBQYGCwUMBQUGJgYKCAQCAwEBAgYECQQrBQkFBQUFCgUeBQoFBQUFCgQYCQUEAwQEAQEEAwQHJgUGBQsGCwbvBAUDBAQCAggCDAcIBwQDBAMEAwcGBw8CCQIDEQQDBAkJBAQDBAECAwIBAgMCAQISAQkEBgEDBAMBAwgEAwEDAQMBCAQEBAEECAEECgj99AQIBQQBBAQEDAQEBAgDAQMEAwQDBAkBDwIBAgMDBQIBBAMDAQIDAwgECQUHBQUQBgIGAg8HBgcDBAMEAwQHCAQEBwIBAgIDBAgEAq8ECAYCAQEBAQECCAIBAgIEARYBAQQDDAUJBAYFBAMKAggBAgECAgIHAQEBAQEEAwQIBPymBAQIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUElAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQ0CGgQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgEAjQBSzs5gFkeFRVtQD5xGhUnASI9SUw/KAcaOE1RISUsMhIjNgEPEQMDAQYZIkQI7QECAgUMBQkEAwYCAQEBAQECAgcCAgMCAgIBAgMDCAUJCAMFAgECAQIBAgICCQIBAQEnAQEDBQIEAQIPAwIBAgkMCQEJBAMDAQMDAQMLBwcJCQQDBAQBAQQDDAMEBgMDAgMDAwMDAwIDAwIDAgMCAwIDBAMWAwMICQkHAwQ8AQEDAwQNBQsCAwMDAwMDAgMDCiMDBwMHBA4LAwcDCAMDAgEBAwIECAkEAgQJBAQEBAQFAwEDAQMEAQMQBAMBAwgDAQoBAwMBAwMBAwMBBgQJAQMCAwMCqwEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBAF3AQIDAwgHBAcEGgMLCwcGBAMHFA8DBwEIBQQFCAMDAgICAgcBAxUBAwsBBxAEBAwEBAQEBAQJBCYFBQgEBAMEoQICAwMECQkJBgEMAQYMBgUDAwIBBAEEAQIFBAICAQEBBAEBAQYBAQECAQECAwcFBAUECAMDCQIBBgMQAwYDAgMCAwIDAgMGAgMDAwMDBwNNAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgECzgEBAQEHAgIEAwQEBAkEBQYDAgkCAQEBAQEBAgUBAgMDCAQFBAkDAwMHAQEBAVMBAgIEBwkJCgEJBwYHAwQDBAMEBwgHFAQNBAQgAwMCAgICAwMICQUGBQUEAQQBBAUcDQQBCAQBBAQECAEDAQMBAwQIAwEDBAMBBgQHAgoBBAQBAwQDAQMMAwEDAQMBCAQEBAEEBAQBBA0XBQUEAQQBCQUKBQwEBQQEAwQBAgUHDB4IBAkEFAcIBwQDBAMEAwcGBAMHBQQECQQEAwT+xQEECAQEBAQeBQoFBQUFCQUnBQQFCQoEMgQFBAgEBgICBggKBRkBBAEUBgUFBQsGJgUGBQsGCwYWBgkIAwMEEAEBBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwIC/rIBAwYECQUEBAQCBQMECQEGCQMCAQIDAgECAwQDCgIIAQIBAgIDAQIDAwgFBAkEAwMDBAIBAgECAQIBEBAGAgMFCQMKAwMBAgICDwIDBQYMFAECAQIBAgECBgIGCQQFBAQEAwMCAQEBAgICAQIBCAwDBAMCAQIDAgECEgEJBAMFBAQEBQkEAwMEAAYAAP+VA74DJAAbADEASQBgAG0AegAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NhcUFSMVMxUzNTM1IzUHMjMVMxUjFSM1IzUzAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUoYn596n59hJSSfn0mgoAMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IitdTk58nJx8nBidSp2dSgAABwAA/5UDvgMkABsAMQBJAGAAaQBtAHMAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcOASYnLgI3Njc+ARcmBwYHBgcGFxYXFhcWNjc+AicmJy4BBzYXFhcWBxYGBwYHBiYnJicmNjc2NzYXBg8BFzUXEQcnFBUnJRQVJyYnAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUpFNmwv0crKGI8BWSpDIgMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IiujJk0hlI+PASiPYGRlZGVlZR4wFwADAAD/mgO4AyIAEAAUAEIAAAEiBwEGFBcBFjI3ATY0JwEmBwkCNyIPBB8CDwIfBD8CHwI/BC8CPwIvBA8CJzUnAfQPC/5hCgoBnwsfCgGfCwv+YQsPAYb+ev569AICBA8DAQEDiYkDAQEDDwQEBQSJiQQFBAQPAwEBA4mJAwEBAw8EBAUEiYkEAyIL/mEKHwv+YQoKAZ8LHwoBnws+/nr+egGGrQEDDwQEBQSJiQQFBAQPAwEBA4mJAwEBAw8EBAUEiYkEBQQEDwMBAQOJiQECAAAAAAMAAP+WA78DIgAbADIASwAAASIHBgcGBwYWFxYXHgE3Njc+ATc2Jy4BJyYnJgc2FxYXFhcWBgcGBwYmJy4BNjc2Nz4BBwYHFwYHFhc3FzY3JyYnNzY3JicGDwEmJwHrbGNgP0AKCzk9PlROtFNXQENQCAcdG3lRU1kND09IRyssAQNAOTxIRpA3OTIQJihAI1JHHTpwODYqLG5xOh4mMhklMRgqLBkwJRkyAyI3Nltda1y3R0onJgscHTs5oVhbU1WLKSoEAY4CKShFR09JiS4xDA4pMDGHlz5BIxUWbh46cTY4LCpucDodJjIZJTAZLCoYMSUZMgAACgAA/5IDwQMjABwANwBRAGoAbwBzAHcAewB/AIMAAAEiIyIHBgcGBwYWFxYXHgE3Njc+ATc2LgEnJicmBzIzNhcWFxYXFgYHBgcOAScmJyYnJjc2Nz4BFyIjIgcGBwYHBhYXFhcWNjc2Nz4BJyYnLgEHMjMyFxYXFhcWBgcGBwYuAScuATc2Nz4BBxURIREFMxEjExUzNQcVMzUHFTM1BxUzNQHzAQJjXltAQhYVHi4vSEiyWVxLSmgVFhteSElWODsFBVxWVDk6DxAqNDVKTLRTVTs9HRwJCi41uG4FBVRPSzI0CQs2ODpMSJ1FRi0wHhQVNjOOTwMDS0VDKywGCDc1OEVEj3YgIgIfITooY2QBLv7q/v4aysrKysrKygMiLi1OUGFWs01OMzUqDg8wLpFWV62bNjcUDjEBLStMTVtSqERGJyoKICFAP1dUWVtKV2krKypISVRPnTs8GxsNJSY/QKNPUjs6QzAnJkBBS0eLMjQSFBtVPkCWQkUrHyJ5DP6QAXwY/rQBJxkZShgYVRgYTRgYAAAAEAAA/6MDuAMiAAsAFwBaALIBCAFLAaAB/QJIAooCzgMRA2gDtQQBBEsAAAEVIxUzFTM1MzUjNQczFTMVIxUjNSM1MxMxIwcjByMVIwcjDwcVHwQzNzM3MzczNzM3MxczFzMXMxczFzM/BTUvBSMnIzUjJyMnFyMPBR8ZPwQ1LwMjJzUnNScjLwEjLwE1JyMnNS8BIy8BNSc1JyM1JyMnIy8BNS8BIyc1JyMvAQUjDwIVByMPARUPAhUHFQcVDwEjDwEVDwIVDwEjDwEjDwEVBxUPASMPAhUfBTM/GTUvBCExIxUjDwMVHwQzNzM3MxczFzMXMxczFzMfBjM/BTUvAiMvCSMnIycjJyM1ByMPAiMPARUHIw8BIwcVByMHIwcjBxUPAyMVDwEVDwIjDwEfBTM/HTM/Ay8DBQ8FHxYVHwQzPwQ1JzUvCDUnNS8EIycjJzUvASMnNSc1LwE1Iy8BNScjJzUvAjUvAwUjDwUVIxUHFQcVIxUHFRcVMxUXFRcVFxUfCTM/BDUvAjUnNSc1JzUnNSc1NzU3NTc1NzU3NS8EBSMPAxUHFxUHFQcVBxUHFQ8IFR8DMz8GNTc1Pwc1NzUzNTc1NzUnNS8DBSMPAxUXFRcVFxUXFR8HFRcVHwIzFR8DMz8ENS8LNSc1JzUnNSc1LwQFIw8FFQcVDw8VHwU/AjU/ATU3Mzc1PwIzPwo1Ny8EBQ8FHwIVFxUXMxczHwIzHwEzFRcVFxUXMx8BMxczFxUXFR8BFR8CFR8CMz8FNS8cBSMPCCMHIwcjByMHIwcjDwQVHwUzNzM3MzczNzM/CzM3Mz8BNT8BNTc1PwQ1LwQFDwUVHwUzHwEVFzMXMx8BFR8BMxcVHwQzFzMfBjM/BTUvFiEjDxcVHwYzPwgzPwQ1NzM3Mzc1PwE1PwEzPwQ1LwQBrn19jH19dV59fV59fS8LBgsFDAUFBiYGCggEAgMBAQIGBAkEKwUJBQUFBQoFHgUKBQUFBQoEGAkFBAMEBAEBBAMEByYFBgULBgsG9AUEBQMEBAICCAIMBwgHBAMEAwQDBwYHDwIJAgMRBAMECQkEBAMEAQIDAgECAwIBAhIBCQQGAQMEAwEDCAQDAQMBAwEIBAQEAQQIAQQK/fEFBAgFBAEEBAQMBAQECAMBAwQDBAMECQEPAgECAwMFAgEEAwMBAgMDCAQJBQcFBRAGAgYCDwcGBwMEAwQDBAcIBAQHAgECAgMECAEEEg0KBQcGAgIDCAgEAQgEJgQIAwQEBAcECAMaAwgHBwMJBQQEBAMEAQQDBwEDBQQEBAQECQQmBAkFBAQFDa0ECAYHAQMIAwEVAwEDAwEJAQwBCQkDAwIBAgMDCAQBBAICAgMDCAQFBAkDCAMCBAMCAwIDAgMCAwMDAgMDAwMDAwMDBgMjAgMEBAICBQQIAYQFCAQDAwMBBgMDAgMCAwIDAgMHFgEEAQQBBgMCAgQEAwQFCQQEBAMEAQICAgECAQIBAgICAQEBCAEBAQICBAECBwMCAQIDAgECBgMJAwMDBP3DBQQIAwMCAgECAQEBAQEBAgEIAgEBAQMDBAQEBQkEAwMEAQIFAwIBAQEBAQECAQECAwMJAu4FBAgGAgEBAQEBAggCAQICBAEWAQEEAwwFCQQGBQQDCgIIAQIBAgICBwEBAQEBBAMECPynBQgIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUC8AQFBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQI/a0ECAQDAgICBQYMBgEMAQYHAwEDAwEHBwMBBwcBAwEDBAgEBAwEBAQECQQEBAUCAQEEBAMVAwQKBxkDCgIDAwMDAwMDAgYDAgMCAwIIAZwFBAQBBwocCwcSBAsEBwQEBwQIBBAIBAMCAgEEBAMEBQwFDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBf5EBAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQISBQQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgCIX2MfX2MfRd9Xn1+XQGVAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQD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AwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCNAFLOzmAWR4VFW1APnEaFScBIj1JTD8oBxo4TVEhJSwyEiM2AQ8RAwMBBhkiRAgBSAECBgECBAIDBAgJBQQEAwQBAgICBAIBAQEBAQUBBgQDCQUICAIEBgEBAU8BAQMDBAQEBQkEBgECAQYDBAIIDgMIAwYDBAECAgMEAwQDBwcDCAMSCggCBAICAwMICgcHAQwIBAQEBAQEBAsBBwMGAgYCAwUDBwMCAwkDAgIHAgICAgEBAQICBAIFDAEEAgICAgIBAgICAgIBBAIBAgIDBAEEAQQBAgMCAwIBAgMCAQIGAgECBgMDAgUCAQIPAwIBAgECAwYDAwMDAwMDAwEECQUIAwMDAQQDAwQGBQMCBQECAwIDAgMCAwMCBQIDAgwCAwIDAgMEAwQDBgMKAwYCCgEGAgUEBAQJCQYEAf7GAQICCAQEBAQzAwkDBgIGAwMDAwMDBgIDAwkCDgMIAQ0FCwUGCgcEBQkEBgICAgIDAwMDBgYGBQEFBw8mAwoDAwcDAwQDAwQDAwQDBwMOAywFCQgEAgMCEwECAwMIBBsGBxMHDRMGBw0GHw0FAQUBBQEFAQUBBQEBBAQEAgICAwMJCQgGBgUGEAsGEQUGBgUvBgUGBgwGBwUHBgL+sgECBgQEBgQJBAMFCQIDBAMMAQQDBAMHCQkIAwMDAgIBBgIGAwIDAgMDDwYBAwMBAwQHDAQBAwQEBAkIBAMCAgIBAwIEBAkEBQQEAwEKAgICAgICAQEHAgUBAQEDAgIBAgECAQEBAQECAgIEAgkBAQQBBAEEBwUECQgEAgMBAwcGAwMGAgIDAQMCAwIJAgMCAQQBBA0BAgAAAwAAAAADIAJYAAMABwAL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BAMIAQ4CCAIGAgQBAgIBAgMCAwUEAwQDCgUGAwkECQQEAwQCBAYBBAMCAwIDAgECCAEFAwQCBAICBQIHAgIBAgkBAgIDBwMCAwIBAgMCBgIKBf3zBQcBAgMCAwIDAgMCAwQBAgIDAgQBBAQBAgICAgICAQICAgQCAgQBAQECAwICCgECAgIBBAECAQIBAgECAgEEBAMOBAgEAwEBAgMBAgICAQIBAgECAQICAwIBAggCAQICAgEEAgQBBgIKAgYDCgMGAwUDAgEEBggFArIEBQMGAgEBAQEBAQEBAgEDBQMFAwQDAgYDAQIDBgQJCQQEAwIBAQMCAQIDAwIBBQEMAwECAQEBAQEBAQEEAwQECPymBAUDBAQBAQMCAQMCAQQBCgEEAwIDAgECAwECBAcJCQQEAwQCBAIDAgcEAQYBAgECCAEBAQEFCAgCsQQECQQFCgQPBQULDwYFCwUhBgsQCAQFAgIEAwQNCQYGFAUBEgYHBQEFAQUBBQEcAQsFAQUFAQULDwEEAQMDAgEEAwQECP3eBAQEAwICAQIDBA0CAQIDAgECAwMLAgEIAwMFAQIBAgECAQIDAwECAQIBAgQDBgMNAx0CAw0EAw0JBQgFAgEEAwQEAwQIAxQCEQMJBRECBgUDBQUFAgEPAgUCAgEEAwQRBAQFAhwM/pABfBj+tAEnGRlKGBhVGBhNGBgCLQECBgECBAIDBAgJBQQEAwQBAgICBAIBAQEBAQUBBgQDCQUICAIEBgEBAU8BAQMDBAQEBQkEBgECAQYDBAIIDgMIAwYDBAECAgMEAwQDBwcDCAMSCggCBAICAwMICgcHAQwIBAQEBAQEBAsBBwMGAgYCAwUDBwMCAwkDAgIHAgICAgEBAQICBAIFDAEEAgICAgIBAgICAgIBBAIBAgIDBAEEAQQBAgMCAwIBAgMCAQIGAgECBgMDAgUCAQIPAwIBAgECAwYDAwMDAwMDAwEECQUIAwMDAQQDAwQGBQMCBQECAwIDAgMCAwMCBQIDAgwCAwIDAgMEAwQDBgMKAwYCCgEGAgUEBAQJCQYEAf7GAQICCAQEBAQzAwkDBgIGAwMDAwMDBgIDAwkCDgMIAQ0FCwUGCgcEBQkEBgICAgIDAwMDBgYGBQEFBw8mAwoDAwcDAwQDAwQDAwQDBwMOAywFCQgEAgMCEwECAwMIBBsGBxMHDRMGBw0G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BAECBwMCAQIDAgECBgMJAwMDBP3DBQQIAwMCAgECAQEBAQEBAgEIAgEBAQMDBAQEBQkEAwMEAQIFAwIBAQEBAQECAQECAwMJAu4FBAgGAgEBAQEBAggCAQICBAEWAQEEAwwFCQQGBQQDCgIIAQIBAgICBwEBAQEBBAMECPynBQgIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUC8AQFBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQI/a0ECAQDAgICBQYMBgEMAQYHAwEDAwEHBwMBBwcBAwEDBAgEBAwEBAQECQQEBAUCAQEEBAMVAwQKBxkDCgIDAwMDAwMDAgYDAgMCAwIIAZwFBAQBBwocCwcSBAsEBwQEBwQIBBAIBAMCAgEEBAMEBQwFDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBf5EBAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQISBQQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgCHAz+kAF8GP60AScZGUoYGFUYGE0YGAIvAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAMEAQICBQwFCQQDBgIBAQEBAQICBwICAwICAgECAwMIBQkIAwUCAQIBAgECAgIJAgEBAScCAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEOwECAwMEDQULAgMDAwMDAwID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bwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAAV0cmFzaBBnYXRld2F5LXBhcmFsbGVsH2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1jYW5jZWwxaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctbWVzc2FnZRhzdGFydC1ldmVudC1jb21wZW5zYXRpb24uc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1wYXJhbGxlbC1tdWx0aXBsZQtsb29wLW1hcmtlchJwYXJhbGxlbC1taS1tYXJrZXIjc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1zaWduYWwvaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctdGltZXIqaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLXBhcmFsbGVsLW11bHRpcGxlJWludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1jb21wZW5zYXRpb24LZ2F0ZXdheS14b3IQZW5kLWV2ZW50LWNhbmNlbCJpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtY29uZGl0aW9uO2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLXBhcmFsbGVsLW11bHRpcGxlFXN0YXJ0LWV2ZW50LWNvbmRpdGlvbiJzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLXRpbWVyFHNlcXVlbnRpYWwtbWktbWFya2VyCXVzZXItdGFzaw1idXNpbmVzcy1ydWxlEnN1Yi1wcm9jZXNzLW1hcmtlch1zdGFydC1ldmVudC1wYXJhbGxlbC1tdWx0aXBsZRFzdGFydC1ldmVudC1lcnJvch9pbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtc2lnbmFsHmludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1lcnJvchZlbmQtZXZlbnQtY29tcGVuc2F0aW9uFHN1YnByb2Nlc3MtY29sbGFwc2VkE3N1YnByb2Nlc3MtZXhwYW5kZWQEdGFzaw9lbmQtZXZlbnQtZXJyb3IjaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWVzY2FsYXRpb24eaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLXRpbWVyFnN0YXJ0LWV2ZW50LWVzY2FsYXRpb24Sc3RhcnQtZXZlbnQtc2lnbmFsEmJ1c2luZXNzLXJ1bGUtdGFzawZtYW51YWwHcmVjZWl2ZQ1jYWxsLWFjdGl2aXR5EXN0YXJ0LWV2ZW50LXRpbWVyE3N0YXJ0LWV2ZW50LW1lc3NhZ2UXaW50ZXJtZWRpYXRlLWV2ZW50LW5vbmUdaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWxpbmsUZW5kLWV2ZW50LWVzY2FsYXRpb24HYnBtbi1pbw9nYXRld2F5LWNvbXBsZXgSZ2F0ZXdheS1ldmVudGJhc2VkDGdhdGV3YXktbm9uZQpnYXRld2F5LW9yE2VuZC1ldmVudC10ZXJtaW5hdGUQZW5kLWV2ZW50LXNpZ25hbA5lbmQtZXZlbnQtbm9uZRJlbmQtZXZlbnQtbXVsdGlwbGURZW5kLWV2ZW50LW1lc3NhZ2UOZW5kLWV2ZW50LWxpbmsgaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW1lc3NhZ2UlaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWNvbXBlbnNhdGlvbhRzdGFydC1ldmVudC1tdWx0aXBsZQZzY3JpcHQLbWFudWFsLXRhc2sEc2VuZAdzZXJ2aWNlDHJlY2VpdmUtdGFzawR1c2VyEHN0YXJ0LWV2ZW50LW5vbmUjaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWVzY2FsYXRpb24haW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW11bHRpcGxlNGludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLWVzY2FsYXRpb24daW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LWxpbmsmc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1jb25kaXRpb24LZGF0YS1vYmplY3QLc2NyaXB0LXRhc2sJc2VuZC10YXNrCmRhdGEtc3RvcmUnc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1lc2NhbGF0aW9uIGludGVybWVkaWF0ZS1ldmVudC10aHJvdy1tZXNzYWdlMmludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLW11bHRpcGxlMGludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLXNpZ25hbCFpbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctbXVsdGlwbGUkc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1tZXNzYWdlDWFkLWhvYy1tYXJrZXIMc2VydmljZS10YXNrCXRhc2stbm9uZRNjb21wZW5zYXRpb24tbWFya2VyJXN0YXJ0LWV2ZW50LW5vbi1pbnRlcnJ1cHRpbmctbXVsdGlwbGUfaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LXNpZ25hbDNpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbm9uLWludGVycnVwdGluZy1jb25kaXRpb24LcGFydGljaXBhbnQZZXZlbnQtc3VicHJvY2Vzcy1leHBhbmRlZBFsYW5lLWluc2VydC1iZWxvdwpzcGFjZS10b29sEGNvbm5lY3Rpb24tbXVsdGkEbGFuZQpsYXNzby10b29sEWxhbmUtaW5zZXJ0LWFib3ZlEWxhbmUtZGl2aWRlLXRocmVlD2xhbmUtZGl2aWRlLXR3bwpkYXRhLWlucHV0C2RhdGEtb3V0cHV0CWhhbmQtdG9vbAVncm91cA90ZXh0LWFubm90YXRpb24LdHJhbnNhY3Rpb24Mc2NyZXctd3JlbmNoCmNvbm5lY3Rpb24QY29uZGl0aW9uYWwtZmxvdwxkZWZhdWx0LWZsb3cAAA==") + format("truetype"); } @font-face { - font-family: 'bpmn'; - src: url('bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?21877404'); - src: url('bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?21877404#iefix') format('embedded-opentype'), - url('bpmn-js/dist/assets/bpmn-font/font/bpmn.woff2?21877404') format('woff2'), - url('bpmn-js/dist/assets/bpmn-font/font/bpmn.woff?21877404') format('woff'), - url('bpmn-js/dist/assets/bpmn-font/font/bpmn.ttf?21877404') format('truetype'), - url('bpmn-js/dist/assets/bpmn-font/font/bpmn.svg?21877404#bpmn') format('svg'); - font-weight: normal; - font-style: normal; -} \ No newline at end of file + font-family: "bpmn"; + src: url("bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?21877404"); + src: url("bpmn-js/dist/assets/bpmn-font/font/bpmn.eot?21877404#iefix") + format("embedded-opentype"), + url("bpmn-js/dist/assets/bpmn-font/font/bpmn.woff2?21877404") + format("woff2"), + url("bpmn-js/dist/assets/bpmn-font/font/bpmn.woff?21877404") format("woff"), + url("bpmn-js/dist/assets/bpmn-font/font/bpmn.ttf?21877404") + format("truetype"), + url("bpmn-js/dist/assets/bpmn-font/font/bpmn.svg?21877404#bpmn") + format("svg"); + font-weight: normal; + font-style: normal; +} diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_buttons.css b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_buttons.css index cb1690e9..2e40f30e 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_buttons.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_buttons.css @@ -1,55 +1,55 @@ .qwm-btn { - min-width: 120px; - height: 35px; - border-radius: 3px; - font-size: 13px; - font-weight: bold; - font-stretch: normal; - font-style: normal; - line-height: normal; - letter-spacing: normal; - text-align: center; - padding-left: 15px; - padding-right: 15px; + min-width: 120px; + height: 35px; + border-radius: 3px; + font-size: 13px; + font-weight: bold; + font-stretch: normal; + font-style: normal; + line-height: normal; + letter-spacing: normal; + text-align: center; + padding-left: 15px; + padding-right: 15px; } .qwm-btn-primary { - color: #fdfdfe; - box-shadow: 0 2px 2px 0 rgba(0,0,0,0.2); - border: solid 1px var(--blue-darken-62); - background-color: var(--blue-base-65); + color: #fdfdfe; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2); + border: solid 1px var(--blue-darken-62); + background-color: var(--blue-base-65); } .qwm-btn-primary:hover { - border: solid 1px var(--blue-darken-55); - background-color: var(--blue-darken-62); + border: solid 1px var(--blue-darken-55); + background-color: var(--blue-darken-62); } .qwm-btn-primary:active { - border: solid 1px var(--blue-darken-48); - background-color: var(--blue-darken-55); + border: solid 1px var(--blue-darken-48); + background-color: var(--blue-darken-55); } .qwm-btn-primary:disabled { - box-shadow: none; - border: solid 1px var(--blue-lighten-82); - background-color: var(--blue-lighten-75); - color: rgba(253,253,254,0.8); + box-shadow: none; + border: solid 1px var(--blue-lighten-82); + background-color: var(--blue-lighten-75); + color: rgba(253, 253, 254, 0.8); } .qwm-btn-secondary { - color: var(--grey-darken-33); - box-shadow: 0 2px 2px 0 var(--color-000000-opacity-10); - border: solid 1px var(--silver-darken-80); - background-color: var(--silver-base-97); + color: var(--grey-darken-33); + box-shadow: 0 2px 2px 0 var(--color-000000-opacity-10); + border: solid 1px var(--silver-darken-80); + background-color: var(--silver-base-97); } .qwm-btn-secondary:hover { - background-color: var(--silver-darken-94); + background-color: var(--silver-darken-94); } .qwm-btn-secondary:active { - background-color: var(--silver-darken-87); + background-color: var(--silver-darken-87); } .qwm-btn-secondary:disabled { - box-shadow: none; - border: solid 1px var(--silver-darken-87); - background-color: var(--silver-base-97); - color: var(--color-535353-opacity-50); + box-shadow: none; + border: solid 1px var(--silver-darken-87); + background-color: var(--silver-base-97); + color: var(--color-535353-opacity-50); } .qwm-btn + .qwm-btn { - margin-left: 15px; + margin-left: 15px; } diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_colors.css b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_colors.css index a0fe49c6..836fc88e 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_colors.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_colors.css @@ -1,37 +1,37 @@ :root { - --blue-base-65: #4d90ff; - --blue-base-65-opacity-10: rgba(77,144,255,0.1); - --blue-darken-48: #005df7; - --blue-darken-55: #1a70ff; - --blue-darken-62: #3c85ff; - --blue-lighten-75: #80b0ff; - --blue-lighten-82: #a2c5ff; - --blue-lighten-93: #dbe9ff; - --silver-base-97: #f8f8f8; - --silver-lighten-99: #fcfcfc; - --silver-darken-80: #cdcdcd; - --silver-darken-87: #dedede; - --silver-darken-94: #efefef; - --grey-base-40: #666666; - --grey-lighten-56: #909090; - --grey-darken-23: #3b3b3b; - --grey-darken-30: #4c4c4c; - --grey-darken-33: #535353; - --red-base-62: #ff3d3d; - --red-lighten-85: #ffb3b3; - --color-000000: #000000; - --color-000000-opacity-10: rgba(0,0,0,0.1); - --color-000000-opacity-20: rgba(0,0,0,0.2); - --color-000000-opacity-30: rgba(0,0,0,0.3); - --color-444444: #444444; - --color-535353-opacity-50: rgba(83,83,83,0.5); - --color-999999: #999999; - --color-cccccc: #cccccc; - --color-dddddd: #dddddd; - --color-eeeeee: #eeeeee; - --color-f6f6f6: #f6f6f6; - --color-fafafa: #fafafa; - --color-fefefe: #fefefe; - --color-ffffff: #ffffff; - --color-ffffff-opacity-80: rgba(255,255,255,0.8); + --blue-base-65: #4d90ff; + --blue-base-65-opacity-10: rgba(77, 144, 255, 0.1); + --blue-darken-48: #005df7; + --blue-darken-55: #1a70ff; + --blue-darken-62: #3c85ff; + --blue-lighten-75: #80b0ff; + --blue-lighten-82: #a2c5ff; + --blue-lighten-93: #dbe9ff; + --silver-base-97: #f8f8f8; + --silver-lighten-99: #fcfcfc; + --silver-darken-80: #cdcdcd; + --silver-darken-87: #dedede; + --silver-darken-94: #efefef; + --grey-base-40: #666666; + --grey-lighten-56: #909090; + --grey-darken-23: #3b3b3b; + --grey-darken-30: #4c4c4c; + --grey-darken-33: #535353; + --red-base-62: #ff3d3d; + --red-lighten-85: #ffb3b3; + --color-000000: #000000; + --color-000000-opacity-10: rgba(0, 0, 0, 0.1); + --color-000000-opacity-20: rgba(0, 0, 0, 0.2); + --color-000000-opacity-30: rgba(0, 0, 0, 0.3); + --color-444444: #444444; + --color-535353-opacity-50: rgba(83, 83, 83, 0.5); + --color-999999: #999999; + --color-cccccc: #cccccc; + --color-dddddd: #dddddd; + --color-eeeeee: #eeeeee; + --color-f6f6f6: #f6f6f6; + --color-fafafa: #fafafa; + --color-fefefe: #fefefe; + --color-ffffff: #ffffff; + --color-ffffff-opacity-80: rgba(255, 255, 255, 0.8); } diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_modal.css b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_modal.css index a29a09b5..f128551c 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_modal.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/_modal.css @@ -1,147 +1,150 @@ .qwm-modal { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10000; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10000; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; } .qwm-modal:before { - content: ''; - position: fixed; - left: 0; - top: 0; - bottom: 0; - right: 0; - background: var(--grey-base-40); - opacity: .2; - z-index: 1001; + content: ""; + position: fixed; + left: 0; + top: 0; + bottom: 0; + right: 0; + background: var(--grey-base-40); + opacity: 0.2; + z-index: 1001; } .qwm-modal ul { - padding-left: 20px; + padding-left: 20px; } .qwm-modal table { - table-layout: fixed; - font-family: arial, sans-serif; - border-collapse: collapse; - width: 100%; + table-layout: fixed; + font-family: arial, sans-serif; + border-collapse: collapse; + width: 100%; } -.qwm-modal td, .qwm-modal th { - border: 1px solid #dddddd; - text-align: left; - padding: 8px; - word-wrap: break-word; - overflow-wrap: break-word; +.qwm-modal td, +.qwm-modal th { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; + word-wrap: break-word; + overflow-wrap: break-word; } .qwm-modal tr:nth-child(even) { - background-color: #dddddd; + background-color: #dddddd; } .qwm-modal #progress { - width: 100%; - background-color: #ddd; + width: 100%; + background-color: #ddd; } .qwm-modal #bar { - width: 1%; - height: 30px; - background-color: #4CAF50; + width: 1%; + height: 30px; + background-color: #4caf50; } -.qwm-modal tr.spaceUnder>td { +.qwm-modal tr.spaceUnder > td { } -.qwm-modal html, body, #container { - height: 100%; +.qwm-modal html, +body, +#container { + height: 100%; } .qwm-modal-dialog { - position: fixed; - left: 0; - right: 0; - width: 638px; - margin: 10vh auto 6vh; - padding: 0; - z-index: 1001; - overflow: hidden; - border: solid 1px var(--silver-darken-87); - border-radius: 3px; - box-shadow: 0 1px 4px var(--color-000000-opacity-30); - background-color: var(--silver-lighten-99); - color: var(--grey-darken-33); + position: fixed; + left: 0; + right: 0; + width: 638px; + margin: 10vh auto 6vh; + padding: 0; + z-index: 1001; + overflow: hidden; + border: solid 1px var(--silver-darken-87); + border-radius: 3px; + box-shadow: 0 1px 4px var(--color-000000-opacity-30); + background-color: var(--silver-lighten-99); + color: var(--grey-darken-33); } .qwm-modal-content, .qwm-modal-content > form { - display: flex; - flex: auto; - flex-direction: column; - max-height: 84vh; + display: flex; + flex: auto; + flex-direction: column; + max-height: 84vh; } .qwm-modal-header { - padding: 20px 60px 20px 20px; - background-color: var(--silver-base-97); - font-size: 16px; - border-bottom: solid 1px var(--silver-darken-87); + padding: 20px 60px 20px 20px; + background-color: var(--silver-base-97); + font-size: 16px; + border-bottom: solid 1px var(--silver-darken-87); } .qwm-modal-title { - margin: 0; - font-size: 16px; - font-weight: bold; + margin: 0; + font-size: 16px; + font-weight: bold; } .qwm-modal-body { - padding: 16px 20px 20px 20px; - overflow-y: auto; - font-size: 14px; - background-color: var(--silver-lighten-99); + padding: 16px 20px 20px 20px; + overflow-y: auto; + font-size: 14px; + background-color: var(--silver-lighten-99); } .qwm-modal-body legend { - font-size: 14px; - font-weight: bold; - font-stretch: normal; - font-style: normal; - line-height: normal; - letter-spacing: normal; - color: var(--grey-darken-33); - margin-bottom: 23px; + font-size: 14px; + font-weight: bold; + font-stretch: normal; + font-style: normal; + line-height: normal; + letter-spacing: normal; + color: var(--grey-darken-33); + margin-bottom: 23px; } .qwm-modal-body fieldset { - border-style: none; - margin: 20px auto; - padding-left: 0; - padding-right: 0; + border-style: none; + margin: 20px auto; + padding-left: 0; + padding-right: 0; } .qwm-modal-body form { - font-size: 1.1em; + font-size: 1.1em; } .qwm-modal-body .qwm-fields { - display: grid; - grid-row-gap: 15px; + display: grid; + grid-row-gap: 15px; } .qwm-modal-footer { - padding: 15px 20px; - border-top: solid 1px var(--silver-darken-87); - text-align: right; + padding: 15px 20px; + border-top: solid 1px var(--silver-darken-87); + text-align: right; } .qwm-modal-dialog .qwm-close { - position: absolute; - top: 20px; - right: 20px; - width: 22px; - height: 22px; - padding: 0; - border: none; - border-radius: 3px; - background-color: transparent; + position: absolute; + top: 20px; + right: 20px; + width: 22px; + height: 22px; + padding: 0; + border: none; + border-radius: 3px; + background-color: transparent; } .qwm-modal-dialog .close:hover { - background-color: var(--silver-darken-94); + background-color: var(--silver-darken-94); } .qwm-modal-dialog .close:focus { - background-color: var(--silver-darken-87); + background-color: var(--silver-darken-87); } diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/style.css b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/style.css index 7c92caa5..b432a540 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/style.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/camunda-styles/style.css @@ -1,3 +1,3 @@ @import url("_colors.css"); @import url("_buttons.css"); -@import url("_modal.css"); \ No newline at end of file +@import url("_modal.css"); diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/editor-ui.css b/components/bpmn-q/modeler-component/editor/resources/styling/editor-ui.css index 205a93c4..e5a0e14a 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/editor-ui.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/editor-ui.css @@ -1,196 +1,198 @@ .qwm-extensible-btn, .qwm-toolbar-btn { - box-sizing: border-box; - outline: none; - background-color: transparent; - border: none; - border-radius: 6px; - font-size: 15px; - font-family: Arial, serif; - font-weight: bold; - margin: 2px; - padding: 8px; - color: #636363; + box-sizing: border-box; + outline: none; + background-color: transparent; + border: none; + border-radius: 6px; + font-size: 15px; + font-family: Arial, serif; + font-weight: bold; + margin: 2px; + padding: 8px; + color: #636363; } .qwm-shortcuts { - right: 0px; - position: absolute; - box-sizing: border-box; - outline: none; - background-color: transparent; - border: none; - border-radius: 6px; - font-size: 15px; - font-family: Arial, serif; - font-weight: bold; - margin: 2px; - padding: 8px; - color: #636363; + right: 0px; + position: absolute; + box-sizing: border-box; + outline: none; + background-color: transparent; + border: none; + border-radius: 6px; + font-size: 15px; + font-family: Arial, serif; + font-weight: bold; + margin: 2px; + padding: 8px; + color: #636363; } .qwm-shortcuts:hover { - background-color: #f7f7f9; + background-color: #f7f7f9; } .qwm-shortcuts:active { - background-color: #e8e8e8; + background-color: #e8e8e8; } .qwm-toolbar-btn:hover { - background-color: #f7f7f9; + background-color: #f7f7f9; } .qwm-toolbar-btn:active { - background-color: #e8e8e8; + background-color: #e8e8e8; } .qwm-toolbar-transformation-btn { - background-color: transparent; - border: none; - border-radius: 6px; + background-color: transparent; + border: none; + border-radius: 6px; } .qwm-toolbar-transformation-btn:hover { - background-color: #e8e8e8; + background-color: #e8e8e8; } .qwm-toolbar-transformation-btn:active { - background-color: #e8e8e8; + background-color: #e8e8e8; } .qwm-toolbar-splitter { - margin: 5px; - border: 1px solid #cccccc; - max-height: 100%; + margin: 5px; + border: 1px solid #cccccc; + max-height: 100%; } .qwm-toolbar { - background: #ffffff; - display: flex; - /*align-items: flex-start;*/ - overflow: auto; + background: #ffffff; + display: flex; + /*align-items: flex-start;*/ + overflow: auto; } .qwm-extensible-btn { - background-color: #e8e8e8; + background-color: #e8e8e8; } .qwm-extensible-buttons-list { - background: #ffffff; - position: absolute; - display: flex; - padding: 4px; - border: 2px solid #e8e8e8; - border-radius: 6px; - z-index: 9999; - overflow: auto; + background: #ffffff; + position: absolute; + display: flex; + padding: 4px; + border: 2px solid #e8e8e8; + border-radius: 6px; + z-index: 9999; + overflow: auto; } .qwm-icon-saving:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("../icons/save-outline-icon.png"); - background-repeat: no-repeat; - display: inline-block; - float: left; + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("../icons/save-outline-icon.png"); + background-repeat: no-repeat; + display: inline-block; + float: left; } .qwm-icon-open-file:before { - content: ""; - width: 20px; - height: 15px; - background-size: contain; - background-image: url("../icons/open-folder-outline-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; + content: ""; + width: 20px; + height: 15px; + background-size: contain; + background-image: url("../icons/open-folder-outline-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; } .qwm-icon-new-file:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("../icons/blank-file-outline-icon.png"); - background-repeat: no-repeat; - display: inline-block; - float: left; + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("../icons/blank-file-outline-icon.png"); + background-repeat: no-repeat; + display: inline-block; + float: left; } .qwm-icon-shortcut:before { - content: ""; - width: 15px; - height: 20px; - margin-top: 3px; - background-size: contain; - background-image: url("../icons/shortcut.png"); - background-repeat: no-repeat; - display: inline-block; - float: left; + content: ""; + width: 15px; + height: 20px; + margin-top: 3px; + background-size: contain; + background-image: url("../icons/shortcut.png"); + background-repeat: no-repeat; + display: inline-block; + float: left; } .qwm-toolbar-transformation-edit-icon { - border-color: #cccccc; - content: ""; - width: 17px; - height: 17px; - background-size: contain; - background-image: url("../icons/wrench-icon-outline.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin-left: 3px; - margin-top: 2px; + border-color: #cccccc; + content: ""; + width: 17px; + height: 17px; + background-size: contain; + background-image: url("../icons/wrench-icon-outline.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin-left: 3px; + margin-top: 2px; } .qwm-workflow-deployment-btn:before { - content: ""; - display: block; - width: 15px; - height: 15px; - background-size: contain; - background: url("../icons/workflow-deployment-icon.png") no-repeat center center; - float: left; + content: ""; + display: block; + width: 15px; + height: 15px; + background-size: contain; + background: url("../icons/workflow-deployment-icon.png") no-repeat center + center; + float: left; } .qwm-workflow-transformation-btn:before { - content: ""; - display: block; - width: 15px; - height: 15px; - background-size: contain; - background: url("../icons/worklow-transformation-icon.png") no-repeat center center; - float: left; + content: ""; + display: block; + width: 15px; + height: 15px; + background-size: contain; + background: url("../icons/worklow-transformation-icon.png") no-repeat center + center; + float: left; } .qwm-popup-menu-more-options:after { - content: ""; - background-size: contain; - background-repeat: no-repeat; - display: inline-block; - background-image: url("../icons/line-angle-right-icon.svg"); - width: 16px; - height: 16px; - float: left; - margin-left: 6px; - margin-top: 2px; + content: ""; + background-size: contain; + background-repeat: no-repeat; + display: inline-block; + background-image: url("../icons/line-angle-right-icon.svg"); + width: 16px; + height: 16px; + float: left; + margin-left: 6px; + margin-top: 2px; } .qwm-popup-menu-less-options:before { - content: ""; - width: 23px; - height: 15px; - background-size: contain; - background-image: url("../icons/line-angle-left-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin-top: 2px; + content: ""; + width: 23px; + height: 15px; + background-size: contain; + background-image: url("../icons/line-angle-left-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin-top: 2px; } .qwm-tmp { - background: url("../icons/save-outline-icon.png") no-repeat center center; -} \ No newline at end of file + background: url("../icons/save-outline-icon.png") no-repeat center center; +} diff --git a/components/bpmn-q/modeler-component/editor/resources/styling/modeler.css b/components/bpmn-q/modeler-component/editor/resources/styling/modeler.css index 92f15955..db064a43 100644 --- a/components/bpmn-q/modeler-component/editor/resources/styling/modeler.css +++ b/components/bpmn-q/modeler-component/editor/resources/styling/modeler.css @@ -1,150 +1,149 @@ .qwm-content, -.qwm-content>div { - width: 100%; - height: 100%; - overflow: hidden; +.qwm-content > div { + width: 100%; + height: 100%; + overflow: hidden; } -.qwm-content>.qwm-message { - text-align: center; - display: table; +.qwm-content > .qwm-message { + text-align: center; + display: table; - font-size: 16px; - color: #111; + font-size: 16px; + color: #111; } -.qwm-content>.qwm-message .qwm-note { - vertical-align: middle; - text-align: center; - display: table-cell; +.qwm-content > .qwm-message .qwm-note { + vertical-align: middle; + text-align: center; + display: table-cell; } .qwm-content .qwm-error .qwm-details { - max-width: 500px; - font-size: 12px; - margin: 20px auto; - text-align: left; + max-width: 500px; + font-size: 12px; + margin: 20px auto; + text-align: left; } .qwm-content .qwm-error pre { - border: solid 1px #CCC; - background: #EEE; - padding: 10px; + border: solid 1px #ccc; + background: #eee; + padding: 10px; } .qwm-content:not(.with-error) .qwm-error, .qwm-content.with-error .qwm-intro, .qwm-content.with-diagram .qwm-intro { - display: none; + display: none; } - .qwm-content .qwm-canvas, .qwm-content.with-error .qwm-canvas { - visibility: hidden; + visibility: hidden; } .qwm-content.with-diagram .qwm-canvas { - visibility: visible; + visibility: visible; } .qwm-buttons { - position: fixed; - bottom: 20px; - left: 20px; + position: fixed; + bottom: 20px; + left: 20px; - padding: 0; - margin: 0; - list-style: none; + padding: 0; + margin: 0; + list-style: none; } -.qwm-buttons>li { - display: inline-block; - margin-right: 10px; +.qwm-buttons > li { + display: inline-block; + margin-right: 10px; } -.qwm-buttons>li>a { - background: #DDD; - border: solid 1px #666; - display: inline-block; - padding: 5px; +.qwm-buttons > li > a { + background: #ddd; + border: solid 1px #666; + display: inline-block; + padding: 5px; } .qwm-buttons a { - opacity: 0.3; + opacity: 0.3; } .qwm-buttons a.active { - opacity: 1.0; + opacity: 1; } .bjs-breadcrumbs { - position: absolute; - display: none; - flex-wrap: wrap; - align-items: center; - top: 20px; - left: 20px; - padding: 0px; - margin: 0px; - font-size: 20px; - line-height: normal; + position: absolute; + display: none; + flex-wrap: wrap; + align-items: center; + top: 20px; + left: 20px; + padding: 0px; + margin: 0px; + font-size: 20px; + line-height: normal; } .bjs-breadcrumbs-shown .bjs-breadcrumbs { - display: flex; + display: flex; } .djs-palette-shown .bjs-breadcrumbs { - left: 90px; + left: 90px; } .djs-palette-shown.djs-palette-two-column .bjs-breadcrumbs { - left: 140px; + left: 140px; } .bjs-breadcrumbs li { - display: inline-flex; - padding-bottom: 5px; - font-size: 17px; - font-family: Arial, serif; - font-weight: bold; + display: inline-flex; + padding-bottom: 5px; + font-size: 17px; + font-family: Arial, serif; + font-weight: bold; } .bjs-breadcrumbs li a { - cursor: pointer; - color: blue + cursor: pointer; + color: blue; } .bjs-breadcrumbs li:last-of-type a { - color: inherit; - cursor: default; + color: inherit; + cursor: default; } .bjs-breadcrumbs li:not(:first-child)::before { - content: url('data:image/svg+xml;utf8,'); - padding: 0 8px; - color: var(--breadcrumbs-arrow-color); - height: 1em; + content: url('data:image/svg+xml;utf8,'); + padding: 0 8px; + color: var(--breadcrumbs-arrow-color); + height: 1em; } .bjs-breadcrumbs .bjs-crumb { - display: inline-block; - max-width: 200px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + display: inline-block; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .bjs-drilldown-empty { - display: none; + display: none; } .selected .bjs-drilldown-empty { - display: inherit; + display: inherit; } .bjs-drilldown { - position: relative; - top: 10px; -} \ No newline at end of file + position: relative; + top: 10px; +} diff --git a/components/bpmn-q/modeler-component/editor/shortcut/ShortcutModal.js b/components/bpmn-q/modeler-component/editor/shortcut/ShortcutModal.js index 36d7c2bf..2562f0e4 100644 --- a/components/bpmn-q/modeler-component/editor/shortcut/ShortcutModal.js +++ b/components/bpmn-q/modeler-component/editor/shortcut/ShortcutModal.js @@ -10,9 +10,9 @@ */ /* eslint-disable no-unused-vars */ -import React from 'react'; -import Modal from '../ui/modal/Modal'; -import '../config/config-modal.css'; +import React from "react"; +import Modal from "../ui/modal/Modal"; +import "../config/config-modal.css"; // polyfill upcoming structural components const Title = Modal.Title || (({ children }) =>

{children}

); @@ -26,62 +26,82 @@ const Body = Modal.Body || (({ children }) =>
{children}
); * @constructor */ export default function ShortcutModal({ onClose }) { + return ( + + Keyboard Shortcuts - return - - Keyboard Shortcuts - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select All⇧ + A
Searchctrl + F
Redoctrl + Y
Undoctrl + Z
Scrolling (Vertical)ctrl + Scrolling
Scrolling (Horizontal)ctrl + ⇧ + Scrolling
DeleteD, Backspace
Hand ToolH
Lasso ToolL
Replace ToolR
Space ToolS
- -
; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Select All + ⇧ + A +
Search + ctrl + F +
Redo + ctrl + Y +
Undo + ctrl + Z +
Scrolling (Vertical) + ctrl + Scrolling +
Scrolling (Horizontal) + ctrl + ⇧ + Scrolling +
Delete + D, Backspace +
Hand Tool + H +
Lasso Tool + L +
Replace Tool + R +
Space Tool + S +
+ +
+ ); } - diff --git a/components/bpmn-q/modeler-component/editor/shortcut/ShortcutPlugin.js b/components/bpmn-q/modeler-component/editor/shortcut/ShortcutPlugin.js index d1bc1266..f0aca3e8 100644 --- a/components/bpmn-q/modeler-component/editor/shortcut/ShortcutPlugin.js +++ b/components/bpmn-q/modeler-component/editor/shortcut/ShortcutPlugin.js @@ -9,42 +9,46 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, {PureComponent, Fragment} from 'react'; +import React, { PureComponent, Fragment } from "react"; import ShortcutModal from "../shortcut/ShortcutModal"; export default class ShortcutPlugin extends PureComponent { - - constructor(props) { - super(props); - - this.state = { - shortcutOpen: false, - }; - - this.handleConfigClosed = this.handleConfigClosed.bind(this); - } - - // callback function to close the shortcut modal - handleConfigClosed() { - this.setState({shortcutOpen: false}); - } - - render() { - - // render button and pop-up menu - return ( -
- -
- {this.state.shortcutOpen && ( - - )} -
); - } + constructor(props) { + super(props); + + this.state = { + shortcutOpen: false, + }; + + this.handleConfigClosed = this.handleConfigClosed.bind(this); + } + + // callback function to close the shortcut modal + handleConfigClosed() { + this.setState({ shortcutOpen: false }); + } + + render() { + // render button and pop-up menu + return ( + +
+ +
+ {this.state.shortcutOpen && ( + + )} +
+ ); + } } diff --git a/components/bpmn-q/modeler-component/editor/ui/ButtonToolbar.js b/components/bpmn-q/modeler-component/editor/ui/ButtonToolbar.js index d0a2ba5e..f8dd05e5 100644 --- a/components/bpmn-q/modeler-component/editor/ui/ButtonToolbar.js +++ b/components/bpmn-q/modeler-component/editor/ui/ButtonToolbar.js @@ -1,4 +1,4 @@ -import React, {Fragment} from 'react'; +import React, { Fragment } from "react"; import SaveButton from "./SaveButton"; import OpenButton from "./OpenButton"; import NewDiagramButton from "./NewDiagramButton"; @@ -7,7 +7,6 @@ import ConfigPlugin from "../config/ConfigPlugin"; import TransformationToolbarButton from "./TransformationToolbarButton"; import ShortcutPlugin from "../shortcut/ShortcutPlugin"; - /** * React component which displays the toolbar of the modeler * @@ -16,35 +15,32 @@ import ShortcutPlugin from "../shortcut/ShortcutPlugin"; * @constructor */ export default function ButtonToolbar(props) { + const { modeler, pluginButtons, transformButtons } = props; - const { - modeler, - pluginButtons, - transformButtons, - } = props; - - const hasTransformations = transformButtons.length > 0; + const hasTransformations = transformButtons.length > 0; - return ( - -
-
- - - -
- -
- {hasTransformations && - } - -
- {React.Children.toArray(pluginButtons)} - -
-
- ); -} \ No newline at end of file + return ( + +
+
+ + + +
+ +
+ {hasTransformations && ( + + )} + +
+ {React.Children.toArray(pluginButtons)} + +
+
+ ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/DeploymentButton.js b/components/bpmn-q/modeler-component/editor/ui/DeploymentButton.js index e6cb6c17..f51fc09f 100644 --- a/components/bpmn-q/modeler-component/editor/ui/DeploymentButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/DeploymentButton.js @@ -1,8 +1,8 @@ -import React from 'react'; -import NotificationHandler from './notifications/NotificationHandler'; -import {deployWorkflowToCamunda} from '../util/IoUtilities'; -import {getCamundaEndpoint} from '../config/EditorConfigManager'; -import {getRootProcess} from '../util/ModellingUtilities'; +import React from "react"; +import NotificationHandler from "./notifications/NotificationHandler"; +import { deployWorkflowToCamunda } from "../util/IoUtilities"; +import { getCamundaEndpoint } from "../config/EditorConfigManager"; +import { getRootProcess } from "../util/ModellingUtilities"; /** * React button for starting the deployment of the workflow. @@ -12,56 +12,66 @@ import {getRootProcess} from '../util/ModellingUtilities'; * @constructor */ export default function DeploymentButton(props) { + const { modeler } = props; - const {modeler} = props; + /** + * Deploy the current workflow to the Camunda engine + */ + async function deploy() { + NotificationHandler.getInstance().displayNotification({ + title: "Deployment started", + content: + "Deployment of the current Workflow to the Camunda Engine under " + + getCamundaEndpoint() + + " started.", + }); - /** - * Deploy the current workflow to the Camunda engine - */ - async function deploy() { + // get XML of the current workflow + const rootElement = getRootProcess(modeler.getDefinitions()); + const xml = (await modeler.saveXML({ format: true })).xml; - NotificationHandler.getInstance().displayNotification({ - title: 'Deployment started', - content: 'Deployment of the current Workflow to the Camunda Engine under ' + getCamundaEndpoint() + ' started.', - }); - - // get XML of the current workflow - const rootElement = getRootProcess(modeler.getDefinitions()); - const xml = (await modeler.saveXML({format: true})).xml; - - // check if there are views defined for the modeler and include them in the deployment - let viewsDict = {}; - if (modeler.views !== undefined) { - console.log('Adding additional views during deployment: ', modeler.views); - viewsDict = modeler.views; - } + // check if there are views defined for the modeler and include them in the deployment + let viewsDict = {}; + if (modeler.views !== undefined) { + console.log("Adding additional views during deployment: ", modeler.views); + viewsDict = modeler.views; + } - // start deployment of workflow and views - let result = await deployWorkflowToCamunda(rootElement.id, xml, viewsDict); + // start deployment of workflow and views + let result = await deployWorkflowToCamunda(rootElement.id, xml, viewsDict); - if (result.status === 'failed') { - NotificationHandler.getInstance().displayNotification({ - type: 'error', - title: 'Unable to deploy workflow', - content: 'Workflow deployment failed. Please check the configured Camunda engine endpoint!', - duration: 20000 - }); - } else { - NotificationHandler.getInstance().displayNotification({ - type: 'info', - title: 'Workflow successfully deployed', - content: 'Workflow successfully deployed under deployment Id: ' + result.deployedProcessDefinition.deploymentId, - duration: 20000 - }); - } + if (result.status === "failed") { + NotificationHandler.getInstance().displayNotification({ + type: "error", + title: "Unable to deploy workflow", + content: + "Workflow deployment failed. Please check the configured Camunda engine endpoint!", + duration: 20000, + }); + } else { + NotificationHandler.getInstance().displayNotification({ + type: "info", + title: "Workflow successfully deployed", + content: + "Workflow successfully deployed under deployment Id: " + + result.deployedProcessDefinition.deploymentId, + duration: 20000, + }); } + } - return ( - <> - - - ); -} \ No newline at end of file + return ( + <> + + + ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/ExtensibleButton.js b/components/bpmn-q/modeler-component/editor/ui/ExtensibleButton.js index c4e2f08f..f78b4c65 100644 --- a/components/bpmn-q/modeler-component/editor/ui/ExtensibleButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/ExtensibleButton.js @@ -1,82 +1,91 @@ -import React, {Component, createRef, useState} from 'react'; +import React, { Component, createRef, useState } from "react"; /** * React component defining a button which displays a list of buttons, the sub buttons under the button if the user clicks on it. Can be used * to group several button into one. */ export default class ExtensibleButton extends Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - const { - subButtons, // array of buttons which are grouped into this button - title, - styleClass, - description, - } = props; + const { + subButtons, // array of buttons which are grouped into this button + title, + styleClass, + description, + } = props; - this.state = { - isToggleOn: false, - subButtons: subButtons, - title: title, - styleClass: styleClass || '', - description: description, - }; - - this.handleClick = this.handleClick.bind(this); - this.openingListener = this.openingListener.bind(this); - - this.wrapperRef = createRef(); - } - - componentDidMount() { - document.addEventListener('new-extensible-button-opened', this.openingListener); - } + this.state = { + isToggleOn: false, + subButtons: subButtons, + title: title, + styleClass: styleClass || "", + description: description, + }; - componentWillUnmount() { - document.removeEventListener('new-extensible-button-opened', this.openingListener); - } + this.handleClick = this.handleClick.bind(this); + this.openingListener = this.openingListener.bind(this); - // open or close sub buttons - handleClick() { + this.wrapperRef = createRef(); + } - if (!this.state.isToggleOn) { + componentDidMount() { + document.addEventListener( + "new-extensible-button-opened", + this.openingListener + ); + } - // dispatch event to close other extensible buttons - const newEvent = new CustomEvent('new-extensible-button-opened', { - detail: { - openButtonId: this.state.title + this.state.styleClass, - }, - }); - return document.dispatchEvent(newEvent); - } + componentWillUnmount() { + document.removeEventListener( + "new-extensible-button-opened", + this.openingListener + ); + } - this.setState(state => ({ - isToggleOn: !state.isToggleOn, - })); + // open or close sub buttons + handleClick() { + if (!this.state.isToggleOn) { + // dispatch event to close other extensible buttons + const newEvent = new CustomEvent("new-extensible-button-opened", { + detail: { + openButtonId: this.state.title + this.state.styleClass, + }, + }); + return document.dispatchEvent(newEvent); } - // callback for a listener to close this button if another extensible button is opening - openingListener = (event) => { - const currentId = this.state.title + this.state.styleClass; - this.setState({ isToggleOn: currentId === event.detail.openButtonId}); - }; + this.setState((state) => ({ + isToggleOn: !state.isToggleOn, + })); + } - render() { - return ( -
- - {this.state.isToggleOn && -
- {React.Children.toArray(this.state.subButtons)} -
- } -
- ); - } -} \ No newline at end of file + // callback for a listener to close this button if another extensible button is opening + openingListener = (event) => { + const currentId = this.state.title + this.state.styleClass; + this.setState({ isToggleOn: currentId === event.detail.openButtonId }); + }; + + render() { + return ( +
+ + {this.state.isToggleOn && ( +
+ {React.Children.toArray(this.state.subButtons)} +
+ )} +
+ ); + } +} diff --git a/components/bpmn-q/modeler-component/editor/ui/NewDiagramButton.js b/components/bpmn-q/modeler-component/editor/ui/NewDiagramButton.js index 7c4354d1..2bff09b4 100644 --- a/components/bpmn-q/modeler-component/editor/ui/NewDiagramButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/NewDiagramButton.js @@ -1,5 +1,5 @@ -import React from 'react'; -import {createNewDiagram} from '../util/IoUtilities'; +import React from "react"; +import { createNewDiagram } from "../util/IoUtilities"; /** * React button which creates a new workflow. @@ -9,14 +9,17 @@ import {createNewDiagram} from '../util/IoUtilities'; * @constructor */ export default function NewDiagramButton(props) { + const { modeler } = props; - const {modeler} = props; - - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/OpenButton.js b/components/bpmn-q/modeler-component/editor/ui/OpenButton.js index 3ceb0cd9..1a207d2b 100644 --- a/components/bpmn-q/modeler-component/editor/ui/OpenButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/OpenButton.js @@ -1,9 +1,9 @@ -import React, {useRef} from 'react'; -import {loadDiagram} from '../util/IoUtilities'; -import {getModeler} from '../ModelerHandler'; -import * as editorConfig from '../config/EditorConfigManager'; -import {dispatchWorkflowEvent} from '../events/EditorEventHandler'; -import {workflowEventTypes} from '../EditorConstants'; +import React, { useRef } from "react"; +import { loadDiagram } from "../util/IoUtilities"; +import { getModeler } from "../ModelerHandler"; +import * as editorConfig from "../config/EditorConfigManager"; +import { dispatchWorkflowEvent } from "../events/EditorEventHandler"; +import { workflowEventTypes } from "../EditorConstants"; import NotificationHandler from "./notifications/NotificationHandler"; /** @@ -13,64 +13,69 @@ import NotificationHandler from "./notifications/NotificationHandler"; * @constructor */ export default function OpenButton() { + const inputRef = useRef(null); - const inputRef = useRef(null); + function handleClick() { + inputRef.current.click(); + } - function handleClick() { - inputRef.current.click(); - } - - function handleChange(event) { - - const file = event.target.files[0]; - - if (file.name.endsWith('.bpmn')) { - - // open file and load its content as bpmn diagram in the modeler - const reader = new FileReader(); - reader.onload = (e) => { + function handleChange(event) { + const file = event.target.files[0]; - const xml = e.target.result; + if (file.name.endsWith(".bpmn")) { + // open file and load its content as bpmn diagram in the modeler + const reader = new FileReader(); + reader.onload = (e) => { + const xml = e.target.result; - loadDiagram(xml, getModeler(), false).then((result) => { - // save file name in editor configs - editorConfig.setFileName(file.name); + loadDiagram(xml, getModeler(), false).then((result) => { + // save file name in editor configs + editorConfig.setFileName(file.name); - dispatchWorkflowEvent(workflowEventTypes.LOADED, xml, file.name); + dispatchWorkflowEvent(workflowEventTypes.LOADED, xml, file.name); - if (result.warnings && result.warnings.some(warning => warning.error)) { - NotificationHandler.getInstance().displayNotification({ - type: 'warning', - title: 'Loaded Diagram contains Problems', - content: `The diagram could not be properly loaded. Maybe it contains modelling elements which are not supported be the currently active plugins.`, - duration: 20000 - }); - } + if ( + result.warnings && + result.warnings.some((warning) => warning.error) + ) { + NotificationHandler.getInstance().displayNotification({ + type: "warning", + title: "Loaded Diagram contains Problems", + content: `The diagram could not be properly loaded. Maybe it contains modelling elements which are not supported be the currently active plugins.`, + duration: 20000, + }); + } - if (result.error) { - NotificationHandler.getInstance().displayNotification({ - type: 'warning', - title: 'Unable to load Diagram', - content: `During the loading of the diagram some errors occurred: ${result.error}`, - duration: 20000 - }); - } - }); - }; - reader.readAsText(file); - } + if (result.error) { + NotificationHandler.getInstance().displayNotification({ + type: "warning", + title: "Unable to load Diagram", + content: `During the loading of the diagram some errors occurred: ${result.error}`, + duration: 20000, + }); + } + }); + }; + reader.readAsText(file); } + } - return ( - <> - handleChange(event)}/> - - - ); -} \ No newline at end of file + return ( + <> + handleChange(event)} + /> + + + ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/SaveButton.js b/components/bpmn-q/modeler-component/editor/ui/SaveButton.js index 0f9195c1..d2a34264 100644 --- a/components/bpmn-q/modeler-component/editor/ui/SaveButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/SaveButton.js @@ -1,5 +1,5 @@ import React from "react"; -import {saveModelerAsLocalFile} from "../util/IoUtilities"; +import { saveModelerAsLocalFile } from "../util/IoUtilities"; /** * React button which saves the current workflow to the users local file system when clicked @@ -9,15 +9,17 @@ import {saveModelerAsLocalFile} from "../util/IoUtilities"; * @constructor */ export default function SaveButton(props) { + const { modeler } = props; - const {modeler} = props; - - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/TransformationButton.js b/components/bpmn-q/modeler-component/editor/ui/TransformationButton.js index 72872ad0..39c39801 100644 --- a/components/bpmn-q/modeler-component/editor/ui/TransformationButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/TransformationButton.js @@ -1,4 +1,4 @@ -import React, {useState} from "react"; +import React, { useState } from "react"; /** * React button which contains a transformation function to transform the workflow. The button contains a button and a @@ -9,34 +9,41 @@ import React, {useState} from "react"; * @constructor */ export default function TransformationButton(props) { + const { + transformWorkflow, // transformation function of this component + title, + name, + className, + selectedCallback, // callback for propagating changes of the checkbox + isChecked, // initial value for the checkbox + } = props; - const { - transformWorkflow, // transformation function of this component - title, - name, - className, - selectedCallback, // callback for propagating changes of the checkbox - isChecked, // initial value for the checkbox - } = props; + const [checked, setChecked] = useState(isChecked); - const [checked, setChecked] = useState(isChecked); + // call selectedCallback if the checkbox changes + const handleCheckboxChange = () => { + setChecked(!checked); + selectedCallback(!checked, name); + }; - // call selectedCallback if the checkbox changes - const handleCheckboxChange = () => { - setChecked(!checked); - selectedCallback(!checked, name); - }; - - return ( -
- - - -
- ); -} \ No newline at end of file + return ( +
+ + +
+ ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/TransformationToolbarButton.js b/components/bpmn-q/modeler-component/editor/ui/TransformationToolbarButton.js index 7dd079d5..a04c2e58 100644 --- a/components/bpmn-q/modeler-component/editor/ui/TransformationToolbarButton.js +++ b/components/bpmn-q/modeler-component/editor/ui/TransformationToolbarButton.js @@ -1,8 +1,8 @@ -import React, {useRef, useState, useEffect} from 'react'; +import React, { useRef, useState, useEffect } from "react"; import TransformationButton from "./TransformationButton"; -import {getXml, handleTransformedWorkflow} from '../util/IoUtilities'; -import NotificationHandler from './notifications/NotificationHandler'; -import {getModeler} from '../ModelerHandler'; +import { getXml, handleTransformedWorkflow } from "../util/IoUtilities"; +import NotificationHandler from "./notifications/NotificationHandler"; +import { getModeler } from "../ModelerHandler"; /** * Component which groups the TransformationButtons defined by the plugins. Each button defines a transformation function which @@ -16,166 +16,173 @@ import {getModeler} from '../ModelerHandler'; * @constructor */ export default function TransformationToolbarButton(props) { + const { + subButtons, // list of transformation buttons + title, + styleClass, + } = props; - const { - subButtons, // list of transformation buttons - title, - styleClass, - } = props; + // flag defining if the subButtons should be displayed or not + const [isToggleOn, setToggleOn] = useState(false); - // flag defining if the subButtons should be displayed or not - const [isToggleOn, setToggleOn] = useState(false); + const wrapperRef = useRef(null); - const wrapperRef = useRef(null); + // initially activate all transformations + const initialTransformationStates = {}; + subButtons.forEach(function (button) { + initialTransformationStates[button.props.name] = true; + }); - // initially activate all transformations - const initialTransformationStates = {}; - subButtons.forEach(function (button) { - initialTransformationStates[button.props.name] = true; - }); - - /* + /* Saves whether a transformation should be executed, if the state of a transformation is true, this transformation will be executed */ - const [transformationStates, setTransformationStates] = useState(initialTransformationStates); - - // execute the transformation functions of each button - async function startTransformation() { - - // get current workflow of the modeler as xml string - const modeler = getModeler(); - let xml = await getXml(modeler); - let tmp; - - // show notification if at least one transformation function is active - if (Object.entries(transformationStates).some((state) => state[1])) { - NotificationHandler.getInstance().displayNotification({ - type: 'info', - title: 'Workflow Transformation Started!', - content: 'Successfully started transformation process for the current workflow!', - duration: 7000 - }); - } - - try { - // start all active transformations - for (let transformationButton of subButtons) { - - if (transformationStates[transformationButton.props.name]) { - - console.log('Starting Transformation for ' + transformationButton.props.name); - - // execute transformation function of the button - tmp = await transformationButton.props.transformWorkflow(xml); - - if (tmp && tmp.status === 'transformed') { - xml = tmp.xml; - - } else { - - // break process if one transformation failes - const cause = tmp.cause || 'Transformation failed because of an unexpected error.'; - - NotificationHandler.getInstance().displayNotification({ - type: 'warning', - title: `Unable to transform ${transformationButton.props.name} elements in the workflow`, - content: cause, - duration: 10000 - }); - } - } - } - if (xml) { + const [transformationStates, setTransformationStates] = useState( + initialTransformationStates + ); + + // execute the transformation functions of each button + async function startTransformation() { + // get current workflow of the modeler as xml string + const modeler = getModeler(); + let xml = await getXml(modeler); + let tmp; + + // show notification if at least one transformation function is active + if (Object.entries(transformationStates).some((state) => state[1])) { + NotificationHandler.getInstance().displayNotification({ + type: "info", + title: "Workflow Transformation Started!", + content: + "Successfully started transformation process for the current workflow!", + duration: 7000, + }); + } - // handle transformed workflow (open in new tab, save to file storage etc.) - await handleTransformedWorkflow(xml); - } + try { + // start all active transformations + for (let transformationButton of subButtons) { + if (transformationStates[transformationButton.props.name]) { + console.log( + "Starting Transformation for " + transformationButton.props.name + ); + + // execute transformation function of the button + tmp = await transformationButton.props.transformWorkflow(xml); + + if (tmp && tmp.status === "transformed") { + xml = tmp.xml; + } else { + // break process if one transformation failes + const cause = + tmp.cause || + "Transformation failed because of an unexpected error."; - } catch (error) { NotificationHandler.getInstance().displayNotification({ - type: 'warning', - title: 'Error during transformation', - content: 'An unexpected error occurred during transformation. Please check the formatting of your workflow.', - duration: 10000 + type: "warning", + title: `Unable to transform ${transformationButton.props.name} elements in the workflow`, + content: cause, + duration: 10000, }); - console.log(error); + } } + } + if (xml) { + // handle transformed workflow (open in new tab, save to file storage etc.) + await handleTransformedWorkflow(xml); + } + } catch (error) { + NotificationHandler.getInstance().displayNotification({ + type: "warning", + title: "Error during transformation", + content: + "An unexpected error occurred during transformation. Please check the formatting of your workflow.", + duration: 10000, + }); + console.log(error); } - - // callback to activate/ deactivate a transformation - function selectedCallback(isActive, transformationName) { - const newState = transformationStates; - newState[transformationName] = isActive; - setTransformationStates(newState); + } + + // callback to activate/ deactivate a transformation + function selectedCallback(isActive, transformationName) { + const newState = transformationStates; + newState[transformationName] = isActive; + setTransformationStates(newState); + } + + // opens/ closes subButtons by inverting isToggleOn + function handleClick() { + if (!isToggleOn) { + // dispatch event to close other extensible buttons + const newEvent = new CustomEvent("new-extensible-button-opened", { + detail: { + openButtonId: title + styleClass, + }, + }); + return document.dispatchEvent(newEvent); } - // opens/ closes subButtons by inverting isToggleOn - function handleClick() { - - if (!isToggleOn) { - - // dispatch event to close other extensible buttons - const newEvent = new CustomEvent('new-extensible-button-opened', { - detail: { - openButtonId: title + styleClass, - }, - }); - return document.dispatchEvent(newEvent); - } - - setToggleOn(!isToggleOn); - } - - // close displayed TransformationButtons if another ExtensibleButton is opening - const openingListener = event => { - const currentId = title + styleClass; - setToggleOn(currentId === event.detail.openButtonId); + setToggleOn(!isToggleOn); + } + + // close displayed TransformationButtons if another ExtensibleButton is opening + const openingListener = (event) => { + const currentId = title + styleClass; + setToggleOn(currentId === event.detail.openButtonId); + }; + + useEffect(() => { + document.addEventListener("new-extensible-button-opened", openingListener); + return () => { + document.removeEventListener( + "new-extensible-button-opened", + openingListener + ); }; - - useEffect(() => { - document.addEventListener('new-extensible-button-opened', openingListener); - return () => { - document.removeEventListener('new-extensible-button-opened', openingListener); - }; - }, []); - - return ( -
- - - {isToggleOn && -
- { - subButtons.map(function (entry, index) { - return (); - }) - } -
- } + }, []); + + return ( +
+ + + {isToggleOn && ( +
+ {subButtons.map(function (entry, index) { + return ( + + ); + })}
- ); -} \ No newline at end of file + )} +
+ ); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/modal/EscapeTrap.js b/components/bpmn-q/modeler-component/editor/ui/modal/EscapeTrap.js index 64d61dc6..b2c2490a 100644 --- a/components/bpmn-q/modeler-component/editor/ui/modal/EscapeTrap.js +++ b/components/bpmn-q/modeler-component/editor/ui/modal/EscapeTrap.js @@ -9,31 +9,28 @@ */ export default function EscapeTrap(onEscape) { - - function handleKeyDown(event) { - if (isEscape(event)) { - onEscape(event); - } + function handleKeyDown(event) { + if (isEscape(event)) { + onEscape(event); } + } - function mount() { - document.addEventListener('keydown', handleKeyDown); - } + function mount() { + document.addEventListener("keydown", handleKeyDown); + } - function unmount() { - document.removeEventListener('keydown', handleKeyDown); - } - - return { - mount, - unmount - }; + function unmount() { + document.removeEventListener("keydown", handleKeyDown); + } + return { + mount, + unmount, + }; } - // helpers /////////////// function isEscape(event) { - return event.key === 'Escape'; + return event.key === "Escape"; } diff --git a/components/bpmn-q/modeler-component/editor/ui/modal/FocusTrap.js b/components/bpmn-q/modeler-component/editor/ui/modal/FocusTrap.js index ea140b29..952ee74d 100644 --- a/components/bpmn-q/modeler-component/editor/ui/modal/FocusTrap.js +++ b/components/bpmn-q/modeler-component/editor/ui/modal/FocusTrap.js @@ -9,115 +9,109 @@ */ const focusableElementsSelector = [ - 'a[href]', - 'button:not([disabled])', - 'area[href]', - 'input:not([disabled])', - 'select:not([disabled])', - 'textarea:not([disabled])', - 'iframe', - 'object', - 'embed', - '*[tabindex]', - '*[contenteditable]' + "a[href]", + "button:not([disabled])", + "area[href]", + "input:not([disabled])", + "select:not([disabled])", + "textarea:not([disabled])", + "iframe", + "object", + "embed", + "*[tabindex]", + "*[contenteditable]", ].join(); export default function FocusTrap(getElement) { + let tabbing = false; - let tabbing = false; + function restoreFocus(target) { + const first = getFirstFocusableElement(), + last = getLastFocusableElement(); - function restoreFocus(target) { - - const first = getFirstFocusableElement(), - last = getLastFocusableElement(); - - // do nothing if there is no focusable element - if (!first) { - return; - } - - if (target !== first) { - first.focus(); - } else { - last.focus(); - } - } - - function handleBlur(event) { - - // do nothing if focus stays inside the modal - if (getElement().contains(event.relatedTarget)) { - return; - } - - if (!tabbing) { - return; - } - - restoreFocus(event.target); + // do nothing if there is no focusable element + if (!first) { + return; } - function handleKeyDown(event) { - if (isTab(event)) { - tabbing = true; - } + if (target !== first) { + first.focus(); + } else { + last.focus(); } + } - function handleKeyUp(event) { - tabbing = false; + function handleBlur(event) { + // do nothing if focus stays inside the modal + if (getElement().contains(event.relatedTarget)) { + return; } - function getFirstFocusableElement() { - return getFocusableElements()[0]; + if (!tabbing) { + return; } - function getLastFocusableElement() { - const elements = getFocusableElements(); + restoreFocus(event.target); + } - return elements[elements.length - 1]; + function handleKeyDown(event) { + if (isTab(event)) { + tabbing = true; } + } - function getFocusableElements() { - return getElement().querySelectorAll(focusableElementsSelector); - } + function handleKeyUp(event) { + tabbing = false; + } - function focus() { + function getFirstFocusableElement() { + return getFocusableElements()[0]; + } - // focus the first focusable element if currently - // focussed element is outside the modal - if (getElement().contains(document.activeElement)) { - return; - } + function getLastFocusableElement() { + const elements = getFocusableElements(); - const focusable = getFirstFocusableElement(); + return elements[elements.length - 1]; + } - focusable && focusable.focus(); + function getFocusableElements() { + return getElement().querySelectorAll(focusableElementsSelector); + } + + function focus() { + // focus the first focusable element if currently + // focussed element is outside the modal + if (getElement().contains(document.activeElement)) { + return; } - function mount() { - focus(); + const focusable = getFirstFocusableElement(); - document.addEventListener('blur', handleBlur, true); - document.addEventListener('keydown', handleKeyDown); - document.addEventListener('keyup', handleKeyUp); - } + focusable && focusable.focus(); + } - function unmount() { - document.removeEventListener('blur', handleBlur, true); - document.removeEventListener('keydown', handleKeyDown); - document.removeEventListener('keyup', handleKeyUp); - } + function mount() { + focus(); - return { - mount, - unmount - }; + document.addEventListener("blur", handleBlur, true); + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("keyup", handleKeyUp); + } -} + function unmount() { + document.removeEventListener("blur", handleBlur, true); + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("keyup", handleKeyUp); + } + return { + mount, + unmount, + }; +} // helpers /////////////// function isTab(event) { - return event.key === 'Tab'; + return event.key === "Tab"; } diff --git a/components/bpmn-q/modeler-component/editor/ui/modal/KeyboardInteractionTrap.js b/components/bpmn-q/modeler-component/editor/ui/modal/KeyboardInteractionTrap.js index ae12c0b0..c09af83d 100644 --- a/components/bpmn-q/modeler-component/editor/ui/modal/KeyboardInteractionTrap.js +++ b/components/bpmn-q/modeler-component/editor/ui/modal/KeyboardInteractionTrap.js @@ -8,10 +8,9 @@ * except in compliance with the MIT License. */ -import React, {PureComponent, createContext} from 'react'; +import React, { PureComponent, createContext } from "react"; -export const KeyboardInteractionTrapContext = createContext(() => { -}); +export const KeyboardInteractionTrapContext = createContext(() => {}); /** * A wrapper around a react component that ensures that @@ -26,72 +25,70 @@ export const KeyboardInteractionTrapContext = createContext(() => { * is a modal that user and keyboard focus. */ export default function KeyboardInteractionTrap(props) { - return ( - - {triggerAction => ( - - {props.children || null} - - )} - - ); + return ( + + {(triggerAction) => ( + + {props.children || null} + + )} + + ); } class KeyboardInteractionTrapComponent extends PureComponent { + handleFocus = (event) => { + this.updateMenu(event.target); + }; - handleFocus = (event) => { - this.updateMenu(event.target); - }; + updateMenu(element) { + const enabled = ["INPUT", "TEXTAREA"].includes(element.tagName); - updateMenu(element) { + const editMenu = [ + [ + { + role: "undo", + enabled, + }, + { + role: "redo", + enabled, + }, + ], + [ + { + role: "copy", + enabled, + }, + { + role: "cut", + enabled, + }, + { + role: "paste", + enabled, + }, + { + role: "selectAll", + enabled, + }, + ], + ]; - const enabled = ['INPUT', 'TEXTAREA'].includes(element.tagName); + this.props.triggerAction("update-menu", { editMenu }); + } - const editMenu = [ - [ - { - role: 'undo', - enabled - }, - { - role: 'redo', - enabled - }, - ], - [ - { - role: 'copy', - enabled - }, - { - role: 'cut', - enabled - }, - { - role: 'paste', - enabled - }, - { - role: 'selectAll', - enabled - } - ] - ]; + componentDidMount() { + window.addEventListener("focus", this.handleFocus); - this.props.triggerAction('update-menu', {editMenu}); - } + this.updateMenu(document.activeElement); + } - componentDidMount() { - window.addEventListener('focus', this.handleFocus); + componentWillUnmount() { + window.removeEventListener("focus", this.handleFocus); + } - this.updateMenu(document.activeElement); - } - - componentWillUnmount() { - window.removeEventListener('focus', this.handleFocus); - } - - render() { - return this.props.children; - } + render() { + return this.props.children; + } } diff --git a/components/bpmn-q/modeler-component/editor/ui/modal/Modal.js b/components/bpmn-q/modeler-component/editor/ui/modal/Modal.js index fdb41868..2179311c 100644 --- a/components/bpmn-q/modeler-component/editor/ui/modal/Modal.js +++ b/components/bpmn-q/modeler-component/editor/ui/modal/Modal.js @@ -7,72 +7,70 @@ * Camunda licenses this file to you under the MIT; you may not use this file * except in compliance with the MIT License. */ -import React, {PureComponent} from 'react'; -import ReactDOM from 'react-dom'; +import React, { PureComponent } from "react"; +import ReactDOM from "react-dom"; -import classNames from 'classnames'; +import classNames from "classnames"; -import FocusTrap from './FocusTrap'; -import EscapeTrap from './EscapeTrap'; -import KeyboardInteractionTrap from './KeyboardInteractionTrap'; +import FocusTrap from "./FocusTrap"; +import EscapeTrap from "./EscapeTrap"; +import KeyboardInteractionTrap from "./KeyboardInteractionTrap"; /** * React component to display a modal. */ export default class Modal extends PureComponent { + constructor(props) { + super(props); - constructor(props) { - super(props); + this.modalRef = React.createRef(); - this.modalRef = React.createRef(); + this.focusTrap = FocusTrap(() => { + return this.modalRef.current; + }); - this.focusTrap = FocusTrap(() => { - return this.modalRef.current; - }); + this.escapeTrap = EscapeTrap(() => { + this.close(); + }); + } - this.escapeTrap = EscapeTrap(() => { - this.close(); - }); - } - - close = () => { - if (this.props.onClose) { - return this.props.onClose(); - } - }; - - componentDidMount() { - this.focusTrap.mount(); - this.escapeTrap.mount(); - } - - componentWillUnmount() { - this.focusTrap.unmount(); - this.escapeTrap.unmount(); - } - - render() { - - const { - className, - children, - onClose - } = this.props; - - return ReactDOM.createPortal( - -
-
-
- {children} - {onClose && ()} -
-
-
-
, - document.body - ); + close = () => { + if (this.props.onClose) { + return this.props.onClose(); } + }; + + componentDidMount() { + this.focusTrap.mount(); + this.escapeTrap.mount(); + } + + componentWillUnmount() { + this.focusTrap.unmount(); + this.escapeTrap.unmount(); + } + + render() { + const { className, children, onClose } = this.props; + + return ReactDOM.createPortal( + +
+
+
+ {children} + {onClose && } +
+
+
+
, + document.body + ); + } } Modal.Body = Body; @@ -83,65 +81,55 @@ Modal.Close = Close; Modal.Footer = Footer; - function Title(props) { - const { - children, - className, - ...rest - } = props; - - return ( -
-

- {children} -

-
- ); + const { children, className, ...rest } = props; + + return ( +
+

{children}

+
+ ); } function Close(props) { - const { - onClick - } = props; - - return ( - - ); + const { onClick } = props; + + return ( + + ); } function Body(props) { - const { - children, - className, - ...rest - } = props; - - return ( -
- {children} -
- ); + const { children, className, ...rest } = props; + + return ( +
+ {children} +
+ ); } function Footer(props) { - const { - children, - className, - ...rest - } = props; - - return ( -
- {props.children} -
- ); + const { children, className, ...rest } = props; + + return ( +
+ {props.children} +
+ ); } diff --git a/components/bpmn-q/modeler-component/editor/ui/modal/index.js b/components/bpmn-q/modeler-component/editor/ui/modal/index.js index 66958478..e4020b9e 100644 --- a/components/bpmn-q/modeler-component/editor/ui/modal/index.js +++ b/components/bpmn-q/modeler-component/editor/ui/modal/index.js @@ -8,4 +8,4 @@ * except in compliance with the MIT License. */ -export {default as Modal} from './Modal'; +export { default as Modal } from "./Modal"; diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.css b/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.css index fdb63990..33d47382 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.css +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.css @@ -1,39 +1,39 @@ .qwm-Notification { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - box-sizing: border-box; - width: 300px; - background: var(--color-ffffff); - box-shadow: 0 1px 4px var(--color-000000-opacity-10); - border-radius: 2px; - padding: 20px; - margin-bottom: 20px; + font-size: 12px; + box-sizing: border-box; + width: 300px; + background: var(--color-ffffff); + box-shadow: 0 1px 4px var(--color-000000-opacity-10); + border-radius: 2px; + padding: 20px; + margin-bottom: 20px; } .qwm-Notification h2 { - margin: 0; - font-weight: normal; + margin: 0; + font-weight: normal; } .qwm-Notification .qwm-content { - margin-top: 10px; + margin-top: 10px; } .qwm-Notification .qwm-close { - vertical-align: middle; - padding: 0 2px; - margin-left: 3px; - font-size: 18px; - line-height: 9px; - width: 13px; - float: right; + vertical-align: middle; + padding: 0 2px; + margin-left: 3px; + font-size: 18px; + line-height: 9px; + width: 13px; + float: right; } .qwm-Notification .qwm-close:before { - content: '×'; + content: "×"; } .qwm-Notification .qwm-close:hover { - color: var(--blue-darken-48); -} \ No newline at end of file + color: var(--blue-darken-48); +} diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.js b/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.js index 3cafffec..df088c03 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/Notification.js @@ -8,9 +8,9 @@ * except in compliance with the MIT License. */ -import React, {PureComponent} from 'react'; +import React, { PureComponent } from "react"; -export const NOTIFICATION_TYPES = ['info', 'success', 'error', 'warning']; +export const NOTIFICATION_TYPES = ["info", "success", "error", "warning"]; /** * React component to display notifications @@ -18,65 +18,61 @@ export const NOTIFICATION_TYPES = ['info', 'success', 'error', 'warning']; * @type {string[]} */ export default class Notification extends PureComponent { - static getDerivedStateFromError() { - return {error: true}; - } + static getDerivedStateFromError() { + return { error: true }; + } - state = { - error: false - }; + state = { + error: false, + }; - componentDidMount() { - const {duration} = this.props; + componentDidMount() { + const { duration } = this.props; - if (duration) { - this.setupTimeout(duration); - } + if (duration) { + this.setupTimeout(duration); } + } - componentDidUpdate(previousProps) { - const currentDuration = this.props.duration; - - const {duration: previousDuration} = previousProps; - - if (currentDuration !== previousDuration) { - this.resetTimeout(); + componentDidUpdate(previousProps) { + const currentDuration = this.props.duration; - currentDuration && this.setupTimeout(currentDuration); - } - } - - componentWillUnmount() { - this.resetTimeout(); - } + const { duration: previousDuration } = previousProps; - setupTimeout(duration) { - this.timeout = setTimeout(() => { - this.props.close(); - }, duration); - } - - resetTimeout() { - this.timeout && clearTimeout(this.timeout); - } - - componentDidCatch() { - this.props.close(); - } + if (currentDuration !== previousDuration) { + this.resetTimeout(); - render() { - const { - close, - content, - title, - } = this.props; - - return this.state.error ? null :
- -

- {title} -

- {content &&
{content}
} -
; + currentDuration && this.setupTimeout(currentDuration); } + } + + componentWillUnmount() { + this.resetTimeout(); + } + + setupTimeout(duration) { + this.timeout = setTimeout(() => { + this.props.close(); + }, duration); + } + + resetTimeout() { + this.timeout && clearTimeout(this.timeout); + } + + componentDidCatch() { + this.props.close(); + } + + render() { + const { close, content, title } = this.props; + + return this.state.error ? null : ( +
+ +

{title}

+ {content &&
{content}
} +
+ ); + } } diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/NotificationHandler.js b/components/bpmn-q/modeler-component/editor/ui/notifications/NotificationHandler.js index 1d834a63..0ef15bfe 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/NotificationHandler.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/NotificationHandler.js @@ -1,7 +1,7 @@ import React from "react"; import Notifications from "./Notifications"; -export const NOTIFICATION_TYPES = ['info', 'success', 'error', 'warning']; +export const NOTIFICATION_TYPES = ["info", "success", "error", "warning"]; /** * Handler to manage notifications displayed to the user. Use getInstance() to get the current instance of the handler. @@ -9,61 +9,65 @@ export const NOTIFICATION_TYPES = ['info', 'success', 'error', 'warning']; * Implements the Singleton pattern. */ export default class NotificationHandler { + static instance = undefined; - static instance = undefined; - - static getInstance() { - if (this.instance) { - return this.instance; - } else { - this.instance = new NotificationHandler([]); - return this.instance; - } + static getInstance() { + if (this.instance) { + return this.instance; + } else { + this.instance = new NotificationHandler([]); + return this.instance; } + } - constructor(notifications) { - this.notifications = notifications; - this.currentNotificationId = -1; - this.notificationRef = React.createRef(); - } + constructor(notifications) { + this.notifications = notifications; + this.currentNotificationId = -1; + this.notificationRef = React.createRef(); + } - /** - * Creates a new Notifications React Component with a fixed ref to access the methods of the component. - * - * @param notifications The initial set of components to display wright after creation. - * @param notificationsContainer DOM element the notifications are rendered into. - * @returns the created Notifications React Component - */ - createNotificationsComponent(notifications, notificationsContainer) { - if (notifications) { - this.notifications = notifications; - } - return ; + /** + * Creates a new Notifications React Component with a fixed ref to access the methods of the component. + * + * @param notifications The initial set of components to display wright after creation. + * @param notificationsContainer DOM element the notifications are rendered into. + * @returns the created Notifications React Component + */ + createNotificationsComponent(notifications, notificationsContainer) { + if (notifications) { + this.notifications = notifications; } + return ( + + ); + } - /** - * Creates and displays a new Notification with the given properties. Calls effectively the respective method of the - * Notification Component. - * - * @param type The NOTIFICATION_TYPES of the notification. - * @param title The title of the notification. - * @param content The text displayed by the notification. - * @param duration The duration in milliseconds. - */ - displayNotification({type = 'info', title, content, duration = 4000}) { - this.notificationRef.current.displayNotification({ - type: type, - title: title, - content: content, - duration: duration - }); - } + /** + * Creates and displays a new Notification with the given properties. Calls effectively the respective method of the + * Notification Component. + * + * @param type The NOTIFICATION_TYPES of the notification. + * @param title The title of the notification. + * @param content The text displayed by the notification. + * @param duration The duration in milliseconds. + */ + displayNotification({ type = "info", title, content, duration = 4000 }) { + this.notificationRef.current.displayNotification({ + type: type, + title: title, + content: content, + duration: duration, + }); + } - /** - * Close all open notifications. - */ - closeNotifications() { - this.notificationRef.current.closeNotifications(); - } -} \ No newline at end of file + /** + * Close all open notifications. + */ + closeNotifications() { + this.notificationRef.current.closeNotifications(); + } +} diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.css b/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.css index 2baffc9e..3ed2dddd 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.css +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.css @@ -1,13 +1,13 @@ .qwm-Notifications { - display: flex; - flex-direction: column-reverse; - z-index: 500; - position: absolute; - top: 100px; - right: 0; - left: 0; - margin-left: auto; - margin-right: auto; - width: 300px; - height: auto; -} \ No newline at end of file + display: flex; + flex-direction: column-reverse; + z-index: 500; + position: absolute; + top: 100px; + right: 0; + left: 0; + margin-left: auto; + margin-right: auto; + width: 300px; + height: auto; +} diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.js b/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.js index 19b67193..b4703818 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/Notifications.js @@ -8,113 +8,115 @@ * except in compliance with the MIT License. */ -import React, {PureComponent} from 'react'; -import {createPortal} from 'react-dom'; -import Notification from './Notification'; -import {NOTIFICATION_TYPES} from "./NotificationHandler"; +import React, { PureComponent } from "react"; +import { createPortal } from "react-dom"; +import Notification from "./Notification"; +import { NOTIFICATION_TYPES } from "./NotificationHandler"; /** * React component to manage Notification components */ export default class Notifications extends PureComponent { - constructor(props) { - super(props); - - this.container = props.container; - this.state = { - notifications: props.notifications || [] - }; - this.currentNotificationId = -1; + constructor(props) { + super(props); + + this.container = props.container; + this.state = { + notifications: props.notifications || [], + }; + this.currentNotificationId = -1; + } + + /** + * Display new Notification of the given type, title and content. Define the duration the Notification should + * be displayed by duration. + * + * @param type The type of the Notification. + * @param title The title of the Notification. + * @param content The content of the Notification. + * @param duration The duration of the Notification. + * @returns {{update: update, close: close}} + */ + displayNotification({ type = "info", title, content, duration = 4000 }) { + const notifications = this.state.notifications; + + if (!NOTIFICATION_TYPES.includes(type)) { + throw new Error("Unknown notification type"); } - /** - * Display new Notification of the given type, title and content. Define the duration the Notification should - * be displayed by duration. - * - * @param type The type of the Notification. - * @param title The title of the Notification. - * @param content The content of the Notification. - * @param duration The duration of the Notification. - * @returns {{update: update, close: close}} - */ - displayNotification({type = 'info', title, content, duration = 4000}) { - const notifications = this.state.notifications; - - if (!NOTIFICATION_TYPES.includes(type)) { - throw new Error('Unknown notification type'); - } - - const id = this.currentNotificationId++; - - const close = () => { - console.log('close'); - this._closeNotification(id); - }; - - const update = newProps => { - this._updateNotification(id, newProps); - }; - - const notification = { - content, - duration, - id, - close, - title, - type - }; - - this.setState({ - notifications: [ - ...notifications, - notification - ] - }); - - return { - close, - update - }; - } - - /** - * Close all displayed Notifications. - */ - closeNotifications() { - this.setState({notifications: []}); - } - - _updateNotification(id, options) { - const notifications = this.state.notifications.map(notification => { - const {id: currentId} = notification; - - return currentId !== id ? notification : {...notification, ...options}; - }); - - this.setState({notifications: notifications}); - } - - /** - * Close the Notification with the given ID. - * - * @param id The ID of the Notification to close. - * @private - */ - _closeNotification(id) { - const notifications = this.state.notifications.filter(({id: currentId}) => currentId !== id); - this.setState({notifications: notifications}); - } - - render() { - let { - notifications - } = this.state; - notifications = notifications || []; - const notificationComponents = notifications.map(({id, ...props}) => { - return ; - }).reverse(); - - // className={ css.Notifications } - return createPortal(
{notificationComponents}
, this.container); - } -} \ No newline at end of file + const id = this.currentNotificationId++; + + const close = () => { + console.log("close"); + this._closeNotification(id); + }; + + const update = (newProps) => { + this._updateNotification(id, newProps); + }; + + const notification = { + content, + duration, + id, + close, + title, + type, + }; + + this.setState({ + notifications: [...notifications, notification], + }); + + return { + close, + update, + }; + } + + /** + * Close all displayed Notifications. + */ + closeNotifications() { + this.setState({ notifications: [] }); + } + + _updateNotification(id, options) { + const notifications = this.state.notifications.map((notification) => { + const { id: currentId } = notification; + + return currentId !== id ? notification : { ...notification, ...options }; + }); + + this.setState({ notifications: notifications }); + } + + /** + * Close the Notification with the given ID. + * + * @param id The ID of the Notification to close. + * @private + */ + _closeNotification(id) { + const notifications = this.state.notifications.filter( + ({ id: currentId }) => currentId !== id + ); + this.setState({ notifications: notifications }); + } + + render() { + let { notifications } = this.state; + notifications = notifications || []; + const notificationComponents = notifications + .map(({ id, ...props }) => { + return ; + }) + .reverse(); + + // className={ css.Notifications } + return createPortal( +
{notificationComponents}
, + this.container + ); + } +} diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js index d02450ca..50d66299 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationSpec.js @@ -10,44 +10,36 @@ /* global sinon */ -import React from 'react'; +import React from "react"; -import { - mount, - shallow -} from 'enzyme'; +import { mount, shallow } from "enzyme"; -import Notification from '../Notification'; -import {describe, it, after, before} from "mocha"; +import Notification from "../Notification"; +import { describe, it, after, before } from "mocha"; -const { expect } = require('chai'); +const { expect } = require("chai"); -describe('', function() { - - it('should render', function() { +describe("", function () { + it("should render", function () { shallow(); }); - - describe('duration', function() { - + describe("duration", function () { let clock; - before(function() { + before(function () { clock = sinon.useFakeTimers(); }); - after(function() { + after(function () { clock.restore(); }); - - it('should close automatically when is set', function() { - + it("should close automatically when is set", function () { // given const closeSpy = sinon.spy(); - shallow(); + shallow(); // when clock.tick(1000); @@ -56,13 +48,13 @@ describe('', function() { expect(closeSpy).to.have.been.calledOnce; }); - - it('should handle changes', function() { - + it("should handle changes", function () { // given const closeSpy = sinon.spy(); - const notification = shallow(); + const notification = shallow( + + ); // when notification.setProps({ duration: 2000 }); @@ -79,13 +71,11 @@ describe('', function() { expect(closeSpy).to.have.been.calledOnce; }); - - it('should NOT close automatically when is NOT set', function() { - + it("should NOT close automatically when is NOT set", function () { // given const closeSpy = sinon.spy(); - shallow(); + shallow(); // when clock.tick(10000); @@ -93,27 +83,22 @@ describe('', function() { // then expect(closeSpy).to.have.not.been.called; }); - }); - - describe('error boundary', function() { - + describe("error boundary", function () { let wrapper; - afterEach(function() { + afterEach(function () { wrapper && wrapper.unmount(); }); - - it('should close notification if it throws', function() { - + it("should close notification if it throws", function () { // given - const Content = () => 'content'; + const Content = () => "content"; const closeSpy = sinon.spy(); - wrapper = mount( } />); + wrapper = mount(} />); // when wrapper.find(Content).simulateError(new Error()); @@ -121,7 +106,5 @@ describe('', function() { // then expect(closeSpy).to.have.been.calledOnce; }); - }); - }); diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js index cf538d47..8ba39f03 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/__tests__/NotificationsSpec.js @@ -8,36 +8,30 @@ * except in compliance with the MIT License. */ -import React from 'react'; +import React from "react"; -import { shallow } from 'enzyme'; +import { shallow } from "enzyme"; -import Notifications from '..'; -import Notification from '../Notification'; -import {expect} from "chai"; +import Notifications from ".."; +import Notification from "../Notification"; +import { expect } from "chai"; - -describe('', function() { - - it('should render', function() { - shallow(); +describe("", function () { + it("should render", function () { + shallow(); }); - - it('should display notification', function() { - + it("should display notification", function () { // given const notification = createNotification(); - const wrapper = shallow(); + const wrapper = shallow(); // then expect(wrapper.find(Notification)).to.have.lengthOf(1); }); - }); - // helpers ////////// function createNotification({ @@ -45,8 +39,8 @@ function createNotification({ content, duration = 0, id = 0, - title = 'title', - type = 'info' + title = "title", + type = "info", } = {}) { return { close, @@ -54,6 +48,6 @@ function createNotification({ duration, id, title, - type + type, }; } diff --git a/components/bpmn-q/modeler-component/editor/ui/notifications/index.js b/components/bpmn-q/modeler-component/editor/ui/notifications/index.js index a9f20564..b0ac5396 100644 --- a/components/bpmn-q/modeler-component/editor/ui/notifications/index.js +++ b/components/bpmn-q/modeler-component/editor/ui/notifications/index.js @@ -8,7 +8,7 @@ * except in compliance with the MIT License. */ -import {default as Notifications} from './Notifications'; +import { default as Notifications } from "./Notifications"; export default Notifications; -export {NOTIFICATION_TYPES} from './Notification'; \ No newline at end of file +export { NOTIFICATION_TYPES } from "./Notification"; diff --git a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js index 3a5a26f5..bcc922d8 100644 --- a/components/bpmn-q/modeler-component/editor/util/IoUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/IoUtilities.js @@ -1,25 +1,29 @@ -import {transformedWorkflowHandlers, workflowEventTypes} from '../EditorConstants'; -import {dispatchWorkflowEvent} from '../events/EditorEventHandler'; +import { + transformedWorkflowHandlers, + workflowEventTypes, +} from "../EditorConstants"; +import { dispatchWorkflowEvent } from "../events/EditorEventHandler"; -const editorConfig = require('../config/EditorConfigManager'); +const editorConfig = require("../config/EditorConfigManager"); -let FormData = require('form-data'); -import fetch from 'node-fetch'; +let FormData = require("form-data"); +import fetch from "node-fetch"; // workflow with a start event to use as template for new workflows -const NEW_DIAGRAM_XML = '\n' + - '\n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ''; +const NEW_DIAGRAM_XML = + '\n' + + '\n' + + ' \n' + + ' \n' + + " \n" + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + " \n" + + " \n" + + " \n" + + ""; /** * Saves a given bpmn diagram as a bpmn file to the locale storage of the user. @@ -28,15 +32,22 @@ const NEW_DIAGRAM_XML = '\n' + * @param fileName The name of the file. * @returns {Promise} */ -export async function saveXmlAsLocalFile(xml, fileName = editorConfig.getFileName()) { - const bpmnFile = await new File([xml], fileName, {type: 'text/xml'}); - - const link = document.createElement('a'); - link.download = fileName; - link.href = URL.createObjectURL(bpmnFile); - link.click(); - - dispatchWorkflowEvent(workflowEventTypes.SAVED, xml, editorConfig.getFileName()); +export async function saveXmlAsLocalFile( + xml, + fileName = editorConfig.getFileName() +) { + const bpmnFile = await new File([xml], fileName, { type: "text/xml" }); + + const link = document.createElement("a"); + link.download = fileName; + link.href = URL.createObjectURL(bpmnFile); + link.click(); + + dispatchWorkflowEvent( + workflowEventTypes.SAVED, + xml, + editorConfig.getFileName() + ); } /** @@ -46,9 +57,12 @@ export async function saveXmlAsLocalFile(xml, fileName = editorConfig.getFileNam * @param fileName The name of the file. * @returns {Promise} */ -export async function saveModelerAsLocalFile(modeler, fileName = editorConfig.getFileName()) { - const xml = await getXml(modeler); - return saveXmlAsLocalFile(xml, fileName); +export async function saveModelerAsLocalFile( + modeler, + fileName = editorConfig.getFileName() +) { + const xml = await getXml(modeler); + return saveXmlAsLocalFile(xml, fileName); } /** @@ -58,8 +72,8 @@ export async function saveModelerAsLocalFile(modeler, fileName = editorConfig.ge * @returns {Promise<*>} The xml diagram. */ export async function getXml(modeler) { - const {xml} = await modeler.saveXML({format: true}); - return xml; + const { xml } = await modeler.saveXML({ format: true }); + return xml; } /** @@ -71,20 +85,23 @@ export async function getXml(modeler) { * @returns {Promise} Undefined, if an error occurred during import. */ export async function loadDiagram(xml, modeler, dispatchEvent = true) { + try { + const result = await modeler.importXML(xml); + + if (dispatchEvent) { + dispatchWorkflowEvent( + workflowEventTypes.LOADED, + xml, + editorConfig.getFileName() + ); + } - try { - const result = await modeler.importXML(xml); - - if (dispatchEvent) { - dispatchWorkflowEvent(workflowEventTypes.LOADED, xml, editorConfig.getFileName()); - } - - return result; - } catch (err) { - console.error(err); + return result; + } catch (err) { + console.error(err); - return {error: err}; - } + return { error: err }; + } } /** @@ -93,7 +110,7 @@ export async function loadDiagram(xml, modeler, dispatchEvent = true) { * @param modeler the given modeler to open the new bpmn diagram in. */ export function createNewDiagram(modeler) { - loadDiagram(NEW_DIAGRAM_XML, modeler).then(); + loadDiagram(NEW_DIAGRAM_XML, modeler).then(); } /** @@ -104,70 +121,96 @@ export function createNewDiagram(modeler) { * @param viewMap a list of views to deploy with the workflow, i.e., the name of the view and the corresponding xml * @return {Promise<{status: string}>} a promise with the deployment status as well as the endpoint of the deployed workflow if successful */ -export async function deployWorkflowToCamunda(workflowName, workflowXml, viewMap) { - console.log('Deploying workflow to Camunda Engine at endpoint: %s', editorConfig.getCamundaEndpoint()); - - // add required form data fields - const form = new FormData(); - form.append('deployment-name', workflowName); - form.append('deployment-source', 'QuantME Modeler'); - form.append('deploy-changed-only', 'false'); - - // add bpmn file ending if not present - let fileName = workflowName; - if (!fileName.endsWith('.bpmn')) { - fileName = fileName + '.bpmn'; - } - - // add diagram to the body - const bpmnFile = new File([workflowXml], fileName, {type: 'text/xml'}); - form.append('data', bpmnFile); - - // upload all provided views - for (const [key, value] of Object.entries(viewMap)) { - console.info('Adding view with name: ', key); - - // add view xml to the body - form.append(key, value, { - filename: fileName.replace('.bpmn', key + '.xml'), - contentType: 'text/xml' - }); - } - - // make the request and wait for the response of the deployment endpoint - try { - const response = await fetch(editorConfig.getCamundaEndpoint() + '/deployment/create', { - method: 'POST', - body: form, - }); - - if (response.ok) { - - // retrieve deployment results from response - const result = await response.json(); - console.info('Deployment provides result: ', result); - console.info('Deployment successful with deployment id: %s', result['id']); - - // abort if there is not exactly one deployed process definition - if (Object.values(result['deployedProcessDefinitions'] || {}).length !== 1) { - console.error('Invalid size of deployed process definitions list: ' + Object.values(result['deployedProcessDefinitions'] || {}).length); - return {status: 'failed'}; - } - - dispatchWorkflowEvent(workflowEventTypes.DEPLOYED, workflowXml, workflowName); - - return { - status: 'deployed', - deployedProcessDefinition: Object.values(result['deployedProcessDefinitions'] || {})[0] - }; - } else { - console.error('Deployment of workflow returned invalid status code: %s', response.status); - return {status: 'failed'}; - } - } catch (error) { - console.error('Error while executing post to deploy workflow: ' + error); - return {status: 'failed'}; +export async function deployWorkflowToCamunda( + workflowName, + workflowXml, + viewMap +) { + console.log( + "Deploying workflow to Camunda Engine at endpoint: %s", + editorConfig.getCamundaEndpoint() + ); + + // add required form data fields + const form = new FormData(); + form.append("deployment-name", workflowName); + form.append("deployment-source", "QuantME Modeler"); + form.append("deploy-changed-only", "false"); + + // add bpmn file ending if not present + let fileName = workflowName; + if (!fileName.endsWith(".bpmn")) { + fileName = fileName + ".bpmn"; + } + + // add diagram to the body + const bpmnFile = new File([workflowXml], fileName, { type: "text/xml" }); + form.append("data", bpmnFile); + + // upload all provided views + for (const [key, value] of Object.entries(viewMap)) { + console.info("Adding view with name: ", key); + + // add view xml to the body + form.append(key, value, { + filename: fileName.replace(".bpmn", key + ".xml"), + contentType: "text/xml", + }); + } + + // make the request and wait for the response of the deployment endpoint + try { + const response = await fetch( + editorConfig.getCamundaEndpoint() + "/deployment/create", + { + method: "POST", + body: form, + } + ); + + if (response.ok) { + // retrieve deployment results from response + const result = await response.json(); + console.info("Deployment provides result: ", result); + console.info( + "Deployment successful with deployment id: %s", + result["id"] + ); + + // abort if there is not exactly one deployed process definition + if ( + Object.values(result["deployedProcessDefinitions"] || {}).length !== 1 + ) { + console.error( + "Invalid size of deployed process definitions list: " + + Object.values(result["deployedProcessDefinitions"] || {}).length + ); + return { status: "failed" }; + } + + dispatchWorkflowEvent( + workflowEventTypes.DEPLOYED, + workflowXml, + workflowName + ); + + return { + status: "deployed", + deployedProcessDefinition: Object.values( + result["deployedProcessDefinitions"] || {} + )[0], + }; + } else { + console.error( + "Deployment of workflow returned invalid status code: %s", + response.status + ); + return { status: "failed" }; } + } catch (error) { + console.error("Error while executing post to deploy workflow: " + error); + return { status: "failed" }; + } } /** @@ -178,28 +221,33 @@ export async function deployWorkflowToCamunda(workflowName, workflowXml, viewMap * @returns {Promise} */ export async function handleTransformedWorkflow(workflowXml) { - const fileName = editorConfig.getFileName().split('.')[0] + '_transformed.bpmn'; - - // dispatch workflow transformed event - const eventNotCaught = dispatchWorkflowEvent(workflowEventTypes.TRANSFORMED, workflowXml, fileName); - - console.log(`Transformed Workflow Event caught? - ${eventNotCaught}`); - - // execute respective handle function if event was not already solved - if (eventNotCaught) { - const handlerId = editorConfig.getTransformedWorkflowHandler(); - - switch (handlerId) { - case transformedWorkflowHandlers.NEW_TAB: // open workflow in new browser tab - openInNewTab(workflowXml, fileName); - break; - case transformedWorkflowHandlers.SAVE_AS_FILE: // save workflow to local file system - await saveXmlAsLocalFile(workflowXml, fileName); - break; - default: - console.log(`Invalid transformed workflow handler ID ${handlerId}`); - } + const fileName = + editorConfig.getFileName().split(".")[0] + "_transformed.bpmn"; + + // dispatch workflow transformed event + const eventNotCaught = dispatchWorkflowEvent( + workflowEventTypes.TRANSFORMED, + workflowXml, + fileName + ); + + console.log(`Transformed Workflow Event caught? - ${eventNotCaught}`); + + // execute respective handle function if event was not already solved + if (eventNotCaught) { + const handlerId = editorConfig.getTransformedWorkflowHandler(); + + switch (handlerId) { + case transformedWorkflowHandlers.NEW_TAB: // open workflow in new browser tab + openInNewTab(workflowXml, fileName); + break; + case transformedWorkflowHandlers.SAVE_AS_FILE: // save workflow to local file system + await saveXmlAsLocalFile(workflowXml, fileName); + break; + default: + console.log(`Invalid transformed workflow handler ID ${handlerId}`); } + } } /** @@ -209,12 +257,13 @@ export async function handleTransformedWorkflow(workflowXml) { * @param fileName The name of the workflow. */ export function openInNewTab(workflowXml, fileName) { - - const newWindow = window.open(window.location.href, "_blank"); - - newWindow.onload = function () { - - // Pass the XML string to the new window using postMessage - newWindow.postMessage({workflow: workflowXml, name: fileName}, window.location.href); - }; -} \ No newline at end of file + const newWindow = window.open(window.location.href, "_blank"); + + newWindow.onload = function () { + // Pass the XML string to the new window using postMessage + newWindow.postMessage( + { workflow: workflowXml, name: fileName }, + window.location.href + ); + }; +} diff --git a/components/bpmn-q/modeler-component/editor/util/ModellingUtilities.js b/components/bpmn-q/modeler-component/editor/util/ModellingUtilities.js index 7c6c5adf..7c2777d7 100644 --- a/components/bpmn-q/modeler-component/editor/util/ModellingUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/ModellingUtilities.js @@ -1,8 +1,8 @@ -import {createTempModelerFromXml} from '../ModelerHandler'; -import {getInputOutput} from './camunda-utils/InputOutputUtil'; -import {getExtension} from './camunda-utils/ExtensionElementsUtil'; -import {useService} from 'bpmn-js-properties-panel'; -import {is} from 'bpmn-js/lib/util/ModelUtil'; +import { createTempModelerFromXml } from "../ModelerHandler"; +import { getInputOutput } from "./camunda-utils/InputOutputUtil"; +import { getExtension } from "./camunda-utils/ExtensionElementsUtil"; +import { useService } from "bpmn-js-properties-panel"; +import { is } from "bpmn-js/lib/util/ModelUtil"; /** * Returns all start events of the workflow defined by the process businessObject @@ -11,7 +11,9 @@ import {is} from 'bpmn-js/lib/util/ModelUtil'; * @returns {*[]} All found start event elements of the workflow. */ export function getStartEvents(processBo) { - return processBo.flowElements.filter((element) => element.$type === 'bpmn:StartEvent'); + return processBo.flowElements.filter( + (element) => element.$type === "bpmn:StartEvent" + ); } /** @@ -22,28 +24,34 @@ export function getStartEvents(processBo) { * @param processVariable The process variable which should be created through the executionn listener */ export function addExecutionListener(element, moddle, processVariable) { - - // create the execution listener for the process variable - const listener = { - event: 'start', - expression: '${execution.setVariable("' + processVariable.name + '", ' + processVariable.value + ')}', - }; - - const elementBo = element.businessObject; - let extensionElements = elementBo.extensionElements; - - // create new extension element if needed - if (!extensionElements) { - extensionElements = moddle.create('bpmn:ExtensionElements'); - } - - if (!extensionElements.values) { - extensionElements.values = []; - } - - // add execution listener to the extension element of the element - extensionElements.values.push(moddle.create('camunda:ExecutionListener', listener)); - elementBo.extensionElements = extensionElements; + // create the execution listener for the process variable + const listener = { + event: "start", + expression: + '${execution.setVariable("' + + processVariable.name + + '", ' + + processVariable.value + + ")}", + }; + + const elementBo = element.businessObject; + let extensionElements = elementBo.extensionElements; + + // create new extension element if needed + if (!extensionElements) { + extensionElements = moddle.create("bpmn:ExtensionElements"); + } + + if (!extensionElements.values) { + extensionElements.values = []; + } + + // add execution listener to the extension element of the element + extensionElements.values.push( + moddle.create("camunda:ExecutionListener", listener) + ); + elementBo.extensionElements = extensionElements; } /** @@ -57,19 +65,31 @@ export function addExecutionListener(element, moddle, processVariable) { * @param moddle The moddle module of the bpmn-js modeler * @param modeling The modeling module of the bpmn-js modeler */ -export function addFormFieldForMap(elementID, name, keyValueMap, elementRegistry, moddle, modeling) { - - // create the properties of the form field - let formFieldData = - { - defaultValue: '', - id: name.replace(/\s+/g, '_'), - label: name, - type: 'string', - }; - - // create the form field for the key value map - addFormFieldDataForMap(elementID, formFieldData, keyValueMap, elementRegistry, moddle, modeling); +export function addFormFieldForMap( + elementID, + name, + keyValueMap, + elementRegistry, + moddle, + modeling +) { + // create the properties of the form field + let formFieldData = { + defaultValue: "", + id: name.replace(/\s+/g, "_"), + label: name, + type: "string", + }; + + // create the form field for the key value map + addFormFieldDataForMap( + elementID, + formFieldData, + keyValueMap, + elementRegistry, + moddle, + modeling + ); } /** @@ -83,13 +103,19 @@ export function addFormFieldForMap(elementID, name, keyValueMap, elementRegistry * @param moddle The moddle module of the bpmn-js modeler * @param modeling The modeling module of the bpmn-js modeler */ -export function addFormFieldDataForMap(elementID, formFieldData, keyValueMap, elementRegistry, moddle, modeling) { - - // create camunda properties for each entry of the key value map - formFieldData.properties = createCamundaProperties(keyValueMap, moddle); - - // create form field for form field data - addFormField(elementID, formFieldData, elementRegistry, moddle, modeling); +export function addFormFieldDataForMap( + elementID, + formFieldData, + keyValueMap, + elementRegistry, + moddle, + modeling +) { + // create camunda properties for each entry of the key value map + formFieldData.properties = createCamundaProperties(keyValueMap, moddle); + + // create form field for form field data + addFormField(elementID, formFieldData, elementRegistry, moddle, modeling); } /** @@ -101,27 +127,35 @@ export function addFormFieldDataForMap(elementID, formFieldData, keyValueMap, el * @param moddle The moddle module of the bpmn-js modeler * @param modeling The modeling module of the bpmn-js modeler */ -export function addFormField(elementID, formFieldData, elementRegistry, moddle, modeling) { - - const element = elementRegistry.get(elementID); - const extensionElements = getExtensionElements(element.businessObject, moddle); - - // get form data extension - let form = getExtension(element.businessObject, 'camunda:FormData'); - - console.log(`Found form data ${form}.`); - - if (!form) { - form = moddle.create('camunda:FormData'); - } - - // create form field - const formField = moddle.create('camunda:FormField', formFieldData); - - // save from field - pushFormField(form, formField); - extensionElements.values = [form]; - modeling.updateProperties(element, {extensionElements: extensionElements}); +export function addFormField( + elementID, + formFieldData, + elementRegistry, + moddle, + modeling +) { + const element = elementRegistry.get(elementID); + const extensionElements = getExtensionElements( + element.businessObject, + moddle + ); + + // get form data extension + let form = getExtension(element.businessObject, "camunda:FormData"); + + console.log(`Found form data ${form}.`); + + if (!form) { + form = moddle.create("camunda:FormData"); + } + + // create form field + const formField = moddle.create("camunda:FormField", formFieldData); + + // save from field + pushFormField(form, formField); + extensionElements.values = [form]; + modeling.updateProperties(element, { extensionElements: extensionElements }); } /** @@ -132,19 +166,19 @@ export function addFormField(elementID, formFieldData, elementRegistry, moddle, * @returns {bpmn:ExtensionElements} The extension elements of the businessObject */ export function getExtensionElements(businessObject, moddle) { - let extensionElements = businessObject.get('extensionElements'); + let extensionElements = businessObject.get("extensionElements"); - // create extension elements if not already defined - if (!extensionElements) { - extensionElements = moddle.create('bpmn:ExtensionElements'); - } + // create extension elements if not already defined + if (!extensionElements) { + extensionElements = moddle.create("bpmn:ExtensionElements"); + } - // init values if undefined - if (!extensionElements.values) { - extensionElements.values = []; - } + // init values if undefined + if (!extensionElements.values) { + extensionElements.values = []; + } - return extensionElements; + return extensionElements; } /** @@ -154,17 +188,18 @@ export function getExtensionElements(businessObject, moddle) { * @param formField The given Camunda form field. */ export function pushFormField(form, formField) { - - // get all fields of the form with the id of the given form field - const existingFieldsWithID = form.get('fields').filter(function (elem) { - return elem.id === formField.id; - }); - - // update existing form fields - for (let i = 0; i < existingFieldsWithID.length; i++) { - form.get('fields').splice(form.get('fields').indexOf(existingFieldsWithID[i])); - } - form.get('fields').push(formField); + // get all fields of the form with the id of the given form field + const existingFieldsWithID = form.get("fields").filter(function (elem) { + return elem.id === formField.id; + }); + + // update existing form fields + for (let i = 0; i < existingFieldsWithID.length; i++) { + form + .get("fields") + .splice(form.get("fields").indexOf(existingFieldsWithID[i])); + } + form.get("fields").push(formField); } /** @@ -174,11 +209,11 @@ export function pushFormField(form, formField) { * @returns {*} the root process element */ export function getRootProcess(definitions) { - for (let i = 0; i < definitions.rootElements.length; i++) { - if (definitions.rootElements[i].$type === 'bpmn:Process') { - return definitions.rootElements[i]; - } + for (let i = 0; i < definitions.rootElements.length; i++) { + if (definitions.rootElements[i].$type === "bpmn:Process") { + return definitions.rootElements[i]; } + } } /** @@ -188,8 +223,8 @@ export function getRootProcess(definitions) { * @return the definitions from the xml definitions */ export async function getDefinitionsFromXml(xml) { - let bpmnModeler = await createTempModelerFromXml(xml); - return bpmnModeler.getDefinitions(); + let bpmnModeler = await createTempModelerFromXml(xml); + return bpmnModeler.getDefinitions(); } /** @@ -199,12 +234,15 @@ export async function getDefinitionsFromXml(xml) { * @return the flow element if only one is defined, or undefined if none or multiple flow elements exist in the process */ export function getSingleFlowElement(process) { - let flowElements = process.flowElements; - if (flowElements.length !== 1) { - console.log('Process contains %i flow elements but must contain exactly one!', flowElements.length); - return undefined; - } - return flowElements[0]; + let flowElements = process.flowElements; + if (flowElements.length !== 1) { + console.log( + "Process contains %i flow elements but must contain exactly one!", + flowElements.length + ); + return undefined; + } + return flowElements[0]; } /** @@ -217,39 +255,47 @@ export function getSingleFlowElement(process) { * @param elementRegistry The element registry containing the elements of the current workflow * @returns {boolean} True, if element1 is connected via sequence flows with element2, false else. */ -export function findSequenceFlowConnection(element1, element2, visited, elementRegistry) { - - // exit condition of the recursion, element2 is reached - if (element1 === element2) { - return true; - } - - // store element1 as visited - visited.add(element1); - - // search recursively for element2 in all outgoing connections - const connections = element1.outgoing; - - for (let i = 0; i < connections.length; i++) { - - const connection = connections[i]; - - // only search in elements connected via sequence flow - if (connection.type === 'bpmn:SequenceFlow') { - - const nextElement = connection.target; - - // recursive call with new element - if (!visited.has(nextElement)) { - - // return true if recursive call finds element2 - if (findSequenceFlowConnection(nextElement, element2, visited, elementRegistry)) { - return true; - } - } +export function findSequenceFlowConnection( + element1, + element2, + visited, + elementRegistry +) { + // exit condition of the recursion, element2 is reached + if (element1 === element2) { + return true; + } + + // store element1 as visited + visited.add(element1); + + // search recursively for element2 in all outgoing connections + const connections = element1.outgoing; + + for (let i = 0; i < connections.length; i++) { + const connection = connections[i]; + + // only search in elements connected via sequence flow + if (connection.type === "bpmn:SequenceFlow") { + const nextElement = connection.target; + + // recursive call with new element + if (!visited.has(nextElement)) { + // return true if recursive call finds element2 + if ( + findSequenceFlowConnection( + nextElement, + element2, + visited, + elementRegistry + ) + ) { + return true; } + } } - return false; + } + return false; } /** @@ -259,49 +305,51 @@ export function findSequenceFlowConnection(element1, element2, visited, elementR * @param bpmnFactory the BPMN factory to create new BPMN elements */ export function getCamundaInputOutput(bo, bpmnFactory) { - - // retrieve InputOutput element if already defined - let inputOutput = getInputOutput(bo); - - // create new InputOutput element if non existent - if (!inputOutput || inputOutput.length === 0) { - - const extensionEntry = addEntry(bo, bo, bpmnFactory.create('camunda:InputOutput'), bpmnFactory); - - if (extensionEntry['extensionElements']) { - bo.extensionElements = extensionEntry['extensionElements']; - } else { - bo.extensionElements = extensionEntry['context']['currentObject']; - } - inputOutput = getExtension(bo, 'camunda:InputOutput'); - - if (!inputOutput) { - let inout = bpmnFactory.create('camunda:InputOutput'); - inout.inputParameters = []; - inout.outputParameters = []; - bo.extensionElements.values.push(inout); - return inout; - } else { - - // initialize parameters as empty arrays to avoid access errors - inputOutput.inputParameters = []; - inputOutput.outputParameters = []; - - // if there are multiple input/output definitions, take the first one as the modeler only uses this one - return inputOutput; - } + // retrieve InputOutput element if already defined + let inputOutput = getInputOutput(bo); + + // create new InputOutput element if non existent + if (!inputOutput || inputOutput.length === 0) { + const extensionEntry = addEntry( + bo, + bo, + bpmnFactory.create("camunda:InputOutput"), + bpmnFactory + ); + + if (extensionEntry["extensionElements"]) { + bo.extensionElements = extensionEntry["extensionElements"]; + } else { + bo.extensionElements = extensionEntry["context"]["currentObject"]; } + inputOutput = getExtension(bo, "camunda:InputOutput"); + + if (!inputOutput) { + let inout = bpmnFactory.create("camunda:InputOutput"); + inout.inputParameters = []; + inout.outputParameters = []; + bo.extensionElements.values.push(inout); + return inout; + } else { + // initialize parameters as empty arrays to avoid access errors + inputOutput.inputParameters = []; + inputOutput.outputParameters = []; - // init input/output parameters if undefined - if (!inputOutput.inputParameters) { - inputOutput.inputParameters = []; + // if there are multiple input/output definitions, take the first one as the modeler only uses this one + return inputOutput; } + } - if (!inputOutput.outputParameters) { - inputOutput.outputParameters = []; - } + // init input/output parameters if undefined + if (!inputOutput.inputParameters) { + inputOutput.inputParameters = []; + } - return inputOutput; + if (!inputOutput.outputParameters) { + inputOutput.outputParameters = []; + } + + return inputOutput; } /** @@ -313,10 +361,10 @@ export function getCamundaInputOutput(bo, bpmnFactory) { * @param bpmnFactory */ export function setInputParameter(task, name, value, bpmnFactory) { - let parameter = getInputParameter(task, name, bpmnFactory); - if (parameter) { - parameter.value = value; - } + let parameter = getInputParameter(task, name, bpmnFactory); + if (parameter) { + parameter.value = value; + } } /** @@ -328,10 +376,10 @@ export function setInputParameter(task, name, value, bpmnFactory) { * @param bpmnFactory */ export function setOutputParameter(task, name, value, bpmnFactory) { - let parameter = getOutputParameter(task, name, bpmnFactory); - if (parameter) { - parameter.value = value; - } + let parameter = getOutputParameter(task, name, bpmnFactory); + if (parameter) { + parameter.value = value; + } } /** @@ -342,15 +390,15 @@ export function setOutputParameter(task, name, value, bpmnFactory) { * @param type The given value */ export function getInputParameter(task, name, bpmnFactory) { - const extensionElement = getCamundaInputOutput(task, bpmnFactory); + const extensionElement = getCamundaInputOutput(task, bpmnFactory); - if (extensionElement && extensionElement.inputParameters) { - for (const parameter of extensionElement.inputParameters) { - if (parameter.name === name) { - return parameter; - } - } + if (extensionElement && extensionElement.inputParameters) { + for (const parameter of extensionElement.inputParameters) { + if (parameter.name === name) { + return parameter; + } } + } } /** @@ -361,15 +409,15 @@ export function getInputParameter(task, name, bpmnFactory) { * @param bpmnFactory */ export function getOutputParameter(task, name, bpmnFactory) { - const extensionElement = getCamundaInputOutput(task, bpmnFactory); + const extensionElement = getCamundaInputOutput(task, bpmnFactory); - if (extensionElement && extensionElement.outputParameters) { - for (const parameter of extensionElement.outputParameters) { - if (parameter.name === name) { - return parameter; - } - } + if (extensionElement && extensionElement.outputParameters) { + for (const parameter of extensionElement.outputParameters) { + if (parameter.name === name) { + return parameter; + } } + } } /** @@ -380,16 +428,25 @@ export function getOutputParameter(task, name, bpmnFactory) { * @param value Value of the input parameter. * @param bpmnFactory The bpmnFactory of the bpmn-js modeler. */ -export function addCamundaInputParameter(businessObject, name, value, bpmnFactory) { - - // get camunda io extension element - const inputOutputExtensions = getCamundaInputOutput(businessObject, bpmnFactory); - - // add new input parameter - inputOutputExtensions.inputParameters.push(bpmnFactory.create('camunda:InputParameter', { - name: name, - value: value, - })); +export function addCamundaInputParameter( + businessObject, + name, + value, + bpmnFactory +) { + // get camunda io extension element + const inputOutputExtensions = getCamundaInputOutput( + businessObject, + bpmnFactory + ); + + // add new input parameter + inputOutputExtensions.inputParameters.push( + bpmnFactory.create("camunda:InputParameter", { + name: name, + value: value, + }) + ); } /** @@ -400,16 +457,25 @@ export function addCamundaInputParameter(businessObject, name, value, bpmnFactor * @param value Value of the output parameter. * @param bpmnFactory The bpmnFactory of the bpmn-js modeler. */ -export function addCamundaOutputParameter(businessObject, name, value, bpmnFactory) { - - // get camunda io extension element - const inputOutputExtensions = getCamundaInputOutput(businessObject, bpmnFactory); - - // add new output parameter - inputOutputExtensions.outputParameters.push(bpmnFactory.create('camunda:OutputParameter', { - name: name, - value: value, - })); +export function addCamundaOutputParameter( + businessObject, + name, + value, + bpmnFactory +) { + // get camunda io extension element + const inputOutputExtensions = getCamundaInputOutput( + businessObject, + bpmnFactory + ); + + // add new output parameter + inputOutputExtensions.outputParameters.push( + bpmnFactory.create("camunda:OutputParameter", { + name: name, + value: value, + }) + ); } /** @@ -420,22 +486,29 @@ export function addCamundaOutputParameter(businessObject, name, value, bpmnFacto * @param keyValueMap key value map of the input parameter. * @param bpmnFactory The bpmnFactory of the bpmn-js modeler. */ -export function addCamundaInputMapParameter(businessObject, name, keyValueMap, bpmnFactory) { - - // get camunda io extension element - const inputOutputExtensions = getCamundaInputOutput(businessObject, bpmnFactory); - - // create a camunda map element for the key value map - const map = createCamundaMap(keyValueMap, bpmnFactory); - - // add the created map as new input parameter - const input = bpmnFactory.create('camunda:InputParameter', { - name: name, - definition: map, - }); - - map.$parent = input; - inputOutputExtensions.inputParameters.push(input); +export function addCamundaInputMapParameter( + businessObject, + name, + keyValueMap, + bpmnFactory +) { + // get camunda io extension element + const inputOutputExtensions = getCamundaInputOutput( + businessObject, + bpmnFactory + ); + + // create a camunda map element for the key value map + const map = createCamundaMap(keyValueMap, bpmnFactory); + + // add the created map as new input parameter + const input = bpmnFactory.create("camunda:InputParameter", { + name: name, + definition: map, + }); + + map.$parent = input; + inputOutputExtensions.inputParameters.push(input); } /** @@ -446,22 +519,29 @@ export function addCamundaInputMapParameter(businessObject, name, keyValueMap, b * @param keyValueMap key value map of the output parameter. * @param bpmnFactory The bpmnFactory of the bpmn-js modeler. */ -export function addCamundaOutputMapParameter(businessObject, name, keyValueMap, bpmnFactory) { - - // get camunda io extension element - const inputOutputExtensions = getCamundaInputOutput(businessObject, bpmnFactory); - - // create a camunda map element for the key value map - const map = createCamundaMap(keyValueMap, bpmnFactory); - - // add the created map as new output parameter - const output = bpmnFactory.create('camunda:OutputParameter', { - name: name, - definition: map, - }); - - map.$parent = output; - inputOutputExtensions.outputParameters.push(output); +export function addCamundaOutputMapParameter( + businessObject, + name, + keyValueMap, + bpmnFactory +) { + // get camunda io extension element + const inputOutputExtensions = getCamundaInputOutput( + businessObject, + bpmnFactory + ); + + // create a camunda map element for the key value map + const map = createCamundaMap(keyValueMap, bpmnFactory); + + // add the created map as new output parameter + const output = bpmnFactory.create("camunda:OutputParameter", { + name: name, + definition: map, + }); + + map.$parent = output; + inputOutputExtensions.outputParameters.push(output); } /** @@ -472,25 +552,24 @@ export function addCamundaOutputMapParameter(businessObject, name, keyValueMap, * @returns {camunda:Map} The created camunda map element */ export function createCamundaMap(keyValueMap, bpmnFactory) { - - // create camunda entry elements for the key value entries - const mapEntries = keyValueMap.map(function ({name, value}) { - return bpmnFactory.create('camunda:Entry', { - key: name, - value: value, - }); + // create camunda entry elements for the key value entries + const mapEntries = keyValueMap.map(function ({ name, value }) { + return bpmnFactory.create("camunda:Entry", { + key: name, + value: value, }); + }); - // create the camunda map for the entries - const map = bpmnFactory.create('camunda:Map', { - entries: mapEntries, - }); + // create the camunda map for the entries + const map = bpmnFactory.create("camunda:Map", { + entries: mapEntries, + }); - for (let entry of mapEntries) { - entry.$parent = map; - } + for (let entry of mapEntries) { + entry.$parent = map; + } - return map; + return map; } /** @@ -502,25 +581,24 @@ export function createCamundaMap(keyValueMap, bpmnFactory) { * @returns {camunda:Properties} The camunda properties element */ export function createCamundaProperties(keyValueMap, moddle) { - - // create camunda property elements for each map entry - const mapEntries = keyValueMap.map(function ({name, value}) { - return moddle.create('camunda:Property', { - id: name, - value: value, - }); + // create camunda property elements for each map entry + const mapEntries = keyValueMap.map(function ({ name, value }) { + return moddle.create("camunda:Property", { + id: name, + value: value, }); + }); - // create camunda properties element containing the created property elements - const map = moddle.create('camunda:Properties', { - values: mapEntries, - }); + // create camunda properties element containing the created property elements + const map = moddle.create("camunda:Properties", { + values: mapEntries, + }); - for (let entry of mapEntries) { - entry.$parent = map; - } + for (let entry of mapEntries) { + entry.$parent = map; + } - return map; + return map; } /** @@ -531,7 +609,7 @@ export function createCamundaProperties(keyValueMap, moddle) { * @return true if the given element is a flow like element, false otherwise */ export function isFlowLikeElement(type) { - return type === 'bpmn:SequenceFlow' || type === 'bpmn:Association'; + return type === "bpmn:SequenceFlow" || type === "bpmn:Association"; } /** @@ -541,17 +619,19 @@ export function isFlowLikeElement(type) { * @return the list of flow elements */ export function getFlowElementsRecursively(startElement) { - let flowElements = []; - for (let i = 0; i < startElement.flowElements.length; i++) { - let flowElement = startElement.flowElements[i]; - - if (flowElement.$type === 'bpmn:SubProcess') { - flowElements = flowElements.concat(getFlowElementsRecursively(flowElement)); - } else { - flowElements.push(flowElement); - } + let flowElements = []; + for (let i = 0; i < startElement.flowElements.length; i++) { + let flowElement = startElement.flowElements[i]; + + if (flowElement.$type === "bpmn:SubProcess") { + flowElements = flowElements.concat( + getFlowElementsRecursively(flowElement) + ); + } else { + flowElements.push(flowElement); } - return flowElements; + } + return flowElements; } /** @@ -561,14 +641,15 @@ export function getFlowElementsRecursively(startElement) { * @returns {string} The documentation property as a string */ export function getDocumentation(businessObject) { - - // get documentation - const documentationArray = businessObject.documentation || []; - - // convert documentation to string - return documentationArray.map(function (documentation) { - return documentation.text; - }).join('\n'); + // get documentation + const documentationArray = businessObject.documentation || []; + + // convert documentation to string + return documentationArray + .map(function (documentation) { + return documentation.text; + }) + .join("\n"); } /** @@ -579,9 +660,11 @@ export function getDocumentation(businessObject) { * @param bpmnFactory The bpmnFactory of the bpmn-js modeler */ export function setDocumentation(element, newDocumentation, bpmnFactory) { - element.businessObject.documentation = [bpmnFactory.create('bpmn:Documentation', { - text: newDocumentation, - })]; + element.businessObject.documentation = [ + bpmnFactory.create("bpmn:Documentation", { + text: newDocumentation, + }), + ]; } /** @@ -594,20 +677,25 @@ export function setDocumentation(element, newDocumentation, bpmnFactory) { * @returns {{extensionElements: elementType}} The updated extension elements */ export function addEntry(businessObject, element, entry, bpmnFactory) { - let extensionElements = businessObject.get('extensionElements'); - - // if there is no extensionElements list, create one - if (!extensionElements) { - extensionElements = createElement('bpmn:ExtensionElements', {values: [entry]}, businessObject, bpmnFactory); - return {extensionElements: extensionElements}; - } - - // add extension element to list if it exists - entry.$parent = extensionElements; - let values = extensionElements.get('values'); - values.push(entry); - extensionElements.set('values', values); - return {extensionElements: extensionElements}; + let extensionElements = businessObject.get("extensionElements"); + + // if there is no extensionElements list, create one + if (!extensionElements) { + extensionElements = createElement( + "bpmn:ExtensionElements", + { values: [entry] }, + businessObject, + bpmnFactory + ); + return { extensionElements: extensionElements }; + } + + // add extension element to list if it exists + entry.$parent = extensionElements; + let values = extensionElements.get("values"); + values.push(entry); + extensionElements.set("values", values); + return { extensionElements: extensionElements }; } /** @@ -620,10 +708,10 @@ export function addEntry(businessObject, element, entry, bpmnFactory) { * @returns {elementType} The created element */ export function createElement(elementType, properties, parent, factory) { - let element = factory.create(elementType, properties); - element.$parent = parent; + let element = factory.create(elementType, properties); + element.$parent = parent; - return element; + return element; } /** @@ -638,21 +726,28 @@ export function createElement(elementType, properties, parent, factory) { * @param autoPlace The create module of the bpmn-js modeler * @returns {Shape} The new created diagram element */ -export function appendElement(type, element, event, bpmnFactory, elementFactory, create, autoPlace) { - - const businessObject = bpmnFactory.create(type); - const shape = elementFactory.createShape({ - type: type, - businessObject: businessObject - }); - - if (autoPlace) { - autoPlace.append(element, shape); - } else { - create.start(event, shape); - } - - return shape; +export function appendElement( + type, + element, + event, + bpmnFactory, + elementFactory, + create, + autoPlace +) { + const businessObject = bpmnFactory.create(type); + const shape = elementFactory.createShape({ + type: type, + businessObject: businessObject, + }); + + if (autoPlace) { + autoPlace.append(element, shape); + } else { + create.start(event, shape); + } + + return shape; } /** @@ -662,12 +757,19 @@ export function appendElement(type, element, event, bpmnFactory, elementFactory, * @param replacementType The type of the new connection. * @param modeling The modeling module of the bpmn-js modeler. */ -export function replaceConnection(connectionElement, replacementType, modeling) { - const sourceElement = connectionElement.source; - const targetElement = connectionElement.target; - - modeling.removeConnection(connectionElement); - modeling.connect(sourceElement, targetElement, {type: replacementType, waypoints: connectionElement.waypoints}); +export function replaceConnection( + connectionElement, + replacementType, + modeling +) { + const sourceElement = connectionElement.source; + const targetElement = connectionElement.target; + + modeling.removeConnection(connectionElement); + modeling.connect(sourceElement, targetElement, { + type: replacementType, + waypoints: connectionElement.waypoints, + }); } /** @@ -678,16 +780,19 @@ export function replaceConnection(connectionElement, replacementType, modeling) * @returns {boolean} True if the given element is connected with an element of the given type, false else. */ export function isConnectedWith(element, connectedElementType) { - - const outgoingConnections = element.outgoing || []; - const incomingConnections = element.incoming || []; - - // check if a source or target of a connection is of the given type - for (let connectedElement of outgoingConnections.concat(incomingConnections)) { - if (is(connectedElement.source, connectedElementType) || is(connectedElement.target, connectedElementType)) { - return true; - } + const outgoingConnections = element.outgoing || []; + const incomingConnections = element.incoming || []; + + // check if a source or target of a connection is of the given type + for (let connectedElement of outgoingConnections.concat( + incomingConnections + )) { + if ( + is(connectedElement.source, connectedElementType) || + is(connectedElement.target, connectedElementType) + ) { + return true; } - return false; + } + return false; } - diff --git a/components/bpmn-q/modeler-component/editor/util/PopupMenuUtilities.js b/components/bpmn-q/modeler-component/editor/util/PopupMenuUtilities.js index 28b44753..d707e6cc 100644 --- a/components/bpmn-q/modeler-component/editor/util/PopupMenuUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/PopupMenuUtilities.js @@ -11,29 +11,35 @@ * @param customStyleClass The style class of the MoreOptionsEntry. * @returns {{label: string, className: string, action: Function}} The created MoreOptionsEntry */ -export function createMoreOptionsEntryWithReturn(originalElement, title, entryName, popupMenu, options, customStyleClass) { - - const lessOptionsEntry = createLessOptionsEntry( - originalElement, - 'Change Element', - 'All Entries', - popupMenu, - undefined, - ); - - // entries of the new popup menu - let entries = {}; - entries['replace-by-less-options'] = lessOptionsEntry; - entries = Object.assign(entries, options); - - return createMoreOptionsEntry( - title, - title, - entryName, - popupMenu, - entries, - customStyleClass, - ); +export function createMoreOptionsEntryWithReturn( + originalElement, + title, + entryName, + popupMenu, + options, + customStyleClass +) { + const lessOptionsEntry = createLessOptionsEntry( + originalElement, + "Change Element", + "All Entries", + popupMenu, + undefined + ); + + // entries of the new popup menu + let entries = {}; + entries["replace-by-less-options"] = lessOptionsEntry; + entries = Object.assign(entries, options); + + return createMoreOptionsEntry( + title, + title, + entryName, + popupMenu, + entries, + customStyleClass + ); } /** @@ -55,27 +61,37 @@ export function createMoreOptionsEntryWithReturn(originalElement, title, entryNa * @returns {{ label: string, className: string, action: function}}: The popup menu entry which shows another popup menu * when clicked. */ -export function createMoreOptionsEntry(optionsType, title, entryName, popupMenu, entries, customStyleClass) { - - // add customStyleClass to the default classname if set - const classname = customStyleClass ? 'qwm-popup-menu-more-options ' + customStyleClass : 'popup-menu-more-options'; - - // create a popup menu entry which triggers a new popup menu for the optionsType - return { - label: entryName, - className: classname, - moreOptions: entries, - action: function () { - - popupMenu.openWithEntries({type: optionsType}, "bpmn-replace", entries, - { - title: title, - width: 300, - search: true, - } - ); +export function createMoreOptionsEntry( + optionsType, + title, + entryName, + popupMenu, + entries, + customStyleClass +) { + // add customStyleClass to the default classname if set + const classname = customStyleClass + ? "qwm-popup-menu-more-options " + customStyleClass + : "popup-menu-more-options"; + + // create a popup menu entry which triggers a new popup menu for the optionsType + return { + label: entryName, + className: classname, + moreOptions: entries, + action: function () { + popupMenu.openWithEntries( + { type: optionsType }, + "bpmn-replace", + entries, + { + title: title, + width: 300, + search: true, } - }; + ); + }, + }; } /** @@ -91,22 +107,25 @@ export function createMoreOptionsEntry(optionsType, title, entryName, popupMenu, * @param entries The entries of the new popup menu (optional) * @returns {{action: action, className: string, label}} The created LessOptionsEntry */ -export function createLessOptionsEntry(originalElement, title, entryName, popupMenu, entries) { - - // create a popup menu entry which triggers a new popup menu for the optionsType - return { - label: entryName, - className: 'qwm-popup-menu-less-options', - action: function () { - popupMenu.openWithEntries(originalElement, "bpmn-replace", entries, - { - title: title, - width: 300, - search: true, - } - ); - } - }; +export function createLessOptionsEntry( + originalElement, + title, + entryName, + popupMenu, + entries +) { + // create a popup menu entry which triggers a new popup menu for the optionsType + return { + label: entryName, + className: "qwm-popup-menu-less-options", + action: function () { + popupMenu.openWithEntries(originalElement, "bpmn-replace", entries, { + title: title, + width: 300, + search: true, + }); + }, + }; } /** @@ -118,17 +137,26 @@ export function createLessOptionsEntry(originalElement, title, entryName, popupM * @param replaceElement The replaceElement function of the bpmn-js modeler. * @returns {{}} Object containing the created menu entries. */ -export function createMenuEntries(element, definitions, translate, replaceElement) { - - let menuEntries = {}; - let id; - - // create menu entries for each entry in the definitions - for (let definition of definitions) { - id = definition.id || definition.actionName; - menuEntries[id] = createMenuEntry(element, definition, translate, replaceElement); - } - return menuEntries; +export function createMenuEntries( + element, + definitions, + translate, + replaceElement +) { + let menuEntries = {}; + let id; + + // create menu entries for each entry in the definitions + for (let definition of definitions) { + id = definition.id || definition.actionName; + menuEntries[id] = createMenuEntry( + element, + definition, + translate, + replaceElement + ); + } + return menuEntries; } /** @@ -141,23 +169,28 @@ export function createMenuEntries(element, definitions, translate, replaceElemen * @param action The action which is triggered when the menu entry is selected, if undefined the replaceAction is used. * @returns {{action: (function(): *), className, label}} The created menu entry. */ -export function createMenuEntry(element, definition, translate, replaceElement, action = undefined) { - - // replace the element by the element type defined in definition.target - const replaceAction = function () { - console.log(definition.target); - return replaceElement(element, definition.target); - }; - - const label = definition.label || ''; - - action = action || replaceAction; - - return { - label: translate(label), - className: definition.className, - action: action - }; +export function createMenuEntry( + element, + definition, + translate, + replaceElement, + action = undefined +) { + // replace the element by the element type defined in definition.target + const replaceAction = function () { + console.log(definition.target); + return replaceElement(element, definition.target); + }; + + const label = definition.label || ""; + + action = action || replaceAction; + + return { + label: translate(label), + className: definition.className, + action: action, + }; } /** @@ -167,16 +200,17 @@ export function createMenuEntry(element, definition, translate, replaceElement, * @returns {*|unknown[]} List of menu entries or a single entry if the given entry is not a MoreOptionsEntry */ export function getMoreOptions(entry) { - if (entry.moreOptions) { - - // skip first entry because this is the entry for returning to the original menu - return Object.entries(entry.moreOptions).slice(1).flatMap(function ([key, value]) { - value.id = key; - - // recursively resolve each menu entry - return getMoreOptions(value); - }); - } else { - return entry; - } -} \ No newline at end of file + if (entry.moreOptions) { + // skip first entry because this is the entry for returning to the original menu + return Object.entries(entry.moreOptions) + .slice(1) + .flatMap(function ([key, value]) { + value.id = key; + + // recursively resolve each menu entry + return getMoreOptions(value); + }); + } else { + return entry; + } +} diff --git a/components/bpmn-q/modeler-component/editor/util/RenderUtilities.js b/components/bpmn-q/modeler-component/editor/util/RenderUtilities.js index 9a9b52fa..15212025 100644 --- a/components/bpmn-q/modeler-component/editor/util/RenderUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/RenderUtilities.js @@ -1,10 +1,10 @@ import { - append as svgAppend, - attr as svgAttr, - create as svgCreate, - innerSVG, - select as svgSelect -} from 'tiny-svg'; + append as svgAppend, + attr as svgAttr, + create as svgCreate, + innerSVG, + select as svgSelect, +} from "tiny-svg"; /** * Draw svg path with the given attributes. @@ -15,14 +15,13 @@ import { * @returns {SVGPathElement} */ export function drawPath(parentGfx, d, attrs) { + const path = svgCreate("path"); + svgAttr(path, { d: d }); + svgAttr(path, attrs); - const path = svgCreate('path'); - svgAttr(path, {d: d}); - svgAttr(path, attrs); + svgAppend(parentGfx, path); - svgAppend(parentGfx, path); - - return path; + return path; } /** @@ -38,21 +37,21 @@ export function drawPath(parentGfx, d, attrs) { * @returns {SVGRectElement} */ function drawRect(parentNode, width, height, borderRadius, color) { - const rect = svgCreate('rect'); + const rect = svgCreate("rect"); - svgAttr(rect, { - width: width, - height: height, - rx: borderRadius, - ry: borderRadius, - stroke: color, - strokeWidth: 2, - fill: color - }); + svgAttr(rect, { + width: width, + height: height, + rx: borderRadius, + ry: borderRadius, + stroke: color, + strokeWidth: 2, + fill: color, + }); - svgAppend(parentNode, rect); + svgAppend(parentNode, rect); - return rect; + return rect; } /** @@ -63,22 +62,22 @@ function drawRect(parentNode, width, height, borderRadius, color) { * @param svgAttributes Attributes for the SVG */ export function drawTaskSVG(parentGfx, importSVG, svgAttributes) { - const innerSvgStr = importSVG.svg, - transformDef = importSVG.transform; + const innerSvgStr = importSVG.svg, + transformDef = importSVG.transform; - const groupDef = svgCreate('g'); - svgAttr(groupDef, {transform: transformDef}); - innerSVG(groupDef, innerSvgStr); + const groupDef = svgCreate("g"); + svgAttr(groupDef, { transform: transformDef }); + innerSVG(groupDef, innerSvgStr); - // set task box opacity to 0 such that icon can be in the background - svgAttr(svgSelect(parentGfx, 'rect'), {'fill-opacity': 0}); + // set task box opacity to 0 such that icon can be in the background + svgAttr(svgSelect(parentGfx, "rect"), { "fill-opacity": 0 }); - if (svgAttributes) { - svgAttr(groupDef, svgAttributes); - } + if (svgAttributes) { + svgAttr(groupDef, svgAttributes); + } - // draw svg in the background - parentGfx.prepend(groupDef); + // draw svg in the background + parentGfx.prepend(groupDef); } /** @@ -89,16 +88,16 @@ export function drawTaskSVG(parentGfx, importSVG, svgAttributes) { * @param svgAttributes Attributes for the SVG */ export function drawDataElementSVG(parentGfx, importSVG, svgAttributes) { - const innerSvgStr = importSVG.svg, - transformDef = importSVG.transform; + const innerSvgStr = importSVG.svg, + transformDef = importSVG.transform; - const groupDef = svgCreate('g'); - svgAttr(groupDef, {transform: transformDef}); - innerSVG(groupDef, innerSvgStr); + const groupDef = svgCreate("g"); + svgAttr(groupDef, { transform: transformDef }); + innerSVG(groupDef, innerSvgStr); - if (svgAttributes) { - svgAttr(groupDef, svgAttributes); - } + if (svgAttributes) { + svgAttr(groupDef, svgAttributes); + } - parentGfx.append(groupDef); -} \ No newline at end of file + parentGfx.append(groupDef); +} diff --git a/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js b/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js index f283945c..f4275a15 100644 --- a/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js +++ b/components/bpmn-q/modeler-component/editor/util/TransformationUtilities.js @@ -1,5 +1,5 @@ -import {isFlowLikeElement} from './ModellingUtilities'; -import {getDi, is} from 'bpmn-js/lib/util/ModelUtil'; +import { isFlowLikeElement } from "./ModellingUtilities"; +import { getDi, is } from "bpmn-js/lib/util/ModelUtil"; /** * Insert the given element and all child elements into the diagram @@ -13,103 +13,137 @@ import {getDi, is} from 'bpmn-js/lib/util/ModelUtil'; * @param oldElement an old element that is only required if it should be replaced by the new element * @return {{success: boolean, idMap: *, element: *}} */ -export function insertShape(definitions, parent, newElement, idMap, replace, modeler, oldElement) { - console.log('Inserting shape for element: ', newElement); - let bpmnReplace = modeler.get('bpmnReplace'); - let bpmnFactory = modeler.get('bpmnFactory'); - let modeling = modeler.get('modeling'); - let elementRegistry = modeler.get('elementRegistry'); - - // create new id map if not provided - if (idMap === undefined) { - idMap = {}; - } - - let element; - if (!isFlowLikeElement(newElement.$type)) { - if (replace) { - - // replace old element to retain attached sequence flow, associations, data objects, ... - element = bpmnReplace.replaceElement(elementRegistry.get(oldElement.id), {type: newElement.$type}); - } else { - - // create new shape for this element - element = modeling.createShape({type: newElement.$type}, {x: 50, y: 50}, parent, {}); - } +export function insertShape( + definitions, + parent, + newElement, + idMap, + replace, + modeler, + oldElement +) { + console.log("Inserting shape for element: ", newElement); + let bpmnReplace = modeler.get("bpmnReplace"); + let bpmnFactory = modeler.get("bpmnFactory"); + let modeling = modeler.get("modeling"); + let elementRegistry = modeler.get("elementRegistry"); + + // create new id map if not provided + if (idMap === undefined) { + idMap = {}; + } + + let element; + if (!isFlowLikeElement(newElement.$type)) { + if (replace) { + // replace old element to retain attached sequence flow, associations, data objects, ... + element = bpmnReplace.replaceElement(elementRegistry.get(oldElement.id), { + type: newElement.$type, + }); } else { - - // create connection between two previously created elements - let sourceElement = elementRegistry.get(idMap[newElement.sourceRef.id]); - let targetElement = elementRegistry.get(idMap[newElement.targetRef.id]); - element = modeling.connect(sourceElement, targetElement, {type: newElement.$type}); + // create new shape for this element + element = modeling.createShape( + { type: newElement.$type }, + { x: 50, y: 50 }, + parent, + {} + ); } - - // store id to create sequence flows - idMap[newElement['id']] = element.id; - - // if the element is a subprocess, check if it is expanded in the replacement fragment and expand the new element - if (['bpmn:SubProcess', 'quantme:QuantumHardwareSelectionSubprocess', 'quantme:CircuitCuttingSubprocess'].includes(newElement.$type)) { - - // get the shape element related to the subprocess - let shape = getDi(element); - shape.isExpanded = true; - - // TODO: fix the following if, as the access to the DI of the new element is not possible with the current BPMN-JS version - /*if (shape && shape.isExpanded) { + } else { + // create connection between two previously created elements + let sourceElement = elementRegistry.get(idMap[newElement.sourceRef.id]); + let targetElement = elementRegistry.get(idMap[newElement.targetRef.id]); + element = modeling.connect(sourceElement, targetElement, { + type: newElement.$type, + }); + } + + // store id to create sequence flows + idMap[newElement["id"]] = element.id; + + // if the element is a subprocess, check if it is expanded in the replacement fragment and expand the new element + if ( + [ + "bpmn:SubProcess", + "quantme:QuantumHardwareSelectionSubprocess", + "quantme:CircuitCuttingSubprocess", + ].includes(newElement.$type) + ) { + // get the shape element related to the subprocess + let shape = getDi(element); + shape.isExpanded = true; + + // TODO: fix the following if, as the access to the DI of the new element is not possible with the current BPMN-JS version + /*if (shape && shape.isExpanded) { // expand the new element elementRegistry.get(element.id).businessObject.di.isExpanded = true; }*/ - // preserve messages defined in ReceiveTasks - } else if (newElement.$type === 'bpmn:ReceiveTask' && newElement.messageRef) { - - // get message from the replacement and check if a corresponding message was already created - let oldMessage = newElement.messageRef; - if (idMap[oldMessage.id] === undefined) { - - // add a new message element to the definitions document and link it to the receive task - let message = bpmnFactory.create('bpmn:Message'); - message.name = oldMessage.name; - definitions.rootElements.push(message); - modeling.updateProperties(element, {'messageRef': message}); - - // store id if other receive tasks reference the same message - idMap[oldMessage.id] = message.id; - } else { - - // reuse already created message and add it to receive task - modeling.updateProperties(element, {'messageRef': idMap[oldMessage.id]}); - } - } - - // add element to which a boundary event is attached - if (newElement.$type === 'bpmn:BoundaryEvent') { - let hostElement = elementRegistry.get(idMap[newElement.attachedToRef.id]); - modeling.updateProperties(element, {'attachedToRef': hostElement.businessObject}); - element.host = hostElement; + // preserve messages defined in ReceiveTasks + } else if (newElement.$type === "bpmn:ReceiveTask" && newElement.messageRef) { + // get message from the replacement and check if a corresponding message was already created + let oldMessage = newElement.messageRef; + if (idMap[oldMessage.id] === undefined) { + // add a new message element to the definitions document and link it to the receive task + let message = bpmnFactory.create("bpmn:Message"); + message.name = oldMessage.name; + definitions.rootElements.push(message); + modeling.updateProperties(element, { messageRef: message }); + + // store id if other receive tasks reference the same message + idMap[oldMessage.id] = message.id; + } else { + // reuse already created message and add it to receive task + modeling.updateProperties(element, { messageRef: idMap[oldMessage.id] }); } - - // update the properties of the new element - modeling.updateProperties(element, getPropertiesToCopy(newElement)); - - // recursively handle children of the current element - let resultTuple = insertChildElements(definitions, element, newElement, idMap, modeler); - - // add artifacts with their shapes to the diagram - let success = resultTuple['success']; - idMap = resultTuple['idMap']; - let artifacts = newElement.artifacts; - if (artifacts) { - console.log('Element contains %i artifacts. Adding corresponding shapes...', artifacts.length); - for (let i = 0; i < artifacts.length; i++) { - let result = insertShape(definitions, element, artifacts[i], idMap, false, modeler); - success = success && result['success']; - idMap = result['idMap']; - } + } + + // add element to which a boundary event is attached + if (newElement.$type === "bpmn:BoundaryEvent") { + let hostElement = elementRegistry.get(idMap[newElement.attachedToRef.id]); + modeling.updateProperties(element, { + attachedToRef: hostElement.businessObject, + }); + element.host = hostElement; + } + + // update the properties of the new element + modeling.updateProperties(element, getPropertiesToCopy(newElement)); + + // recursively handle children of the current element + let resultTuple = insertChildElements( + definitions, + element, + newElement, + idMap, + modeler + ); + + // add artifacts with their shapes to the diagram + let success = resultTuple["success"]; + idMap = resultTuple["idMap"]; + let artifacts = newElement.artifacts; + if (artifacts) { + console.log( + "Element contains %i artifacts. Adding corresponding shapes...", + artifacts.length + ); + for (let i = 0; i < artifacts.length; i++) { + let result = insertShape( + definitions, + element, + artifacts[i], + idMap, + false, + modeler + ); + success = success && result["success"]; + idMap = result["idMap"]; } + } - // return success flag and idMap with id mappings of this element and all children - return {success: success, idMap: idMap, element: element}; + // return success flag and idMap with id mappings of this element and all children + return { success: success, idMap: idMap, element: element }; } /** @@ -122,47 +156,75 @@ export function insertShape(definitions, parent, newElement, idMap, replace, mod * @param modeler the BPMN modeler containing the target BPMN diagram * @return {{success: boolean, idMap: *, element: *}} */ -export function insertChildElements(definitions, parent, newElement, idMap, modeler) { - - let success = true; - let flowElements = newElement.flowElements; - let boundaryEvents = []; - let sequenceflows = []; - if (flowElements) { - console.log('Element contains %i children. Adding corresponding shapes...', flowElements.length); - for (let i = 0; i < flowElements.length; i++) { - - // skip elements with references and add them after all other elements to set correct references - if (flowElements[i].$type === 'bpmn:SequenceFlow') { - sequenceflows.push(flowElements[i]); - continue; - } - if (flowElements[i].$type === 'bpmn:BoundaryEvent') { - boundaryEvents.push(flowElements[i]); - continue; - } - - let result = insertShape(definitions, parent, flowElements[i], idMap, false, modeler); - success = success && result['success']; - idMap = result['idMap']; - } +export function insertChildElements( + definitions, + parent, + newElement, + idMap, + modeler +) { + let success = true; + let flowElements = newElement.flowElements; + let boundaryEvents = []; + let sequenceflows = []; + if (flowElements) { + console.log( + "Element contains %i children. Adding corresponding shapes...", + flowElements.length + ); + for (let i = 0; i < flowElements.length; i++) { + // skip elements with references and add them after all other elements to set correct references + if (flowElements[i].$type === "bpmn:SequenceFlow") { + sequenceflows.push(flowElements[i]); + continue; + } + if (flowElements[i].$type === "bpmn:BoundaryEvent") { + boundaryEvents.push(flowElements[i]); + continue; + } + + let result = insertShape( + definitions, + parent, + flowElements[i], + idMap, + false, + modeler + ); + success = success && result["success"]; + idMap = result["idMap"]; + } - // handle boundary events with new ids of added elements - for (let i = 0; i < boundaryEvents.length; i++) { - let result = insertShape(definitions, parent, boundaryEvents[i], idMap, false, modeler); - success = success && result['success']; - idMap = result['idMap']; - } + // handle boundary events with new ids of added elements + for (let i = 0; i < boundaryEvents.length; i++) { + let result = insertShape( + definitions, + parent, + boundaryEvents[i], + idMap, + false, + modeler + ); + success = success && result["success"]; + idMap = result["idMap"]; + } - // handle boundary events with new ids of added elements - for (let i = 0; i < sequenceflows.length; i++) { - let result = insertShape(definitions, parent, sequenceflows[i], idMap, false, modeler); - success = success && result['success']; - idMap = result['idMap']; - } + // handle boundary events with new ids of added elements + for (let i = 0; i < sequenceflows.length; i++) { + let result = insertShape( + definitions, + parent, + sequenceflows[i], + idMap, + false, + modeler + ); + success = success && result["success"]; + idMap = result["idMap"]; } + } - return {success: success, idMap: idMap, element: parent}; + return { success: success, idMap: idMap, element: parent }; } /** @@ -172,43 +234,42 @@ export function insertChildElements(definitions, parent, newElement, idMap, mode * @return the properties to copy */ export function getPropertiesToCopy(element) { - let properties = {}; - for (let key in element) { - - // ignore properties from parent element - if (!element.hasOwnProperty(key)) { - continue; - } - - // ignore properties such as type - if (key.startsWith('$')) { - continue; - } + let properties = {}; + for (let key in element) { + // ignore properties from parent element + if (!element.hasOwnProperty(key)) { + continue; + } - // ignore id as it is automatically generated with the shape - if (key === 'id') { - continue; - } + // ignore properties such as type + if (key.startsWith("$")) { + continue; + } - // ignore flow elements, as the children are added afterwards - if (key === 'flowElements') { - continue; - } + // ignore id as it is automatically generated with the shape + if (key === "id") { + continue; + } - // ignore artifacts, as they are added afterwards with their shapes - if (key === 'artifacts') { - continue; - } + // ignore flow elements, as the children are added afterwards + if (key === "flowElements") { + continue; + } - // ignore messages, as they are added before - if (key === 'messageRef') { - continue; - } + // ignore artifacts, as they are added afterwards with their shapes + if (key === "artifacts") { + continue; + } - properties[key] = element[key]; + // ignore messages, as they are added before + if (key === "messageRef") { + continue; } - return properties; + properties[key] = element[key]; + } + + return properties; } /** @@ -219,25 +280,31 @@ export function getPropertiesToCopy(element) { * @param elementType The searched element type * @returns {*[]} All elements of the process with the elementType */ -export function getAllElementsInProcess(processBo, elementRegistry, elementType) { - - // retrieve parent object for later replacement - const processElement = elementRegistry.get(processBo.id); - - const elements = []; - const flowElementBos = processBo.flowElements; - for (let i = 0; i < flowElementBos.length; i++) { - let flowElementBo = flowElementBos[i]; - if (flowElementBo.$type && flowElementBo.$type === elementType) { - elements.push({element: flowElementBo, parent: processElement}); - } +export function getAllElementsInProcess( + processBo, + elementRegistry, + elementType +) { + // retrieve parent object for later replacement + const processElement = elementRegistry.get(processBo.id); + + const elements = []; + const flowElementBos = processBo.flowElements; + for (let i = 0; i < flowElementBos.length; i++) { + let flowElementBo = flowElementBos[i]; + if (flowElementBo.$type && flowElementBo.$type === elementType) { + elements.push({ element: flowElementBo, parent: processElement }); + } - // recursively retrieve service tasks if subprocess is found - if (flowElementBo.$type && flowElementBo.$type === 'bpmn:SubProcess') { - Array.prototype.push.apply(elements, getAllElementsInProcess(flowElementBo, elementRegistry, elementType)); - } + // recursively retrieve service tasks if subprocess is found + if (flowElementBo.$type && flowElementBo.$type === "bpmn:SubProcess") { + Array.prototype.push.apply( + elements, + getAllElementsInProcess(flowElementBo, elementRegistry, elementType) + ); } - return elements; + } + return elements; } /** @@ -248,18 +315,21 @@ export function getAllElementsInProcess(processBo, elementRegistry, elementType) * @param elementType The searched element type * @returns {*[]} All elements of the process with the elementType */ -export function getAllElementsForProcess(processBo, elementRegistry, elementType) { - - // retrieve parent object for later replacement - const processElement = elementRegistry.get(processBo.id); - - const elements = []; - const flowElements = processBo.flowElements; - for (let i = 0; i < flowElements.length; i++) { - let flowElement = flowElements[i]; - if (is(flowElement, elementType)) { - elements.push({element: flowElement, parent: processElement}); - } +export function getAllElementsForProcess( + processBo, + elementRegistry, + elementType +) { + // retrieve parent object for later replacement + const processElement = elementRegistry.get(processBo.id); + + const elements = []; + const flowElements = processBo.flowElements; + for (let i = 0; i < flowElements.length; i++) { + let flowElement = flowElements[i]; + if (is(flowElement, elementType)) { + elements.push({ element: flowElement, parent: processElement }); } - return elements; + } + return elements; } diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ConnectorUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ConnectorUtil.js index c0c238d0..09c7981d 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ConnectorUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ConnectorUtil.js @@ -4,24 +4,24 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import { - getServiceTaskLikeBusinessObject -} from './ImplementationTypeUtils'; -import {getExtensionElementsList} from "./ExtensionElementsUtil"; -import {getImplementationType} from "../../../extensions/quantme/utilities/ImplementationTypeHelperExtension"; +import { getServiceTaskLikeBusinessObject } from "./ImplementationTypeUtils"; +import { getExtensionElementsList } from "./ExtensionElementsUtil"; +import { getImplementationType } from "../../../extensions/quantme/utilities/ImplementationTypeHelperExtension"; export function areConnectorsSupported(element) { - const businessObject = getServiceTaskLikeBusinessObject(element); - return businessObject && getImplementationType(businessObject) === 'connector'; + const businessObject = getServiceTaskLikeBusinessObject(element); + return ( + businessObject && getImplementationType(businessObject) === "connector" + ); } export function getConnectors(businessObject) { - return getExtensionElementsList(businessObject, 'camunda:Connector'); + return getExtensionElementsList(businessObject, "camunda:Connector"); } export function getConnector(element) { - const businessObject = getServiceTaskLikeBusinessObject(element); - const connectors = getConnectors(businessObject); + const businessObject = getServiceTaskLikeBusinessObject(element); + const connectors = getConnectors(businessObject); - return connectors[0]; -} \ No newline at end of file + return connectors[0]; +} diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ElementUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ElementUtil.js index 3d356d04..42d6e04f 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ElementUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ElementUtil.js @@ -4,11 +4,9 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import Ids from 'ids'; +import Ids from "ids"; -import { - is -} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; /** * Create a new element and set its parent. @@ -21,48 +19,48 @@ import { * @returns {djs.model.Base} element which is created */ export function createElement(elementType, properties, parent, factory) { - const element = factory.create(elementType, properties); + const element = factory.create(elementType, properties); - if (parent) { - element.$parent = parent; - } + if (parent) { + element.$parent = parent; + } - return element; + return element; } /** * generate a semantic id with given prefix */ export function nextId(prefix) { - const ids = new Ids([32, 32, 1]); + const ids = new Ids([32, 32, 1]); - return ids.nextPrefixed(prefix); + return ids.nextPrefixed(prefix); } export function getRoot(businessObject) { - let parent = businessObject; + let parent = businessObject; - while (parent.$parent) { - parent = parent.$parent; - } + while (parent.$parent) { + parent = parent.$parent; + } - return parent; + return parent; } export function filterElementsByType(objectList, type) { - const list = objectList || []; + const list = objectList || []; - return list.filter(element => is(element, type)); + return list.filter((element) => is(element, type)); } export function findRootElementsByType(businessObject, referencedType) { - const root = getRoot(businessObject); + const root = getRoot(businessObject); - return filterElementsByType(root.get('rootElements'), referencedType); + return filterElementsByType(root.get("rootElements"), referencedType); } export function findRootElementById(businessObject, type, id) { - const elements = findRootElementsByType(businessObject, type); + const elements = findRootElementsByType(businessObject, type); - return elements.find(element => element.id === id); + return elements.find((element) => element.id === id); } diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/EventDefinitionUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/EventDefinitionUtil.js index 8711935a..460eacec 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/EventDefinitionUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/EventDefinitionUtil.js @@ -4,37 +4,34 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import { - isAny -} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; +import { isAny } from "bpmn-js/lib/features/modeling/util/ModelingUtil"; -import { - getBusinessObject, - is -} from 'bpmn-js/lib/util/ModelUtil'; +import { getBusinessObject, is } from "bpmn-js/lib/util/ModelUtil"; -import { - find -} from 'min-dash'; +import { find } from "min-dash"; export function isErrorSupported(element) { - return isAny(element, [ - 'bpmn:StartEvent', - 'bpmn:BoundaryEvent', - 'bpmn:EndEvent' - ]) && !!getErrorEventDefinition(element); + return ( + isAny(element, [ + "bpmn:StartEvent", + "bpmn:BoundaryEvent", + "bpmn:EndEvent", + ]) && !!getErrorEventDefinition(element) + ); } export function getErrorEventDefinition(element) { - return getEventDefinition(element, 'bpmn:ErrorEventDefinition'); + return getEventDefinition(element, "bpmn:ErrorEventDefinition"); } export function isTimerSupported(element) { - return isAny(element, [ - 'bpmn:StartEvent', - 'bpmn:IntermediateCatchEvent', - 'bpmn:BoundaryEvent' - ]) && !!getTimerEventDefinition(element); + return ( + isAny(element, [ + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:BoundaryEvent", + ]) && !!getTimerEventDefinition(element) + ); } /** @@ -45,125 +42,132 @@ export function isTimerSupported(element) { * @return {string|undefined} the timer definition type */ export function getTimerDefinitionType(timer) { + if (!timer) { + return; + } - if (!timer) { - return; - } + const timeDate = timer.get("timeDate"); + if (typeof timeDate !== "undefined") { + return "timeDate"; + } - const timeDate = timer.get('timeDate'); - if (typeof timeDate !== 'undefined') { - return 'timeDate'; - } + const timeCycle = timer.get("timeCycle"); + if (typeof timeCycle !== "undefined") { + return "timeCycle"; + } - const timeCycle = timer.get('timeCycle'); - if (typeof timeCycle !== 'undefined') { - return 'timeCycle'; - } - - const timeDuration = timer.get('timeDuration'); - if (typeof timeDuration !== 'undefined') { - return 'timeDuration'; - } + const timeDuration = timer.get("timeDuration"); + if (typeof timeDuration !== "undefined") { + return "timeDuration"; + } } export function getTimerEventDefinition(element) { - return getEventDefinition(element, 'bpmn:TimerEventDefinition'); + return getEventDefinition(element, "bpmn:TimerEventDefinition"); } export function getError(element) { - const errorEventDefinition = getErrorEventDefinition(element); + const errorEventDefinition = getErrorEventDefinition(element); - return errorEventDefinition && errorEventDefinition.get('errorRef'); + return errorEventDefinition && errorEventDefinition.get("errorRef"); } export function getEventDefinition(element, eventType) { - const businessObject = getBusinessObject(element); + const businessObject = getBusinessObject(element); - const eventDefinitions = businessObject.get('eventDefinitions') || []; + const eventDefinitions = businessObject.get("eventDefinitions") || []; - return find(eventDefinitions, function (definition) { - return is(definition, eventType); - }); + return find(eventDefinitions, function (definition) { + return is(definition, eventType); + }); } export function isMessageSupported(element) { - return is(element, 'bpmn:ReceiveTask') || ( - isAny(element, [ - 'bpmn:StartEvent', - 'bpmn:EndEvent', - 'bpmn:IntermediateThrowEvent', - 'bpmn:BoundaryEvent', - 'bpmn:IntermediateCatchEvent' - ]) && !!getMessageEventDefinition(element) - ); + return ( + is(element, "bpmn:ReceiveTask") || + (isAny(element, [ + "bpmn:StartEvent", + "bpmn:EndEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:BoundaryEvent", + "bpmn:IntermediateCatchEvent", + ]) && + !!getMessageEventDefinition(element)) + ); } export function getMessageEventDefinition(element) { - if (is(element, 'bpmn:ReceiveTask')) { - return getBusinessObject(element); - } + if (is(element, "bpmn:ReceiveTask")) { + return getBusinessObject(element); + } - return getEventDefinition(element, 'bpmn:MessageEventDefinition'); + return getEventDefinition(element, "bpmn:MessageEventDefinition"); } export function getMessage(element) { - const messageEventDefinition = getMessageEventDefinition(element); + const messageEventDefinition = getMessageEventDefinition(element); - return messageEventDefinition && messageEventDefinition.get('messageRef'); + return messageEventDefinition && messageEventDefinition.get("messageRef"); } export function getLinkEventDefinition(element) { - return getEventDefinition(element, 'bpmn:LinkEventDefinition'); + return getEventDefinition(element, "bpmn:LinkEventDefinition"); } export function getSignalEventDefinition(element) { - return getEventDefinition(element, 'bpmn:SignalEventDefinition'); + return getEventDefinition(element, "bpmn:SignalEventDefinition"); } export function isLinkSupported(element) { - return isAny(element, [ - 'bpmn:IntermediateThrowEvent', - 'bpmn:IntermediateCatchEvent' - ]) && !!getLinkEventDefinition(element); + return ( + isAny(element, [ + "bpmn:IntermediateThrowEvent", + "bpmn:IntermediateCatchEvent", + ]) && !!getLinkEventDefinition(element) + ); } export function isSignalSupported(element) { - return is(element, 'bpmn:Event') && !!getSignalEventDefinition(element); + return is(element, "bpmn:Event") && !!getSignalEventDefinition(element); } export function getSignal(element) { - const signalEventDefinition = getSignalEventDefinition(element); + const signalEventDefinition = getSignalEventDefinition(element); - return signalEventDefinition && signalEventDefinition.get('signalRef'); + return signalEventDefinition && signalEventDefinition.get("signalRef"); } export function getEscalationEventDefinition(element) { - return getEventDefinition(element, 'bpmn:EscalationEventDefinition'); + return getEventDefinition(element, "bpmn:EscalationEventDefinition"); } export function isEscalationSupported(element) { - return is(element, 'bpmn:Event') && !!getEscalationEventDefinition(element); + return is(element, "bpmn:Event") && !!getEscalationEventDefinition(element); } export function getEscalation(element) { - const escalationEventDefinition = getEscalationEventDefinition(element); + const escalationEventDefinition = getEscalationEventDefinition(element); - return escalationEventDefinition && escalationEventDefinition.get('escalationRef'); + return ( + escalationEventDefinition && escalationEventDefinition.get("escalationRef") + ); } export function isCompensationSupported(element) { - return isAny(element, [ - 'bpmn:EndEvent', - 'bpmn:IntermediateThrowEvent' - ]) && !!getCompensateEventDefinition(element); + return ( + isAny(element, ["bpmn:EndEvent", "bpmn:IntermediateThrowEvent"]) && + !!getCompensateEventDefinition(element) + ); } export function getCompensateEventDefinition(element) { - return getEventDefinition(element, 'bpmn:CompensateEventDefinition'); + return getEventDefinition(element, "bpmn:CompensateEventDefinition"); } export function getCompensateActivity(element) { - const compensateEventDefinition = getCompensateEventDefinition(element); + const compensateEventDefinition = getCompensateEventDefinition(element); - return compensateEventDefinition && compensateEventDefinition.get('activityRef'); + return ( + compensateEventDefinition && compensateEventDefinition.get("activityRef") + ); } diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ExtensionElementsUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ExtensionElementsUtil.js index d39748d2..b66abacc 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ExtensionElementsUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ExtensionElementsUtil.js @@ -4,11 +4,11 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import {is} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; -import {createElement} from './ElementUtil'; +import { createElement } from "./ElementUtil"; -import {isArray} from 'min-dash'; +import { isArray } from "min-dash"; /** * Get extension elements of business object. Optionally filter by type. @@ -18,33 +18,33 @@ import {isArray} from 'min-dash'; * @returns {Array} */ export function getExtensionElementsList(businessObject, type = undefined) { - const extensionElements = businessObject.get('extensionElements'); + const extensionElements = businessObject.get("extensionElements"); - if (!extensionElements) { - return []; - } + if (!extensionElements) { + return []; + } - const values = extensionElements.get('values'); + const values = extensionElements.get("values"); - if (!values || !values.length) { - return []; - } + if (!values || !values.length) { + return []; + } - if (type) { - return values.filter(value => is(value, type)); - } - return values; + if (type) { + return values.filter((value) => is(value, type)); + } + return values; } export function getExtension(element, type) { - const extensionElements = getExtensionElementsList(element); - if (!extensionElements) { - return null; - } - - return extensionElements.filter(function (e) { - return e.$instanceOf(type); - })[0]; + const extensionElements = getExtensionElementsList(element); + if (!extensionElements) { + return null; + } + + return extensionElements.filter(function (e) { + return e.$instanceOf(type); + })[0]; } /** @@ -55,49 +55,55 @@ export function getExtension(element, type) { * @param {ModdleElement|Array} extensionElementsToAdd * @param {CommandStack} commandStack */ -export function addExtensionElements(element, businessObject, extensionElementToAdd, bpmnFactory, commandStack) { - const commands = []; - - let extensionElements = businessObject.get('extensionElements'); - - // (1) create bpmn:ExtensionElements if it doesn't exist - if (!extensionElements) { - extensionElements = createElement( - 'bpmn:ExtensionElements', - { - values: [] - }, - businessObject, - bpmnFactory - ); - - commands.push({ - cmd: 'element.updateModdleProperties', - context: { - element, - moddleElement: businessObject, - properties: { - extensionElements - } - } - }); - } - - extensionElementToAdd.$parent = extensionElements; - - // (2) add extension element to list +export function addExtensionElements( + element, + businessObject, + extensionElementToAdd, + bpmnFactory, + commandStack +) { + const commands = []; + + let extensionElements = businessObject.get("extensionElements"); + + // (1) create bpmn:ExtensionElements if it doesn't exist + if (!extensionElements) { + extensionElements = createElement( + "bpmn:ExtensionElements", + { + values: [], + }, + businessObject, + bpmnFactory + ); + commands.push({ - cmd: 'element.updateModdleProperties', - context: { - element, - moddleElement: extensionElements, - properties: { - values: [...extensionElements.get('values'), extensionElementToAdd] - } - } + cmd: "element.updateModdleProperties", + context: { + element, + moddleElement: businessObject, + properties: { + extensionElements, + }, + }, }); - - commandStack.execute('properties-panel.multi-command-executor', commands); + } + + extensionElementToAdd.$parent = extensionElements; + + // (2) add extension element to list + commands.push({ + cmd: "element.updateModdleProperties", + context: { + element, + moddleElement: extensionElements, + properties: { + values: [...extensionElements.get("values"), extensionElementToAdd], + }, + }, + }); + + commandStack.execute("properties-panel.multi-command-executor", commands); } /** @@ -108,19 +114,26 @@ export function addExtensionElements(element, businessObject, extensionElementTo * @param {ModdleElement|Array} extensionElementsToRemove * @param {CommandStack} commandStack */ -export function removeExtensionElements(element, businessObject, extensionElementsToRemove, commandStack) { - if (!isArray(extensionElementsToRemove)) { - extensionElementsToRemove = [extensionElementsToRemove]; - } - - const extensionElements = businessObject.get('extensionElements'), - values = extensionElements.get('values').filter(value => !extensionElementsToRemove.includes(value)); - - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: extensionElements, - properties: { - values - } - }); +export function removeExtensionElements( + element, + businessObject, + extensionElementsToRemove, + commandStack +) { + if (!isArray(extensionElementsToRemove)) { + extensionElementsToRemove = [extensionElementsToRemove]; + } + + const extensionElements = businessObject.get("extensionElements"), + values = extensionElements + .get("values") + .filter((value) => !extensionElementsToRemove.includes(value)); + + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: extensionElements, + properties: { + values, + }, + }); } diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/FormTypeUtils.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/FormTypeUtils.js index 15773e02..97c1cf68 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/FormTypeUtils.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/FormTypeUtils.js @@ -4,30 +4,30 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import {isDefined} from 'min-dash'; +import { isDefined } from "min-dash"; -import { - getBusinessObject, - is -} from 'bpmn-js/lib/util/ModelUtil'; +import { getBusinessObject, is } from "bpmn-js/lib/util/ModelUtil"; export function getFormRefBinding(element) { - const businessObject = getBusinessObject(element); + const businessObject = getBusinessObject(element); - return businessObject.get('camunda:formRefBinding') || 'latest'; + return businessObject.get("camunda:formRefBinding") || "latest"; } export function getFormType(element) { - const businessObject = getBusinessObject(element); + const businessObject = getBusinessObject(element); - if (isDefined(businessObject.get('camunda:formKey'))) { - return 'formKey'; - } else if (isDefined(businessObject.get('camunda:formRef'))) { - return 'formRef'; - } + if (isDefined(businessObject.get("camunda:formKey"))) { + return "formKey"; + } else if (isDefined(businessObject.get("camunda:formRef"))) { + return "formRef"; + } } export function isFormSupported(element) { - return (is(element, 'bpmn:StartEvent') && !is(element.parent, 'bpmn:SubProcess')) - || is(element, 'bpmn:UserTask'); -} \ No newline at end of file + return ( + (is(element, "bpmn:StartEvent") && + !is(element.parent, "bpmn:SubProcess")) || + is(element, "bpmn:UserTask") + ); +} diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ImplementationTypeUtils.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ImplementationTypeUtils.js index ecdd4755..e2c52f11 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ImplementationTypeUtils.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ImplementationTypeUtils.js @@ -4,16 +4,11 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import { - getBusinessObject, - is -} from 'bpmn-js/lib/util/ModelUtil'; +import { getBusinessObject, is } from "bpmn-js/lib/util/ModelUtil"; -import { - isAny -} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; -import {getMessageEventDefinition} from "./EventDefinitionUtil"; -import {getExtensionElementsList} from "./ExtensionElementsUtil"; +import { isAny } from "bpmn-js/lib/features/modeling/util/ModelingUtil"; +import { getMessageEventDefinition } from "./EventDefinitionUtil"; +import { getExtensionElementsList } from "./ExtensionElementsUtil"; /** * Check whether an element is camunda:ServiceTaskLike @@ -23,7 +18,7 @@ import {getExtensionElementsList} from "./ExtensionElementsUtil"; * @return {boolean} a boolean value */ export function isServiceTaskLike(element) { - return is(element, 'camunda:ServiceTaskLike'); + return is(element, "camunda:ServiceTaskLike"); } /** @@ -34,7 +29,7 @@ export function isServiceTaskLike(element) { * @return {boolean} a boolean value */ export function isDmnCapable(element) { - return is(element, 'camunda:DmnCapable'); + return is(element, "camunda:DmnCapable"); } /** @@ -45,7 +40,7 @@ export function isDmnCapable(element) { * @return {boolean} a boolean value */ export function isExternalCapable(element) { - return is(element, 'camunda:ExternalCapable'); + return is(element, "camunda:ExternalCapable"); } /** @@ -56,7 +51,7 @@ export function isExternalCapable(element) { * @return {boolean} true */ export function isDeploymentCapable(element) { - return true; + return true; } /** @@ -69,20 +64,21 @@ export function isDeploymentCapable(element) { * @return {ModdleElement} the 'camunda:ServiceTaskLike' business object */ export function getServiceTaskLikeBusinessObject(element) { - - if (is(element, 'bpmn:IntermediateThrowEvent') || is(element, 'bpmn:EndEvent')) { - - // change business object to 'messageEventDefinition' when - // the element is a message intermediate throw event or message end event - // because the camunda extensions (e.g. camunda:class) are in the message - // event definition tag and not in the intermediate throw event or end event tag - const messageEventDefinition = getMessageEventDefinition(element); - if (messageEventDefinition) { - element = messageEventDefinition; - } + if ( + is(element, "bpmn:IntermediateThrowEvent") || + is(element, "bpmn:EndEvent") + ) { + // change business object to 'messageEventDefinition' when + // the element is a message intermediate throw event or message end event + // because the camunda extensions (e.g. camunda:class) are in the message + // event definition tag and not in the intermediate throw event or end event tag + const messageEventDefinition = getMessageEventDefinition(element); + if (messageEventDefinition) { + element = messageEventDefinition; } + } - return isServiceTaskLike(element) && getBusinessObject(element); + return isServiceTaskLike(element) && getBusinessObject(element); } // /** @@ -156,11 +152,13 @@ export function getServiceTaskLikeBusinessObject(element) { // } export function isListener(element) { - return this.isTaskListener(element) || this.isExecutionListener(element); + return this.isTaskListener(element) || this.isExecutionListener(element); } export function getListenerBusinessObject(businessObject) { - if (isAny(businessObject, ['camunda:ExecutionListener', 'camunda:TaskListener'])) { - return businessObject; - } + if ( + isAny(businessObject, ["camunda:ExecutionListener", "camunda:TaskListener"]) + ) { + return businessObject; + } } diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/InputOutputUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/InputOutputUtil.js index 7cf6645e..c30c287a 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/InputOutputUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/InputOutputUtil.js @@ -4,25 +4,20 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import { - isAny -} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; +import { isAny } from "bpmn-js/lib/features/modeling/util/ModelingUtil"; -import { - getBusinessObject, - is -} from 'bpmn-js/lib/util/ModelUtil'; -import {getExtensionElementsList} from "./ExtensionElementsUtil"; -import {createElement, nextId} from "./ElementUtil"; +import { getBusinessObject, is } from "bpmn-js/lib/util/ModelUtil"; +import { getExtensionElementsList } from "./ExtensionElementsUtil"; +import { createElement, nextId } from "./ElementUtil"; function getElements(businessObject, type, property) { - const elements = getExtensionElementsList(businessObject, type); - return !property ? elements : (elements[0] || {})[property] || []; + const elements = getExtensionElementsList(businessObject, type); + return !property ? elements : (elements[0] || {})[property] || []; } function getParameters(element, prop) { - const inputOutput = getInputOutput(element); - return (inputOutput && inputOutput.get(prop)) || []; + const inputOutput = getInputOutput(element); + return (inputOutput && inputOutput.get(prop)) || []; } /** @@ -33,16 +28,15 @@ function getParameters(element, prop) { * @return {ModdleElement} the inputOutput object */ export function getInputOutput(element) { - if (is(element, 'camunda:Connector')) { - return element.get('inputOutput'); - } + if (is(element, "camunda:Connector")) { + return element.get("inputOutput"); + } - const businessObject = getBusinessObject(element); + const businessObject = getBusinessObject(element); - return (getElements(businessObject, 'camunda:InputOutput') || [])[0]; + return (getElements(businessObject, "camunda:InputOutput") || [])[0]; } - /** * Return all input parameters existing in the business object, and * an empty array if none exist. @@ -52,7 +46,7 @@ export function getInputOutput(element) { * @return {Array} a list of input parameter objects */ export function getInputParameters(element) { - return getParameters(element, 'inputParameters'); + return getParameters(element, "inputParameters"); } /** @@ -64,122 +58,137 @@ export function getInputParameters(element) { * @return {Array} a list of output parameter objects */ export function getOutputParameters(element) { - return getParameters(element, 'outputParameters'); + return getParameters(element, "outputParameters"); } - export function isInputOutputSupported(element) { - const businessObject = getBusinessObject(element); - - return ( - is(businessObject, 'bpmn:FlowNode') && !( - isAny(businessObject, ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:Gateway']) || - is(businessObject, 'bpmn:SubProcess') && businessObject.get('triggeredByEvent') - ) - ); + const businessObject = getBusinessObject(element); + + return ( + is(businessObject, "bpmn:FlowNode") && + !( + isAny(businessObject, [ + "bpmn:StartEvent", + "bpmn:BoundaryEvent", + "bpmn:Gateway", + ]) || + (is(businessObject, "bpmn:SubProcess") && + businessObject.get("triggeredByEvent")) + ) + ); } export function areInputParametersSupported(element) { - return isInputOutputSupported(element); + return isInputOutputSupported(element); } export function areOutputParametersSupported(element) { - const businessObject = getBusinessObject(element); - return ( - isInputOutputSupported(element) && - !is(businessObject, 'bpmn:EndEvent') && - !businessObject.loopCharacteristics - ); + const businessObject = getBusinessObject(element); + return ( + isInputOutputSupported(element) && + !is(businessObject, "bpmn:EndEvent") && + !businessObject.loopCharacteristics + ); } export function getInputOutputType(parameter) { - const definitionTypes = { - 'camunda:Map': 'map', - 'camunda:List': 'list', - 'camunda:Script': 'script' - }; + const definitionTypes = { + "camunda:Map": "map", + "camunda:List": "list", + "camunda:Script": "script", + }; - let type = 'stringOrExpression'; + let type = "stringOrExpression"; - const definition = parameter.get('definition'); - if (typeof definition !== 'undefined') { - type = definitionTypes[definition.$type]; - } + const definition = parameter.get("definition"); + if (typeof definition !== "undefined") { + type = definitionTypes[definition.$type]; + } - return type; + return type; } export function CreateParameterCmd(element, type, parent, bpmnFactory) { - const isInput = type === 'camunda:InputParameter'; - - const newParameter = createElement(type, { - name: nextId(isInput ? 'Input_' : 'Output_') - }, parent, bpmnFactory); - - const propertyName = isInput ? 'inputParameters' : 'outputParameters'; - - return { - cmd: 'element.updateModdleProperties', - context: { - element, - moddleElement: parent, - properties: { - [propertyName]: [...parent.get(propertyName), newParameter] - } - } - }; + const isInput = type === "camunda:InputParameter"; + + const newParameter = createElement( + type, + { + name: nextId(isInput ? "Input_" : "Output_"), + }, + parent, + bpmnFactory + ); + + const propertyName = isInput ? "inputParameters" : "outputParameters"; + + return { + cmd: "element.updateModdleProperties", + context: { + element, + moddleElement: parent, + properties: { + [propertyName]: [...parent.get(propertyName), newParameter], + }, + }, + }; } export function AddParameterCmd(element, type, bpmnFactory) { - const commands = []; - const businessObject = getBusinessObject(element); - - let extensionElements = businessObject.get('extensionElements'); - - // (1) ensure extension elements - if (!extensionElements) { - extensionElements = createElement( - 'bpmn:ExtensionElements', - {values: []}, - businessObject, - bpmnFactory - ); - - commands.push({ - cmd: 'element.updateModdleProperties', - context: { - element, - moddleElement: businessObject, - properties: {extensionElements} - } - }); - } - - // (2) ensure inputOutput - let inputOutput = getInputOutput(element); - - if (!inputOutput) { - const parent = extensionElements; - - inputOutput = createElement('camunda:InputOutput', { - inputParameters: [], - outputParameters: [] - }, parent, bpmnFactory); - - commands.push({ - cmd: 'element.updateModdleProperties', - context: { - element, - moddleElement: extensionElements, - properties: { - values: [...extensionElements.get('values'), inputOutput] - } - } - }); - } - - // (3) create + add parameter - commands.push(CreateParameterCmd(element, type, inputOutput, bpmnFactory)); - - return commands; -} \ No newline at end of file + const commands = []; + const businessObject = getBusinessObject(element); + + let extensionElements = businessObject.get("extensionElements"); + + // (1) ensure extension elements + if (!extensionElements) { + extensionElements = createElement( + "bpmn:ExtensionElements", + { values: [] }, + businessObject, + bpmnFactory + ); + + commands.push({ + cmd: "element.updateModdleProperties", + context: { + element, + moddleElement: businessObject, + properties: { extensionElements }, + }, + }); + } + + // (2) ensure inputOutput + let inputOutput = getInputOutput(element); + + if (!inputOutput) { + const parent = extensionElements; + + inputOutput = createElement( + "camunda:InputOutput", + { + inputParameters: [], + outputParameters: [], + }, + parent, + bpmnFactory + ); + + commands.push({ + cmd: "element.updateModdleProperties", + context: { + element, + moddleElement: extensionElements, + properties: { + values: [...extensionElements.get("values"), inputOutput], + }, + }, + }); + } + + // (3) create + add parameter + commands.push(CreateParameterCmd(element, type, inputOutput, bpmnFactory)); + + return commands; +} diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ValidationUtil.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ValidationUtil.js index 38375c25..a2d1b959 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/ValidationUtil.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/ValidationUtil.js @@ -22,36 +22,34 @@ const ID_REGEX = /^[a-z_][\w-.]*$/i; * @return {String} error message */ export function isIdValid(element, idValue, translate) { - const assigned = element.$model.ids.assigned(idValue); - const idAlreadyExists = assigned && assigned !== element; + const assigned = element.$model.ids.assigned(idValue); + const idAlreadyExists = assigned && assigned !== element; - if (!idValue) { - return translate('ID must not be empty.'); - } + if (!idValue) { + return translate("ID must not be empty."); + } - if (idAlreadyExists) { - return translate('ID must be unique.'); - } + if (idAlreadyExists) { + return translate("ID must be unique."); + } - return validateId(idValue, translate); + return validateId(idValue, translate); } export function validateId(idValue, translate) { + if (containsSpace(idValue)) { + return translate("ID must not contain spaces."); + } - if (containsSpace(idValue)) { - return translate('ID must not contain spaces.'); + if (!ID_REGEX.test(idValue)) { + if (QNAME_REGEX.test(idValue)) { + return translate("ID must not contain prefix."); } - if (!ID_REGEX.test(idValue)) { - - if (QNAME_REGEX.test(idValue)) { - return translate('ID must not contain prefix.'); - } - - return translate('ID must be a valid QName.'); - } + return translate("ID must be a valid QName."); + } } export function containsSpace(value) { - return SPACE_REGEX.test(value); -} \ No newline at end of file + return SPACE_REGEX.test(value); +} diff --git a/components/bpmn-q/modeler-component/editor/util/camunda-utils/generateImage.js b/components/bpmn-q/modeler-component/editor/util/camunda-utils/generateImage.js index a7ae2aa5..9b45f361 100644 --- a/components/bpmn-q/modeler-component/editor/util/camunda-utils/generateImage.js +++ b/components/bpmn-q/modeler-component/editor/util/camunda-utils/generateImage.js @@ -4,56 +4,59 @@ * This code and the accompanying materials are made available by camunda under the * terms of the MIT License. */ -import canvg from 'canvg-browser'; +import canvg from "canvg-browser"; // list of defined encodings -const ENCODINGS = [ - 'image/png', - 'image/jpeg' -]; +const ENCODINGS = ["image/png", "image/jpeg"]; const INITIAL_SCALE = 3; const FINAL_SCALE = 1; const SCALE_STEP = 1; -const DATA_URL_REGEX = /^data:((?:\w+\/(?:(?!;).)+)?)((?:;[\w\W]*?[^;])*),(.+)$/; - +const DATA_URL_REGEX = + /^data:((?:\w+\/(?:(?!;).)+)?)((?:;[\w\W]*?[^;])*),(.+)$/; export default function generateImage(type, svg) { - const encoding = 'image/' + type; - - if (ENCODINGS.indexOf(encoding) === -1) { - throw new Error('<' + type + '> is an unknown type for converting svg to image'); - } + const encoding = "image/" + type; - const initialSVG = svg; + if (ENCODINGS.indexOf(encoding) === -1) { + throw new Error( + "<" + type + "> is an unknown type for converting svg to image" + ); + } - let dataURL = ''; + const initialSVG = svg; - for (let scale = INITIAL_SCALE; scale >= FINAL_SCALE; scale -= SCALE_STEP) { + let dataURL = ""; - let canvas = document.createElement('canvas'); + for (let scale = INITIAL_SCALE; scale >= FINAL_SCALE; scale -= SCALE_STEP) { + let canvas = document.createElement("canvas"); - svg = initialSVG.replace(/width="([^"]+)" height="([^"]+)"/, function (_, widthStr, heightStr) { - return `width="${parseInt(widthStr, 10) * scale}" height="${parseInt(heightStr, 10) * scale}"`; - }); + svg = initialSVG.replace( + /width="([^"]+)" height="([^"]+)"/, + function (_, widthStr, heightStr) { + return `width="${parseInt(widthStr, 10) * scale}" height="${ + parseInt(heightStr, 10) * scale + }"`; + } + ); - canvg(canvas, svg); + canvg(canvas, svg); - // make the background white for every format - let context = canvas.getContext('2d'); + // make the background white for every format + let context = canvas.getContext("2d"); - context.globalCompositeOperation = 'destination-over'; - context.fillStyle = 'white'; + context.globalCompositeOperation = "destination-over"; + context.fillStyle = "white"; - context.fillRect(0, 0, canvas.width, canvas.height); + context.fillRect(0, 0, canvas.width, canvas.height); - dataURL = canvas.toDataURL(encoding); + dataURL = canvas.toDataURL(encoding); - if (DATA_URL_REGEX.test(dataURL)) { - return dataURL; - } + if (DATA_URL_REGEX.test(dataURL)) { + return dataURL; } + } - throw new Error('Error happened generating image. Diagram size is too big.'); + throw new Error("Error happened generating image. Diagram size is too big."); } diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/Constants.js b/components/bpmn-q/modeler-component/extensions/data-extension/Constants.js index ba0289c1..707137f7 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/Constants.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/Constants.js @@ -1,19 +1,21 @@ // Type names of the data flow extension elements -export const DATA_MAP_OBJECT = 'dataflow:DataMapObject'; -export const DATA_STORE_MAP = 'dataflow:DataStoreMap'; -export const TRANSFORMATION_TASK = 'dataflow:TransformationTask'; -export const TRANSFORMATION_ASSOCIATION = 'dataflow:TransformationAssociation'; -export const INPUT_TRANSFORMATION_ASSOCIATION = 'dataflow:InputTransformationAssociation'; -export const OUTPUT_TRANSFORMATION_ASSOCIATION = 'dataflow:OutputTransformationAssociation'; -export const KEY_VALUE_ENTRY = 'dataflow:KeyValueEntry'; +export const DATA_MAP_OBJECT = "dataflow:DataMapObject"; +export const DATA_STORE_MAP = "dataflow:DataStoreMap"; +export const TRANSFORMATION_TASK = "dataflow:TransformationTask"; +export const TRANSFORMATION_ASSOCIATION = "dataflow:TransformationAssociation"; +export const INPUT_TRANSFORMATION_ASSOCIATION = + "dataflow:InputTransformationAssociation"; +export const OUTPUT_TRANSFORMATION_ASSOCIATION = + "dataflow:OutputTransformationAssociation"; +export const KEY_VALUE_ENTRY = "dataflow:KeyValueEntry"; // Property names of the data flow extension elements -export const CONTENT = 'content'; -export const DETAILS = 'details'; -export const PARAMETERS = 'parameters'; -export const EXPRESSIONS = 'expressions'; +export const CONTENT = "content"; +export const DETAILS = "details"; +export const PARAMETERS = "parameters"; +export const EXPRESSIONS = "expressions"; // Unique identifiers of the icon SVGs -export const TASK_TYPE_TRANSFORMATION_TASK = 'TASK_TYPE_TRANSFORMATION_TASK'; -export const DATA_TYPE_DATA_MAP_OBJECT = 'DATA_TYPE_DATA_MAP_OBJECT'; -export const DATA_TYPE_DATA_STORE_MAP = 'DATA_TYPE_DATA_STORE_MAP'; \ No newline at end of file +export const TASK_TYPE_TRANSFORMATION_TASK = "TASK_TYPE_TRANSFORMATION_TASK"; +export const DATA_TYPE_DATA_MAP_OBJECT = "DATA_TYPE_DATA_MAP_OBJECT"; +export const DATA_TYPE_DATA_STORE_MAP = "DATA_TYPE_DATA_STORE_MAP"; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js b/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js index 0f458134..29ad9097 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/DataFlowPlugin.js @@ -1,40 +1,46 @@ -import React from 'react'; +import React from "react"; -import DataFlowExtensionModule from './'; -import TransformationButton from '../../editor/ui/TransformationButton'; -import {getModeler} from '../../editor/ModelerHandler'; -import {getXml} from '../../editor/util/IoUtilities'; -import {startDataFlowReplacementProcess} from './transformation/TransformationManager'; -import TransformationTaskConfigurationsTab from './transf-task-configs/TransformationTaskConfigurationsTab'; -import dataStyles from './resources/data-flow-styles.css'; +import DataFlowExtensionModule from "./"; +import TransformationButton from "../../editor/ui/TransformationButton"; +import { getModeler } from "../../editor/ModelerHandler"; +import { getXml } from "../../editor/util/IoUtilities"; +import { startDataFlowReplacementProcess } from "./transformation/TransformationManager"; +import TransformationTaskConfigurationsTab from "./transf-task-configs/TransformationTaskConfigurationsTab"; +import dataStyles from "./resources/data-flow-styles.css"; import ExtensibleButton from "../../editor/ui/ExtensibleButton"; import UpdateTransformationTaskConfigurationsButton from "./ui/UpdateTransformationConfigurations"; -let dataflowModdleDescriptor = require('./resources/data-flow-extension.json'); +let dataflowModdleDescriptor = require("./resources/data-flow-extension.json"); /** * Plugin Object of the DataFlow extension. Used to register the plugin in the plugin handler of the modeler. */ export default { - name: 'dataflow', - buttons: []} - title="DataFlow" - styleClass="dataflow-plugin-icon" - description="Show buttons of the QHAna plugin"/> - ], - configTabs: [ - { - tabId: 'DataEndpointsTab', - tabTitle: 'Data Endpoints', - configTab: TransformationTaskConfigurationsTab, - }, - ], - extensionModule: DataFlowExtensionModule, - moddleDescription: dataflowModdleDescriptor, - styling: [dataStyles], - transformExtensionButton: { - return await startDataFlowReplacementProcess(xml); - } - }/>, -}; \ No newline at end of file + name: "dataflow", + buttons: [ + ]} + title="DataFlow" + styleClass="dataflow-plugin-icon" + description="Show buttons of the QHAna plugin" + />, + ], + configTabs: [ + { + tabId: "DataEndpointsTab", + tabTitle: "Data Endpoints", + configTab: TransformationTaskConfigurationsTab, + }, + ], + extensionModule: DataFlowExtensionModule, + moddleDescription: dataflowModdleDescriptor, + styling: [dataStyles], + transformExtensionButton: ( + { + return await startDataFlowReplacementProcess(xml); + }} + /> + ), +}; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/config/DataConfigManager.js b/components/bpmn-q/modeler-component/extensions/data-extension/config/DataConfigManager.js index d42aa0f6..72d296b7 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/config/DataConfigManager.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/config/DataConfigManager.js @@ -1,8 +1,8 @@ -import {getPluginConfig} from "../../../editor/plugin/PluginConfigHandler"; +import { getPluginConfig } from "../../../editor/plugin/PluginConfigHandler"; // default config entries used if no value is specified in the initial plugin config const defaultConfig = { - configurationsEndpoint: process.env.SERVICE_DATA_CONFIG + configurationsEndpoint: process.env.SERVICE_DATA_CONFIG, }; // current config @@ -14,10 +14,13 @@ let config = {}; * @return {string} the currently specified endpoint url of the Configurations endpoint */ export function getConfigurationsEndpoint() { - if (config.configurationsEndpoint === undefined) { - setConfigurationsEndpoint(getPluginConfig('dataflow').configurationsEndpoint || defaultConfig.configurationsEndpoint); - } - return config.configurationsEndpoint; + if (config.configurationsEndpoint === undefined) { + setConfigurationsEndpoint( + getPluginConfig("dataflow").configurationsEndpoint || + defaultConfig.configurationsEndpoint + ); + } + return config.configurationsEndpoint; } /** @@ -26,16 +29,15 @@ export function getConfigurationsEndpoint() { * @param configurationsEndpoint the endpoint url of the transformation task Configurations endpoint */ export function setConfigurationsEndpoint(configurationsEndpoint) { - if (configurationsEndpoint !== null && configurationsEndpoint !== undefined) { - - // remove trailing slashes - config.configurationsEndpoint = configurationsEndpoint.replace(/\/$/, ''); - } + if (configurationsEndpoint !== null && configurationsEndpoint !== undefined) { + // remove trailing slashes + config.configurationsEndpoint = configurationsEndpoint.replace(/\/$/, ""); + } } /** * Resets the all config entries */ export function resetConfig() { - config = {}; -} \ No newline at end of file + config = {}; +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/index.js b/components/bpmn-q/modeler-component/extensions/data-extension/index.js index 50f5a2f4..61d2e849 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/index.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/index.js @@ -1,23 +1,23 @@ -import DataFlowRenderer from './rendering/DataFlowRenderer'; -import DataFlowReplaceMenuProvider from './menu/DataFlowReplaceMenuProvider'; -import DataFlowPaletteProvider from './palette/DataFlowPaletteProvider'; -import DataFlowRulesProvider from './rules/DataFlowRulesProvider'; -import DataReplaceConnectionBehavior from './rules/DataReplaceConnectionBehaviour'; -import DataFlowPropertiesProvider from './properties-panel/DataFlowPropertiesProvider'; +import DataFlowRenderer from "./rendering/DataFlowRenderer"; +import DataFlowReplaceMenuProvider from "./menu/DataFlowReplaceMenuProvider"; +import DataFlowPaletteProvider from "./palette/DataFlowPaletteProvider"; +import DataFlowRulesProvider from "./rules/DataFlowRulesProvider"; +import DataReplaceConnectionBehavior from "./rules/DataReplaceConnectionBehaviour"; +import DataFlowPropertiesProvider from "./properties-panel/DataFlowPropertiesProvider"; export default { - __init__: [ - 'dataFlowRenderer', - 'dataFlowMenuProvider', - 'dataFlowPaletteProvider', - 'dataFlowRules', - 'replaceConnectionBehavior', - 'dataFlowPropertiesProvider' - ], - dataFlowRenderer: ['type', DataFlowRenderer], - dataFlowMenuProvider: ['type', DataFlowReplaceMenuProvider], - dataFlowPaletteProvider: ['type', DataFlowPaletteProvider], - dataFlowRules: ['type', DataFlowRulesProvider], - replaceConnectionBehavior: ['type', DataReplaceConnectionBehavior], - dataFlowPropertiesProvider: ['type', DataFlowPropertiesProvider] -}; \ No newline at end of file + __init__: [ + "dataFlowRenderer", + "dataFlowMenuProvider", + "dataFlowPaletteProvider", + "dataFlowRules", + "replaceConnectionBehavior", + "dataFlowPropertiesProvider", + ], + dataFlowRenderer: ["type", DataFlowRenderer], + dataFlowMenuProvider: ["type", DataFlowReplaceMenuProvider], + dataFlowPaletteProvider: ["type", DataFlowPaletteProvider], + dataFlowRules: ["type", DataFlowRulesProvider], + replaceConnectionBehavior: ["type", DataReplaceConnectionBehavior], + dataFlowPropertiesProvider: ["type", DataFlowPropertiesProvider], +}; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceMenuProvider.js b/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceMenuProvider.js index 7934fdf4..0df94351 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceMenuProvider.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceMenuProvider.js @@ -1,259 +1,315 @@ -import {is} from 'bpmn-js/lib/util/ModelUtil'; -import * as replaceOptions from './DataFlowReplaceOptions'; +import { is } from "bpmn-js/lib/util/ModelUtil"; +import * as replaceOptions from "./DataFlowReplaceOptions"; import { - createMenuEntries, - createMenuEntry, - createMoreOptionsEntryWithReturn + createMenuEntries, + createMenuEntry, + createMoreOptionsEntryWithReturn, } from "../../../editor/util/PopupMenuUtilities"; -import * as consts from '../Constants'; -import {createConfigurationsEntries} from '../../../editor/configurations/ConfigurationsUtil'; -import {getTransformationTaskConfigurations} from '../transf-task-configs/TransformationTaskConfigurations'; -import {replaceConnection} from '../../../editor/util/ModellingUtilities'; -import { filter } from 'min-dash'; -import { isDifferentType } from 'bpmn-js/lib/features/popup-menu/util/TypeUtil'; +import * as consts from "../Constants"; +import { createConfigurationsEntries } from "../../../editor/configurations/ConfigurationsUtil"; +import { getTransformationTaskConfigurations } from "../transf-task-configs/TransformationTaskConfigurations"; +import { replaceConnection } from "../../../editor/util/ModellingUtilities"; +import { filter } from "min-dash"; +import { isDifferentType } from "bpmn-js/lib/features/popup-menu/util/TypeUtil"; /** * Menu Provider for bpmn-replace which is opened for a diagram element. Adds replacement entries to replace the element with the * data flow extension elements. */ export default class DataFlowReplaceMenuProvider { - constructor(popupMenu, translate, bpmnReplace, modeling, bpmnFactory, moddle, elementRegistry, commandStack) { - popupMenu.registerProvider("bpmn-replace", this); + constructor( + popupMenu, + translate, + bpmnReplace, + modeling, + bpmnFactory, + moddle, + elementRegistry, + commandStack + ) { + popupMenu.registerProvider("bpmn-replace", this); - this.replaceElement = bpmnReplace.replaceElement; - this.translate = translate; - this.modeling = modeling; - this.bpmnFactory = bpmnFactory; - this.moddle = moddle; - this.elementRegistry = elementRegistry; - this.commandStack = commandStack; - this.popupMenu = popupMenu; - } - - /** - * Define header entries for the data flow elements - * - * @param element - * @returns {(function(*): ({}))|*} - */ - getPopupMenuHeaderEntries(element) { - return function (entries) { - - // remove all header entries (it is only the collection marker) for DataMapObjects because they do not support them - if (is(element, consts.DATA_MAP_OBJECT)) { - return {}; - } - return entries; - }; - } - - /** - * Overwrites the default menu provider to add menu entries to replace the element with DataFlow extension elements - * - * @param element the element for which the replacement entries are requested - * @returns {*} an array with menu entries - */ - getPopupMenuEntries(element) { - const self = this; - return function (entries) { - - // do not show entries for extension elements of other plugins - if (!(element.type.startsWith('bpmn') || element.type.startsWith('dataflow'))) { - return entries; - } + this.replaceElement = bpmnReplace.replaceElement; + this.translate = translate; + this.modeling = modeling; + this.bpmnFactory = bpmnFactory; + this.moddle = moddle; + this.elementRegistry = elementRegistry; + this.commandStack = commandStack; + this.popupMenu = popupMenu; + } - // set entries for the transformation task configurations as replacement for a DataFlow transformation task - if (is(element, consts.TRANSFORMATION_TASK)) { - let configEntries = {}; - const dataConfigurations = createConfigurationsEntries( - element, - 'dataflow-transformation-task-icon', - getTransformationTaskConfigurations(), - self.bpmnFactory, - self.modeling, - self.commandStack, - self.replaceElement - ); + /** + * Define header entries for the data flow elements + * + * @param element + * @returns {(function(*): ({}))|*} + */ + getPopupMenuHeaderEntries(element) { + return function (entries) { + // remove all header entries (it is only the collection marker) for DataMapObjects because they do not support them + if (is(element, consts.DATA_MAP_OBJECT)) { + return {}; + } + return entries; + }; + } - if (element.businessObject.name) { - configEntries = createMenuEntries(element, replaceOptions.TASK, self.translate, self.replaceElement); - return Object.assign(configEntries, dataConfigurations); - } - return Object.assign(dataConfigurations, entries); - } + /** + * Overwrites the default menu provider to add menu entries to replace the element with DataFlow extension elements + * + * @param element the element for which the replacement entries are requested + * @returns {*} an array with menu entries + */ + getPopupMenuEntries(element) { + const self = this; + return function (entries) { + // do not show entries for extension elements of other plugins + if ( + !( + element.type.startsWith("bpmn") || element.type.startsWith("dataflow") + ) + ) { + return entries; + } - // add MoreOptionsEntry for transformation task as replacement for BPMN task types - if (is(element, 'bpmn:Task')) { - const taskEntries = self.createTransformationTasksEntries(element); - return Object.assign(taskEntries, entries); - } - - // add entries for data map objects as replacement for BPMN data objects - if (is(element, 'bpmn:DataObjectReference')) { - let filteredOptions = filter(replaceOptions.DATA_OBJECT, isDifferentType(element)); - const dataEntries = createMenuEntries(element, filteredOptions, self.translate, self.replaceElement); - return Object.assign(dataEntries, entries); - } - - // add entries for data store maps as replacement for BPMN data stores - if (is(element, 'bpmn:DataStoreReference')) { - let filteredOptions = filter(replaceOptions.DATA_STORE, isDifferentType(element)); - const storeEntries = createMenuEntries(element, filteredOptions, self.translate, self.replaceElement); - return Object.assign(storeEntries, entries); - } + // set entries for the transformation task configurations as replacement for a DataFlow transformation task + if (is(element, consts.TRANSFORMATION_TASK)) { + let configEntries = {}; + const dataConfigurations = createConfigurationsEntries( + element, + "dataflow-transformation-task-icon", + getTransformationTaskConfigurations(), + self.bpmnFactory, + self.modeling, + self.commandStack, + self.replaceElement + ); - // set entry for transformation association as replacement for BPMN data association - if (is(element, 'bpmn:DataAssociation') && !is(element, consts.TRANSFORMATION_ASSOCIATION)) { - const associationEntry = self.createTransformationAssociationEntry(element); - if (associationEntry) { - return associationEntry; - } - } + if (element.businessObject.name) { + configEntries = createMenuEntries( + element, + replaceOptions.TASK, + self.translate, + self.replaceElement + ); + return Object.assign(configEntries, dataConfigurations); + } + return Object.assign(dataConfigurations, entries); + } - // add entry for data association as replacement for DataFlow transformation association - if (is(element, consts.TRANSFORMATION_ASSOCIATION)) { - const dataAssociationEntry = self.createDataAssociationEntry(element); - if (dataAssociationEntry) { - return Object.assign(dataAssociationEntry, entries); - } - } - return entries; - }; - } + // add MoreOptionsEntry for transformation task as replacement for BPMN task types + if (is(element, "bpmn:Task")) { + const taskEntries = self.createTransformationTasksEntries(element); + return Object.assign(taskEntries, entries); + } - /** - * Create MoreOptionEntries with replacement entries for a transformation task, including the loaded configurations - * for transformation tasks. - * - * @param element The element the replacement entries are requested for - * @returns {{'replace-by-more-transf-task-options': {label: string, className: string, action: Function}}} - */ - createTransformationTasksEntries(element) { - const popupMenu = this.popupMenu; - const translate = this.translate; - const replaceElement = this.replaceElement; - const bpmnFactory = this.bpmnFactory; - const modeling = this.modeling; - const commandStack = this.commandStack; + // add entries for data map objects as replacement for BPMN data objects + if (is(element, "bpmn:DataObjectReference")) { + let filteredOptions = filter( + replaceOptions.DATA_OBJECT, + isDifferentType(element) + ); + const dataEntries = createMenuEntries( + element, + filteredOptions, + self.translate, + self.replaceElement + ); + return Object.assign(dataEntries, entries); + } - // create replacement entries for each loaded transformation task configuration - let options = createConfigurationsEntries( - element, - 'dataflow-transformation-task-icon', - getTransformationTaskConfigurations(), - bpmnFactory, - modeling, - commandStack, - replaceElement + // add entries for data store maps as replacement for BPMN data stores + if (is(element, "bpmn:DataStoreReference")) { + let filteredOptions = filter( + replaceOptions.DATA_STORE, + isDifferentType(element) ); - let filteredOptions = filter(replaceOptions.TASK, isDifferentType(element)); - options = Object.assign(createMenuEntries(element, filteredOptions, translate, replaceElement), options); + const storeEntries = createMenuEntries( + element, + filteredOptions, + self.translate, + self.replaceElement + ); + return Object.assign(storeEntries, entries); + } - return { - ['replace-by-more-transf-task-options']: createMoreOptionsEntryWithReturn( - element, - 'Transformation Tasks', - 'Transformation Tasks', - popupMenu, - options, - 'dataflow-transformation-task-icon' - ) - }; - } + // set entry for transformation association as replacement for BPMN data association + if ( + is(element, "bpmn:DataAssociation") && + !is(element, consts.TRANSFORMATION_ASSOCIATION) + ) { + const associationEntry = + self.createTransformationAssociationEntry(element); + if (associationEntry) { + return associationEntry; + } + } - /** - * Create replacement entry to replace a data association with a DataFlow transformation association if the data - * association connects a DataMapObject with an activity. - * - * @param element The element the replacement entries are requested for - * @returns {{}} The created replacement entry - */ - createTransformationAssociationEntry(element) { + // add entry for data association as replacement for DataFlow transformation association + if (is(element, consts.TRANSFORMATION_ASSOCIATION)) { + const dataAssociationEntry = self.createDataAssociationEntry(element); + if (dataAssociationEntry) { + return Object.assign(dataAssociationEntry, entries); + } + } + return entries; + }; + } - const modeling = this.modeling; - const translate = this.translate; - const replaceElement = this.replaceElement; + /** + * Create MoreOptionEntries with replacement entries for a transformation task, including the loaded configurations + * for transformation tasks. + * + * @param element The element the replacement entries are requested for + * @returns {{'replace-by-more-transf-task-options': {label: string, className: string, action: Function}}} + */ + createTransformationTasksEntries(element) { + const popupMenu = this.popupMenu; + const translate = this.translate; + const replaceElement = this.replaceElement; + const bpmnFactory = this.bpmnFactory; + const modeling = this.modeling; + const commandStack = this.commandStack; - const entryId = 'replace-with-transformation-flow'; + // create replacement entries for each loaded transformation task configuration + let options = createConfigurationsEntries( + element, + "dataflow-transformation-task-icon", + getTransformationTaskConfigurations(), + bpmnFactory, + modeling, + commandStack, + replaceElement + ); + let filteredOptions = filter(replaceOptions.TASK, isDifferentType(element)); + options = Object.assign( + createMenuEntries(element, filteredOptions, translate, replaceElement), + options + ); - // if DataObjectMap -- TransformationAssociation -> Activity - if (is(element.source, consts.DATA_MAP_OBJECT) && - (is(element.target, 'bpmn:Activity') && !is(element.target, consts.DATA_MAP_OBJECT))) { + return { + ["replace-by-more-transf-task-options"]: createMoreOptionsEntryWithReturn( + element, + "Transformation Tasks", + "Transformation Tasks", + popupMenu, + options, + "dataflow-transformation-task-icon" + ), + }; + } - // create definition for menu entry to replace with a transformation association - const definition = { - label: 'Transformation Association', - id: entryId, - className: 'dataflow-transformation-association-icon', - }; + /** + * Create replacement entry to replace a data association with a DataFlow transformation association if the data + * association connects a DataMapObject with an activity. + * + * @param element The element the replacement entries are requested for + * @returns {{}} The created replacement entry + */ + createTransformationAssociationEntry(element) { + const modeling = this.modeling; + const translate = this.translate; + const replaceElement = this.replaceElement; - // define action to replace with a transformation association - const action = function () { - let associationType = consts.OUTPUT_TRANSFORMATION_ASSOCIATION; + const entryId = "replace-with-transformation-flow"; - // replace with an input or output transformation association depending on the type of the data association - if (is(element, 'bpmn:DataInputAssociation')) { - associationType = consts.INPUT_TRANSFORMATION_ASSOCIATION; - } - replaceConnection(element, associationType, modeling); - }; + // if DataObjectMap -- TransformationAssociation -> Activity + if ( + is(element.source, consts.DATA_MAP_OBJECT) && + is(element.target, "bpmn:Activity") && + !is(element.target, consts.DATA_MAP_OBJECT) + ) { + // create definition for menu entry to replace with a transformation association + const definition = { + label: "Transformation Association", + id: entryId, + className: "dataflow-transformation-association-icon", + }; - // create menu entry - return { - [entryId]: createMenuEntry(element, definition, translate, replaceElement, action), - }; - } - } + // define action to replace with a transformation association + const action = function () { + let associationType = consts.OUTPUT_TRANSFORMATION_ASSOCIATION; - /** - * Create replacement entry to replace a DataFlow transformation association with a data association if the - * transformation association does NOT connect two DataMapObjects. - * - * @param element The element the replacement entries are requested for - * @returns {{}} The created replacement entry - */ - createDataAssociationEntry(element) { - const modeling = this.modeling; - const translate = this.translate; - const replaceElement = this.replaceElement; + // replace with an input or output transformation association depending on the type of the data association + if (is(element, "bpmn:DataInputAssociation")) { + associationType = consts.INPUT_TRANSFORMATION_ASSOCIATION; + } + replaceConnection(element, associationType, modeling); + }; - // create entry if transformation association does NOT connect two data map objects - if (!(is(element.source, consts.DATA_MAP_OBJECT) && is(element.target, consts.DATA_MAP_OBJECT))) { + // create menu entry + return { + [entryId]: createMenuEntry( + element, + definition, + translate, + replaceElement, + action + ), + }; + } + } - // create definition of menu entry - const entryId = 'replace-with-data-association'; - const definition = { - label: 'Data Association', - id: entryId, - className: 'dataflow-data-association-icon', - }; + /** + * Create replacement entry to replace a DataFlow transformation association with a data association if the + * transformation association does NOT connect two DataMapObjects. + * + * @param element The element the replacement entries are requested for + * @returns {{}} The created replacement entry + */ + createDataAssociationEntry(element) { + const modeling = this.modeling; + const translate = this.translate; + const replaceElement = this.replaceElement; - // create action to replace the transformation association by a data association - const action = function () { - let associationType = 'bpmn:DataOutputAssociation'; + // create entry if transformation association does NOT connect two data map objects + if ( + !( + is(element.source, consts.DATA_MAP_OBJECT) && + is(element.target, consts.DATA_MAP_OBJECT) + ) + ) { + // create definition of menu entry + const entryId = "replace-with-data-association"; + const definition = { + label: "Data Association", + id: entryId, + className: "dataflow-data-association-icon", + }; - // replace with an input or output data association depending on the type of the transformation association - if (is(element, consts.INPUT_TRANSFORMATION_ASSOCIATION)) { - associationType = 'bpmn:DataInputAssociation'; - } - replaceConnection(element, associationType, modeling); - }; + // create action to replace the transformation association by a data association + const action = function () { + let associationType = "bpmn:DataOutputAssociation"; - // create menu entry - return { - [entryId]: createMenuEntry(element, definition, translate, replaceElement, action), - }; + // replace with an input or output data association depending on the type of the transformation association + if (is(element, consts.INPUT_TRANSFORMATION_ASSOCIATION)) { + associationType = "bpmn:DataInputAssociation"; } + replaceConnection(element, associationType, modeling); + }; + + // create menu entry + return { + [entryId]: createMenuEntry( + element, + definition, + translate, + replaceElement, + action + ), + }; } + } } DataFlowReplaceMenuProvider.$inject = [ - 'popupMenu', - 'translate', - 'bpmnReplace', - 'modeling', - 'bpmnFactory', - 'moddle', - 'elementRegistry', - 'commandStack', + "popupMenu", + "translate", + "bpmnReplace", + "modeling", + "bpmnFactory", + "moddle", + "elementRegistry", + "commandStack", ]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceOptions.js b/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceOptions.js index 8e966d7c..47451ead 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceOptions.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/menu/DataFlowReplaceOptions.js @@ -1,37 +1,37 @@ -import * as consts from '../Constants'; +import * as consts from "../Constants"; // replace options for a BPMN task export const TASK = [ - { - id: 'dataflow-transformation-task', - label: 'Data Transformation Task', - className: 'dataflow-transformation-task-icon', - target: { - type: consts.TRANSFORMATION_TASK - } + { + id: "dataflow-transformation-task", + label: "Data Transformation Task", + className: "dataflow-transformation-task-icon", + target: { + type: consts.TRANSFORMATION_TASK, }, + }, ]; // replace options for a BPMN data store export const DATA_STORE = [ - { - id: 'dataflow-data-store-map', - label: 'Data Store Map', - className: 'dataflow-data-store-map-icon', - target: { - type: consts.DATA_STORE_MAP - } + { + id: "dataflow-data-store-map", + label: "Data Store Map", + className: "dataflow-data-store-map-icon", + target: { + type: consts.DATA_STORE_MAP, }, + }, ]; // replace options for BPMN data object export const DATA_OBJECT = [ - { - id: 'dataflow-data-map-object', - label: 'Data Map Object', - className: 'dataflow-data-map-object-icon', - target: { - type: consts.DATA_MAP_OBJECT - } + { + id: "dataflow-data-map-object", + label: "Data Map Object", + className: "dataflow-data-map-object-icon", + target: { + type: consts.DATA_MAP_OBJECT, }, -]; \ No newline at end of file + }, +]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/palette/DataFlowPaletteProvider.js b/components/bpmn-q/modeler-component/extensions/data-extension/palette/DataFlowPaletteProvider.js index 64e22b3d..eae924e6 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/palette/DataFlowPaletteProvider.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/palette/DataFlowPaletteProvider.js @@ -1,106 +1,104 @@ -import * as consts from '../Constants'; +import * as consts from "../Constants"; /** * Custom palette provider to add entries for creating the modelling elements of the DataFlow extension to the * palette of the bpmn-js modeler. */ export default class DataFlowPaletteProvider { + constructor(bpmnFactory, create, elementFactory, palette, translate) { + this.bpmnFactory = bpmnFactory; + this.create = create; + this.elementFactory = elementFactory; + this.translate = translate; - constructor(bpmnFactory, create, elementFactory, palette, translate) { + palette.registerProvider(this); + } - this.bpmnFactory = bpmnFactory; - this.create = create; - this.elementFactory = elementFactory; - this.translate = translate; + /** + * Returns the palette entries for the DataFlow elements + */ + getPaletteEntries() { + return this.createDataFlowEntries(); + } - palette.registerProvider(this); - } + /** + * Returns palette entries to create DataMapObjects, DataStoreMaps and TransformationTasks + */ + createDataFlowEntries() { + const { bpmnFactory, create, elementFactory, translate } = this; - /** - * Returns the palette entries for the DataFlow elements - */ - getPaletteEntries() { - return this.createDataFlowEntries(); + // start creation of a DataMapObject + function createDataMapObject(event) { + const businessObject = bpmnFactory.create(consts.DATA_MAP_OBJECT); + let shape = elementFactory.createShape({ + type: consts.DATA_MAP_OBJECT, + businessObject: businessObject, + }); + create.start(event, shape); } - /** - * Returns palette entries to create DataMapObjects, DataStoreMaps and TransformationTasks - */ - createDataFlowEntries() { - const {bpmnFactory, create, elementFactory, translate} = this; - - // start creation of a DataMapObject - function createDataMapObject(event) { - const businessObject = bpmnFactory.create(consts.DATA_MAP_OBJECT); - let shape = elementFactory.createShape({ - type: consts.DATA_MAP_OBJECT, - businessObject: businessObject - }); - create.start(event, shape); - } - - // start creation of a DataStoreMap - function createDataStoreMap(event) { - const businessObject = bpmnFactory.create(consts.DATA_STORE_MAP); - let shape = elementFactory.createShape({ - type: consts.DATA_STORE_MAP, - businessObject: businessObject - }); - create.start(event, shape); - } - - // start creation of a TransformationTask - function createTransformationTask(event) { - const businessObject = bpmnFactory.create(consts.TRANSFORMATION_TASK); - let shape = elementFactory.createShape({ - type: consts.TRANSFORMATION_TASK, - businessObject: businessObject - }); - create.start(event, shape); - } + // start creation of a DataStoreMap + function createDataStoreMap(event) { + const businessObject = bpmnFactory.create(consts.DATA_STORE_MAP); + let shape = elementFactory.createShape({ + type: consts.DATA_STORE_MAP, + businessObject: businessObject, + }); + create.start(event, shape); + } - // create and return new palette entries - return { - // add separator line to delimit the new group - 'dataflow-separator': { - group: 'dataflowExt', - separator: true - }, - 'create.dataflow-data-map-object': { - group: 'dataflowExt', - className: 'dataflow-data-map-object-palette-icon', - title: translate('Creates a Data Map Object to model data items'), - action: { - click: createDataMapObject, - dragstart: createDataMapObject, - } - }, - 'create.dataflow-data-store-map': { - group: 'dataflowExt', - className: 'dataflow-data-store-map-task-palette-icon', - title: translate('Creates a Data Store Map to model data stores'), - action: { - click: createDataStoreMap, - dragstart: createDataStoreMap, - } - }, - 'create.data-flow-transformation-task': { - group: 'dataflowExt', - className: 'dataflow-transformation-task-palette-icon', - title: translate('Creates a task ot specify data transformations in'), - action: { - click: createTransformationTask, - dragstart: createTransformationTask, - } - }, - }; + // start creation of a TransformationTask + function createTransformationTask(event) { + const businessObject = bpmnFactory.create(consts.TRANSFORMATION_TASK); + let shape = elementFactory.createShape({ + type: consts.TRANSFORMATION_TASK, + businessObject: businessObject, + }); + create.start(event, shape); } + + // create and return new palette entries + return { + // add separator line to delimit the new group + "dataflow-separator": { + group: "dataflowExt", + separator: true, + }, + "create.dataflow-data-map-object": { + group: "dataflowExt", + className: "dataflow-data-map-object-palette-icon", + title: translate("Creates a Data Map Object to model data items"), + action: { + click: createDataMapObject, + dragstart: createDataMapObject, + }, + }, + "create.dataflow-data-store-map": { + group: "dataflowExt", + className: "dataflow-data-store-map-task-palette-icon", + title: translate("Creates a Data Store Map to model data stores"), + action: { + click: createDataStoreMap, + dragstart: createDataStoreMap, + }, + }, + "create.data-flow-transformation-task": { + group: "dataflowExt", + className: "dataflow-transformation-task-palette-icon", + title: translate("Creates a task ot specify data transformations in"), + action: { + click: createTransformationTask, + dragstart: createTransformationTask, + }, + }, + }; + } } DataFlowPaletteProvider.$inject = [ - 'bpmnFactory', - 'create', - 'elementFactory', - 'palette', - 'translate' + "bpmnFactory", + "create", + "elementFactory", + "palette", + "translate", ]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/DataFlowPropertiesProvider.js b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/DataFlowPropertiesProvider.js index 0d779baf..bd30798d 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/DataFlowPropertiesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/DataFlowPropertiesProvider.js @@ -1,10 +1,10 @@ -import keyValueMap from './KeyValueMap'; -import {is} from 'bpmn-js/lib/util/ModelUtil'; -import {ListGroup} from '@bpmn-io/properties-panel'; -import * as consts from '../Constants'; -import * as configConsts from '../../../editor/configurations/Constants'; -import ConfigurationsProperties from '../../../editor/configurations/ConfigurationsProperties'; -import {getTransformationTaskConfiguration} from '../transf-task-configs/TransformationTaskConfigurations'; +import keyValueMap from "./KeyValueMap"; +import { is } from "bpmn-js/lib/util/ModelUtil"; +import { ListGroup } from "@bpmn-io/properties-panel"; +import * as consts from "../Constants"; +import * as configConsts from "../../../editor/configurations/Constants"; +import ConfigurationsProperties from "../../../editor/configurations/ConfigurationsProperties"; +import { getTransformationTaskConfiguration } from "../transf-task-configs/TransformationTaskConfigurations"; const LOW_PRIORITY = 500; @@ -16,64 +16,82 @@ const LOW_PRIORITY = 500; * @param {Function} translate The translate function of the bpmn-js modeler * @param injector Injector module of the bpmn-js modeler used to load the required dependencies. */ -export default function DataFlowPropertiesProvider(propertiesPanel, translate, injector) { - +export default function DataFlowPropertiesProvider( + propertiesPanel, + translate, + injector +) { + /** + * Return the property groups provided for the given element. + * + * @param element The given element + * + * @return groups middleware + */ + this.getGroups = function (element) { /** - * Return the property groups provided for the given element. + * Return a middleware that adds groups for the properties of the DataFlow elements * - * @param element The given element + * @param {Object[]} groups The default groups for the element * - * @return groups middleware + * @return {Object[]} modified groups */ - this.getGroups = function (element) { - - /** - * Return a middleware that adds groups for the properties of the DataFlow elements - * - * @param {Object[]} groups The default groups for the element - * - * @return {Object[]} modified groups - */ - return function (groups) { - - // add group for displaying the content attribute of a DataMapObject as a key value map - if (is(element, consts.DATA_MAP_OBJECT)) { - groups.push(createDataMapObjectGroup(element, injector, translate)); - } - - // add group for displaying the details attribute of a DataStoreMap as a key value map - if (is(element, consts.DATA_STORE_MAP)) { - groups.push(createDataStoreMapGroup(element, injector, translate)); - } - - // add group for displaying the properties of transformation task and its configurations - if (is(element, consts.TRANSFORMATION_TASK)) { - - // load applied configuration - const selectedConfiguration = getTransformationTaskConfiguration(element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID)); - if (selectedConfiguration) { - - // add properties group for properties defined by the configuration - groups.splice(1, 0, createTransformationTaskConfigurationsGroup(element, injector, translate, selectedConfiguration)); - } - - // add entries for the parameters attribute of a transformation task - groups.push(createTransformationTaskGroup(element, injector, translate)); - } - - // add group for displaying the expressions attribute fo the transformation association - if (is(element, consts.TRANSFORMATION_ASSOCIATION)) { - groups.push(createTransformationAssociationGroup(element, injector, translate)); - } - - return groups; - }; + return function (groups) { + // add group for displaying the content attribute of a DataMapObject as a key value map + if (is(element, consts.DATA_MAP_OBJECT)) { + groups.push(createDataMapObjectGroup(element, injector, translate)); + } + + // add group for displaying the details attribute of a DataStoreMap as a key value map + if (is(element, consts.DATA_STORE_MAP)) { + groups.push(createDataStoreMapGroup(element, injector, translate)); + } + + // add group for displaying the properties of transformation task and its configurations + if (is(element, consts.TRANSFORMATION_TASK)) { + // load applied configuration + const selectedConfiguration = getTransformationTaskConfiguration( + element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID) + ); + if (selectedConfiguration) { + // add properties group for properties defined by the configuration + groups.splice( + 1, + 0, + createTransformationTaskConfigurationsGroup( + element, + injector, + translate, + selectedConfiguration + ) + ); + } + + // add entries for the parameters attribute of a transformation task + groups.push( + createTransformationTaskGroup(element, injector, translate) + ); + } + + // add group for displaying the expressions attribute fo the transformation association + if (is(element, consts.TRANSFORMATION_ASSOCIATION)) { + groups.push( + createTransformationAssociationGroup(element, injector, translate) + ); + } + + return groups; }; + }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); + propertiesPanel.registerProvider(LOW_PRIORITY, this); } -DataFlowPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'injector']; +DataFlowPropertiesProvider.$inject = [ + "propertiesPanel", + "translate", + "injector", +]; /** * Creates a properties group for displaying the custom properties of a DataFlow data map object. This group contains @@ -85,13 +103,13 @@ DataFlowPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'injector' * @returns {{add: function(*): void, component: ((function(import('../PropertiesPanel').ListGroupDefinition): preact.VNode)|*), id: string, label, items: *}} */ function createDataMapObjectGroup(element, injector, translate) { - const attributeName = consts.CONTENT; - return { - id: 'dataMapObjectProperties', - label: translate('Content'), - component: ListGroup, - ...keyValueMap({element, injector, attributeName}) - }; + const attributeName = consts.CONTENT; + return { + id: "dataMapObjectProperties", + label: translate("Content"), + component: ListGroup, + ...keyValueMap({ element, injector, attributeName }), + }; } /** @@ -104,13 +122,13 @@ function createDataMapObjectGroup(element, injector, translate) { * @returns {{add: function(*): void, component: ((function(import('../PropertiesPanel').ListGroupDefinition): preact.VNode)|*), id: string, label, items: *}} */ function createDataStoreMapGroup(element, injector, translate) { - const attributeName = consts.DETAILS; - return { - id: 'dataStoreMapProperties', - label: translate('Details'), - component: ListGroup, - ...keyValueMap({element, injector, attributeName}) - }; + const attributeName = consts.DETAILS; + return { + id: "dataStoreMapProperties", + label: translate("Details"), + component: ListGroup, + ...keyValueMap({ element, injector, attributeName }), + }; } /** @@ -123,13 +141,13 @@ function createDataStoreMapGroup(element, injector, translate) { * @returns {{add: function(*): void, component: ((function(import('../PropertiesPanel').ListGroupDefinition): preact.VNode)|*), id: string, label, items: *}} */ function createTransformationTaskGroup(element, injector, translate) { - const attributeName = consts.PARAMETERS; - return { - id: 'transformationTaskProperties', - label: translate('Parameters'), - component: ListGroup, - ...keyValueMap({element, injector, attributeName}) - }; + const attributeName = consts.PARAMETERS; + return { + id: "transformationTaskProperties", + label: translate("Parameters"), + component: ListGroup, + ...keyValueMap({ element, injector, attributeName }), + }; } /** @@ -142,13 +160,13 @@ function createTransformationTaskGroup(element, injector, translate) { * @returns {{add: function(*): void, component: ((function(import('../PropertiesPanel').ListGroupDefinition): preact.VNode)|*), id: string, label, items: *}} */ function createTransformationAssociationGroup(element, injector, translate) { - const attributeName = consts.EXPRESSIONS; - return { - id: 'transformationAssociationProperties', - label: translate('Expressions'), - component: ListGroup, - ...keyValueMap({element, injector, attributeName}) - }; + const attributeName = consts.EXPRESSIONS; + return { + id: "transformationAssociationProperties", + label: translate("Expressions"), + component: ListGroup, + ...keyValueMap({ element, injector, attributeName }), + }; } /** @@ -160,11 +178,20 @@ function createTransformationAssociationGroup(element, injector, translate) { * @param configuration The given configuration applied to the element * @returns {{entries: (*), id: string, label}} The created properties group. */ -function createTransformationTaskConfigurationsGroup(element, injector, translate, configuration) { - - return { - id: 'serviceTaskConfigurationsGroupProperties', - label: translate(configuration.groupLabel || 'Configurations Properties'), - entries: ConfigurationsProperties(element, injector, translate, configuration) - }; -} \ No newline at end of file +function createTransformationTaskConfigurationsGroup( + element, + injector, + translate, + configuration +) { + return { + id: "serviceTaskConfigurationsGroupProperties", + label: translate(configuration.groupLabel || "Configurations Properties"), + entries: ConfigurationsProperties( + element, + injector, + translate, + configuration + ), + }; +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueEntry.js b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueEntry.js index 96a37267..fe4712a9 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueEntry.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueEntry.js @@ -1,109 +1,97 @@ import React from "@bpmn-io/properties-panel/preact/compat"; -import {TextFieldEntry} from '@bpmn-io/properties-panel'; +import { TextFieldEntry } from "@bpmn-io/properties-panel"; -import {useService} from 'bpmn-js-properties-panel'; +import { useService } from "bpmn-js-properties-panel"; /** * Preact component for an entry for the properties panel which displays a Name and a Value Entry. */ export default function KeyValueEntry(props) { + const { idPrefix, parameter } = props; - const { - idPrefix, - parameter - } = props; - - return [ - { - id: idPrefix + '-key', - component: Key, - idPrefix, - parameter - }, - { - id: idPrefix + '-value', - component: Value, - idPrefix, - parameter - }, - ]; + return [ + { + id: idPrefix + "-key", + component: Key, + idPrefix, + parameter, + }, + { + id: idPrefix + "-value", + component: Value, + idPrefix, + parameter, + }, + ]; } /** * Preact component consisting of a TextFieldEntry for the name property of the given parameter. */ function Key(props) { - const { - idPrefix, - element, - parameter - } = props; + const { idPrefix, element, parameter } = props; - const commandStack = useService('commandStack'); - const translate = useService('translate'); - const debounce = useService('debounceInput'); + const commandStack = useService("commandStack"); + const translate = useService("translate"); + const debounce = useService("debounceInput"); - // set name property of parameter to the new value - const setValue = (value) => { - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: parameter, - properties: { - name: value - } - }); - }; + // set name property of parameter to the new value + const setValue = (value) => { + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: parameter, + properties: { + name: value, + }, + }); + }; - const getValue = (parameter) => { - return parameter.name; - }; + const getValue = (parameter) => { + return parameter.name; + }; - return TextFieldEntry({ - element: parameter, - id: idPrefix + '-name', - label: translate('Name'), - getValue, - setValue, - debounce - }); + return TextFieldEntry({ + element: parameter, + id: idPrefix + "-name", + label: translate("Name"), + getValue, + setValue, + debounce, + }); } /** * Preact component consisting of a TextFieldEntry for the key property of the given parameter. */ function Value(props) { - const { - idPrefix, - element, - parameter - } = props; + const { idPrefix, element, parameter } = props; - const commandStack = useService('commandStack'); - const translate = useService('translate'); - const debounce = useService('debounceInput'); + const commandStack = useService("commandStack"); + const translate = useService("translate"); + const debounce = useService("debounceInput"); - // set value property of parameter to the new value - const setValue = (value) => { - // return parameter.value = value; - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: parameter, - properties: { - value: value - } - }); - }; + // set value property of parameter to the new value + const setValue = (value) => { + // return parameter.value = value; + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: parameter, + properties: { + value: value, + }, + }); + }; - const getValue = (parameter) => { - return parameter.value; - }; + const getValue = (parameter) => { + return parameter.value; + }; - return TextFieldEntry({ - element: parameter, - id: idPrefix + '-value', - label: translate('Value'), - getValue, - setValue, - debounce - }); -} \ No newline at end of file + return TextFieldEntry({ + element: parameter, + id: idPrefix + "-value", + label: translate("Value"), + getValue, + setValue, + debounce, + }); +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueMap.js b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueMap.js index 78155f6c..aa29b48e 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueMap.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/properties-panel/KeyValueMap.js @@ -1,10 +1,8 @@ -import { - getBusinessObject -} from 'bpmn-js/lib/util/ModelUtil'; -import KeyValueEntry from './KeyValueEntry'; -import {without} from 'min-dash'; -import * as consts from '../Constants'; -import {nextId} from '../../../editor/util/camunda-utils/ElementUtil'; +import { getBusinessObject } from "bpmn-js/lib/util/ModelUtil"; +import KeyValueEntry from "./KeyValueEntry"; +import { without } from "min-dash"; +import * as consts from "../Constants"; +import { nextId } from "../../../editor/util/camunda-utils/ElementUtil"; /** * Entry of the properties panel which displays a key value map. Entries can be added or removed over the UI of the @@ -17,36 +15,39 @@ import {nextId} from '../../../editor/util/camunda-utils/ElementUtil'; * @returns {{add: ((function(*): void)|*), items: {entries: [{component: function(*): preact.VNode, parameter: *, idPrefix: *, id: string},{component: function(*): preact.VNode, parameter: *, idPrefix: *, id: string}], autoFocusEntry: string, id: string, label, remove: function(*): void}[]}} * @constructor */ -export default function KeyValueMap({element, injector, attributeName}) { +export default function KeyValueMap({ element, injector, attributeName }) { + const bpmnFactory = injector.get("bpmnFactory"), + commandStack = injector.get("commandStack"); - const bpmnFactory = injector.get('bpmnFactory'), - commandStack = injector.get('commandStack'); + // load key value map property + const keyValueMap = element.businessObject.get(attributeName) || []; - // load key value map property - const keyValueMap = element.businessObject.get(attributeName) || []; - - // create a KeyValueEntry for each entry of keyValueMap - const keyValueEntires = keyValueMap.map((keyValueEntry, index) => { - - const id = element.id + '-parameter-' + index; - - return { - id, - label: keyValueEntry.get('name') || '', - entries: KeyValueEntry({ - idPrefix: id, - element, - parameter: keyValueEntry - }), - autoFocusEntry: id + '-name', - remove: removeFactory({commandStack, element, parameter: keyValueEntry, attributeName}) - }; - }); + // create a KeyValueEntry for each entry of keyValueMap + const keyValueEntires = keyValueMap.map((keyValueEntry, index) => { + const id = element.id + "-parameter-" + index; return { - items: keyValueEntires, - add: addFactory({element, bpmnFactory, commandStack, attributeName}) + id, + label: keyValueEntry.get("name") || "", + entries: KeyValueEntry({ + idPrefix: id, + element, + parameter: keyValueEntry, + }), + autoFocusEntry: id + "-name", + remove: removeFactory({ + commandStack, + element, + parameter: keyValueEntry, + attributeName, + }), }; + }); + + return { + items: keyValueEntires, + add: addFactory({ element, bpmnFactory, commandStack, attributeName }), + }; } /** @@ -59,23 +60,28 @@ export default function KeyValueMap({element, injector, attributeName}) { * @param attributeName The attributeName defining the property with the key value map in it. * @returns {(function(*): void)|*} */ -function removeFactory({commandStack, element, keyValueEntry, attributeName}) { - return function (event) { - event.stopPropagation(); +function removeFactory({ + commandStack, + element, + keyValueEntry, + attributeName, +}) { + return function (event) { + event.stopPropagation(); - // get key value map - let keyValueMap = element.businessObject.get(attributeName) || []; + // get key value map + let keyValueMap = element.businessObject.get(attributeName) || []; - // remove the given key value entry - keyValueMap = without(keyValueMap, keyValueEntry); + // remove the given key value entry + keyValueMap = without(keyValueMap, keyValueEntry); - // save updated key value map in the element - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: element.businessObject, - properties: {[attributeName]: keyValueMap}, - }); - }; + // save updated key value map in the element + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: element.businessObject, + properties: { [attributeName]: keyValueMap }, + }); + }; } /** @@ -88,21 +94,24 @@ function removeFactory({commandStack, element, keyValueEntry, attributeName}) { * @param attributeName The name of the property the key value map is saved in. * @returns {(function(*): void)|*} */ -function addFactory({element, bpmnFactory, commandStack, attributeName}) { - return function (event) { - event.stopPropagation(); +function addFactory({ element, bpmnFactory, commandStack, attributeName }) { + return function (event) { + event.stopPropagation(); - const businessObject = getBusinessObject(element); - const keyValueMap = businessObject.get(attributeName); + const businessObject = getBusinessObject(element); + const keyValueMap = businessObject.get(attributeName); - // create a new key value entry - const param = bpmnFactory.create(consts.KEY_VALUE_ENTRY, {name: nextId('Entry_'), value: ''}); + // create a new key value entry + const param = bpmnFactory.create(consts.KEY_VALUE_ENTRY, { + name: nextId("Entry_"), + value: "", + }); - // update key value map - commandStack.execute('element.updateModdleProperties', { - element, - moddleElement: businessObject, - properties: {[attributeName]: keyValueMap.concat(param)}, - }); - }; + // update key value map + commandStack.execute("element.updateModdleProperties", { + element, + moddleElement: businessObject, + properties: { [attributeName]: keyValueMap.concat(param) }, + }); + }; } diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowRenderer.js b/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowRenderer.js index 51ddb976..21bc117a 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowRenderer.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowRenderer.js @@ -1,133 +1,155 @@ import BpmnRenderer from "bpmn-js/lib/draw/BpmnRenderer"; -import * as consts from '../Constants'; -import {attr as svgAttr} from 'tiny-svg'; -import {drawDataElementSVG, drawTaskSVG} from "../../../editor/util/RenderUtilities"; -import {getSVG} from "./DataFlowSVGMap"; -import {extractConfigSVG} from '../../../editor/configurations/ConfigurationsUtil'; +import * as consts from "../Constants"; +import { attr as svgAttr } from "tiny-svg"; +import { + drawDataElementSVG, + drawTaskSVG, +} from "../../../editor/util/RenderUtilities"; +import { getSVG } from "./DataFlowSVGMap"; +import { extractConfigSVG } from "../../../editor/configurations/ConfigurationsUtil"; /** * Custom renderer for the DataFlow elements. Extends the BpmnRenderer of bpmn-js. */ export default class DataFlowRenderer extends BpmnRenderer { - - constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { - super(config, eventBus, styles, pathMap, canvas, textRenderer, 1001); - - // create handlers to render the data flow extension elements - this.dataFlowHandler = { - [consts.DATA_MAP_OBJECT]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:DataObject')(parentGfx, element); - - let svg = extractConfigSVG(element) || getSVG(consts.DATA_TYPE_DATA_MAP_OBJECT); - drawDataElementSVG(parentGfx, svg); - - return task; - }, - [consts.DATA_STORE_MAP]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:DataStoreReference')(parentGfx, element); - - let svg = extractConfigSVG(element) || getSVG(consts.DATA_TYPE_DATA_STORE_MAP); - drawDataElementSVG(parentGfx, svg); - - return task; - }, - [consts.TRANSFORMATION_TASK]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:Task')(parentGfx, element); - - let svg = extractConfigSVG(element) || getSVG(consts.TASK_TYPE_TRANSFORMATION_TASK); - drawTaskSVG(parentGfx, svg); - - return task; - }, - [consts.TRANSFORMATION_ASSOCIATION]: function (self, parentGfx, element) { - const flow = self.renderer('bpmn:DataOutputAssociation')(parentGfx, element); - - svgAttr(flow, { - strokeDasharray: '15, 10', //width, space of the stroke - strokeLinecap: 'square', - }); - - return flow; - }, - [consts.INPUT_TRANSFORMATION_ASSOCIATION]: function (self, parentGfx, element) { - const flow = self.renderer('bpmn:DataInputAssociation')(parentGfx, element); - - svgAttr(flow, { - strokeDasharray: '15, 10', //width, space of the stroke - strokeLinecap: 'square', - }); - - return flow; - }, - [consts.OUTPUT_TRANSFORMATION_ASSOCIATION]: function (self, parentGfx, element) { - const flow = self.renderer('bpmn:DataOutputAssociation')(parentGfx, element); - - svgAttr(flow, { - strokeDasharray: '15, 10', //width, space of the stroke - strokeLinecap: 'square', - }); - - return flow; - } - }; - } - - renderer(type) { - - return this.handlers[type]; + constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { + super(config, eventBus, styles, pathMap, canvas, textRenderer, 1001); + + // create handlers to render the data flow extension elements + this.dataFlowHandler = { + [consts.DATA_MAP_OBJECT]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:DataObject")(parentGfx, element); + + let svg = + extractConfigSVG(element) || getSVG(consts.DATA_TYPE_DATA_MAP_OBJECT); + drawDataElementSVG(parentGfx, svg); + + return task; + }, + [consts.DATA_STORE_MAP]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:DataStoreReference")( + parentGfx, + element + ); + + let svg = + extractConfigSVG(element) || getSVG(consts.DATA_TYPE_DATA_STORE_MAP); + drawDataElementSVG(parentGfx, svg); + + return task; + }, + [consts.TRANSFORMATION_TASK]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:Task")(parentGfx, element); + + let svg = + extractConfigSVG(element) || + getSVG(consts.TASK_TYPE_TRANSFORMATION_TASK); + drawTaskSVG(parentGfx, svg); + + return task; + }, + [consts.TRANSFORMATION_ASSOCIATION]: function (self, parentGfx, element) { + const flow = self.renderer("bpmn:DataOutputAssociation")( + parentGfx, + element + ); + + svgAttr(flow, { + strokeDasharray: "15, 10", //width, space of the stroke + strokeLinecap: "square", + }); + + return flow; + }, + [consts.INPUT_TRANSFORMATION_ASSOCIATION]: function ( + self, + parentGfx, + element + ) { + const flow = self.renderer("bpmn:DataInputAssociation")( + parentGfx, + element + ); + + svgAttr(flow, { + strokeDasharray: "15, 10", //width, space of the stroke + strokeLinecap: "square", + }); + + return flow; + }, + [consts.OUTPUT_TRANSFORMATION_ASSOCIATION]: function ( + self, + parentGfx, + element + ) { + const flow = self.renderer("bpmn:DataOutputAssociation")( + parentGfx, + element + ); + + svgAttr(flow, { + strokeDasharray: "15, 10", //width, space of the stroke + strokeLinecap: "square", + }); + + return flow; + }, + }; + } + + renderer(type) { + return this.handlers[type]; + } + + canRender(element) { + // only return true if handler for rendering is registered + return this.dataFlowHandler[element.type]; + } + + /** + * Draw new shape for the given element based on its type. + * + * @param parentNode Parent of the given element + * @param element The given element + * @returns {*} + */ + drawShape(parentNode, element) { + console.log("Draw Shape of type " + element.type); + + // handle DataFlow elements + if (element.type in this.dataFlowHandler) { + const h = this.dataFlowHandler[element.type]; + + /* jshint -W040 */ + return h(this, parentNode, element); } - - canRender(element) { - - // only return true if handler for rendering is registered - return this.dataFlowHandler[element.type]; + } + + /** + * Draw new connection for the given connection element. + * + * @param parentGfx Parent of the given element + * @param connectionElement The given connection element + * @returns {*} + */ + drawConnection(parentGfx, connectionElement) { + console.log("Draw Connection of type " + connectionElement.type); + + if (connectionElement.type in this.dataFlowHandler) { + let h = this.dataFlowHandler[connectionElement.type]; + return h(this, parentGfx, connectionElement); } - /** - * Draw new shape for the given element based on its type. - * - * @param parentNode Parent of the given element - * @param element The given element - * @returns {*} - */ - drawShape(parentNode, element) { - - console.log("Draw Shape of type " + element.type); - - // handle DataFlow elements - if (element.type in this.dataFlowHandler) { - const h = this.dataFlowHandler[element.type]; - - /* jshint -W040 */ - return h(this, parentNode, element); - } - } - - /** - * Draw new connection for the given connection element. - * - * @param parentGfx Parent of the given element - * @param connectionElement The given connection element - * @returns {*} - */ - drawConnection(parentGfx, connectionElement) { - - console.log("Draw Connection of type " + connectionElement.type); - - if (connectionElement.type in this.dataFlowHandler) { - let h = this.dataFlowHandler[connectionElement.type]; - return h(this, parentGfx, connectionElement); - } - - return super.drawConnection(parentGfx, connectionElement); - } + return super.drawConnection(parentGfx, connectionElement); + } } DataFlowRenderer.$inject = [ - 'config', - 'eventBus', - 'styles', - 'pathMap', - 'canvas', - 'textRenderer' -]; \ No newline at end of file + "config", + "eventBus", + "styles", + "pathMap", + "canvas", + "textRenderer", +]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowSVGMap.js b/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowSVGMap.js index 1564de86..9c907ba2 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowSVGMap.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/rendering/DataFlowSVGMap.js @@ -1,6 +1,7 @@ import * as consts from "../Constants"; -const DATA_FLOW_MAP_LOGO = ''; +const DATA_FLOW_MAP_LOGO = + ''; /** * Return SVG for the given ID @@ -9,21 +10,23 @@ const DATA_FLOW_MAP_LOGO = '', + }; + svgMap[consts.DATA_TYPE_DATA_MAP_OBJECT] = { + transform: "matrix(0.033, 0, 0, 0.033, 5, 5)", + svg: DATA_FLOW_MAP_LOGO, + }; + svgMap[consts.DATA_TYPE_DATA_STORE_MAP] = { + transform: "matrix(0.034, 0, 0, 0.034, 17, 29)", + svg: DATA_FLOW_MAP_LOGO, + }; - // to insert svgs easily just open them in your browser, copy the outer html and insert it using ctrl + alt + shift + v in intellij to avoid formatting,escaping etc. - // matrix( Scalingfactor, 0, 0, Scalingfactor, shift X, shift y) - // IMPORTANT: ensure that definition Ids for new SVGs are UNIQUE - // viewbox is not required - const svgMap = {}; - svgMap[consts.TASK_TYPE_TRANSFORMATION_TASK] = { - transform: 'matrix(0.17, 0, 0, 0.17, 7, 7)', - svg: '' - }; - svgMap[consts.DATA_TYPE_DATA_MAP_OBJECT] = {transform: 'matrix(0.033, 0, 0, 0.033, 5, 5)', svg: DATA_FLOW_MAP_LOGO}; - svgMap[consts.DATA_TYPE_DATA_STORE_MAP] = { - transform: 'matrix(0.034, 0, 0, 0.034, 17, 29)', - svg: DATA_FLOW_MAP_LOGO - }; - - return svgMap[svgId]; -} \ No newline at end of file + return svgMap[svgId]; +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-extension.json b/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-extension.json index c14c9614..cc98865e 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-extension.json +++ b/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-extension.json @@ -8,7 +8,7 @@ "types": [ { "name": "DataMapObject", - "superClass": [ "bpmn:DataObjectReference" ], + "superClass": ["bpmn:DataObjectReference"], "properties": [ { "name": "content", @@ -19,7 +19,7 @@ }, { "name": "DataStoreMap", - "superClass": [ "bpmn:DataStoreReference" ], + "superClass": ["bpmn:DataStoreReference"], "properties": [ { "name": "details", @@ -30,7 +30,7 @@ }, { "name": "TransformationTask", - "superClass": [ "bpmn:Task" ], + "superClass": ["bpmn:Task"], "properties": [ { "name": "parameters", @@ -41,7 +41,7 @@ }, { "name": "TransformationAssociation", - "superClass": [ "bpmn:Association" ], + "superClass": ["bpmn:Association"], "properties": [ { "name": "expressions", @@ -52,12 +52,12 @@ }, { "name": "InputTransformationAssociation", - "superClass": [ "TransformationAssociation" ], + "superClass": ["TransformationAssociation"], "properties": [] }, { "name": "OutputTransformationAssociation", - "superClass": [ "TransformationAssociation" ], + "superClass": ["TransformationAssociation"], "properties": [] }, { @@ -78,4 +78,4 @@ ], "enumerations": [], "associations": [] -} \ No newline at end of file +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-styles.css b/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-styles.css index ccc202a9..86ff1077 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-styles.css +++ b/components/bpmn-q/modeler-component/extensions/data-extension/resources/data-flow-styles.css @@ -1,107 +1,107 @@ .dataflow-transformation-task-icon:before { - content: ""; - width: 20px; - height: 15px; - background-size: contain; - background-image: url("../resources/icons/change-exchange-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 15px; + background-size: contain; + background-image: url("../resources/icons/change-exchange-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-data-store-map-icon:before { - content: ""; - width: 20px; - height: 15px; - background-size: contain; - background-image: url("../resources/icons/data-store-map-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 15px; + background-size: contain; + background-image: url("../resources/icons/data-store-map-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-data-map-object-icon:before { - content: ""; - width: 20px; - height: 15px; - background-size: contain; - background-image: url("../resources/icons/data-map-object-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 15px; + background-size: contain; + background-image: url("../resources/icons/data-map-object-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-transformation-task-palette-icon { - content: url("../resources/icons/transformation-task.svg"); - padding: 11px 8px 11px 10px; - box-sizing: border-box; - margin: 0; - outline: none; + content: url("../resources/icons/transformation-task.svg"); + padding: 11px 8px 11px 10px; + box-sizing: border-box; + margin: 0; + outline: none; } .dataflow-data-store-map-task-palette-icon { - content: url("../resources/icons/data-store-map-icon.svg"); - padding: 10px; - box-sizing: border-box; - margin: 0; - outline: none; + content: url("../resources/icons/data-store-map-icon.svg"); + padding: 10px; + box-sizing: border-box; + margin: 0; + outline: none; } .dataflow-data-map-object-palette-icon { - content: url("../resources/icons/data-map-object-icon.svg"); - padding: 10px 13px 10px 14px; - box-sizing: border-box; - margin: 0; - outline: none; + content: url("../resources/icons/data-map-object-icon.svg"); + padding: 10px 13px 10px 14px; + box-sizing: border-box; + margin: 0; + outline: none; } .dataflow-transformation-association-icon:before { - content: ""; - width: 20px; - height: 20px; - background-size: contain; - background-image: url("../resources/icons/transformation-association-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 20px; + background-size: contain; + background-image: url("../resources/icons/transformation-association-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-data-association-icon:before { - content: ""; - width: 20px; - height: 20px; - background-size: contain; - background-image: url("../resources/icons/data-association.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 20px; + background-size: contain; + background-image: url("../resources/icons/data-association.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-plugin-icon:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("./icons/structure-diagram-icon-or.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("./icons/structure-diagram-icon-or.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .dataflow-update-transformation-task-configs:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("./icons/update-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; -} \ No newline at end of file + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("./icons/update-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js index 65205bb1..9a85918f 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataFlowRulesProvider.js @@ -1,215 +1,247 @@ -import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules'; -import { - is, - isAny -} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; -import * as consts from '../Constants'; -import { isConnectedWith } from '../../../editor/util/ModellingUtilities'; +import BpmnRules from "bpmn-js/lib/features/rules/BpmnRules"; +import { is, isAny } from "bpmn-js/lib/features/modeling/util/ModelingUtil"; +import * as consts from "../Constants"; +import { isConnectedWith } from "../../../editor/util/ModellingUtilities"; /** * Custom rules provider for the DataFlow elements. Extends the BpmnRules. */ export default class CustomRulesProvider extends BpmnRules { + constructor(eventBus) { + super(eventBus); - constructor(eventBus) { - super(eventBus); - - const canConnectDataExtension = this.canConnectDataExtension; - const canConnect = this.canConnect.bind(this); - const canCreate = this.canCreate.bind(this); - - // persist into local storage whenever - // copy took place - eventBus.on('copyPaste.elementsCopied', event => { - const { tree } = event; - - // persist in local storage, encoded as json - localStorage.setItem('bpmnClipboard', JSON.stringify(tree)); - }); - - /** - * Fired during creation of a new connection (while you selected the target of a connection) - */ - this.addRule('connection.create', 200000, function (context) { - - const source = context.source, - target = context.target; - - return canConnect(source, target); - }); - - /** - * Fired when a connection between two elements is drawn again, e.g. after dragging an element - */ - this.addRule('connection.reconnect', 200000000000, function (context) { - - const source = context.source, - target = context.target; - - let canConnectData = canConnectDataExtension(source, target); - - if (canConnectData || canConnectData === false) { - return canConnectData; - } - }); - - /** - * Fired when a new shape for an element is created - */ - this.addRule('shape.create', 200000000000, function (context) { - - return canCreate( - context.shape, - context.target, - context.source, - context.position - ); - }); - } - - /** - * Returns the type of the connection if the given source and target elements can be connected by the given - * connection element, False else. - * - * @param source The given source element - * @param target The given target element - * @param connection The given connection element - */ - canConnect(source, target, connection) { - console.log('##### can connect'); + const canConnectDataExtension = this.canConnectDataExtension; + const canConnect = this.canConnect.bind(this); + const canCreate = this.canCreate.bind(this); - // test connection via transformation association if source or target are DataMapObjects - if (is(source, consts.DATA_MAP_OBJECT) || is(target, consts.DATA_MAP_OBJECT)) { - return this.canConnectDataExtension(source, target); - } + // persist into local storage whenever + // copy took place + eventBus.on("copyPaste.elementsCopied", (event) => { + const { tree } = event; - if (!is(connection, 'bpmn:DataAssociation')) { + // persist in local storage, encoded as json + localStorage.setItem("bpmnClipboard", JSON.stringify(tree)); + }); - // test connection via sequence flow - if (this.canConnectSequenceFlow(source, target)) { - return { type: 'bpmn:SequenceFlow' }; - } - } + /** + * Fired during creation of a new connection (while you selected the target of a connection) + */ + this.addRule("connection.create", 200000, function (context) { + const source = context.source, + target = context.target; - // test connection via super.canConnect - return super.canConnect(source, target, connection); - } + return canConnect(source, target); + }); /** - * Returns True if the given source and target element can be connected via a sequence flow, False else - * - * @param source The given source element - * @param target The given target element + * Fired when a connection between two elements is drawn again, e.g. after dragging an element */ - canConnectSequenceFlow(source, target) { - console.log('##### canConnectSequenceFlow'); + this.addRule("connection.reconnect", 200000000000, function (context) { + const source = context.source, + target = context.target; - // do not allow sequence flow connections with DataMapObjects - if (is(source, consts.DATA_MAP_OBJECT) || is(target, consts.DATA_MAP_OBJECT)) { - return false; - } + let canConnectData = canConnectDataExtension(source, target); - return super.canConnectSequenceFlow(source, target); - } + if (canConnectData || canConnectData === false) { + return canConnectData; + } + }); /** - * Returns the type of the connection if a connection between the given source and target element is possible with a - * transformation association, False else. - * - * @param source The given source element - * @param target The given target element - * @returns {{type: string}|boolean} + * Fired when a new shape for an element is created */ - canConnectDataExtension(source, target) { - console.log('##### can connect data extension'); - - // block outgoing connections from loop, parallel und multi instance markers to data map objects - if (source.businessObject.loopCharacteristics && is(target, consts.DATA_MAP_OBJECT)) { - return false; - } - - // block connections from or to a data map object that is connected with a start event - if ((is(source, consts.DATA_MAP_OBJECT) && isConnectedWith(source, 'bpmn:StartEvent')) - || (is(target, consts.DATA_MAP_OBJECT) && isConnectedWith(target, 'bpmn:StartEvent'))) { - return false; - } - - // add rule for connections via a DataTransformationAssociation - if (isAny(source, [consts.DATA_MAP_OBJECT]) && - isAny(target, [consts.DATA_MAP_OBJECT])) { - console.log('Create connection between DataMapObjects with ' + consts.OUTPUT_TRANSFORMATION_ASSOCIATION); - return { type: consts.OUTPUT_TRANSFORMATION_ASSOCIATION }; - } - - // the normal rules for a DataObject - if (isAny(source, [consts.DATA_MAP_OBJECT]) && isAny(target, ['bpmn:Activity', 'bpmn:ThrowEvent'])) { - console.log('Map to act'); - return { type: 'bpmn:DataInputAssociation' }; - } - if (isAny(target, [consts.DATA_MAP_OBJECT]) && isAny(source, ['bpmn:ThrowEvent'])) { - console.log('Map to act'); - return false; - } - if (isAny(target, [consts.DATA_MAP_OBJECT]) && isAny(source, ['bpmn:Activity', 'bpmn:CatchEvent'])) { - return { type: 'bpmn:DataOutputAssociation' }; - } - if (isAny(source, [consts.DATA_MAP_OBJECT]) && isAny(target, ['bpmn:CatchEvent'])) { - return false; - } - - // restrict connections via sequence flow - if (isAny(source, [consts.DATA_MAP_OBJECT]) && - isAny(target, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference', 'bpmn:Gateway'])) { - console.log('No data association between DataObjectMap and DataObjectReference.'); - return false; - } - if (isAny(source, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference', 'bpmn:Gateway']) && - isAny(target, [consts.DATA_MAP_OBJECT])) { - return false; - } + this.addRule("shape.create", 200000000000, function (context) { + return canCreate( + context.shape, + context.target, + context.source, + context.position + ); + }); + } + + /** + * Returns the type of the connection if the given source and target elements can be connected by the given + * connection element, False else. + * + * @param source The given source element + * @param target The given target element + * @param connection The given connection element + */ + canConnect(source, target, connection) { + console.log("##### can connect"); + + // test connection via transformation association if source or target are DataMapObjects + if ( + is(source, consts.DATA_MAP_OBJECT) || + is(target, consts.DATA_MAP_OBJECT) + ) { + return this.canConnectDataExtension(source, target); } - /** - * Returns True if the given shape can be created in the connection between the source and target element, False else. - * - * @param shape The given shape - * @param target The given target element - * @param source The given source element - * @param position The position where the shape should be created - * @returns {boolean|*|boolean} - */ - canCreate(shape, target, source, position) { - console.log('##### can create'); + if (!is(connection, "bpmn:DataAssociation")) { + // test connection via sequence flow + if (this.canConnectSequenceFlow(source, target)) { + return { type: "bpmn:SequenceFlow" }; + } + } + + // test connection via super.canConnect + return super.canConnect(source, target, connection); + } + + /** + * Returns True if the given source and target element can be connected via a sequence flow, False else + * + * @param source The given source element + * @param target The given target element + */ + canConnectSequenceFlow(source, target) { + console.log("##### canConnectSequenceFlow"); + + // do not allow sequence flow connections with DataMapObjects + if ( + is(source, consts.DATA_MAP_OBJECT) || + is(target, consts.DATA_MAP_OBJECT) + ) { + return false; + } - // do not allow insertion of DataMapObjects - if (is(shape, 'data:DataObjectMapReference')) { + return super.canConnectSequenceFlow(source, target); + } + + /** + * Returns the type of the connection if a connection between the given source and target element is possible with a + * transformation association, False else. + * + * @param source The given source element + * @param target The given target element + * @returns {{type: string}|boolean} + */ + canConnectDataExtension(source, target) { + console.log("##### can connect data extension"); + + // block outgoing connections from loop, parallel und multi instance markers to data map objects + if ( + source.businessObject.loopCharacteristics && + is(target, consts.DATA_MAP_OBJECT) + ) { + return false; + } - if (isAny(target, ['bpmn:SequenceFlow', 'bpmn:DataAssociation'])) { - return false; - } - } + // block connections from or to a data map object that is connected with a start event + if ( + (is(source, consts.DATA_MAP_OBJECT) && + isConnectedWith(source, "bpmn:StartEvent")) || + (is(target, consts.DATA_MAP_OBJECT) && + isConnectedWith(target, "bpmn:StartEvent")) + ) { + return false; + } - return super.canCreate(shape, target, source, position); + // add rule for connections via a DataTransformationAssociation + if ( + isAny(source, [consts.DATA_MAP_OBJECT]) && + isAny(target, [consts.DATA_MAP_OBJECT]) + ) { + console.log( + "Create connection between DataMapObjects with " + + consts.OUTPUT_TRANSFORMATION_ASSOCIATION + ); + return { type: consts.OUTPUT_TRANSFORMATION_ASSOCIATION }; } - /** - * Returns the type of the connection if the given source and target element can be connected via a association connection, False else. - * - * @param source The given source element - * @param target The given target element - * @returns {{type: string}|boolean|*|boolean} - */ - canConnectAssociation(source, target) { - let canConnectData = this.canConnectDataExtension(source, target); + // the normal rules for a DataObject + if ( + isAny(source, [consts.DATA_MAP_OBJECT]) && + isAny(target, ["bpmn:Activity", "bpmn:ThrowEvent"]) + ) { + console.log("Map to act"); + return { type: "bpmn:DataInputAssociation" }; + } + if ( + isAny(target, [consts.DATA_MAP_OBJECT]) && + isAny(source, ["bpmn:ThrowEvent"]) + ) { + console.log("Map to act"); + return false; + } + if ( + isAny(target, [consts.DATA_MAP_OBJECT]) && + isAny(source, ["bpmn:Activity", "bpmn:CatchEvent"]) + ) { + return { type: "bpmn:DataOutputAssociation" }; + } + if ( + isAny(source, [consts.DATA_MAP_OBJECT]) && + isAny(target, ["bpmn:CatchEvent"]) + ) { + return false; + } - if (canConnectData) { - return canConnectData; - } + // restrict connections via sequence flow + if ( + isAny(source, [consts.DATA_MAP_OBJECT]) && + isAny(target, [ + "bpmn:DataObjectReference", + "bpmn:DataStoreReference", + "bpmn:Gateway", + ]) + ) { + console.log( + "No data association between DataObjectMap and DataObjectReference." + ); + return false; + } + if ( + isAny(source, [ + "bpmn:DataObjectReference", + "bpmn:DataStoreReference", + "bpmn:Gateway", + ]) && + isAny(target, [consts.DATA_MAP_OBJECT]) + ) { + return false; + } + } + + /** + * Returns True if the given shape can be created in the connection between the source and target element, False else. + * + * @param shape The given shape + * @param target The given target element + * @param source The given source element + * @param position The position where the shape should be created + * @returns {boolean|*|boolean} + */ + canCreate(shape, target, source, position) { + console.log("##### can create"); + + // do not allow insertion of DataMapObjects + if (is(shape, "data:DataObjectMapReference")) { + if (isAny(target, ["bpmn:SequenceFlow", "bpmn:DataAssociation"])) { + return false; + } + } - return super.canConnectAssociation(source, target); + return super.canCreate(shape, target, source, position); + } + + /** + * Returns the type of the connection if the given source and target element can be connected via a association connection, False else. + * + * @param source The given source element + * @param target The given target element + * @returns {{type: string}|boolean|*|boolean} + */ + canConnectAssociation(source, target) { + let canConnectData = this.canConnectDataExtension(source, target); + + if (canConnectData) { + return canConnectData; } -} -CustomRulesProvider.$inject = [ - 'eventBus', -]; + return super.canConnectAssociation(source, target); + } +} +CustomRulesProvider.$inject = ["eventBus"]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataReplaceConnectionBehaviour.js b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataReplaceConnectionBehaviour.js index e4737af2..758659a5 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataReplaceConnectionBehaviour.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/rules/DataReplaceConnectionBehaviour.js @@ -1,187 +1,193 @@ -import { - forEach, - find, - matchPattern -} from 'min-dash'; +import { forEach, find, matchPattern } from "min-dash"; -import { - is, -} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; +import { is } from "bpmn-js/lib/features/modeling/util/ModelingUtil"; -import inherits from 'inherits-browser'; +import inherits from "inherits-browser"; -import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; +import CommandInterceptor from "diagram-js/lib/command/CommandInterceptor"; /** * Custom ReplaceConnectionBehaviour for the DataFlow extension elements. Extends the ReplaceConnectionBehaviour to use * the custom DataFlow rules. */ -export default function DataReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector, dataFlowRules) { - - CommandInterceptor.call(this, eventBus); - - var dragging = injector.get('dragging', false); - - function fixConnection(connection) { - var source = connection.source, - target = connection.target, - parent = connection.parent; - - // do not do anything if connection - // is already deleted (may happen due to other - // behaviors plugged-in before) - if (!parent) { - return; - } - - var replacementType, - remove; - - /* - * Check if incoming or outgoing connections - * can stay or could be substituted with an - * appropriate replacement. - * - * This holds true for SequenceFlow <> MessageFlow. - */ - if (is(connection, 'bpmn:SequenceFlow')) { - if (!dataFlowRules.canConnectSequenceFlow(source, target)) { - remove = true; - } - - if (bpmnRules.canConnectMessageFlow(source, target)) { - replacementType = 'bpmn:MessageFlow'; - } - } - - // transform message flows into sequence flows, if possible - if (is(connection, 'bpmn:MessageFlow')) { - - if (!bpmnRules.canConnectMessageFlow(source, target)) { - remove = true; - } - - if (dataFlowRules.canConnectSequenceFlow(source, target)) { - replacementType = 'bpmn:SequenceFlow'; - } - } - - if (is(connection, 'bpmn:Association') && !dataFlowRules.canConnectAssociation(source, target)) { - remove = true; - } - - - // remove invalid connection, - // unless it has been removed already - if (remove) { - modeling.removeConnection(connection); - } - - // replace SequenceFlow <> MessageFlow - - if (replacementType) { - modeling.connect(source, target, { - type: replacementType, - waypoints: connection.waypoints.slice() - }); - } +export default function DataReplaceConnectionBehavior( + eventBus, + modeling, + bpmnRules, + injector, + dataFlowRules +) { + CommandInterceptor.call(this, eventBus); + + var dragging = injector.get("dragging", false); + + function fixConnection(connection) { + var source = connection.source, + target = connection.target, + parent = connection.parent; + + // do not do anything if connection + // is already deleted (may happen due to other + // behaviors plugged-in before) + if (!parent) { + return; } - function replaceReconnectedConnection(event) { - - var context = event.context, - connection = context.connection, - source = context.newSource || connection.source, - target = context.newTarget || connection.target, - allowed, - replacement; - - allowed = dataFlowRules.canConnect(source, target); - - if (!allowed || allowed.type === connection.type) { - return; - } + var replacementType, remove; + + /* + * Check if incoming or outgoing connections + * can stay or could be substituted with an + * appropriate replacement. + * + * This holds true for SequenceFlow <> MessageFlow. + */ + if (is(connection, "bpmn:SequenceFlow")) { + if (!dataFlowRules.canConnectSequenceFlow(source, target)) { + remove = true; + } + + if (bpmnRules.canConnectMessageFlow(source, target)) { + replacementType = "bpmn:MessageFlow"; + } + } - replacement = modeling.connect(source, target, { - type: allowed.type, - waypoints: connection.waypoints.slice() - }); + // transform message flows into sequence flows, if possible + if (is(connection, "bpmn:MessageFlow")) { + if (!bpmnRules.canConnectMessageFlow(source, target)) { + remove = true; + } - // remove old connection - modeling.removeConnection(connection); + if (dataFlowRules.canConnectSequenceFlow(source, target)) { + replacementType = "bpmn:SequenceFlow"; + } + } - // replace connection in context to reconnect end/start - context.connection = replacement; + if ( + is(connection, "bpmn:Association") && + !dataFlowRules.canConnectAssociation(source, target) + ) { + remove = true; + } - if (dragging) { - cleanDraggingSelection(connection, replacement); - } + // remove invalid connection, + // unless it has been removed already + if (remove) { + modeling.removeConnection(connection); } - // monkey-patch selection saved in dragging in order to re-select it when operation is finished - function cleanDraggingSelection(oldConnection, newConnection) { - var context = dragging.context(), - previousSelection = context && context.payload.previousSelection, - index; + // replace SequenceFlow <> MessageFlow - // do nothing if not dragging or no selection was present - if (!previousSelection || !previousSelection.length) { - return; - } + if (replacementType) { + modeling.connect(source, target, { + type: replacementType, + waypoints: connection.waypoints.slice(), + }); + } + } - index = previousSelection.indexOf(oldConnection); + function replaceReconnectedConnection(event) { + var context = event.context, + connection = context.connection, + source = context.newSource || connection.source, + target = context.newTarget || connection.target, + allowed, + replacement; - if (index === -1) { - return; - } + allowed = dataFlowRules.canConnect(source, target); - previousSelection.splice(index, 1, newConnection); + if (!allowed || allowed.type === connection.type) { + return; } - // lifecycle hooks + replacement = modeling.connect(source, target, { + type: allowed.type, + waypoints: connection.waypoints.slice(), + }); - this.postExecuted('elements.move', function (context) { + // remove old connection + modeling.removeConnection(connection); - var closure = context.closure, - allConnections = closure.allConnections; + // replace connection in context to reconnect end/start + context.connection = replacement; - forEach(allConnections, fixConnection); - }, true); + if (dragging) { + cleanDraggingSelection(connection, replacement); + } + } - this.preExecute('connection.reconnect', replaceReconnectedConnection); + // monkey-patch selection saved in dragging in order to re-select it when operation is finished + function cleanDraggingSelection(oldConnection, newConnection) { + var context = dragging.context(), + previousSelection = context && context.payload.previousSelection, + index; - this.postExecuted('element.updateProperties', function (event) { - var context = event.context, - properties = context.properties, - element = context.element, - businessObject = element.businessObject, - connection; + // do nothing if not dragging or no selection was present + if (!previousSelection || !previousSelection.length) { + return; + } - // remove condition on change to default - if (properties.default) { - connection = find( - element.outgoing, - matchPattern({id: element.businessObject.default.id}) - ); + index = previousSelection.indexOf(oldConnection); - if (connection) { - modeling.updateProperties(connection, {conditionExpression: undefined}); - } - } + if (index === -1) { + return; + } - // remove default from source on change to conditional - if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) { - modeling.updateProperties(element.source, {default: undefined}); - } - }); + previousSelection.splice(index, 1, newConnection); + } + + // lifecycle hooks + + this.postExecuted( + "elements.move", + function (context) { + var closure = context.closure, + allConnections = closure.allConnections; + + forEach(allConnections, fixConnection); + }, + true + ); + + this.preExecute("connection.reconnect", replaceReconnectedConnection); + + this.postExecuted("element.updateProperties", function (event) { + var context = event.context, + properties = context.properties, + element = context.element, + businessObject = element.businessObject, + connection; + + // remove condition on change to default + if (properties.default) { + connection = find( + element.outgoing, + matchPattern({ id: element.businessObject.default.id }) + ); + + if (connection) { + modeling.updateProperties(connection, { + conditionExpression: undefined, + }); + } + } + + // remove default from source on change to conditional + if ( + properties.conditionExpression && + businessObject.sourceRef.default === businessObject + ) { + modeling.updateProperties(element.source, { default: undefined }); + } + }); } inherits(DataReplaceConnectionBehavior, CommandInterceptor); DataReplaceConnectionBehavior.$inject = [ - 'eventBus', - 'modeling', - 'bpmnRules', - 'injector', - 'dataFlowRules', + "eventBus", + "modeling", + "bpmnRules", + "injector", + "dataFlowRules", ]; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurations.js b/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurations.js index 2ea4827d..bccfcdcc 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurations.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurations.js @@ -1,5 +1,5 @@ -import ConfigurationsEndpoint from '../../../editor/configurations/ConfigurationEndpoint'; -import * as consts from '../Constants'; +import ConfigurationsEndpoint from "../../../editor/configurations/ConfigurationEndpoint"; +import * as consts from "../Constants"; import * as dataConfig from "../config/DataConfigManager"; /** @@ -15,7 +15,7 @@ let endpoint; * @return The list of configurations fot transformation tasks */ export function getTransformationTaskConfigurations() { - return transformationConfigs().getConfigurations(consts.TRANSFORMATION_TASK); + return transformationConfigs().getConfigurations(consts.TRANSFORMATION_TASK); } /** @@ -25,14 +25,14 @@ export function getTransformationTaskConfigurations() { * @returns the configuration with the given id */ export function getTransformationTaskConfiguration(id) { - return transformationConfigs().getConfiguration(id); + return transformationConfigs().getConfiguration(id); } /** * Update the loaded configurations by fetching them form the endpoint again. */ export function updateTransformationTaskConfigurations() { - transformationConfigs().fetchConfigurations(); + transformationConfigs().fetchConfigurations(); } /** @@ -41,8 +41,10 @@ export function updateTransformationTaskConfigurations() { * @return {ConfigurationsEndpoint} the instance of the ConfigurationsEndpoint */ export function transformationConfigs() { - if (!endpoint) { - endpoint = new ConfigurationsEndpoint(dataConfig.getConfigurationsEndpoint()); - } - return endpoint; -} \ No newline at end of file + if (!endpoint) { + endpoint = new ConfigurationsEndpoint( + dataConfig.getConfigurationsEndpoint() + ); + } + return endpoint; +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurationsTab.js b/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurationsTab.js index 862503fe..47e6da7c 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurationsTab.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/transf-task-configs/TransformationTaskConfigurationsTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as dataConfigManager from "../config/DataConfigManager"; /** @@ -9,35 +9,42 @@ import * as dataConfigManager from "../config/DataConfigManager"; * @constructor */ export default function TransformationTaskConfigurationsTab() { + const [configurationsEndpoint, setConfigurationsEndpoint] = useState( + dataConfigManager.getConfigurationsEndpoint() + ); - const [configurationsEndpoint, setConfigurationsEndpoint] = useState(dataConfigManager.getConfigurationsEndpoint()); + // save changed endpoint url if the modal is closed + TransformationTaskConfigurationsTab.prototype.onClose = () => { + dataConfigManager.setConfigurationsEndpoint(configurationsEndpoint); + }; - // save changed endpoint url if the modal is closed - TransformationTaskConfigurationsTab.prototype.onClose = () => { - dataConfigManager.setConfigurationsEndpoint(configurationsEndpoint); - }; - - return (<> -

Data Configurations endpoint configuration:

- - - - - - - -
Configurations Endpoint - setConfigurationsEndpoint(event.target.value)}/> -
- ); + return ( + <> +

Data Configurations endpoint configuration:

+ + + + + + + +
Configurations Endpoint + + setConfigurationsEndpoint(event.target.value) + } + /> +
+ + ); } TransformationTaskConfigurationsTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.transformationTaskConfigurationsEndpointChanged = dataConfigManager.getConfigurationsEndpoint(); -}; \ No newline at end of file + modeler.config.transformationTaskConfigurationsEndpointChanged = + dataConfigManager.getConfigurationsEndpoint(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/transformation/TransformationManager.js b/components/bpmn-q/modeler-component/extensions/data-extension/transformation/TransformationManager.js index 9671fcb8..59ad87df 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/transformation/TransformationManager.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/transformation/TransformationManager.js @@ -1,20 +1,23 @@ -import { is } from 'bpmn-js/lib/util/ModelUtil'; -import { getXml } from '../../../editor/util/IoUtilities'; -import { createTempModelerFromXml } from '../../../editor/ModelerHandler'; -import * as consts from '../Constants'; +import { is } from "bpmn-js/lib/util/ModelUtil"; +import { getXml } from "../../../editor/util/IoUtilities"; +import { createTempModelerFromXml } from "../../../editor/ModelerHandler"; +import * as consts from "../Constants"; import { - getAllElementsForProcess, - getAllElementsInProcess, - insertShape -} from '../../../editor/util/TransformationUtilities'; + getAllElementsForProcess, + getAllElementsInProcess, + insertShape, +} from "../../../editor/util/TransformationUtilities"; import { - addCamundaInputMapParameter, - addCamundaInputParameter, - addCamundaOutputMapParameter, - addFormField, findSequenceFlowConnection, getDocumentation, - getRootProcess, setDocumentation, -} from '../../../editor/util/ModellingUtilities'; -import { layout } from '../../quantme/replacement/layouter/Layouter'; + addCamundaInputMapParameter, + addCamundaInputParameter, + addCamundaOutputMapParameter, + addFormField, + findSequenceFlowConnection, + getDocumentation, + getRootProcess, + setDocumentation, +} from "../../../editor/util/ModellingUtilities"; +import { layout } from "../../quantme/replacement/layouter/Layouter"; /** * Replace data flow extensions with camunda bpmn elements so that it complies with the standard @@ -23,182 +26,260 @@ import { layout } from '../../quantme/replacement/layouter/Layouter'; * @returns {Promise<{xml: *, status: string}|{cause: string, status: string}>} */ export async function startDataFlowReplacementProcess(xml) { - let modeler = await createTempModelerFromXml(xml); - let elementRegistry = modeler.get('elementRegistry'); - let modeling = modeler.get('modeling'); - - // get root element of the current diagram - const definitions = modeler.getDefinitions(); - const rootProcess = getRootProcess(definitions); - - console.log(rootProcess); - - if (typeof rootProcess === 'undefined') { - - console.log('Unable to retrieve root process element from definitions!'); - return { status: 'failed', cause: 'Unable to retrieve root process element from definitions!' }; + let modeler = await createTempModelerFromXml(xml); + let elementRegistry = modeler.get("elementRegistry"); + let modeling = modeler.get("modeling"); + + // get root element of the current diagram + const definitions = modeler.getDefinitions(); + const rootProcess = getRootProcess(definitions); + + console.log(rootProcess); + + if (typeof rootProcess === "undefined") { + console.log("Unable to retrieve root process element from definitions!"); + return { + status: "failed", + cause: "Unable to retrieve root process element from definitions!", + }; + } + + // Mark process as executable + rootProcess.isExecutable = true; + + const bpmnFactory = modeler.get("bpmnFactory"); + const moddle = modeler.get("moddle"); + + // for each transformation association + const transformationAssociations = elementRegistry.filter(function (element) { + console.log(element.id); + return is(element, consts.TRANSFORMATION_ASSOCIATION); + }); + console.log( + "Found " + + transformationAssociations.length + + " TransformationAssociations." + ); + + let targetDataMapObject, + sourceDataMapObject, + targetActivityElement, + targetContent; + + for (let transformationAssociation of transformationAssociations) { + // if source === DataMapObject: expressions als inputs im target + if ( + transformationAssociation.source.type === consts.DATA_MAP_OBJECT && + transformationAssociation.target.type !== consts.DATA_MAP_OBJECT + ) { + targetActivityElement = transformationAssociation.target; + + const expressions = transformationAssociation.businessObject.get( + consts.EXPRESSIONS + ); + for (let expression of expressions) { + addCamundaInputParameter( + targetActivityElement.businessObject, + expression.name, + expression.value, + bpmnFactory + ); + } } - // Mark process as executable - rootProcess.isExecutable = true; - - const bpmnFactory = modeler.get('bpmnFactory'); - const moddle = modeler.get('moddle'); - - // for each transformation association - const transformationAssociations = elementRegistry.filter(function (element) { - console.log(element.id); - return is(element, consts.TRANSFORMATION_ASSOCIATION); - }); - console.log('Found ' + transformationAssociations.length + ' TransformationAssociations.'); - - let targetDataMapObject, + // if target && source === DataMapObject: add expressions to content of target data map object + if ( + transformationAssociation.source.type === consts.DATA_MAP_OBJECT && + transformationAssociation.target.type === consts.DATA_MAP_OBJECT + ) { + targetDataMapObject = transformationAssociation.target; + sourceDataMapObject = transformationAssociation.source; + targetContent = + targetDataMapObject.businessObject.get(consts.CONTENT) || []; + + const expressions = transformationAssociation.businessObject.get( + consts.EXPRESSIONS + ); + for (let expression of expressions) { + targetContent.push( + bpmnFactory.create(consts.KEY_VALUE_ENTRY, { + name: expression.name, + value: expression.value, + }) + ); + } + + // mark target data map objects as created through a transformation association + sourceDataMapObject.businessObject.createsThroughTransformation = true; + targetDataMapObject.businessObject.createdByTransformation = true; + + // document the transformation in the source and target elements + const currentSourceDoc = + getDocumentation(sourceDataMapObject.businessObject) || ""; + setDocumentation( sourceDataMapObject, - targetActivityElement, - targetContent; - - for (let transformationAssociation of transformationAssociations) { - - // if source === DataMapObject: expressions als inputs im target - if ((transformationAssociation.source.type === consts.DATA_MAP_OBJECT) && (transformationAssociation.target.type !== consts.DATA_MAP_OBJECT)) { - targetActivityElement = transformationAssociation.target; - - const expressions = transformationAssociation.businessObject.get(consts.EXPRESSIONS); - for (let expression of expressions) { - addCamundaInputParameter(targetActivityElement.businessObject, expression.name, expression.value, bpmnFactory); - } - } - - // if target && source === DataMapObject: add expressions to content of target data map object - if ((transformationAssociation.source.type === consts.DATA_MAP_OBJECT) && (transformationAssociation.target.type === consts.DATA_MAP_OBJECT)) { - targetDataMapObject = transformationAssociation.target; - sourceDataMapObject = transformationAssociation.source; - targetContent = targetDataMapObject.businessObject.get(consts.CONTENT) || []; - - const expressions = transformationAssociation.businessObject.get(consts.EXPRESSIONS); - for (let expression of expressions) { - targetContent.push(bpmnFactory.create(consts.KEY_VALUE_ENTRY, { - name: expression.name, - value: expression.value - })); - } - - // mark target data map objects as created through a transformation association - sourceDataMapObject.businessObject.createsThroughTransformation = true; - targetDataMapObject.businessObject.createdByTransformation = true; - - // document the transformation in the source and target elements - const currentSourceDoc = getDocumentation(sourceDataMapObject.businessObject) || ''; - setDocumentation(sourceDataMapObject, currentSourceDoc.concat(createTransformationSourceDocs(transformationAssociation)), bpmnFactory); - - const currentTargetDoc = getDocumentation(targetDataMapObject.businessObject) || ''; - setDocumentation(targetDataMapObject, currentTargetDoc.concat(createTransformationTargetDocs(transformationAssociation)), bpmnFactory); - } + currentSourceDoc.concat( + createTransformationSourceDocs(transformationAssociation) + ), + bpmnFactory + ); + + const currentTargetDoc = + getDocumentation(targetDataMapObject.businessObject) || ""; + setDocumentation( + targetDataMapObject, + currentTargetDoc.concat( + createTransformationTargetDocs(transformationAssociation) + ), + bpmnFactory + ); } - - // for each data association - const dataAssociations = elementRegistry.filter(function (element) { - return is(element, 'bpmn:DataAssociation'); - }); - console.log('Found ' + dataAssociations.length + ' DataAssociations.'); - - let source, - target, - dataMapObject, - activity, - businessObject; - - for (let dataAssociation of dataAssociations) { - source = dataAssociation.source; - target = dataAssociation.target; - - // if source === DataMapObject: content als input in target activity - if (source.type === consts.DATA_MAP_OBJECT) { - activity = target; - dataMapObject = source; - businessObject = dataMapObject.businessObject; - - addCamundaInputMapParameter(activity.businessObject, businessObject.name, businessObject.get(consts.CONTENT), bpmnFactory); - } - - // if target === DataMapObject: content als output in source - if (target.type === consts.DATA_MAP_OBJECT) { - dataMapObject = target; - activity = source; - businessObject = dataMapObject.businessObject; - - if (source.type === 'bpmn:StartEvent') { - - const name = businessObject.get('name'); - - for (let c of businessObject.get(consts.CONTENT)) { - let formField = - { - 'defaultValue': c.value, - 'id': name + '.' + c.name, - 'label': name + '.' + c.name, - 'type': 'string' - }; - addFormField(activity.id, formField, elementRegistry, moddle, modeling); - } - - } else { - addCamundaOutputMapParameter(activity.businessObject, businessObject.name, businessObject.get(consts.CONTENT), bpmnFactory); - } - } - } - - const globalProcessVariables = {}; - - // transform DataMapObjects to data objects - let transformationSuccess = transformDataMapObjects(rootProcess, definitions, globalProcessVariables, modeler); - if (!transformationSuccess) { - const failureMessage = `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + transformationSuccess.failedData.id + ' failed. Aborting process!'; - console.log(failureMessage); - return { - status: 'failed', - cause: failureMessage, - }; + } + + // for each data association + const dataAssociations = elementRegistry.filter(function (element) { + return is(element, "bpmn:DataAssociation"); + }); + console.log("Found " + dataAssociations.length + " DataAssociations."); + + let source, target, dataMapObject, activity, businessObject; + + for (let dataAssociation of dataAssociations) { + source = dataAssociation.source; + target = dataAssociation.target; + + // if source === DataMapObject: content als input in target activity + if (source.type === consts.DATA_MAP_OBJECT) { + activity = target; + dataMapObject = source; + businessObject = dataMapObject.businessObject; + + addCamundaInputMapParameter( + activity.businessObject, + businessObject.name, + businessObject.get(consts.CONTENT), + bpmnFactory + ); } - // transform DataStoreMap to data stores - transformationSuccess = transformDataStoreMaps(rootProcess, definitions, globalProcessVariables, modeler); - if (!transformationSuccess) { - const failureMessage = `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + transformationSuccess.failedData.id + ' failed. Aborting process!'; - console.log(failureMessage); - return { - status: 'failed', - cause: failureMessage, - }; + // if target === DataMapObject: content als output in source + if (target.type === consts.DATA_MAP_OBJECT) { + dataMapObject = target; + activity = source; + businessObject = dataMapObject.businessObject; + + if (source.type === "bpmn:StartEvent") { + const name = businessObject.get("name"); + + for (let c of businessObject.get(consts.CONTENT)) { + let formField = { + defaultValue: c.value, + id: name + "." + c.name, + label: name + "." + c.name, + type: "string", + }; + addFormField( + activity.id, + formField, + elementRegistry, + moddle, + modeling + ); + } + } else { + addCamundaOutputMapParameter( + activity.businessObject, + businessObject.name, + businessObject.get(consts.CONTENT), + bpmnFactory + ); + } } - - // transform TransformationTasks to service tasks - transformationSuccess = transformTransformationTask(rootProcess, definitions, globalProcessVariables, modeler); + } + + const globalProcessVariables = {}; + + // transform DataMapObjects to data objects + let transformationSuccess = transformDataMapObjects( + rootProcess, + definitions, + globalProcessVariables, + modeler + ); + if (!transformationSuccess) { + const failureMessage = + `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + + transformationSuccess.failedData.id + + " failed. Aborting process!"; + console.log(failureMessage); + return { + status: "failed", + cause: failureMessage, + }; + } + + // transform DataStoreMap to data stores + transformationSuccess = transformDataStoreMaps( + rootProcess, + definitions, + globalProcessVariables, + modeler + ); + if (!transformationSuccess) { + const failureMessage = + `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + + transformationSuccess.failedData.id + + " failed. Aborting process!"; + console.log(failureMessage); + return { + status: "failed", + cause: failureMessage, + }; + } + + // transform TransformationTasks to service tasks + transformationSuccess = transformTransformationTask( + rootProcess, + definitions, + globalProcessVariables, + modeler + ); + if (!transformationSuccess) { + const failureMessage = + `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + + transformationSuccess.failedData.id + + " failed. Aborting process!"; + console.log(failureMessage); + return { + status: "failed", + cause: failureMessage, + }; + } + + if (Object.entries(globalProcessVariables).length > 0) { + transformationSuccess = createProcessContextVariablesTask( + globalProcessVariables, + rootProcess, + definitions, + modeler + ); if (!transformationSuccess) { - const failureMessage = `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + transformationSuccess.failedData.id + ' failed. Aborting process!'; - console.log(failureMessage); - return { - status: 'failed', - cause: failureMessage, - }; + const failureMessage = + `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + + transformationSuccess.failedData.id + + " failed. Aborting process!"; + console.log(failureMessage); + return { + status: "failed", + cause: failureMessage, + }; } + } - if (Object.entries(globalProcessVariables).length > 0) { - transformationSuccess = createProcessContextVariablesTask(globalProcessVariables, rootProcess, definitions, modeler); - if (!transformationSuccess) { - const failureMessage = `Replacement of Data modeling construct ${transformationSuccess.failedData.type} with Id ` + transformationSuccess.failedData.id + ' failed. Aborting process!'; - console.log(failureMessage); - return { - status: 'failed', - cause: failureMessage, - }; - } - } - - layout(modeling, elementRegistry, rootProcess); + layout(modeling, elementRegistry, rootProcess); - const transformedXML = await getXml(modeler); - return { status: 'transformed', xml: transformedXML }; + const transformedXML = await getXml(modeler); + return { status: "transformed", xml: transformedXML }; } /** @@ -213,59 +294,79 @@ export async function startDataFlowReplacementProcess(xml) { * @return {{success: boolean}|{success: boolean, failedData: *}} Success flag with True if transformation was successful, * False else with details in failedData. */ -function transformDataMapObjects(rootProcess, definitions, processContextVariables, modeler) { - let bpmnFactory = modeler.get('bpmnFactory'); - let elementRegistry = modeler.get('elementRegistry'); - - // get all data map objects of the current process including subprocesses - const dataObjectMaps = getAllElementsInProcess(rootProcess, elementRegistry, consts.DATA_MAP_OBJECT); - console.log('Found ' + dataObjectMaps.length + ' DataObjectMapReferences to replace.'); - - // replace all data map objects with data objects and transform the content attribute - for (let dataElement of dataObjectMaps) { - - const dataMapObjectBo = dataElement.element; - const dataMapObjectElement = elementRegistry.get(dataMapObjectBo.id); - - const isUsedBeforeInit = isDataMapObjectUsedBeforeInitialized(dataMapObjectElement, elementRegistry); - - // check if the content of the data map object has to be published in process content - if (dataMapObjectBo.createdByTransformation - || dataMapObjectBo.createsThroughTransformation - || !dataMapObjectElement.incoming - || dataMapObjectElement.incoming.length === 0 - || isUsedBeforeInit) { - - // const startEvents = getStartEvents(); - const processElement = dataElement.parent; - - if (!processContextVariables[processElement.id]) { - processContextVariables[processElement.id] = []; - } - - // publish content of the data map object as process variable in process context - processContextVariables[processElement.id].push({ - name: dataMapObjectBo.name, - map: dataMapObjectBo.get(consts.CONTENT) - }); - } - - // replace data map object by data object - const dataObject = bpmnFactory.create('bpmn:DataObjectReference'); - const result = insertShape(definitions, dataObject.parent, dataObject, {}, true, modeler, dataMapObjectBo); +function transformDataMapObjects( + rootProcess, + definitions, + processContextVariables, + modeler +) { + let bpmnFactory = modeler.get("bpmnFactory"); + let elementRegistry = modeler.get("elementRegistry"); + + // get all data map objects of the current process including subprocesses + const dataObjectMaps = getAllElementsInProcess( + rootProcess, + elementRegistry, + consts.DATA_MAP_OBJECT + ); + console.log( + "Found " + dataObjectMaps.length + " DataObjectMapReferences to replace." + ); + + // replace all data map objects with data objects and transform the content attribute + for (let dataElement of dataObjectMaps) { + const dataMapObjectBo = dataElement.element; + const dataMapObjectElement = elementRegistry.get(dataMapObjectBo.id); + + const isUsedBeforeInit = isDataMapObjectUsedBeforeInitialized( + dataMapObjectElement, + elementRegistry + ); + + // check if the content of the data map object has to be published in process content + if ( + dataMapObjectBo.createdByTransformation || + dataMapObjectBo.createsThroughTransformation || + !dataMapObjectElement.incoming || + dataMapObjectElement.incoming.length === 0 || + isUsedBeforeInit + ) { + // const startEvents = getStartEvents(); + const processElement = dataElement.parent; + + if (!processContextVariables[processElement.id]) { + processContextVariables[processElement.id] = []; + } - if (result.success) { + // publish content of the data map object as process variable in process context + processContextVariables[processElement.id].push({ + name: dataMapObjectBo.name, + map: dataMapObjectBo.get(consts.CONTENT), + }); + } - // set documentation property of the data object to document the data map object it replaces - const currentDoc = getDocumentation(dataMapObjectBo) || ''; - const dataDoc = createDataMapObjectDocs(dataMapObjectBo); - setDocumentation(result.element, currentDoc.concat(dataDoc), bpmnFactory); - } else { - return { success: false, failedData: dataMapObjectBo }; - } + // replace data map object by data object + const dataObject = bpmnFactory.create("bpmn:DataObjectReference"); + const result = insertShape( + definitions, + dataObject.parent, + dataObject, + {}, + true, + modeler, + dataMapObjectBo + ); + if (result.success) { + // set documentation property of the data object to document the data map object it replaces + const currentDoc = getDocumentation(dataMapObjectBo) || ""; + const dataDoc = createDataMapObjectDocs(dataMapObjectBo); + setDocumentation(result.element, currentDoc.concat(dataDoc), bpmnFactory); + } else { + return { success: false, failedData: dataMapObjectBo }; } - return { success: true }; + } + return { success: true }; } /** @@ -279,23 +380,40 @@ function transformDataMapObjects(rootProcess, definitions, processContextVariabl * @return {{success: boolean}|{success: boolean, failedData: *}} Success flag with True if transformation was successful, * False else with details in failedData. */ -function transformDataStoreMaps(rootProcess, definitions, processContextVariables, modeler) { - let elementRegistry = modeler.get('elementRegistry'); - - // get all data store maps of the current process including the data store maps in subprocesses - const dataStoreElements = getAllElementsInProcess(rootProcess, elementRegistry, consts.DATA_STORE_MAP); - console.log('Found ' + dataStoreElements.length + ' DataObjectMapReferences to replace.'); - - // replace all data store maps and transform their details attributes - for (let dataElement of dataStoreElements) { - const result = transformDataStoreMap(dataElement.element, dataElement.parent, definitions, processContextVariables, modeler); - - if (!result.success) { - // break transformation and propagate failure - return { success: false, failedData: dataElement.element }; - } +function transformDataStoreMaps( + rootProcess, + definitions, + processContextVariables, + modeler +) { + let elementRegistry = modeler.get("elementRegistry"); + + // get all data store maps of the current process including the data store maps in subprocesses + const dataStoreElements = getAllElementsInProcess( + rootProcess, + elementRegistry, + consts.DATA_STORE_MAP + ); + console.log( + "Found " + dataStoreElements.length + " DataObjectMapReferences to replace." + ); + + // replace all data store maps and transform their details attributes + for (let dataElement of dataStoreElements) { + const result = transformDataStoreMap( + dataElement.element, + dataElement.parent, + definitions, + processContextVariables, + modeler + ); + + if (!result.success) { + // break transformation and propagate failure + return { success: false, failedData: dataElement.element }; } - return { success: true }; + } + return { success: true }; } /** @@ -310,38 +428,49 @@ function transformDataStoreMaps(rootProcess, definitions, processContextVariable * @return {{success: boolean}|{success: boolean, failedData: *}} Success flag with True if transformation was successful, * False else with details in failedData. */ -export function transformDataStoreMap(dataStoreMap, parentElement, definitions, processContextVariables, modeler) { - - const bpmnFactory = modeler.get('bpmnFactory'); - - const processElement = parentElement; - if (!processContextVariables[processElement.id]) { - processContextVariables[processElement.id] = []; - } - - // publish details of the data store map as process variable in process context - processContextVariables[processElement.id].push({ - name: dataStoreMap.name, - map: dataStoreMap.get(consts.DETAILS) - }); - - // replace data store map by data store - const dataStore = bpmnFactory.create('bpmn:DataStoreReference'); - const result = insertShape(definitions, dataStore.parent, dataStore, {}, true, modeler, dataStoreMap); - - if (result.success) { - - // set documentation property of the data store to document the data store map it replaces - const currentDoc = getDocumentation(dataStoreMap) || ''; - const dataDoc = createDataStoreMapDocs(dataStoreMap); - setDocumentation(result.element, currentDoc.concat(dataDoc), bpmnFactory); - } else { - return { success: false, failedData: dataStoreMap }; - } - return { success: true }; +export function transformDataStoreMap( + dataStoreMap, + parentElement, + definitions, + processContextVariables, + modeler +) { + const bpmnFactory = modeler.get("bpmnFactory"); + + const processElement = parentElement; + if (!processContextVariables[processElement.id]) { + processContextVariables[processElement.id] = []; + } + + // publish details of the data store map as process variable in process context + processContextVariables[processElement.id].push({ + name: dataStoreMap.name, + map: dataStoreMap.get(consts.DETAILS), + }); + + // replace data store map by data store + const dataStore = bpmnFactory.create("bpmn:DataStoreReference"); + const result = insertShape( + definitions, + dataStore.parent, + dataStore, + {}, + true, + modeler, + dataStoreMap + ); + + if (result.success) { + // set documentation property of the data store to document the data store map it replaces + const currentDoc = getDocumentation(dataStoreMap) || ""; + const dataDoc = createDataStoreMapDocs(dataStoreMap); + setDocumentation(result.element, currentDoc.concat(dataDoc), bpmnFactory); + } else { + return { success: false, failedData: dataStoreMap }; + } + return { success: true }; } - /** * Transform TransformationTasks to service tasks. Add the parameters attribute of the TransformationTask as a camunda map * inputs of the service task. @@ -353,31 +482,56 @@ export function transformDataStoreMap(dataStoreMap, parentElement, definitions, * @return {{success: boolean}|{success: boolean, failedData: *}} Success flag with True if transformation was successful, * False else with details in failedData. */ -function transformTransformationTask(rootProcess, definitions, processContextVariables, modeler) { - let bpmnFactory = modeler.get('bpmnFactory'); - let elementRegistry = modeler.get('elementRegistry'); - - // get all transformation task of the root process including the tasks in subprocesses - const transformationTasks = getAllElementsInProcess(rootProcess, elementRegistry, consts.TRANSFORMATION_TASK); - console.log('Found ' + transformationTasks.length + ' DataObjectMapReferences to replace.'); - - // transform each task into a service task and add the parameters attribute to the inputs of the service task. - for (let taskElement of transformationTasks) { - - const transformationTask = taskElement.element; - - // replace transformation task by new service task - const serviceTask = bpmnFactory.create('bpmn:ServiceTask'); - const result = insertShape(definitions, serviceTask.parent, serviceTask, {}, true, modeler, transformationTask); - - if (!result.success) { - return { success: false, failedData: transformationTask }; - } - - // add parameters attribute as camunda map to service task inputs - addCamundaInputMapParameter(result.element.businessObject, consts.PARAMETERS, transformationTask.get(consts.PARAMETERS), bpmnFactory); +function transformTransformationTask( + rootProcess, + definitions, + processContextVariables, + modeler +) { + let bpmnFactory = modeler.get("bpmnFactory"); + let elementRegistry = modeler.get("elementRegistry"); + + // get all transformation task of the root process including the tasks in subprocesses + const transformationTasks = getAllElementsInProcess( + rootProcess, + elementRegistry, + consts.TRANSFORMATION_TASK + ); + console.log( + "Found " + + transformationTasks.length + + " DataObjectMapReferences to replace." + ); + + // transform each task into a service task and add the parameters attribute to the inputs of the service task. + for (let taskElement of transformationTasks) { + const transformationTask = taskElement.element; + + // replace transformation task by new service task + const serviceTask = bpmnFactory.create("bpmn:ServiceTask"); + const result = insertShape( + definitions, + serviceTask.parent, + serviceTask, + {}, + true, + modeler, + transformationTask + ); + + if (!result.success) { + return { success: false, failedData: transformationTask }; } - return { success: true }; + + // add parameters attribute as camunda map to service task inputs + addCamundaInputMapParameter( + result.element.businessObject, + consts.PARAMETERS, + transformationTask.get(consts.PARAMETERS), + bpmnFactory + ); + } + return { success: true }; } /** @@ -390,36 +544,60 @@ function transformTransformationTask(rootProcess, definitions, processContextVar * @param modeler The modeler containing the workflow to transform * @return {{success: boolean}} True if the ProcessVariablesTask could be successfully created, False else. */ -export function createProcessContextVariablesTask(processContextVariables, rootProcess, definitions, modeler) { - const elementRegistry = modeler.get('elementRegistry'); - const bpmnFactory = modeler.get('bpmnFactory'); - const modeling = modeler.get('modeling'); - - // add for each process or subprocess a new task to create process variables - for (let processEntry of Object.entries(processContextVariables)) { - const processId = processEntry[0]; - const processBo = elementRegistry.get(processId).businessObject; - - const startEvents = getAllElementsForProcess(processBo, elementRegistry, 'bpmn:StartEvent'); - - console.log(`Found ${startEvents && startEvents.length} StartEvents in process ${processId}`); - console.log(startEvents); - - // add ProcessVariablesTask after each start event - for (let event of startEvents) { - const startEventBo = event.element; - const startEventElement = elementRegistry.get(startEventBo.id); - - const newTaskBo = getProcessContextVariablesTask(startEventElement, event.parent, bpmnFactory, modeling, elementRegistry); - - // add camunda map to outputs for each entry - for (let processVariable of processContextVariables[processId]) { - addCamundaOutputMapParameter(newTaskBo, processVariable.name, processVariable.map, bpmnFactory); - } - } +export function createProcessContextVariablesTask( + processContextVariables, + rootProcess, + definitions, + modeler +) { + const elementRegistry = modeler.get("elementRegistry"); + const bpmnFactory = modeler.get("bpmnFactory"); + const modeling = modeler.get("modeling"); + + // add for each process or subprocess a new task to create process variables + for (let processEntry of Object.entries(processContextVariables)) { + const processId = processEntry[0]; + const processBo = elementRegistry.get(processId).businessObject; + + const startEvents = getAllElementsForProcess( + processBo, + elementRegistry, + "bpmn:StartEvent" + ); + + console.log( + `Found ${ + startEvents && startEvents.length + } StartEvents in process ${processId}` + ); + console.log(startEvents); + + // add ProcessVariablesTask after each start event + for (let event of startEvents) { + const startEventBo = event.element; + const startEventElement = elementRegistry.get(startEventBo.id); + + const newTaskBo = getProcessContextVariablesTask( + startEventElement, + event.parent, + bpmnFactory, + modeling, + elementRegistry + ); + + // add camunda map to outputs for each entry + for (let processVariable of processContextVariables[processId]) { + addCamundaOutputMapParameter( + newTaskBo, + processVariable.name, + processVariable.map, + bpmnFactory + ); + } } + } - return { success: true }; + return { success: true }; } /** @@ -433,52 +611,69 @@ export function createProcessContextVariablesTask(processContextVariables, rootP * @param elementRegistry The elementRegistry containing the current elements of the workflow. * @return {bpmn:Task} The ProcessContextVariables task for the start event */ -function getProcessContextVariablesTask(startEventElement, parent, bpmnFactory, modeling, elementRegistry) { - - const startEventBo = startEventElement.businessObject; - - let processVariablesTaskBo; - - // check if ProcessContextVariables task already exists - if (startEventElement.outgoing[0] - && startEventElement.outgoing[0].target - && startEventElement.outgoing[0].target.businessObject.name === 'Create Process Variables [Generated]') { - processVariablesTaskBo = startEventElement.outgoing[0].target.businessObject; - - } else { - processVariablesTaskBo = bpmnFactory.create('bpmn:Task'); - processVariablesTaskBo.name = 'Create Process Variables [Generated]'; - - const outgoingFlowElements = startEventBo.outgoing || []; - - // height difference between the position of the center of a start event and a task - const Y_OFFSET_TASK = 19; - - // create new task - const newTaskElement = modeling.createShape({ - type: 'bpmn:Task', - businessObject: processVariablesTaskBo, - }, { x: startEventElement.x, y: startEventElement.y + Y_OFFSET_TASK }, parent, {}); - - modeling.updateProperties(newTaskElement, processVariablesTaskBo); - - // move start event to the left to create space for the new task - modeling.moveElements([startEventElement], { x: -120, y: 0 }); - - // connect new Task with activities which were connected with the start event - modeling.connect(startEventElement, newTaskElement, { type: 'bpmn:SequenceFlow' }); - for (let outgoingConnectionBo of outgoingFlowElements) { - const outgoingConnectionElement = elementRegistry.get(outgoingConnectionBo.id); - const target = outgoingConnectionElement.target; - - modeling.removeConnection(outgoingConnectionElement); - modeling.connect(newTaskElement, target, { - type: outgoingConnectionElement.type, - waypoints: outgoingConnectionElement.waypoints - }); - } +function getProcessContextVariablesTask( + startEventElement, + parent, + bpmnFactory, + modeling, + elementRegistry +) { + const startEventBo = startEventElement.businessObject; + + let processVariablesTaskBo; + + // check if ProcessContextVariables task already exists + if ( + startEventElement.outgoing[0] && + startEventElement.outgoing[0].target && + startEventElement.outgoing[0].target.businessObject.name === + "Create Process Variables [Generated]" + ) { + processVariablesTaskBo = + startEventElement.outgoing[0].target.businessObject; + } else { + processVariablesTaskBo = bpmnFactory.create("bpmn:Task"); + processVariablesTaskBo.name = "Create Process Variables [Generated]"; + + const outgoingFlowElements = startEventBo.outgoing || []; + + // height difference between the position of the center of a start event and a task + const Y_OFFSET_TASK = 19; + + // create new task + const newTaskElement = modeling.createShape( + { + type: "bpmn:Task", + businessObject: processVariablesTaskBo, + }, + { x: startEventElement.x, y: startEventElement.y + Y_OFFSET_TASK }, + parent, + {} + ); + + modeling.updateProperties(newTaskElement, processVariablesTaskBo); + + // move start event to the left to create space for the new task + modeling.moveElements([startEventElement], { x: -120, y: 0 }); + + // connect new Task with activities which were connected with the start event + modeling.connect(startEventElement, newTaskElement, { + type: "bpmn:SequenceFlow", + }); + for (let outgoingConnectionBo of outgoingFlowElements) { + const outgoingConnectionElement = elementRegistry.get( + outgoingConnectionBo.id + ); + const target = outgoingConnectionElement.target; + + modeling.removeConnection(outgoingConnectionElement); + modeling.connect(newTaskElement, target, { + type: outgoingConnectionElement.type, + waypoints: outgoingConnectionElement.waypoints, + }); } - return processVariablesTaskBo; + } + return processVariablesTaskBo; } /** @@ -488,34 +683,41 @@ function getProcessContextVariablesTask(startEventElement, parent, bpmnFactory, * @param elementRegistry The elementRegistry containing all elements of the current workflow * @return {boolean} */ -function isDataMapObjectUsedBeforeInitialized(dataMapObjectElement, elementRegistry) { - - // return false if the element does not have incoming and outgoing connections - if (!dataMapObjectElement.incoming - || dataMapObjectElement.incoming.length === 0 - || !dataMapObjectElement.outgoing - || dataMapObjectElement.outgoing.length === 0) { - return false; +function isDataMapObjectUsedBeforeInitialized( + dataMapObjectElement, + elementRegistry +) { + // return false if the element does not have incoming and outgoing connections + if ( + !dataMapObjectElement.incoming || + dataMapObjectElement.incoming.length === 0 || + !dataMapObjectElement.outgoing || + dataMapObjectElement.outgoing.length === 0 + ) { + return false; + } + + // if there is one outgoing that connection with a target located before the first outgoing connection, return false + for (let incomingConnection of dataMapObjectElement.incoming) { + // check if there exists at least one outgoing connection to an element that is located in the sequence flow before + // the target of the incomingConnection + for (let outgoingConnection of dataMapObjectElement.outgoing) { + const found = findSequenceFlowConnection( + outgoingConnection.target, + incomingConnection.source, + new Set(), + elementRegistry + ); + if (found) { + // there is an outgoing connection with a target before the incoming connection + break; + } + + // found one incoming connection that is located before all outgoing connections + return false; } - - // if there is one outgoing that connection with a target located before the first outgoing connection, return false - for (let incomingConnection of dataMapObjectElement.incoming) { - - // check if there exists at least one outgoing connection to an element that is located in the sequence flow before - // the target of the incomingConnection - for (let outgoingConnection of dataMapObjectElement.outgoing) { - const found = findSequenceFlowConnection(outgoingConnection.target, incomingConnection.source, new Set(), elementRegistry); - if (found) { - - // there is an outgoing connection with a target before the incoming connection - break; - } - - // found one incoming connection that is located before all outgoing connections - return false; - } - } - return true; + } + return true; } /** @@ -525,14 +727,14 @@ function isDataMapObjectUsedBeforeInitialized(dataMapObjectElement, elementRegis * @return {string} The documentation as a string. */ function createDataMapObjectDocs(dataMapObjectBo) { - let doc = '\n \n Replaced DataMapObject, represents the following data: \n'; + let doc = "\n \n Replaced DataMapObject, represents the following data: \n"; - const contentMap = {}; - for (let contentEntry of dataMapObjectBo.get(consts.CONTENT)) { - contentMap[contentEntry.name] = contentEntry.value; - } + const contentMap = {}; + for (let contentEntry of dataMapObjectBo.get(consts.CONTENT)) { + contentMap[contentEntry.name] = contentEntry.value; + } - return doc.concat(JSON.stringify(contentMap)); + return doc.concat(JSON.stringify(contentMap)); } /** @@ -542,14 +744,14 @@ function createDataMapObjectDocs(dataMapObjectBo) { * @return {string} The documentation as a string. */ function createDataStoreMapDocs(dataStoreMapBo) { - let doc = '\n \n Replaced DataStoreMap, represents the following data: \n'; + let doc = "\n \n Replaced DataStoreMap, represents the following data: \n"; - const detailsMap = {}; - for (let detailsEntry of dataStoreMapBo.get(consts.DETAILS)) { - detailsMap[detailsEntry.name] = detailsEntry.value; - } + const detailsMap = {}; + for (let detailsEntry of dataStoreMapBo.get(consts.DETAILS)) { + detailsMap[detailsEntry.name] = detailsEntry.value; + } - return doc.concat(JSON.stringify(detailsMap)); + return doc.concat(JSON.stringify(detailsMap)); } /** @@ -560,16 +762,20 @@ function createDataStoreMapDocs(dataStoreMapBo) { * @return {string} The documentation string */ function createTransformationSourceDocs(transformationAssociationElement) { - const target = transformationAssociationElement.target; + const target = transformationAssociationElement.target; - const doc = `\n \n This object was transformed into ${target.name || target.id}. The transformation was defined by the following expressions: \n`; + const doc = `\n \n This object was transformed into ${ + target.name || target.id + }. The transformation was defined by the following expressions: \n`; - const expressionsMap = {}; - for (let expression of transformationAssociationElement.businessObject.get(consts.EXPRESSIONS)) { - expressionsMap[expression.name] = expression.value; - } + const expressionsMap = {}; + for (let expression of transformationAssociationElement.businessObject.get( + consts.EXPRESSIONS + )) { + expressionsMap[expression.name] = expression.value; + } - return doc.concat(JSON.stringify(expressionsMap)); + return doc.concat(JSON.stringify(expressionsMap)); } /** @@ -580,14 +786,18 @@ function createTransformationSourceDocs(transformationAssociationElement) { * @return {string} The documentation string */ function createTransformationTargetDocs(transformationAssociationElement) { - const source = transformationAssociationElement.source; + const source = transformationAssociationElement.source; - const doc = `\n \n This object was created through a transformation of ${source.name || source.id}. The transformation was defined by the following expressions: \n`; + const doc = `\n \n This object was created through a transformation of ${ + source.name || source.id + }. The transformation was defined by the following expressions: \n`; - const expressionsMap = {}; - for (let expression of transformationAssociationElement.businessObject.get(consts.EXPRESSIONS)) { - expressionsMap[expression.name] = expression.value; - } + const expressionsMap = {}; + for (let expression of transformationAssociationElement.businessObject.get( + consts.EXPRESSIONS + )) { + expressionsMap[expression.name] = expression.value; + } - return doc.concat(JSON.stringify(expressionsMap)); -} \ No newline at end of file + return doc.concat(JSON.stringify(expressionsMap)); +} diff --git a/components/bpmn-q/modeler-component/extensions/data-extension/ui/UpdateTransformationConfigurations.js b/components/bpmn-q/modeler-component/extensions/data-extension/ui/UpdateTransformationConfigurations.js index f0acc97b..86e8ef51 100644 --- a/components/bpmn-q/modeler-component/extensions/data-extension/ui/UpdateTransformationConfigurations.js +++ b/components/bpmn-q/modeler-component/extensions/data-extension/ui/UpdateTransformationConfigurations.js @@ -1,5 +1,5 @@ -import React from 'react'; -import {updateTransformationTaskConfigurations} from "../transf-task-configs/TransformationTaskConfigurations"; +import React from "react"; +import { updateTransformationTaskConfigurations } from "../transf-task-configs/TransformationTaskConfigurations"; /** * React button component which updates the transformation task configurations when clicked. @@ -8,12 +8,18 @@ import {updateTransformationTaskConfigurations} from "../transf-task-configs/Tra * @constructor */ export default function UpdateTransformationTaskConfigurationsButton() { - - return
- -
; + return ( +
+ +
+ ); } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPaletteProvider.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPaletteProvider.js index bc3f5e7b..b28e5857 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPaletteProvider.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPaletteProvider.js @@ -1,74 +1,74 @@ -import * as consts from './utilities/Constants'; +import * as consts from "./utilities/Constants"; export default class PlanQKPaletteProvider { + constructor(bpmnFactory, create, elementFactory, palette, translate) { + this.bpmnFactory = bpmnFactory; + this.create = create; + this.elementFactory = elementFactory; + this.translate = translate; - constructor(bpmnFactory, create, elementFactory, palette, translate) { + palette.registerProvider(this); + } - this.bpmnFactory = bpmnFactory; - this.create = create; - this.elementFactory = elementFactory; - this.translate = translate; + getPaletteEntries() { + return this.createPlanqkServiceTaskEntry(); + } - palette.registerProvider(this); - } + createPlanqkServiceTaskEntry() { + const { bpmnFactory, create, elementFactory, translate } = this; - getPaletteEntries() { - return this.createPlanqkServiceTaskEntry(); + function createPlanQKServiceTask(event) { + const businessObject = bpmnFactory.create(consts.PLANQK_SERVICE_TASK); + let shape = elementFactory.createShape({ + type: consts.PLANQK_SERVICE_TASK, + businessObject: businessObject, + }); + create.start(event, shape); } - createPlanqkServiceTaskEntry() { - const {bpmnFactory, create, elementFactory, translate} = this; - - function createPlanQKServiceTask(event) { - const businessObject = bpmnFactory.create(consts.PLANQK_SERVICE_TASK); - let shape = elementFactory.createShape({ - type: consts.PLANQK_SERVICE_TASK, - businessObject: businessObject - }); - create.start(event, shape); - } - - function createDataPool(event) { - const businessObject = bpmnFactory.create(consts.PLANQK_DATA_POOL); - let shape = elementFactory.createShape({ - type: consts.PLANQK_DATA_POOL, - businessObject: businessObject - }); - create.start(event, shape); - } - - return { - // add separator line to delimit the new group - 'planqk-separator': { - group: 'planqk', - separator: true - }, - 'create.planqk-service-task': { - group: 'planqk', - className: 'qwm-planqk-icon-palette-service-task', - title: translate('Creates a task that calls a PlanQK service you subscribed to'), - action: { - click: createPlanQKServiceTask, - dragstart: createPlanQKServiceTask, - } - }, - 'create.planqk-data-pool': { - group: 'planqk', - className: 'qwm-planqk-icon-palette-data-pool', - title: translate('Creates a PlanQK Data Pool to fetch data from'), - action: { - click: createDataPool, - dragstart: createDataPool, - } - }, - }; + function createDataPool(event) { + const businessObject = bpmnFactory.create(consts.PLANQK_DATA_POOL); + let shape = elementFactory.createShape({ + type: consts.PLANQK_DATA_POOL, + businessObject: businessObject, + }); + create.start(event, shape); } + + return { + // add separator line to delimit the new group + "planqk-separator": { + group: "planqk", + separator: true, + }, + "create.planqk-service-task": { + group: "planqk", + className: "qwm-planqk-icon-palette-service-task", + title: translate( + "Creates a task that calls a PlanQK service you subscribed to" + ), + action: { + click: createPlanQKServiceTask, + dragstart: createPlanQKServiceTask, + }, + }, + "create.planqk-data-pool": { + group: "planqk", + className: "qwm-planqk-icon-palette-data-pool", + title: translate("Creates a PlanQK Data Pool to fetch data from"), + action: { + click: createDataPool, + dragstart: createDataPool, + }, + }, + }; + } } PlanQKPaletteProvider.$inject = [ - 'bpmnFactory', - 'create', - 'elementFactory', - 'palette', - 'translate' + "bpmnFactory", + "create", + "elementFactory", + "palette", + "translate", ]; diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js index 35cd2c0d..5b445b40 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKPlugin.js @@ -1,23 +1,25 @@ -import React from 'react'; -import planqkStyles from './resources/css/planqk-icons.css'; -import PlanQKExtensionModule from './'; -import {startPlanqkReplacementProcess} from "./exec-completion/PlanQKServiceTaskCompletion"; +import React from "react"; +import planqkStyles from "./resources/css/planqk-icons.css"; +import PlanQKExtensionModule from "./"; +import { startPlanqkReplacementProcess } from "./exec-completion/PlanQKServiceTaskCompletion"; import TransformationButton from "../../editor/ui/TransformationButton"; -let planqkModdleDescriptor = require('./resources/planqk-service-task-ext.json'); +let planqkModdleDescriptor = require("./resources/planqk-service-task-ext.json"); /** * Plugin Object of the PlanQK extension. Used to register the plugin in the plugin handler of the modeler. */ export default { - name: 'planqk', - extensionModule: PlanQKExtensionModule, - moddleDescription: planqkModdleDescriptor, - styling: [planqkStyles], - transformExtensionButton: { - - return await startPlanqkReplacementProcess(xml); - } - }/>, -}; \ No newline at end of file + name: "planqk", + extensionModule: PlanQKExtensionModule, + moddleDescription: planqkModdleDescriptor, + styling: [planqkStyles], + transformExtensionButton: ( + { + return await startPlanqkReplacementProcess(xml); + }} + /> + ), +}; diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKRenderer.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKRenderer.js index 1ecf6050..30ffbd5f 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKRenderer.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKRenderer.js @@ -1,77 +1,82 @@ -import { - getRoundRectPath -} from 'bpmn-js/lib/draw/BpmnRenderUtil'; +import { getRoundRectPath } from "bpmn-js/lib/draw/BpmnRenderUtil"; -import { - is, -} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; import BpmnRenderer from "bpmn-js/lib/draw/BpmnRenderer"; -import * as consts from './utilities/Constants'; -import {getSVG} from "./SVGMap"; -import {drawDataElementSVG, drawTaskSVG} from "../../editor/util/RenderUtilities"; +import * as consts from "./utilities/Constants"; +import { getSVG } from "./SVGMap"; +import { + drawDataElementSVG, + drawTaskSVG, +} from "../../editor/util/RenderUtilities"; const HIGH_PRIORITY = 14001, - TASK_BORDER_RADIUS = 2; + TASK_BORDER_RADIUS = 2; export default class PlanQKRenderer extends BpmnRenderer { - constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { - super(config, eventBus, styles, pathMap, canvas, textRenderer, HIGH_PRIORITY); - - // define render functions for planqk extension elements - this.planqkHandlers = { - [consts.PLANQK_SERVICE_TASK]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:Task')(parentGfx, element); - drawTaskSVG(parentGfx, getSVG('TASK_TYPE_PLANQK_SERVICE_TASK')); - - return task; - }, - [consts.PLANQK_DATA_POOL]: function (self, parentGfx, element) { - const store = self.renderer('bpmn:DataStoreReference')(parentGfx, element); - drawDataElementSVG(parentGfx, getSVG('DATA_TYPE_DATA_POOL')); - - return store; - }, - }; - } - - renderer(type) { - return this.handlers[type]; - } - - canRender(element) { - - // only return true if handler for rendering is registered - return this.planqkHandlers[element.type]; + constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { + super( + config, + eventBus, + styles, + pathMap, + canvas, + textRenderer, + HIGH_PRIORITY + ); + + // define render functions for planqk extension elements + this.planqkHandlers = { + [consts.PLANQK_SERVICE_TASK]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:Task")(parentGfx, element); + drawTaskSVG(parentGfx, getSVG("TASK_TYPE_PLANQK_SERVICE_TASK")); + + return task; + }, + [consts.PLANQK_DATA_POOL]: function (self, parentGfx, element) { + const store = self.renderer("bpmn:DataStoreReference")( + parentGfx, + element + ); + drawDataElementSVG(parentGfx, getSVG("DATA_TYPE_DATA_POOL")); + + return store; + }, + }; + } + + renderer(type) { + return this.handlers[type]; + } + + canRender(element) { + // only return true if handler for rendering is registered + return this.planqkHandlers[element.type]; + } + + drawShape(parentNode, element) { + if (element.type in this.planqkHandlers) { + const h = this.planqkHandlers[element.type]; + + return h(this, parentNode, element); } + } - drawShape(parentNode, element) { - - if (element.type in this.planqkHandlers) { - const h = this.planqkHandlers[element.type]; - - return h(this, parentNode, element); - } + getShapePath(shape) { + if (is(shape, consts.PLANQK_SERVICE_TASK)) { + return getRoundRectPath(shape, TASK_BORDER_RADIUS); } - getShapePath(shape) { - if (is(shape, consts.PLANQK_SERVICE_TASK)) { - return getRoundRectPath(shape, TASK_BORDER_RADIUS); - } - - return super.getShapePath(shape); - } + return super.getShapePath(shape); + } } PlanQKRenderer.$inject = [ - 'config', - 'eventBus', - 'styles', - 'pathMap', - 'canvas', - 'textRenderer' + "config", + "eventBus", + "styles", + "pathMap", + "canvas", + "textRenderer", ]; - - - diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceMenuProvider.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceMenuProvider.js index 0b276c90..7f722280 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceMenuProvider.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceMenuProvider.js @@ -1,252 +1,296 @@ -import * as planqkReplaceOptions from './PlanQKReplaceOptions'; -import { is } from 'bpmn-js/lib/util/ModelUtil'; -import * as consts from './utilities/Constants'; -import { createMenuEntries, createMoreOptionsEntryWithReturn } from "../../editor/util/PopupMenuUtilities"; +import * as planqkReplaceOptions from "./PlanQKReplaceOptions"; +import { is } from "bpmn-js/lib/util/ModelUtil"; +import * as consts from "./utilities/Constants"; +import { + createMenuEntries, + createMoreOptionsEntryWithReturn, +} from "../../editor/util/PopupMenuUtilities"; import { getPluginConfig } from "../../editor/plugin/PluginConfigHandler"; -import * as planqkConsts from './utilities/Constants'; -import { filter } from 'min-dash'; -import { isDifferentType } from 'bpmn-js/lib/features/popup-menu/util/TypeUtil'; +import * as planqkConsts from "./utilities/Constants"; +import { filter } from "min-dash"; +import { isDifferentType } from "bpmn-js/lib/features/popup-menu/util/TypeUtil"; /** * Replace menu provider of the PlanQK plugin. Adds custom replacement entries to model PlanQk service tasks and PlanQK data pools. */ export default class PlanQKMenuProvider { - - constructor(popupMenu, translate, modeling, bpmnReplace, activeSubscriptions, dataPools, oauthInfoByAppMap, contextPad, bpmnFactory) { - - this.popupMenu = popupMenu; - this.replaceElement = bpmnReplace.replaceElement; - this.activeSubscriptions = activeSubscriptions; - this.dataPools = dataPools; - this.oauthInfoByAppMap = oauthInfoByAppMap; - this.modeling = modeling; - this.translate = translate; - this.contextPad = contextPad; - this.bpmnFactory = bpmnFactory; - this.bpmnReplace = bpmnReplace; - - popupMenu.registerProvider("bpmn-replace", this); + constructor( + popupMenu, + translate, + modeling, + bpmnReplace, + activeSubscriptions, + dataPools, + oauthInfoByAppMap, + contextPad, + bpmnFactory + ) { + this.popupMenu = popupMenu; + this.replaceElement = bpmnReplace.replaceElement; + this.activeSubscriptions = activeSubscriptions; + this.dataPools = dataPools; + this.oauthInfoByAppMap = oauthInfoByAppMap; + this.modeling = modeling; + this.translate = translate; + this.contextPad = contextPad; + this.bpmnFactory = bpmnFactory; + this.bpmnReplace = bpmnReplace; + + popupMenu.registerProvider("bpmn-replace", this); + } + + /** + * Overwrites the default menu provider to add custom menu entries for PlanQK service tasks, data pools and subscriptions + * + * @param element the element for which the replacement entries are requested + * @returns {*} an array with menu entries + */ + getPopupMenuEntries(element) { + const self = this; + return function (entries) { + // do not show entries for extension elements of other plugins + if ( + !(element.type.startsWith("bpmn") || element.type.startsWith("planqk")) + ) { + return entries; + } + + // add replacement entries for the active service subscription as replacements for a PlanQK service task + if (is(element, consts.PLANQK_SERVICE_TASK)) { + let serviceTaskEntries = self.createTaskEntries( + element, + self.activeSubscriptions + ); + return Object.assign(serviceTaskEntries, entries); + } + + // add replacement entries for the available data pools as replacements for a PlanQK data pool + if (is(element, consts.PLANQK_DATA_POOL)) { + const dataPoolEntries = self.createDataPoolEntries( + element, + self.dataPools + ); + return Object.assign(dataPoolEntries, entries); + } + + // add entry to replace a data object by a data pool + if (is(element, "bpmn:DataObjectReference")) { + let filteredOptions = filter( + planqkReplaceOptions.DATA_STORE, + isDifferentType(element) + ); + const planqkEntries = createMenuEntries( + element, + filteredOptions, + self.translate, + self.replaceElement + ); + return Object.assign(entries, planqkEntries); + } + + // add entry to replace a task by a PlanQK service task + if (is(element, "bpmn:Task")) { + const planqkEntries = self.createTaskEntries(element); + entries = Object.assign(planqkEntries, entries); + return entries; + } + + return entries; + }; + } + + /** + * Creates a MoreOptionsEntry for the popup menu which displays the active subscriptions for PlanQK service tasks. + * + * @param element The element the menu entries are requested for. + * @return {{'replace-by-more-planqk-task-options': {label: string, className: string, action: Function}}} + */ + createTaskEntries(element) { + const popupMenu = this.popupMenu; + const translate = this.translate; + const replaceElement = this.replaceElement; + const activeSubscriptions = this.activeSubscriptions; + const self = this; + let options = self.createPlanQKServiceTaskEntries( + element, + activeSubscriptions + ); + if (element.type !== consts.PLANQK_SERVICE_TASK) { + options = Object.assign( + createMenuEntries( + element, + planqkReplaceOptions.TASK, + translate, + replaceElement + ), + options + ); } - /** - * Overwrites the default menu provider to add custom menu entries for PlanQK service tasks, data pools and subscriptions - * - * @param element the element for which the replacement entries are requested - * @returns {*} an array with menu entries - */ - getPopupMenuEntries(element) { - const self = this; - return function (entries) { - - // do not show entries for extension elements of other plugins - if (!(element.type.startsWith('bpmn') || element.type.startsWith('planqk'))) { - return entries; - } - - // add replacement entries for the active service subscription as replacements for a PlanQK service task - if (is(element, consts.PLANQK_SERVICE_TASK)) { - let serviceTaskEntries = self.createTaskEntries(element, self.activeSubscriptions); - return Object.assign(serviceTaskEntries, entries); - } - - // add replacement entries for the available data pools as replacements for a PlanQK data pool - if (is(element, consts.PLANQK_DATA_POOL)) { - const dataPoolEntries = self.createDataPoolEntries(element, self.dataPools); - return Object.assign(dataPoolEntries, entries); - } - - // add entry to replace a data object by a data pool - if (is(element, 'bpmn:DataObjectReference')) { - let filteredOptions = filter(planqkReplaceOptions.DATA_STORE, isDifferentType(element)); - const planqkEntries = createMenuEntries(element, filteredOptions, self.translate, self.replaceElement); - return Object.assign(entries, planqkEntries); - } - - // add entry to replace a task by a PlanQK service task - if (is(element, 'bpmn:Task')) { - const planqkEntries = self.createTaskEntries(element); - entries = Object.assign(planqkEntries, entries); - return entries; - } - - return entries; - }; + return { + ["replace-by-more-planqk-task-options"]: createMoreOptionsEntryWithReturn( + element, + "PlanQK Service Tasks", + "PlanQK Service Tasks", + popupMenu, + options, + "qwm-planqk-icon-service-task" + ), + }; + } + + /** + * Create a replacement entry for every subscription the modeler was configured with + * + * @param element the element the replacement entries are requested for + * @param subscriptions the subscriptions the modeler was configured with + * @returns {{}} the created replacement entries for each subscription + */ + createPlanQKServiceTaskEntries(element, subscriptions) { + const subscriptionEntries = {}; + + // create a menu entry for each subscription + for (let subscription of subscriptions) { + subscriptionEntries["replace-with-" + subscription.id + " (2)"] = + this.createServiceTaskEntryNew(element, subscription); } - - /** - * Creates a MoreOptionsEntry for the popup menu which displays the active subscriptions for PlanQK service tasks. - * - * @param element The element the menu entries are requested for. - * @return {{'replace-by-more-planqk-task-options': {label: string, className: string, action: Function}}} - */ - createTaskEntries(element) { - const popupMenu = this.popupMenu; - const translate = this.translate; - const replaceElement = this.replaceElement; - const activeSubscriptions = this.activeSubscriptions; - const self = this; - let options = self.createPlanQKServiceTaskEntries(element, activeSubscriptions); - if (element.type !== consts.PLANQK_SERVICE_TASK) { - options = Object.assign(createMenuEntries(element, planqkReplaceOptions.TASK, translate, replaceElement), options); - } - - return { - ['replace-by-more-planqk-task-options']: createMoreOptionsEntryWithReturn( - element, - 'PlanQK Service Tasks', - 'PlanQK Service Tasks', - popupMenu, - options, - 'qwm-planqk-icon-service-task' - ) - }; - } - - /** - * Create a replacement entry for every subscription the modeler was configured with - * - * @param element the element the replacement entries are requested for - * @param subscriptions the subscriptions the modeler was configured with - * @returns {{}} the created replacement entries for each subscription - */ - createPlanQKServiceTaskEntries(element, subscriptions) { - const subscriptionEntries = {}; - - // create a menu entry for each subscription - for (let subscription of subscriptions) { - subscriptionEntries['replace-with-' + subscription.id + ' (2)'] = this.createServiceTaskEntryNew(element, subscription); - } - return subscriptionEntries; - } - - /** - * Creates a replacement menu entry which sets the properties of the element to the respective values of the - * subscription it represents if selected - * - * @param element the element the replacement entries are requested for - * @param subscription the subscription which is represented by this entry - * @returns {{action: action, className: string, label: string}} the replacement menu entry for the subscription - */ - createServiceTaskEntryNew(element, subscription) { - - const self = this.modeling; - const replaceElement = this.replaceElement; - const oauthInfoByAppMap = this.oauthInfoByAppMap; - const serviceEndpointBaseUrl = getPluginConfig('planqk').serviceEndpointBaseUrl; - - /* + return subscriptionEntries; + } + + /** + * Creates a replacement menu entry which sets the properties of the element to the respective values of the + * subscription it represents if selected + * + * @param element the element the replacement entries are requested for + * @param subscription the subscription which is represented by this entry + * @returns {{action: action, className: string, label: string}} the replacement menu entry for the subscription + */ + createServiceTaskEntryNew(element, subscription) { + const self = this.modeling; + const replaceElement = this.replaceElement; + const oauthInfoByAppMap = this.oauthInfoByAppMap; + const serviceEndpointBaseUrl = + getPluginConfig("planqk").serviceEndpointBaseUrl; + + /* create a replacement menu entry for a subscription which sets the properties of the selected PlanQK service task to the properties specified in the subscription */ - return { - label: subscription.api.name + '@' + subscription.application.name, - className: 'bpmn-icon-service', - action: function () { - - - - // replace selected element if it is not already a PlanQK service task - let newElement; - if (element.type !== planqkConsts.PLANQK_SERVICE_TASK) { - newElement = replaceElement(element, { type: planqkConsts.PLANQK_SERVICE_TASK }); - } - let serviceElement = newElement || element; - - const oAuthInfo = oauthInfoByAppMap[subscription.application.id]; - - // set the properties of the currently selected element to the respective values of the subscription - self.updateProperties(serviceElement, { - name: subscription.api.name, - subscriptionId: subscription.id, - applicationName: subscription.application.name, - serviceName: subscription.api.name, - tokenEndpoint: subscription.api.gatewayEndpoint, - consumerKey: oAuthInfo.consumerKey, - consumerSecret: oAuthInfo.consumerSecret, - serviceEndpoint: serviceEndpointBaseUrl + subscription.api.context + '/' + subscription.api.version, - data: '{}', - params: '{}', - result: '${output}' - }); - } - }; - } - - /** - * Creates replacement menu entries for each data pool the modeler was configured with - * - * @param element the element the replacement entries are requested for - * @param dataPools an array of data pools the modeler was configured with - * @returns {{}} the created replacement menu entries - */ - createDataPoolEntries(element, dataPools) { - let dataPoolEntries = {}; - - // add entry for a generic, unspecific data pool - if (element.businessObject.name) { - dataPoolEntries['replace-with-generic-data-pool'] = this.createNewDataPoolEntry(element, { - label: 'PlanQK Data Pool', name: '', link: '', description: '' - }); + return { + label: subscription.api.name + "@" + subscription.application.name, + className: "bpmn-icon-service", + action: function () { + // replace selected element if it is not already a PlanQK service task + let newElement; + if (element.type !== planqkConsts.PLANQK_SERVICE_TASK) { + newElement = replaceElement(element, { + type: planqkConsts.PLANQK_SERVICE_TASK, + }); } + let serviceElement = newElement || element; + + const oAuthInfo = oauthInfoByAppMap[subscription.application.id]; + + // set the properties of the currently selected element to the respective values of the subscription + self.updateProperties(serviceElement, { + name: subscription.api.name, + subscriptionId: subscription.id, + applicationName: subscription.application.name, + serviceName: subscription.api.name, + tokenEndpoint: subscription.api.gatewayEndpoint, + consumerKey: oAuthInfo.consumerKey, + consumerSecret: oAuthInfo.consumerSecret, + serviceEndpoint: + serviceEndpointBaseUrl + + subscription.api.context + + "/" + + subscription.api.version, + data: "{}", + params: "{}", + result: "${output}", + }); + }, + }; + } + + /** + * Creates replacement menu entries for each data pool the modeler was configured with + * + * @param element the element the replacement entries are requested for + * @param dataPools an array of data pools the modeler was configured with + * @returns {{}} the created replacement menu entries + */ + createDataPoolEntries(element, dataPools) { + let dataPoolEntries = {}; + + // add entry for a generic, unspecific data pool + if (element.businessObject.name) { + dataPoolEntries["replace-with-generic-data-pool"] = + this.createNewDataPoolEntry(element, { + label: "PlanQK Data Pool", + name: "", + link: "", + description: "", + }); + } - console.log(`Create menu entries for ${dataPools.length} data pools`); - - // create a replacement menu entry for each data pool - for (let dataPool of dataPools) { - if (element.businessObject.name !== dataPool.name) { - dataPoolEntries['replace-with-' + dataPool.id + ' (2)'] = this.createNewDataPoolEntry(element, dataPool); - } - } + console.log(`Create menu entries for ${dataPools.length} data pools`); - return dataPoolEntries; + // create a replacement menu entry for each data pool + for (let dataPool of dataPools) { + if (element.businessObject.name !== dataPool.name) { + dataPoolEntries["replace-with-" + dataPool.id + " (2)"] = + this.createNewDataPoolEntry(element, dataPool); + } } - /** - * Creates a replacement menu entry for the given data pool which sets the property of the currently selected element - * to the respective value of the data pool - * - * @param element the element the replacement entries are requested for - * @param dataPool the PlanQK data pool which is represented by this entry - * @returns {{action: action, className: string, label: *}} the created replacement menu entry for the data pool - */ - createNewDataPoolEntry(element, dataPool) { + return dataPoolEntries; + } - const self = this.modeling; + /** + * Creates a replacement menu entry for the given data pool which sets the property of the currently selected element + * to the respective value of the data pool + * + * @param element the element the replacement entries are requested for + * @param dataPool the PlanQK data pool which is represented by this entry + * @returns {{action: action, className: string, label: *}} the created replacement menu entry for the data pool + */ + createNewDataPoolEntry(element, dataPool) { + const self = this.modeling; - const label = dataPool.label || dataPool.name; + const label = dataPool.label || dataPool.name; - console.log(`Create menu entry for data pool ${dataPool.name}`); + console.log(`Create menu entry for data pool ${dataPool.name}`); - /* + /* create a replacement menu entry for a data pool which sets the properties of the selected PlanQK data pool to the properties specified in the data pool */ - return { - label: label, - className: 'qwm-planqk-logo', - action: function () { - - // set the properties of the currently selected element to the respective values of the data pool - self.updateProperties(element, { - name: dataPool.name, - dataPoolName: dataPool.name, - dataPoolId: dataPool.id, - dataPoolLink: dataPool.link, - dataPoolDescription: dataPool.description, - }); - } - }; - } + return { + label: label, + className: "qwm-planqk-logo", + action: function () { + // set the properties of the currently selected element to the respective values of the data pool + self.updateProperties(element, { + name: dataPool.name, + dataPoolName: dataPool.name, + dataPoolId: dataPool.id, + dataPoolLink: dataPool.link, + dataPoolDescription: dataPool.description, + }); + }, + }; + } } PlanQKMenuProvider.$inject = [ - 'popupMenu', - 'translate', - 'modeling', - 'bpmnReplace', - 'activeSubscriptions', - 'dataPools', - 'oauthInfoByAppMap', - 'contextPad', - 'bpmnFactory', + "popupMenu", + "translate", + "modeling", + "bpmnReplace", + "activeSubscriptions", + "dataPools", + "oauthInfoByAppMap", + "contextPad", + "bpmnFactory", ]; diff --git a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceOptions.js b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceOptions.js index 9d0c8bf5..7ad4f366 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceOptions.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/PlanQKReplaceOptions.js @@ -1,25 +1,25 @@ -import * as consts from './utilities/Constants'; +import * as consts from "./utilities/Constants"; // replace options for a BPMN task export const TASK = [ - { - id: 'planqk-service-task', - label: 'PlanQK Service Task', - className: 'qwm-planqk-icon-service-task', - target: { - type: consts.PLANQK_SERVICE_TASK - } + { + id: "planqk-service-task", + label: "PlanQK Service Task", + className: "qwm-planqk-icon-service-task", + target: { + type: consts.PLANQK_SERVICE_TASK, }, + }, ]; // replace options for a BPMN data store export const DATA_STORE = [ - { - id: 'planqk-data-pool', - label: 'PlanQK Data Pool', - className: 'qwm-planqk-icon-data-pool', - target: { - type: consts.PLANQK_DATA_POOL - } + { + id: "planqk-data-pool", + label: "PlanQK Data Pool", + className: "qwm-planqk-icon-data-pool", + target: { + type: consts.PLANQK_DATA_POOL, }, -]; \ No newline at end of file + }, +]; diff --git a/components/bpmn-q/modeler-component/extensions/planqk/SVGMap.js b/components/bpmn-q/modeler-component/extensions/planqk/SVGMap.js index bbf7ad60..1b50d1c1 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/SVGMap.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/SVGMap.js @@ -1,5 +1,5 @@ - -const PLAN_QK_LOGO = ' '; +const PLAN_QK_LOGO = + ' '; /** * Return SVG for the given ID @@ -8,16 +8,20 @@ const PLAN_QK_LOGO = '} */ export async function startPlanqkReplacementProcess(xml) { - let modeler = await createTempModelerFromXml(xml); - let elementRegistry = modeler.get('elementRegistry'); - let modeling = modeler.get('modeling'); - - // get root element of the current diagram - const definitions = modeler.getDefinitions(); - const rootProcess = getRootProcess(definitions); - - console.log(rootProcess); - - if (typeof rootProcess === 'undefined') { - console.log('Unable to retrieve root process element from definitions!'); - return { status: 'failed', cause: 'Unable to retrieve root process element from definitions!' }; - } - - // Mark process as executable - rootProcess.isExecutable = true; - - // get all PlanQK Service Tasks from the process - const planqkServiceTasks = getPlanqkServiceTasks(rootProcess, elementRegistry); - console.log('Process contains ' + planqkServiceTasks.length + ' Planqk service tasks to replace...'); - let isTransformed = !planqkServiceTasks || !planqkServiceTasks.length; - - // replace each PlanQK Service Task with the subprocess that implements service interaction to retrieve standard-compliant BPMN - for (let planqkServiceTask of planqkServiceTasks) { - - let replacementSuccess = false; - console.log('Replacing task with id %s with PlanQK service interaction subprocess ', planqkServiceTask.task.id); - const replacementSubprocess = require('../resources/workflows/planqk_service_call_subprocess.bpmn'); - - // replace PlanQK Service Task with replacementSubprocess - replacementSuccess = await replaceByInteractionSubprocess(definitions, planqkServiceTask.task, planqkServiceTask.parent, replacementSubprocess, modeler); - - if (!replacementSuccess) { - console.log('Replacement of service task with id ' + planqkServiceTask.task.id + ' failed. Aborting process!'); - return { - status: 'failed', - cause: 'Replacement of service task with id ' + planqkServiceTask.task.id + ' failed. Aborting process!' - }; - } + let modeler = await createTempModelerFromXml(xml); + let elementRegistry = modeler.get("elementRegistry"); + let modeling = modeler.get("modeling"); + + // get root element of the current diagram + const definitions = modeler.getDefinitions(); + const rootProcess = getRootProcess(definitions); + + console.log(rootProcess); + + if (typeof rootProcess === "undefined") { + console.log("Unable to retrieve root process element from definitions!"); + return { + status: "failed", + cause: "Unable to retrieve root process element from definitions!", + }; + } + + // Mark process as executable + rootProcess.isExecutable = true; + + // get all PlanQK Service Tasks from the process + const planqkServiceTasks = getPlanqkServiceTasks( + rootProcess, + elementRegistry + ); + console.log( + "Process contains " + + planqkServiceTasks.length + + " Planqk service tasks to replace..." + ); + let isTransformed = !planqkServiceTasks || !planqkServiceTasks.length; + + // replace each PlanQK Service Task with the subprocess that implements service interaction to retrieve standard-compliant BPMN + for (let planqkServiceTask of planqkServiceTasks) { + let replacementSuccess = false; + console.log( + "Replacing task with id %s with PlanQK service interaction subprocess ", + planqkServiceTask.task.id + ); + const replacementSubprocess = require("../resources/workflows/planqk_service_call_subprocess.bpmn"); + + // replace PlanQK Service Task with replacementSubprocess + replacementSuccess = await replaceByInteractionSubprocess( + definitions, + planqkServiceTask.task, + planqkServiceTask.parent, + replacementSubprocess, + modeler + ); + + if (!replacementSuccess) { + console.log( + "Replacement of service task with id " + + planqkServiceTask.task.id + + " failed. Aborting process!" + ); + return { + status: "failed", + cause: + "Replacement of service task with id " + + planqkServiceTask.task.id + + " failed. Aborting process!", + }; } - - // get all PlanQK data pools - const planqkDataPools = getPlanqkDataPools(rootProcess, elementRegistry); - console.log('Process contains ' + planqkDataPools.length + ' Planqk data pools to replace...'); - isTransformed = isTransformed && (!planqkDataPools || !planqkDataPools.length); - - // check if transformation is necessary - if (isTransformed) { - return { status: 'transformed', xml: xml }; - } - - const processContextVariables = {}; - - // replace each PlanQK data pool with a bpmn:dataStore and a process variable - for (let dataPool of planqkDataPools) { - - let replacementSuccess = false; - console.log('Replacing data pool with id %s ', dataPool.pool.id); - - // replace data pool by data store - replacementSuccess = await replaceByDataStore(definitions, dataPool.pool, dataPool.parent, processContextVariables, modeler); - - if (!replacementSuccess) { - console.log('Replacement of data pool with id ' + dataPool.pool.id + ' failed. Aborting process!'); - return { - status: 'failed', - cause: 'Replacement of data pool with id ' + dataPool.pool.id + ' failed. Aborting process!' - }; - } - } - - // create task to publish process variables in process context - if (Object.entries(processContextVariables).length > 0) { - createProcessContextVariablesTask(processContextVariables, rootProcess, definitions, modeler); + } + + // get all PlanQK data pools + const planqkDataPools = getPlanqkDataPools(rootProcess, elementRegistry); + console.log( + "Process contains " + + planqkDataPools.length + + " Planqk data pools to replace..." + ); + isTransformed = + isTransformed && (!planqkDataPools || !planqkDataPools.length); + + // check if transformation is necessary + if (isTransformed) { + return { status: "transformed", xml: xml }; + } + + const processContextVariables = {}; + + // replace each PlanQK data pool with a bpmn:dataStore and a process variable + for (let dataPool of planqkDataPools) { + let replacementSuccess = false; + console.log("Replacing data pool with id %s ", dataPool.pool.id); + + // replace data pool by data store + replacementSuccess = await replaceByDataStore( + definitions, + dataPool.pool, + dataPool.parent, + processContextVariables, + modeler + ); + + if (!replacementSuccess) { + console.log( + "Replacement of data pool with id " + + dataPool.pool.id + + " failed. Aborting process!" + ); + return { + status: "failed", + cause: + "Replacement of data pool with id " + + dataPool.pool.id + + " failed. Aborting process!", + }; } - - layout(modeling, elementRegistry, rootProcess); - - const transformedXml = await getXml(modeler); - return { status: 'transformed', xml: transformedXml }; + } + + // create task to publish process variables in process context + if (Object.entries(processContextVariables).length > 0) { + createProcessContextVariablesTask( + processContextVariables, + rootProcess, + definitions, + modeler + ); + } + + layout(modeling, elementRegistry, rootProcess); + + const transformedXml = await getXml(modeler); + return { status: "transformed", xml: transformedXml }; } /** @@ -119,35 +165,63 @@ export async function startPlanqkReplacementProcess(xml) { * @param modeler The current modeler * @return {Promise} True if replacement was successful, False else */ -async function replaceByInteractionSubprocess(definitions, task, parent, replacement, modeler) { - - if (!replacement) { - console.log('Replacement interaction subprocess is undefined. Aborting replacement!'); - return false; - } - - // get the root process of the replacement fragment - let replacementProcess = getRootProcess(await getDefinitionsFromXml(replacement)); - let replacementIASubprocess = getSingleFlowElement(replacementProcess); - if (replacementIASubprocess === null || replacementIASubprocess === undefined) { - console.log('Unable to retrieve replacement subprocess: ', replacement); - return false; - } - - console.log('Replacement interaction subprocess: ', replacementIASubprocess); - - // replace task by replacementIASubprocess - let result = insertShape(definitions, parent, replacementIASubprocess, {}, true, modeler, task); - const subprocessShape = result['element']; - subprocessShape.collapsed = false; - subprocessShape.isExpanded = true; - - // create inputs and outputs for the subprocess - const bpmnFactory = modeler.get('bpmnFactory'); - applyTaskInput2Subprocess(task, result['element'].businessObject, bpmnFactory); - applyTaskOutput2Subprocess(task, result['element'].businessObject, bpmnFactory); - - return result['success']; +async function replaceByInteractionSubprocess( + definitions, + task, + parent, + replacement, + modeler +) { + if (!replacement) { + console.log( + "Replacement interaction subprocess is undefined. Aborting replacement!" + ); + return false; + } + + // get the root process of the replacement fragment + let replacementProcess = getRootProcess( + await getDefinitionsFromXml(replacement) + ); + let replacementIASubprocess = getSingleFlowElement(replacementProcess); + if ( + replacementIASubprocess === null || + replacementIASubprocess === undefined + ) { + console.log("Unable to retrieve replacement subprocess: ", replacement); + return false; + } + + console.log("Replacement interaction subprocess: ", replacementIASubprocess); + + // replace task by replacementIASubprocess + let result = insertShape( + definitions, + parent, + replacementIASubprocess, + {}, + true, + modeler, + task + ); + const subprocessShape = result["element"]; + subprocessShape.collapsed = false; + subprocessShape.isExpanded = true; + + // create inputs and outputs for the subprocess + const bpmnFactory = modeler.get("bpmnFactory"); + applyTaskInput2Subprocess( + task, + result["element"].businessObject, + bpmnFactory + ); + applyTaskOutput2Subprocess( + task, + result["element"].businessObject, + bpmnFactory + ); + + return result["success"]; } /** @@ -158,18 +232,37 @@ async function replaceByInteractionSubprocess(definitions, task, parent, replace * @param bpmnFactory */ function applyTaskInput2Subprocess(taskBO, subprocessBO, bpmnFactory) { - - const taskIo = getCamundaInputOutput(taskBO, bpmnFactory); - const subProcessIo = getCamundaInputOutput(subprocessBO, bpmnFactory); - - subProcessIo.inputParameters.push(...taskIo.inputParameters); - - setInputParameter(subprocessBO, "params", taskBO.params, bpmnFactory); - setInputParameter(subprocessBO, "data", taskBO.data, bpmnFactory); - setInputParameter(subprocessBO, "serviceEndpoint", taskBO.serviceEndpoint, bpmnFactory); - setInputParameter(subprocessBO, "tokenEndpoint", taskBO.tokenEndpoint, bpmnFactory); - setInputParameter(subprocessBO, "consumerSecret", taskBO.consumerSecret, bpmnFactory); - setInputParameter(subprocessBO, "consumerKey", taskBO.consumerKey, bpmnFactory); + const taskIo = getCamundaInputOutput(taskBO, bpmnFactory); + const subProcessIo = getCamundaInputOutput(subprocessBO, bpmnFactory); + + subProcessIo.inputParameters.push(...taskIo.inputParameters); + + setInputParameter(subprocessBO, "params", taskBO.params, bpmnFactory); + setInputParameter(subprocessBO, "data", taskBO.data, bpmnFactory); + setInputParameter( + subprocessBO, + "serviceEndpoint", + taskBO.serviceEndpoint, + bpmnFactory + ); + setInputParameter( + subprocessBO, + "tokenEndpoint", + taskBO.tokenEndpoint, + bpmnFactory + ); + setInputParameter( + subprocessBO, + "consumerSecret", + taskBO.consumerSecret, + bpmnFactory + ); + setInputParameter( + subprocessBO, + "consumerKey", + taskBO.consumerKey, + bpmnFactory + ); } /** @@ -180,13 +273,12 @@ function applyTaskInput2Subprocess(taskBO, subprocessBO, bpmnFactory) { * @param bpmnFactory */ function applyTaskOutput2Subprocess(taskBO, subprocessBO, bpmnFactory) { + const taskIo = getCamundaInputOutput(taskBO, bpmnFactory); + const subProcessIo = getCamundaInputOutput(subprocessBO, bpmnFactory); - const taskIo = getCamundaInputOutput(taskBO, bpmnFactory); - const subProcessIo = getCamundaInputOutput(subprocessBO, bpmnFactory); - - subProcessIo.outputParameters.push(...taskIo.outputParameters); + subProcessIo.outputParameters.push(...taskIo.outputParameters); - setOutputParameter(subprocessBO, "result", taskBO.params, bpmnFactory); + setOutputParameter(subprocessBO, "result", taskBO.params, bpmnFactory); } /** @@ -200,19 +292,33 @@ function applyTaskOutput2Subprocess(taskBO, subprocessBO, bpmnFactory) { * @param modeler The current modeler the workflow is opened in. * @return {Promise} True if replacement was successful, False else. */ -async function replaceByDataStore(definitions, dataPool, parentProcess, processContextVariables, modeler) { - - const bpmnFactory = modeler.get('bpmnFactory'); - - // add data pool link to details attribute of data pool - const parameters = dataPool.get(dataConsts.DETAILS) || []; - const linkParam = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, { name: consts.DATA_POOL_LINK, value: dataPool.get(consts.DATA_POOL_LINK) }); - parameters.push(linkParam); - - // transform data pool like data store map - const result = transformDataStoreMap(dataPool, parentProcess, definitions, processContextVariables, modeler); - - return result.success; +async function replaceByDataStore( + definitions, + dataPool, + parentProcess, + processContextVariables, + modeler +) { + const bpmnFactory = modeler.get("bpmnFactory"); + + // add data pool link to details attribute of data pool + const parameters = dataPool.get(dataConsts.DETAILS) || []; + const linkParam = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, { + name: consts.DATA_POOL_LINK, + value: dataPool.get(consts.DATA_POOL_LINK), + }); + parameters.push(linkParam); + + // transform data pool like data store map + const result = transformDataStoreMap( + dataPool, + parentProcess, + definitions, + processContextVariables, + modeler + ); + + return result.success; } /** @@ -223,24 +329,26 @@ async function replaceByDataStore(definitions, dataPool, parentProcess, processC * @return {*[]} Array of {task: flowElement, parent: processBo} for each PlanQK service task */ export function getPlanqkServiceTasks(process, elementRegistry) { + // retrieve parent object for later replacement + const processBo = elementRegistry.get(process.id); + + const planqkServiceTasks = []; + const flowElements = process.flowElements; + for (let i = 0; i < flowElements.length; i++) { + let flowElement = flowElements[i]; + if (flowElement.$type && flowElement.$type === consts.PLANQK_SERVICE_TASK) { + planqkServiceTasks.push({ task: flowElement, parent: processBo }); + } - // retrieve parent object for later replacement - const processBo = elementRegistry.get(process.id); - - const planqkServiceTasks = []; - const flowElements = process.flowElements; - for (let i = 0; i < flowElements.length; i++) { - let flowElement = flowElements[i]; - if (flowElement.$type && flowElement.$type === consts.PLANQK_SERVICE_TASK) { - planqkServiceTasks.push({ task: flowElement, parent: processBo }); - } - - // recursively retrieve service tasks if subprocess is found - if (flowElement.$type && flowElement.$type === 'bpmn:SubProcess') { - Array.prototype.push.apply(planqkServiceTasks, getPlanqkServiceTasks(flowElement, elementRegistry)); - } + // recursively retrieve service tasks if subprocess is found + if (flowElement.$type && flowElement.$type === "bpmn:SubProcess") { + Array.prototype.push.apply( + planqkServiceTasks, + getPlanqkServiceTasks(flowElement, elementRegistry) + ); } - return planqkServiceTasks; + } + return planqkServiceTasks; } /** @@ -251,22 +359,24 @@ export function getPlanqkServiceTasks(process, elementRegistry) { * @return {*[]} Array of {task: flowElement, parent: processBo} for each PlanQK service task */ export function getPlanqkDataPools(process, elementRegistry) { + // retrieve parent object for later replacement + const processBo = elementRegistry.get(process.id); + + const planqkDataPools = []; + const flowElements = process.flowElements; + for (let i = 0; i < flowElements.length; i++) { + let flowElement = flowElements[i]; + if (flowElement.$type && flowElement.$type === consts.PLANQK_DATA_POOL) { + planqkDataPools.push({ pool: flowElement, parent: processBo }); + } - // retrieve parent object for later replacement - const processBo = elementRegistry.get(process.id); - - const planqkDataPools = []; - const flowElements = process.flowElements; - for (let i = 0; i < flowElements.length; i++) { - let flowElement = flowElements[i]; - if (flowElement.$type && flowElement.$type === consts.PLANQK_DATA_POOL) { - planqkDataPools.push({ pool: flowElement, parent: processBo }); - } - - // recursively retrieve service tasks if subprocess is found - if (flowElement.$type && flowElement.$type === 'bpmn:SubProcess') { - Array.prototype.push.apply(planqkDataPools, getPlanqkDataPools(flowElement, elementRegistry)); - } + // recursively retrieve service tasks if subprocess is found + if (flowElement.$type && flowElement.$type === "bpmn:SubProcess") { + Array.prototype.push.apply( + planqkDataPools, + getPlanqkDataPools(flowElement, elementRegistry) + ); } - return planqkDataPools; -} \ No newline at end of file + } + return planqkDataPools; +} diff --git a/components/bpmn-q/modeler-component/extensions/planqk/index.js b/components/bpmn-q/modeler-component/extensions/planqk/index.js index 11711e30..60cd0709 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/index.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/index.js @@ -1,24 +1,41 @@ import PlanQKReplaceMenuProvider from "./PlanQKReplaceMenuProvider"; import PlanQKPaletteProvider from "./PlanQKPaletteProvider"; import PlanQKRenderer from "./PlanQKRenderer"; -import ServiceTaskPropertiesProvider from './propeties/service-task-properties/ServiceTaskPropertiesProvider'; +import ServiceTaskPropertiesProvider from "./propeties/service-task-properties/ServiceTaskPropertiesProvider"; import DataPoolPropertiesProvider from "./propeties/data-pool-properties/DataPoolPropertiesProvider"; -import {getPluginConfig} from "../../editor/plugin/PluginConfigHandler"; +import { getPluginConfig } from "../../editor/plugin/PluginConfigHandler"; export default { - __init__: ["planqkPaletteProvider", "customRenderer", "serviceTaskPropertiesProvider", "dataPoolPropertiesProvider", "planqkReplaceMenuProvider", "activeSubscriptions", "dataPools"], - planqkReplaceMenuProvider: ["type", PlanQKReplaceMenuProvider], - planqkPaletteProvider: ["type", PlanQKPaletteProvider], - customRenderer: ['type', PlanQKRenderer], - serviceTaskPropertiesProvider: ['type', ServiceTaskPropertiesProvider], - dataPoolPropertiesProvider: ['type', DataPoolPropertiesProvider], - activeSubscriptions: ['type', () => { - return getPluginConfig('planqk').subscriptions || []; - }], - oauthInfoByAppMap: ['type', () => { - return getPluginConfig('planqk').oauthInfoByAppMap || {}; - }], - dataPools: ['type', () => { - return getPluginConfig('planqk').dataPools || []; - }], + __init__: [ + "planqkPaletteProvider", + "customRenderer", + "serviceTaskPropertiesProvider", + "dataPoolPropertiesProvider", + "planqkReplaceMenuProvider", + "activeSubscriptions", + "dataPools", + ], + planqkReplaceMenuProvider: ["type", PlanQKReplaceMenuProvider], + planqkPaletteProvider: ["type", PlanQKPaletteProvider], + customRenderer: ["type", PlanQKRenderer], + serviceTaskPropertiesProvider: ["type", ServiceTaskPropertiesProvider], + dataPoolPropertiesProvider: ["type", DataPoolPropertiesProvider], + activeSubscriptions: [ + "type", + () => { + return getPluginConfig("planqk").subscriptions || []; + }, + ], + oauthInfoByAppMap: [ + "type", + () => { + return getPluginConfig("planqk").oauthInfoByAppMap || {}; + }, + ], + dataPools: [ + "type", + () => { + return getPluginConfig("planqk").dataPools || []; + }, + ], }; diff --git a/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolProperties.js b/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolProperties.js index 7be3a668..7a0e7e29 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolProperties.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolProperties.js @@ -1,5 +1,9 @@ -import {TextFieldEntry, isTextFieldEntryEdited, TextAreaEntry} from '@bpmn-io/properties-panel'; -import {useService} from 'bpmn-js-properties-panel'; +import { + TextFieldEntry, + isTextFieldEntryEdited, + TextAreaEntry, +} from "@bpmn-io/properties-panel"; +import { useService } from "bpmn-js-properties-panel"; /** * Properties group for the properties panel. Contains entries for all attributes for the PlanQK Data Pool. @@ -9,29 +13,28 @@ import {useService} from 'bpmn-js-properties-panel'; * @constructor */ export default function DataPoolProperties(element) { - - return [ - { - id: 'name', - element, - component: Name, - isEdited: isTextFieldEntryEdited - }, - - { - id: 'link', - element, - component: Link, - isEdited: isTextFieldEntryEdited - }, - - { - id: 'description', - element, - component: Description, - isEdited: isTextFieldEntryEdited - } - ]; + return [ + { + id: "name", + element, + component: Name, + isEdited: isTextFieldEntryEdited, + }, + + { + id: "link", + element, + component: Link, + isEdited: isTextFieldEntryEdited, + }, + + { + id: "description", + element, + component: Description, + isEdited: isTextFieldEntryEdited, + }, + ]; } /** @@ -42,31 +45,31 @@ export default function DataPoolProperties(element) { * @constructor */ function Name(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - const modeling = useService('modeling'); - - const getValue = () => { - return element.businessObject.dataPoolName; - }; - - const setValue = (value) => { - modeling.updateProperties(element, { - dataPoolName: value, - }); - }; - - return TextFieldEntry({ - element, - id: 'data_pool_name', - label: translate('Data Pool Name'), - description: translate('Provide a name or select a data pool.'), - getValue, - setValue, - debounce, + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + const modeling = useService("modeling"); + + const getValue = () => { + return element.businessObject.dataPoolName; + }; + + const setValue = (value) => { + modeling.updateProperties(element, { + dataPoolName: value, }); + }; + + return TextFieldEntry({ + element, + id: "data_pool_name", + label: translate("Data Pool Name"), + description: translate("Provide a name or select a data pool."), + getValue, + setValue, + debounce, + }); } /** @@ -77,31 +80,31 @@ function Name(props) { * @constructor */ function Link(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - const modeling = useService('modeling'); - - const getValue = () => { - return element.businessObject.dataPoolLink; - }; - - const setValue = (value) => { - modeling.updateProperties(element, { - dataPoolLink: value, - }); - }; - - return TextFieldEntry({ - element, - id: 'data_pool_link', - label: translate('Link to PlanQK Platform'), - description: translate('Provide a link or select a data pool.'), - getValue, - setValue, - debounce, + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + const modeling = useService("modeling"); + + const getValue = () => { + return element.businessObject.dataPoolLink; + }; + + const setValue = (value) => { + modeling.updateProperties(element, { + dataPoolLink: value, }); + }; + + return TextFieldEntry({ + element, + id: "data_pool_link", + label: translate("Link to PlanQK Platform"), + description: translate("Provide a link or select a data pool."), + getValue, + setValue, + debounce, + }); } /** @@ -112,30 +115,30 @@ function Link(props) { * @constructor */ function Description(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - const modeling = useService('modeling'); - - const getValue = () => { - return element.businessObject.dataPoolDescription; - }; - - const setValue = (value) => { - modeling.updateProperties(element, { - dataPoolDescription: value, - }); - }; - - return TextAreaEntry({ - element, - id: 'data_pool_description', - label: translate('Short Description'), - description: translate('Provide a description or select a data pool.'), - getValue, - setValue, - debounce, - rows: 3 + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + const modeling = useService("modeling"); + + const getValue = () => { + return element.businessObject.dataPoolDescription; + }; + + const setValue = (value) => { + modeling.updateProperties(element, { + dataPoolDescription: value, }); + }; + + return TextAreaEntry({ + element, + id: "data_pool_description", + label: translate("Short Description"), + description: translate("Provide a description or select a data pool."), + getValue, + setValue, + debounce, + rows: 3, + }); } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolPropertiesProvider.js b/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolPropertiesProvider.js index 55f4ed30..74c8e94a 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolPropertiesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/propeties/data-pool-properties/DataPoolPropertiesProvider.js @@ -1,11 +1,10 @@ -import planqkDataPoolProps from './DataPoolProperties'; -import * as consts from '../../utilities/Constants'; +import planqkDataPoolProps from "./DataPoolProperties"; +import * as consts from "../../utilities/Constants"; -import {is} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; const LOW_PRIORITY = 500; - /** * A provider of the properties panel of the bpmn-js modeler. Provides custom groups for PlanQK data pools. * @@ -13,38 +12,39 @@ const LOW_PRIORITY = 500; * @param {Function} translate The translate function of the bpmn-js modeler. */ export default function DataPoolPropertiesProvider(propertiesPanel, translate) { - + /** + * Return the groups provided for the given element. + * + * @param element The element the groups are requested for. + * + * @return groups middleware + */ + this.getGroups = function (element) { /** - * Return the groups provided for the given element. + * Add custom group for PlanQK Data Pools * - * @param element The element the groups are requested for. + * @param {Object[]} groups * - * @return groups middleware + * @return {Object[]} modified groups */ - this.getGroups = function (element) { - - /** - * Add custom group for PlanQK Data Pools - * - * @param {Object[]} groups - * - * @return {Object[]} modified groups - */ - return function (groups) { + return function (groups) { + // Adds properties group for PlanQK Data Pool properties + if (is(element, consts.PLANQK_DATA_POOL)) { + groups.unshift(createDataPoolDetailsGroup(element, translate)); + } - // Adds properties group for PlanQK Data Pool properties - if (is(element, consts.PLANQK_DATA_POOL)) { - groups.unshift(createDataPoolDetailsGroup(element, translate)); - } - - return groups; - }; + return groups; }; + }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); + propertiesPanel.registerProvider(LOW_PRIORITY, this); } -DataPoolPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'dataPools']; +DataPoolPropertiesProvider.$inject = [ + "propertiesPanel", + "translate", + "dataPools", +]; /** * Creates a group to display the name, link a description attributes of the given PlanQK data pool @@ -54,10 +54,9 @@ DataPoolPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'dataPools * @return {{entries: ([{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element}]|*), id: string, label}} */ function createDataPoolDetailsGroup(element, translate) { - - return { - id: 'dataPoolProperties', - label: translate('Data Pool Properties'), - entries: planqkDataPoolProps(element) - }; + return { + id: "dataPoolProperties", + label: translate("Data Pool Properties"), + entries: planqkDataPoolProps(element), + }; } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/InputOutputProperties.js b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/InputOutputProperties.js index 532f72ee..a61531a8 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/InputOutputProperties.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/InputOutputProperties.js @@ -1,5 +1,8 @@ -import {isTextFieldEntryEdited, TextAreaEntry} from '@bpmn-io/properties-panel'; -import {useService} from 'bpmn-js-properties-panel'; +import { + isTextFieldEntryEdited, + TextAreaEntry, +} from "@bpmn-io/properties-panel"; +import { useService } from "bpmn-js-properties-panel"; /** * Properties group for input and output data of PlanQK service tasks. @@ -9,28 +12,27 @@ import {useService} from 'bpmn-js-properties-panel'; * @constructor */ export default function (element) { - - return [ - { - id: 'inputData', - element, - component: InputData, - isEdited: isTextFieldEntryEdited - }, - { - id: 'inputParams', - element, - component: InputParams, - isEdited: isTextFieldEntryEdited - }, - - { - id: 'result', - element, - component: ResultData, - isEdited: isTextFieldEntryEdited - } - ]; + return [ + { + id: "inputData", + element, + component: InputData, + isEdited: isTextFieldEntryEdited, + }, + { + id: "inputParams", + element, + component: InputParams, + isEdited: isTextFieldEntryEdited, + }, + + { + id: "result", + element, + component: ResultData, + isEdited: isTextFieldEntryEdited, + }, + ]; } /** @@ -41,30 +43,32 @@ export default function (element) { * @constructor */ function InputData(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.data || '{}'; - }; - - const setValue = (data) => { - return element.businessObject.data = data; - }; - - return TextAreaEntry({ - element, - id: 'inputDataTxt', - label: translate('Input Data'), - description: translate('Provide constant JSON string or start typing "${}" to create an expression.'), - getValue, - setValue, - disabled: false, - debounce, - rows: 3 - }); + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return element.businessObject.data || "{}"; + }; + + const setValue = (data) => { + return (element.businessObject.data = data); + }; + + return TextAreaEntry({ + element, + id: "inputDataTxt", + label: translate("Input Data"), + description: translate( + 'Provide constant JSON string or start typing "${}" to create an expression.' + ), + getValue, + setValue, + disabled: false, + debounce, + rows: 3, + }); } /** @@ -75,30 +79,32 @@ function InputData(props) { * @constructor */ function InputParams(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.params || '{}'; - }; - - const setValue = (params) => { - return element.businessObject.params = params; - }; - - return TextAreaEntry({ - element, - id: 'inputParamsTxt', - label: translate('Parameters'), - description: translate('Provide constant JSON string or start typing "${}" to create an expression.'), - getValue, - setValue, - disabled: false, - debounce, - rows: 3 - }); + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return element.businessObject.params || "{}"; + }; + + const setValue = (params) => { + return (element.businessObject.params = params); + }; + + return TextAreaEntry({ + element, + id: "inputParamsTxt", + label: translate("Parameters"), + description: translate( + 'Provide constant JSON string or start typing "${}" to create an expression.' + ), + getValue, + setValue, + disabled: false, + debounce, + rows: 3, + }); } /** @@ -109,28 +115,28 @@ function InputParams(props) { * @constructor */ function ResultData(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.result || '${}'; - }; - - const setValue = (result) => { - return element.businessObject.result = result; - }; - - return TextAreaEntry({ - element, - id: 'resultDataTxt', - label: translate('Result'), - description: translate('Start typing "${}" to create an expression.'), - getValue, - setValue, - disabled: false, - debounce, - rows: 1 - }); + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return element.businessObject.result || "${}"; + }; + + const setValue = (result) => { + return (element.businessObject.result = result); + }; + + return TextAreaEntry({ + element, + id: "resultDataTxt", + label: translate("Result"), + description: translate('Start typing "${}" to create an expression.'), + getValue, + setValue, + disabled: false, + debounce, + rows: 1, + }); } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/ServiceTaskPropertiesProvider.js b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/ServiceTaskPropertiesProvider.js index 9bf157b4..a2aa11b9 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/ServiceTaskPropertiesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/ServiceTaskPropertiesProvider.js @@ -1,50 +1,53 @@ -import planqkServiceProps from './SubscriptionProperties'; -import inputOutputProps from './InputOutputProperties'; +import planqkServiceProps from "./SubscriptionProperties"; +import inputOutputProps from "./InputOutputProperties"; -import {is} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; const LOW_PRIORITY = 500; - /** * A provider of the properties panel of the bpmn-js modeler. Provides custom groups for PlanQK service tasks. * * @param propertiesPanel The properties panel this provider is registered at. * @param {Function} translate The translate function of the bpmn-js modeler. */ -export default function ServiceTaskPropertiesProvider(propertiesPanel, translate) { - +export default function ServiceTaskPropertiesProvider( + propertiesPanel, + translate +) { + /** + * Return the groups provided for the given element. + * + * @param element The element the groups are requested for. + * + * @return groups middleware + */ + this.getGroups = function (element) { /** - * Return the groups provided for the given element. + * Add custom properties group for PlanQK service task * - * @param element The element the groups are requested for. + * @param {Object[]} groups * - * @return groups middleware + * @return {Object[]} modified groups */ - this.getGroups = function (element) { - - /** - * Add custom properties group for PlanQK service task - * - * @param {Object[]} groups - * - * @return {Object[]} modified groups - */ - return function (groups) { + return function (groups) { + if (is(element, "planqk:ServiceTask")) { + groups.unshift(createInputOutputGroup(element, translate)); + groups.unshift(createSubscriptionGroup(element, translate)); + } - if (is(element, 'planqk:ServiceTask')) { - groups.unshift(createInputOutputGroup(element, translate)); - groups.unshift(createSubscriptionGroup(element, translate)); - } - - return groups; - }; + return groups; }; + }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); + propertiesPanel.registerProvider(LOW_PRIORITY, this); } -ServiceTaskPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'activeSubscriptions']; +ServiceTaskPropertiesProvider.$inject = [ + "propertiesPanel", + "translate", + "activeSubscriptions", +]; /** * Creates a group to display subscription details of the given PlanQK service task @@ -54,13 +57,11 @@ ServiceTaskPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'active * @return {{entries: ([{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element}]|*), id: string, label}} */ function createSubscriptionGroup(element, translate) { - - return { - id: 'subscriptionProperties', - label: translate('Subscription'), - entries: planqkServiceProps(element) - }; - + return { + id: "subscriptionProperties", + label: translate("Subscription"), + entries: planqkServiceProps(element), + }; } /** @@ -71,10 +72,9 @@ function createSubscriptionGroup(element, translate) { * @return {{entries: ([{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element},{component: (function(*): VNode<*>), isEdited: ((function(*): *)|*), id: string, element}]|*), id: string, label}} */ function createInputOutputGroup(element, translate) { - - return { - id: 'inputOutputProperties', - label: translate('Input / Output'), - entries: inputOutputProps(element) - }; + return { + id: "inputOutputProperties", + label: translate("Input / Output"), + entries: inputOutputProps(element), + }; } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/SubscriptionProperties.js b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/SubscriptionProperties.js index 963c7d72..76e9ffab 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/SubscriptionProperties.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/propeties/service-task-properties/SubscriptionProperties.js @@ -1,5 +1,8 @@ -import {isTextFieldEntryEdited, TextAreaEntry} from '@bpmn-io/properties-panel'; -import {useService} from 'bpmn-js-properties-panel'; +import { + isTextFieldEntryEdited, + TextAreaEntry, +} from "@bpmn-io/properties-panel"; +import { useService } from "bpmn-js-properties-panel"; /** * Properties group for subscription details of PlanQK service tasks. @@ -9,99 +12,103 @@ import {useService} from 'bpmn-js-properties-panel'; * @constructor */ export default function (element) { - - return [ - { - id: 'applications', - element, - component: Applications, - isEdited: isTextFieldEntryEdited - }, - - { - id: 'subscribedServices', - element, - component: SubscribedServices, - isEdited: isTextFieldEntryEdited - }, - - { - id: 'subscriptionId', - element, - component: SubscriptionId, - isEdited: isTextFieldEntryEdited - } - ]; + return [ + { + id: "applications", + element, + component: Applications, + isEdited: isTextFieldEntryEdited, + }, + + { + id: "subscribedServices", + element, + component: SubscribedServices, + isEdited: isTextFieldEntryEdited, + }, + + { + id: "subscriptionId", + element, + component: SubscriptionId, + isEdited: isTextFieldEntryEdited, + }, + ]; } /** * TextAreaEntry for the application name property of the PlanQK service task. */ function Applications(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.applicationName || 'Select an application and service'; - }; - - return TextAreaEntry({ - element, - id: 'subscribing_app_name', - label: translate('Application'), - getValue, - disabled: true, - debounce, - rows: 1 - }); + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return ( + element.businessObject.applicationName || + "Select an application and service" + ); + }; + + return TextAreaEntry({ + element, + id: "subscribing_app_name", + label: translate("Application"), + getValue, + disabled: true, + debounce, + rows: 1, + }); } /** * TextAreaEntry for the service name property of the PlanQK service task. */ function SubscribedServices(props) { - const {element} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.serviceName || 'Select an application and service'; - }; - - return TextAreaEntry({ - element, - id: 'subscribed_service_name', - label: translate('Service'), - getValue, - disabled: true, - debounce, - rows: 1 - }); + const { element } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return ( + element.businessObject.serviceName || "Select an application and service" + ); + }; + + return TextAreaEntry({ + element, + id: "subscribed_service_name", + label: translate("Service"), + getValue, + disabled: true, + debounce, + rows: 1, + }); } /** * TextAreaEntry for the subscription ID property of the PlanQK service task. */ function SubscriptionId(props) { - const {element, id} = props; - - const translate = useService('translate'); - const debounce = useService('debounceInput'); - - const getValue = () => { - return element.businessObject.subscriptionId || 'undefined'; - }; - - return TextAreaEntry({ - element, - id: 'subscription_id', - label: translate('Subscription Id'), - getValue, - disabled: true, - debounce, - rows: 1 - }); + const { element, id } = props; + + const translate = useService("translate"); + const debounce = useService("debounceInput"); + + const getValue = () => { + return element.businessObject.subscriptionId || "undefined"; + }; + + return TextAreaEntry({ + element, + id: "subscription_id", + label: translate("Subscription Id"), + getValue, + disabled: true, + debounce, + rows: 1, + }); } diff --git a/components/bpmn-q/modeler-component/extensions/planqk/resources/css/planqk-icons.css b/components/bpmn-q/modeler-component/extensions/planqk/resources/css/planqk-icons.css index dec10147..195ce158 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/resources/css/planqk-icons.css +++ b/components/bpmn-q/modeler-component/extensions/planqk/resources/css/planqk-icons.css @@ -1,49 +1,49 @@ .qwm-planqk-icon-palette-service-task { - content: url("../icons/service-task-palette-icon.svg"); - padding: 11px 8px 11px 10px; - box-sizing: border-box; - margin: 0; - outline: none; + content: url("../icons/service-task-palette-icon.svg"); + padding: 11px 8px 11px 10px; + box-sizing: border-box; + margin: 0; + outline: none; } .qwm-planqk-icon-palette-data-pool { - content: url("../icons/data-pool-palette-icon.svg"); - padding: 10px; - box-sizing: border-box; - margin: 0; - outline: none; + content: url("../icons/data-pool-palette-icon.svg"); + padding: 10px; + box-sizing: border-box; + margin: 0; + outline: none; } .qwm-planqk-icon-service-task:before { - content: ""; - width: 20px; - height: 20px; - background-size: contain; - background-image: url("../icons/service-task-palette-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 20px; + background-size: contain; + background-image: url("../icons/service-task-palette-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .qwm-planqk-icon-data-pool:before { - content: ""; - width: 20px; - height: 20px; - background-size: contain; - background-image: url("../icons/data-pool-palette-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 20px; + height: 20px; + background-size: contain; + background-image: url("../icons/data-pool-palette-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .qwm-planqk-logo:before { - content: ""; - display: block; - background: url("../icons/planqk-icon.png") no-repeat; - width: 20px; - height: 20px; - float: left; - margin: 0 6px 0 0; -} \ No newline at end of file + content: ""; + display: block; + background: url("../icons/planqk-icon.png") no-repeat; + width: 20px; + height: 20px; + float: left; + margin: 0 6px 0 0; +} diff --git a/components/bpmn-q/modeler-component/extensions/planqk/resources/planqk-service-task-ext.json b/components/bpmn-q/modeler-component/extensions/planqk/resources/planqk-service-task-ext.json index 15e8f6c2..b74e1049 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/resources/planqk-service-task-ext.json +++ b/components/bpmn-q/modeler-component/extensions/planqk/resources/planqk-service-task-ext.json @@ -1,6 +1,6 @@ { "name": "ServiceTask", - "superClass": [ "bpmn:Task" ], + "superClass": ["bpmn:Task"], "uri": "https://platform.planqk.de", "prefix": "planqk", "xml": { @@ -9,9 +9,7 @@ "types": [ { "name": "ServiceTask", - "superClass": [ - "bpmn:Task" - ], + "superClass": ["bpmn:Task"], "properties": [ { "name": "subscriptionId", @@ -67,7 +65,7 @@ }, { "name": "DataPool", - "superClass": [ "dataflow:DataStoreMap" ], + "superClass": ["dataflow:DataStoreMap"], "properties": [ { "name": "dataPoolName", diff --git a/components/bpmn-q/modeler-component/extensions/planqk/utilities/Constants.js b/components/bpmn-q/modeler-component/extensions/planqk/utilities/Constants.js index 0a2f2fd7..aa7e3ff3 100644 --- a/components/bpmn-q/modeler-component/extensions/planqk/utilities/Constants.js +++ b/components/bpmn-q/modeler-component/extensions/planqk/utilities/Constants.js @@ -1,8 +1,8 @@ // type names of the PlanQK extension elements -export const PLANQK_SERVICE_TASK = 'planqk:ServiceTask'; -export const PLANQK_DATA_POOL = 'planqk:DataPool'; +export const PLANQK_SERVICE_TASK = "planqk:ServiceTask"; +export const PLANQK_DATA_POOL = "planqk:DataPool"; // attribute values of the PlanQK extension elements -export const DATA_POOL_NAME = 'dataPoolName'; -export const DATA_POOL_LINK = 'dataPoolLink'; -export const DATA_POOL_DESCRIPTION = 'dataPoolDescription'; +export const DATA_POOL_NAME = "dataPoolName"; +export const DATA_POOL_LINK = "dataPoolLink"; +export const DATA_POOL_DESCRIPTION = "dataPoolDescription"; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaConstants.js b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaConstants.js index d9318874..bef0e2f3 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaConstants.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaConstants.js @@ -1,14 +1,15 @@ // Type names of the QHAna extension element -export const QHANA_SERVICE_TASK = 'qhana:QHAnaServiceTask'; -export const QHANA_SERVICE_STEP_TASK = 'qhana:QHAnaServiceStepTask'; +export const QHANA_SERVICE_TASK = "qhana:QHAnaServiceTask"; +export const QHANA_SERVICE_STEP_TASK = "qhana:QHAnaServiceStepTask"; // Property name of the QHAna extension element -export const IDENTIFIER = 'qhanaIdentifier'; -export const VERSION = 'qhanaVersion'; -export const NAME = 'qhanaName'; -export const DESCRIPTION = 'qhanaDescription'; -export const NEXT_STEP = 'qhanaNextStep'; +export const IDENTIFIER = "qhanaIdentifier"; +export const VERSION = "qhanaVersion"; +export const NAME = "qhanaName"; +export const DESCRIPTION = "qhanaDescription"; +export const NEXT_STEP = "qhanaNextStep"; // Unique identifiers of the icon SVGs -export const TASK_TYPE_QHANA_SERVICE_TASK = 'TASK_TYPE_QHANA_SERVICE_TASK'; -export const TASK_TYPE_QHANA_SERVICE_STEP_TASK = 'TASK_TYPE_QHANA_SERVICE_STEP_TASK'; +export const TASK_TYPE_QHANA_SERVICE_TASK = "TASK_TYPE_QHANA_SERVICE_TASK"; +export const TASK_TYPE_QHANA_SERVICE_STEP_TASK = + "TASK_TYPE_QHANA_SERVICE_STEP_TASK"; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js index 4219772f..d230b13f 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/QHAnaPlugin.js @@ -1,38 +1,44 @@ -import React from 'react'; +import React from "react"; -import QHAnaExtensionModule from './'; -import TransformationButton from '../../editor/ui/TransformationButton'; -import ExtensibleButton from '../../editor/ui/ExtensibleButton'; -import UpdateQHAnaConfigurationsButton from './ui/UpdateQHAnaConfigurationsButton'; -import QHAnaConfigurationsTab from './configurations/QHAnaConfigurationsTab'; -import {startQHAnaReplacementProcess} from './transformation/QHAnaTransformationHandler'; -import qhanaStyles from './resources/qhana-icons.css'; +import QHAnaExtensionModule from "./"; +import TransformationButton from "../../editor/ui/TransformationButton"; +import ExtensibleButton from "../../editor/ui/ExtensibleButton"; +import UpdateQHAnaConfigurationsButton from "./ui/UpdateQHAnaConfigurationsButton"; +import QHAnaConfigurationsTab from "./configurations/QHAnaConfigurationsTab"; +import { startQHAnaReplacementProcess } from "./transformation/QHAnaTransformationHandler"; +import qhanaStyles from "./resources/qhana-icons.css"; -let qhanaModdleDescriptor = require('./resources/qhana-extension.json'); +let qhanaModdleDescriptor = require("./resources/qhana-extension.json"); /** * Plugin Object of the QHAna extension. Used to register the plugin in the plugin handler of the modeler. */ export default { - name: 'qhana', - buttons: []} - title="QHAna" - styleClass="qwm-qhana-service-task" - description="Show buttons of the QHAna plugin"/> - ], - configTabs: [ - { - tabId: 'QHAnaEndpointsTab', - tabTitle: 'QHAna Endpoints', - configTab: QHAnaConfigurationsTab, - }, - ], - extensionModule: QHAnaExtensionModule, - moddleDescription: qhanaModdleDescriptor, - styling: [qhanaStyles], - transformExtensionButton: { - return await startQHAnaReplacementProcess(xml); - } - }/>, -}; \ No newline at end of file + name: "qhana", + buttons: [ + ]} + title="QHAna" + styleClass="qwm-qhana-service-task" + description="Show buttons of the QHAna plugin" + />, + ], + configTabs: [ + { + tabId: "QHAnaEndpointsTab", + tabTitle: "QHAna Endpoints", + configTab: QHAnaConfigurationsTab, + }, + ], + extensionModule: QHAnaExtensionModule, + moddleDescription: qhanaModdleDescriptor, + styling: [qhanaStyles], + transformExtensionButton: ( + { + return await startQHAnaReplacementProcess(xml); + }} + /> + ), +}; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/config/QHAnaConfigManager.js b/components/bpmn-q/modeler-component/extensions/qhana/config/QHAnaConfigManager.js index cafc8f44..b8c3a5bf 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/config/QHAnaConfigManager.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/config/QHAnaConfigManager.js @@ -1,9 +1,9 @@ -import {getPluginConfig} from '../../../editor/plugin/PluginConfigHandler'; +import { getPluginConfig } from "../../../editor/plugin/PluginConfigHandler"; // default config entries used if no value is specified in the initial plugin config const defaultConfig = { - qhanaListPluginsURL: process.env.QHANA_LIST_PLUGINS_URL, - qhanqGetPluginURL: process.env.QHANA_GET_PLUGIN_URL, + qhanaListPluginsURL: process.env.QHANA_LIST_PLUGINS_URL, + qhanqGetPluginURL: process.env.QHANA_GET_PLUGIN_URL, }; const config = {}; @@ -14,10 +14,13 @@ const config = {}; * @return {string} the url */ export function getListPluginsURL() { - if (config.qhanaListPluginsURL === undefined) { - setListPluginsURL(getPluginConfig('qhana').qhanaListPluginsURL || defaultConfig.qhanaListPluginsURL); - } - return config.qhanaListPluginsURL; + if (config.qhanaListPluginsURL === undefined) { + setListPluginsURL( + getPluginConfig("qhana").qhanaListPluginsURL || + defaultConfig.qhanaListPluginsURL + ); + } + return config.qhanaListPluginsURL; } /** @@ -26,11 +29,10 @@ export function getListPluginsURL() { * @return {string} the url */ export function setListPluginsURL(url) { - if (url !== null && url !== undefined) { - - // remove trailing slashes - config.qhanaListPluginsURL = url.replace(/\/$/, ''); - } + if (url !== null && url !== undefined) { + // remove trailing slashes + config.qhanaListPluginsURL = url.replace(/\/$/, ""); + } } /** @@ -39,10 +41,13 @@ export function setListPluginsURL(url) { * @return {string} the url */ export function getGetPluginsURL() { - if (config.qhanqGetPluginURL === undefined) { - setGetPluginsURL(getPluginConfig('qhana').qhanqGetPluginURL || defaultConfig.qhanqGetPluginURL); - } - return config.qhanqGetPluginURL; + if (config.qhanqGetPluginURL === undefined) { + setGetPluginsURL( + getPluginConfig("qhana").qhanqGetPluginURL || + defaultConfig.qhanqGetPluginURL + ); + } + return config.qhanqGetPluginURL; } /** @@ -51,9 +56,8 @@ export function getGetPluginsURL() { * @return {string} the url */ export function setGetPluginsURL(url) { - if (url !== null && url !== undefined) { - - // remove trailing slashes - config.qhanqGetPluginURL = url; - } -} \ No newline at end of file + if (url !== null && url !== undefined) { + // remove trailing slashes + config.qhanqGetPluginURL = url; + } +} diff --git a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js index 8b00a45c..bac9d2f0 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurations.js @@ -1,86 +1,102 @@ -import ConfigurationsEndpoint from '../../../editor/configurations/ConfigurationEndpoint'; -import * as configManager from '../config/QHAnaConfigManager'; -import * as consts from '../QHAnaConstants'; +import ConfigurationsEndpoint from "../../../editor/configurations/ConfigurationEndpoint"; +import * as configManager from "../config/QHAnaConfigManager"; +import * as consts from "../QHAnaConstants"; /** * Custom ConfigurationsEndpoint for the QHAna Plugin. Extends the ConfigurationsEndpoint to fetch the configurations directly * from the QHAna plugin registry. */ export default class QHAnaConfigurationsEndpoint extends ConfigurationsEndpoint { - constructor() { - super(''); - } - - /** - * Fetch all plugins from the QHAna plugin registry and transform them into configurations for QHAna service tasks. - */ - fetchConfigurations() { - - const self = this; - - // fetch all QHAna services from the QHAna plugin registry - fetch(configManager.getListPluginsURL()) - .then(response => response.json()) - .then(data => { - try { - const allServices = data.data.items; - console.log('Received ' + allServices.length + ' QHAna services: '); - - let serviceId; - - // fetch details for each service and create configuration - allServices.forEach(function (service) { - serviceId = service.resourceKey.pluginId; - - // fetch plugin details for serviceId - fetch(configManager.getGetPluginsURL() + serviceId + '/') - .then(response => response.json()) - .then(data => { - const serviceData = data.data; - console.log('Received QHAna service details for service ' + serviceId); - console.log(serviceData); - - // create configuration from serviceData - self._configurations.push(createConfigurationForServiceData(serviceData)); - - }) - .catch(error => { - console.error('Error fetching QHAna service with id ' + serviceId + ': \n' + error); - }); - }); - - } catch (error) { - console.error('Error while parsing QHAna services from ' + configManager.getGetPluginsURL() + ': \n' + error); - } - }) - .catch(error => { - console.error('Error fetching configurations from ' + configManager.getListPluginsURL() + ': \n' + error); - }); - } - - /** - * Returns all Configurations for QHAna service tasks which are saved in this endpoint. - */ - getQHAnaServiceConfigurations() { - return this.getConfigurations(consts.QHANA_SERVICE_TASK); - } - - /** - * Retruns the configuration with the given ID. - * - * @param id The given ID. - * @return {*} - */ - getQHAnaServiceConfiguration(id) { - return this.getConfiguration(id); - } - - /** - * Updates the saved configurations by fetching again all plugins from the QHAna plugin registry - */ - updateQHAnaServiceConfigurations() { - this.fetchConfigurations(); - } + constructor() { + super(""); + } + + /** + * Fetch all plugins from the QHAna plugin registry and transform them into configurations for QHAna service tasks. + */ + fetchConfigurations() { + const self = this; + + // fetch all QHAna services from the QHAna plugin registry + fetch(configManager.getListPluginsURL()) + .then((response) => response.json()) + .then((data) => { + try { + const allServices = data.data.items; + console.log("Received " + allServices.length + " QHAna services: "); + + let serviceId; + + // fetch details for each service and create configuration + allServices.forEach(function (service) { + serviceId = service.resourceKey.pluginId; + + // fetch plugin details for serviceId + fetch(configManager.getGetPluginsURL() + serviceId + "/") + .then((response) => response.json()) + .then((data) => { + const serviceData = data.data; + console.log( + "Received QHAna service details for service " + serviceId + ); + console.log(serviceData); + + // create configuration from serviceData + self._configurations.push( + createConfigurationForServiceData(serviceData) + ); + }) + .catch((error) => { + console.error( + "Error fetching QHAna service with id " + + serviceId + + ": \n" + + error + ); + }); + }); + } catch (error) { + console.error( + "Error while parsing QHAna services from " + + configManager.getGetPluginsURL() + + ": \n" + + error + ); + } + }) + .catch((error) => { + console.error( + "Error fetching configurations from " + + configManager.getListPluginsURL() + + ": \n" + + error + ); + }); + } + + /** + * Returns all Configurations for QHAna service tasks which are saved in this endpoint. + */ + getQHAnaServiceConfigurations() { + return this.getConfigurations(consts.QHANA_SERVICE_TASK); + } + + /** + * Retruns the configuration with the given ID. + * + * @param id The given ID. + * @return {*} + */ + getQHAnaServiceConfiguration(id) { + return this.getConfiguration(id); + } + + /** + * Updates the saved configurations by fetching again all plugins from the QHAna plugin registry + */ + updateQHAnaServiceConfigurations() { + this.fetchConfigurations(); + } } let configEndpointInstance; @@ -91,11 +107,11 @@ let configEndpointInstance; * @return {QHAnaConfigurationsEndpoint} the current instance. */ export function instance() { - if (!configEndpointInstance) { - // create new QHAna endpoint if no instance exists - configEndpointInstance = new QHAnaConfigurationsEndpoint(); - } - return configEndpointInstance; + if (!configEndpointInstance) { + // create new QHAna endpoint if no instance exists + configEndpointInstance = new QHAnaConfigurationsEndpoint(); + } + return configEndpointInstance; } /** @@ -106,110 +122,109 @@ export function instance() { * @return {{name, description, appliesTo: string, groupLabel: string, attributes: [{editable: string, name: string, label: string, type: string, value, bindTo: {name: string}},{editable: string, name: string, label: string, type: string, value, bindTo: {name: string}},{editable: string, name: string, label: string, type: string, value, bindTo: {name: string}},{editable: string, name: string, label: string, type: string, value, bindTo: {name: string}}], id}} */ export function createConfigurationForServiceData(serviceData) { - const configuration = { - name: serviceData.title, - id: serviceData.identifier, - description: serviceData.description, - appliesTo: 'qhana:QHAnaServiceTask', - groupLabel: 'Service Properties', - attributes: [ - { - name: 'identifier', - label: 'Identifier', - type: 'string', - value: serviceData.identifier, - editable: 'true', - bindTo: { - name: 'qhanaIdentifier', - }, - }, - { - name: 'version', - label: 'Version', - type: 'string', - value: serviceData.version, - editable: 'true', - bindTo: { - name: 'qhanaVersion', - }, - }, - { - name: 'name', - label: 'Title', - type: 'string', - value: serviceData.title, - editable: 'true', - bindTo: { - name: 'qhanaName', - }, - }, - { - name: 'description', - label: 'Description', - type: 'string', - value: serviceData.description, - editable: 'true', - bindTo: { - name: 'qhanaDescription', - }, - }, - ] - }; - - // add inputs - serviceData.entryPoint.dataInput.forEach(function (input, index) { - configuration.attributes.push({ - name: 'qinput.input_' + index, - label: 'qinput.' + (input.parameter || 'input_' + index), - type: 'String', - value: [ - {name: 'value', value: ''}, - {name: 'dataType', value: input.dataType}, - {name: 'required', value: '"' + input.required + '"'}, - {name: 'parameter', value: input.parameter}, - {name: 'contentType', value: JSON.stringify(input.contentType)}, - ], - hide: true, - bindTo: { - name: 'inputParameters', - type: 'camunda:InputMapParameter', - }, - }); + const configuration = { + name: serviceData.title, + id: serviceData.identifier, + description: serviceData.description, + appliesTo: "qhana:QHAnaServiceTask", + groupLabel: "Service Properties", + attributes: [ + { + name: "identifier", + label: "Identifier", + type: "string", + value: serviceData.identifier, + editable: "true", + bindTo: { + name: "qhanaIdentifier", + }, + }, + { + name: "version", + label: "Version", + type: "string", + value: serviceData.version, + editable: "true", + bindTo: { + name: "qhanaVersion", + }, + }, + { + name: "name", + label: "Title", + type: "string", + value: serviceData.title, + editable: "true", + bindTo: { + name: "qhanaName", + }, + }, + { + name: "description", + label: "Description", + type: "string", + value: serviceData.description, + editable: "true", + bindTo: { + name: "qhanaDescription", + }, + }, + ], + }; + + // add inputs + serviceData.entryPoint.dataInput.forEach(function (input, index) { + configuration.attributes.push({ + name: "qinput.input_" + index, + label: "qinput." + (input.parameter || "input_" + index), + type: "String", + value: [ + { name: "value", value: "" }, + { name: "dataType", value: input.dataType }, + { name: "required", value: '"' + input.required + '"' }, + { name: "parameter", value: input.parameter }, + { name: "contentType", value: JSON.stringify(input.contentType) }, + ], + hide: true, + bindTo: { + name: "inputParameters", + type: "camunda:InputMapParameter", + }, }); - - // add outputs - serviceData.entryPoint.dataOutput.forEach(function (output, index) { - - const value = [ - { - value: '', - dataType: output.dataType, - required: '"' + output.required + '"', - contentType: JSON.stringify(output.contentType) - }, - ]; - - // [ - // {name: 'value', value: ''}, - // {name: 'dataType', value: output.dataType}, - // {name: 'required', value: '"' + output.required + '"'}, - // {name: 'contentType', value: JSON.stringify(output.contentType)}, - // ]; - - configuration.attributes.push({ - name: 'qoutput.output_' + index, - label: 'qoutput.output_' + index, - type: 'String', - value: `\$\{output\}`,//JSON.stringify(value), - hide: true, - bindTo: { - name: 'outputParameters', - type: 'camunda:OutputMapParameter', - }, - }); + }); + + // add outputs + serviceData.entryPoint.dataOutput.forEach(function (output, index) { + const value = [ + { + value: "", + dataType: output.dataType, + required: '"' + output.required + '"', + contentType: JSON.stringify(output.contentType), + }, + ]; + + // [ + // {name: 'value', value: ''}, + // {name: 'dataType', value: output.dataType}, + // {name: 'required', value: '"' + output.required + '"'}, + // {name: 'contentType', value: JSON.stringify(output.contentType)}, + // ]; + + configuration.attributes.push({ + name: "qoutput.output_" + index, + label: "qoutput.output_" + index, + type: "String", + value: `\$\{output\}`, //JSON.stringify(value), + hide: true, + bindTo: { + name: "outputParameters", + type: "camunda:OutputMapParameter", + }, }); + }); - console.log('Created configuration for QHAna service'); - console.log(configuration); - return configuration; + console.log("Created configuration for QHAna service"); + console.log(configuration); + return configuration; } diff --git a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurationsTab.js b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurationsTab.js index 3bf22ff5..1a78dd9b 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurationsTab.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/configurations/QHAnaConfigurationsTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import * as configManager from '../config/QHAnaConfigManager'; +import React, { useState } from "react"; +import * as configManager from "../config/QHAnaConfigManager"; /** * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change @@ -9,44 +9,50 @@ import * as configManager from '../config/QHAnaConfigManager'; * @constructor */ export default function QHAnaConfigurationsTab() { + const [listPluginsEndpoint, setListPluginsEndpoint] = useState( + configManager.getListPluginsURL() + ); + const [getPluginEndpoint, setGetPluginEndpoint] = useState( + configManager.getGetPluginsURL() + ); - const [listPluginsEndpoint, setListPluginsEndpoint] = useState(configManager.getListPluginsURL()); - const [getPluginEndpoint, setGetPluginEndpoint] = useState(configManager.getGetPluginsURL()); + // save changed values on close + QHAnaConfigurationsTab.prototype.onClose = () => { + configManager.setListPluginsURL(listPluginsEndpoint); + configManager.setGetPluginsURL(getPluginEndpoint); + }; - // save changed values on close - QHAnaConfigurationsTab.prototype.onClose = () => { - configManager.setListPluginsURL(listPluginsEndpoint); - configManager.setGetPluginsURL(getPluginEndpoint); - }; - - return (<> -

QHAna endpoint configuration:

- - - - - - - - - - - -
List Plugins Endpoint - setListPluginsEndpoint(event.target.value)}/> -
Get Plugin Endpoint - setGetPluginEndpoint(event.target.value)}/> -
- ); + return ( + <> +

QHAna endpoint configuration:

+ + + + + + + + + + + +
List Plugins Endpoint + setListPluginsEndpoint(event.target.value)} + /> +
Get Plugin Endpoint + setGetPluginEndpoint(event.target.value)} + /> +
+ + ); } -QHAnaConfigurationsTab.prototype.config = () => { -}; \ No newline at end of file +QHAnaConfigurationsTab.prototype.config = () => {}; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/index.js b/components/bpmn-q/modeler-component/extensions/qhana/index.js index 983d033d..a7db9e89 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/index.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/index.js @@ -1,14 +1,10 @@ -import QHAnaRenderer from './rendering/QHAnaRenderer'; -import QHAnaReplaceMenuProvider from './menu/QHAnaReplaceMenuProvider'; -import QHAnaPropertiesProvider from './properties/QHAnaPropertiesProvider'; +import QHAnaRenderer from "./rendering/QHAnaRenderer"; +import QHAnaReplaceMenuProvider from "./menu/QHAnaReplaceMenuProvider"; +import QHAnaPropertiesProvider from "./properties/QHAnaPropertiesProvider"; export default { - __init__: [ - 'qhanaRenderer', - 'qhanaReplaceMenu', - 'qhanaPropertiesProvider', - ], - qhanaRenderer: ['type', QHAnaRenderer], - qhanaReplaceMenu: ['type', QHAnaReplaceMenuProvider], - qhanaPropertiesProvider: ['type', QHAnaPropertiesProvider], -}; \ No newline at end of file + __init__: ["qhanaRenderer", "qhanaReplaceMenu", "qhanaPropertiesProvider"], + qhanaRenderer: ["type", QHAnaRenderer], + qhanaReplaceMenu: ["type", QHAnaReplaceMenuProvider], + qhanaPropertiesProvider: ["type", QHAnaPropertiesProvider], +}; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceMenuProvider.js b/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceMenuProvider.js index 9daf6f90..f30f0ec7 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceMenuProvider.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceMenuProvider.js @@ -1,205 +1,255 @@ -import {is} from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; import { - createConfigurationsEntries, - handleConfigurationsAction, -} from '../../../editor/configurations/ConfigurationsUtil'; -import * as consts from '../QHAnaConstants'; -import {instance as qhanaServiceConfigs} from '../configurations/QHAnaConfigurations'; -import {createMenuEntries, createMoreOptionsEntryWithReturn} from '../../../editor/util/PopupMenuUtilities'; -import * as qhanaReplaceOptions from './QHAnaReplaceOptions'; -import * as dataConsts from '../../data-extension/Constants'; -import {appendElement} from '../../../editor/util/ModellingUtilities'; -import { filter } from 'min-dash'; -import { isDifferentType } from 'bpmn-js/lib/features/popup-menu/util/TypeUtil'; + createConfigurationsEntries, + handleConfigurationsAction, +} from "../../../editor/configurations/ConfigurationsUtil"; +import * as consts from "../QHAnaConstants"; +import { instance as qhanaServiceConfigs } from "../configurations/QHAnaConfigurations"; +import { + createMenuEntries, + createMoreOptionsEntryWithReturn, +} from "../../../editor/util/PopupMenuUtilities"; +import * as qhanaReplaceOptions from "./QHAnaReplaceOptions"; +import * as dataConsts from "../../data-extension/Constants"; +import { appendElement } from "../../../editor/util/ModellingUtilities"; +import { filter } from "min-dash"; +import { isDifferentType } from "bpmn-js/lib/features/popup-menu/util/TypeUtil"; /** * Menu Provider for bpmn-replace which is opened for a diagram element. Adds replacement entries to replace the custom * elements of the QHAna plugin. */ export default class QHAnaReplaceMenuProvider { + constructor( + popupMenu, + bpmnReplace, + modeling, + bpmnFactory, + commandStack, + translate, + elementFactory, + create, + autoPlace + ) { + popupMenu.registerProvider("bpmn-replace", this); + + this.replaceElement = bpmnReplace.replaceElement; + this.modeling = modeling; + this.bpmnFactory = bpmnFactory; + this.popupMenu = popupMenu; + this.commandStack = commandStack; + this.translate = translate; + this.elementFactory = elementFactory; + this.create = create; + this.autoPlace = autoPlace; + } + + /** + * Overwrites the default menu provider to add menu entries to replace the element with QHAna extension elements + * + * @param element the element for which the replacement entries are requested + * @returns {*} an array with menu entries + */ + getPopupMenuEntries(element) { + const self = this; + return function (entries) { + // do not show entries for extension elements of other plugins + if ( + !(element.type.startsWith("bpmn") || element.type.startsWith("qhana")) + ) { + return entries; + } + + // set entries to apply QHAna service task configurations to a QHAna service task + if (is(element, consts.QHANA_SERVICE_TASK)) { + // create menu entries for each configuration loaded from the QHAna Configurations Endpoint + const configEntries = createConfigurationsEntries( + element, + "qwm-qhana-service-task", + qhanaServiceConfigs().getQHAnaServiceConfigurations(), + self.bpmnFactory, + self.modeling, + self.commandStack, + self.replaceElement + ); - constructor(popupMenu, bpmnReplace, modeling, bpmnFactory, commandStack, translate, elementFactory, create, autoPlace) { - popupMenu.registerProvider("bpmn-replace", this); - - this.replaceElement = bpmnReplace.replaceElement; - this.modeling = modeling; - this.bpmnFactory = bpmnFactory; - this.popupMenu = popupMenu; - this.commandStack = commandStack; - this.translate = translate; - this.elementFactory = elementFactory; - this.create = create; - this.autoPlace = autoPlace; - } - - /** - * Overwrites the default menu provider to add menu entries to replace the element with QHAna extension elements - * - * @param element the element for which the replacement entries are requested - * @returns {*} an array with menu entries - */ - getPopupMenuEntries(element) { - const self = this; - return function (entries) { - - // do not show entries for extension elements of other plugins - if (!(element.type.startsWith('bpmn') || element.type.startsWith('qhana'))) { - return entries; - } - - // set entries to apply QHAna service task configurations to a QHAna service task - if (is(element, consts.QHANA_SERVICE_TASK)) { - - // create menu entries for each configuration loaded from the QHAna Configurations Endpoint - const configEntries = createConfigurationsEntries(element, 'qwm-qhana-service-task', qhanaServiceConfigs().getQHAnaServiceConfigurations(), self.bpmnFactory, self.modeling, self.commandStack, self.replaceElement); - - if (Object.entries(configEntries).length > 0) { - return configEntries; - } - } - - // add entries for QHAna service tasks and QHAna service step tasks as replacement for BPMN tasks - if (is(element, 'bpmn:Task')) { - const qhanaEntry = self.createQHAnaEntry(element); - return Object.assign(qhanaEntry, entries); - } - - return entries; - }; - } - - /** - * Create menu entries to replace a BPMN task element with the QHAna extension elements, namely a QHAna Service Step Task, - * a QHAna service task or a configuration for a QHAna service task. - * - * @param element The element the menu entries are requested for - * @return {{'replace-by-qhana-tasks': {label: string, className: string, action: Function}}} - */ - createQHAnaEntry(element) { - const popupMenu = this.popupMenu; - const translate = this.translate; - const replaceElement = this.replaceElement; - - let filteredOptions = filter(qhanaReplaceOptions.TASK, isDifferentType(element)); - const qhanaTasksEntries = createMenuEntries(element, filteredOptions, translate, replaceElement); - - // get entry for QHAna service tasks and its configurations - const qhanaServiceTaskEntry = this.createQHAnaServiceTaskEntry(element); - - const qhanaEntries = Object.assign(qhanaTasksEntries, qhanaServiceTaskEntry); - return { - ['replace-by-qhana-tasks']: createMoreOptionsEntryWithReturn( - element, - 'QHAna Tasks', - 'QHAna Tasks', - popupMenu, - qhanaEntries, - 'qwm-qhana-service-task' - ) - }; - } - - /** - * Create a MoreOptionsEntry consisting of menu entries for all configurations loaded for QHAna service tasks and - * a QHAna service task element. - * - * @param element The element the menu entries are requested for. - * @return {{'replace-by-qhana-options': {label: string, className: string, action: Function}}} - */ - createQHAnaServiceTaskEntry(element) { - const bpmnFactory = this.bpmnFactory; - const modeling = this.modeling; - const popupMenu = this.popupMenu; - const replaceElement = this.replaceElement; - const commandStack = this.commandStack; - const elementFactory = this.elementFactory; - const create = this.create; - const autoPlace = this.autoPlace; - - /* + if (Object.entries(configEntries).length > 0) { + return configEntries; + } + } + + // add entries for QHAna service tasks and QHAna service step tasks as replacement for BPMN tasks + if (is(element, "bpmn:Task")) { + const qhanaEntry = self.createQHAnaEntry(element); + return Object.assign(qhanaEntry, entries); + } + + return entries; + }; + } + + /** + * Create menu entries to replace a BPMN task element with the QHAna extension elements, namely a QHAna Service Step Task, + * a QHAna service task or a configuration for a QHAna service task. + * + * @param element The element the menu entries are requested for + * @return {{'replace-by-qhana-tasks': {label: string, className: string, action: Function}}} + */ + createQHAnaEntry(element) { + const popupMenu = this.popupMenu; + const translate = this.translate; + const replaceElement = this.replaceElement; + + let filteredOptions = filter( + qhanaReplaceOptions.TASK, + isDifferentType(element) + ); + const qhanaTasksEntries = createMenuEntries( + element, + filteredOptions, + translate, + replaceElement + ); + + // get entry for QHAna service tasks and its configurations + const qhanaServiceTaskEntry = this.createQHAnaServiceTaskEntry(element); + + const qhanaEntries = Object.assign( + qhanaTasksEntries, + qhanaServiceTaskEntry + ); + return { + ["replace-by-qhana-tasks"]: createMoreOptionsEntryWithReturn( + element, + "QHAna Tasks", + "QHAna Tasks", + popupMenu, + qhanaEntries, + "qwm-qhana-service-task" + ), + }; + } + + /** + * Create a MoreOptionsEntry consisting of menu entries for all configurations loaded for QHAna service tasks and + * a QHAna service task element. + * + * @param element The element the menu entries are requested for. + * @return {{'replace-by-qhana-options': {label: string, className: string, action: Function}}} + */ + createQHAnaServiceTaskEntry(element) { + const bpmnFactory = this.bpmnFactory; + const modeling = this.modeling; + const popupMenu = this.popupMenu; + const replaceElement = this.replaceElement; + const commandStack = this.commandStack; + const elementFactory = this.elementFactory; + const create = this.create; + const autoPlace = this.autoPlace; + + /* create a QHAna service task with its properties set as defined in the configuration and create a data map object for the outputs of the service task as defined in the configuration */ - function action(event, config) { - - // replace element with configuration type if types mismatch - let newElement; - if (element.type !== config.appliesTo) { - newElement = replaceElement(element, {type: config.appliesTo}); - } - - // split config attributes in output and non output attributes - const outputAttributes = config.attributes.filter(attribute => attribute.bindTo.type === 'camunda:OutputMapParameter') || []; - const nonOutputAttributes = config.attributes.filter(attribute => attribute.bindTo.type !== 'camunda:OutputMapParameter'); - - const newConfig = { - name: config.name, - id: config.id, - attributes: nonOutputAttributes, - }; - - // set properties of the QHAna service task based on the configuration - handleConfigurationsAction(newElement || element, newConfig, bpmnFactory, modeling, commandStack); - - // create a data map object and set ist content to the outputs if output attributes are defined - if (outputAttributes.length > 0) { - // create a data map object for the output data - const dataMapObject = appendElement(dataConsts.DATA_MAP_OBJECT, newElement, event, bpmnFactory, elementFactory, create, autoPlace); - const dataMapObjectBusinessObject = dataMapObject.businessObject; - - // set name of new created data map object - modeling.updateProperties(dataMapObject, { - name: config.name.replace(/\s+/g, '_') + '_output', - }); - - // add the output attributes to the content attribute of the DataMapObject - for (let outputAttribute of outputAttributes) { - - const attributeContent = dataMapObjectBusinessObject.get(dataConsts.CONTENT); - - const param = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, { - name: outputAttribute.name, - value: outputAttribute.value - }); - attributeContent.push(param); - } - } - } - - // create menu entries for the configurations loaded from the QHAna configurations endpoint - let options = createConfigurationsEntries( - element, - 'qwm-qhana-service-task', - qhanaServiceConfigs().getQHAnaServiceConfigurations(), - bpmnFactory, - modeling, - commandStack, - replaceElement, - action + function action(event, config) { + // replace element with configuration type if types mismatch + let newElement; + if (element.type !== config.appliesTo) { + newElement = replaceElement(element, { type: config.appliesTo }); + } + + // split config attributes in output and non output attributes + const outputAttributes = + config.attributes.filter( + (attribute) => attribute.bindTo.type === "camunda:OutputMapParameter" + ) || []; + const nonOutputAttributes = config.attributes.filter( + (attribute) => attribute.bindTo.type !== "camunda:OutputMapParameter" + ); + + const newConfig = { + name: config.name, + id: config.id, + attributes: nonOutputAttributes, + }; + + // set properties of the QHAna service task based on the configuration + handleConfigurationsAction( + newElement || element, + newConfig, + bpmnFactory, + modeling, + commandStack + ); + + // create a data map object and set ist content to the outputs if output attributes are defined + if (outputAttributes.length > 0) { + // create a data map object for the output data + const dataMapObject = appendElement( + dataConsts.DATA_MAP_OBJECT, + newElement, + event, + bpmnFactory, + elementFactory, + create, + autoPlace ); - - // create a MoreOptionsEntry displaying the configurations entries - return { - ['replace-by-qhana-options']: createMoreOptionsEntryWithReturn( - element, - 'QHAna Service Tasks', - 'QHAna Service Tasks', - popupMenu, - options, - 'qwm-qhana-service-task' - ) - }; + const dataMapObjectBusinessObject = dataMapObject.businessObject; + + // set name of new created data map object + modeling.updateProperties(dataMapObject, { + name: config.name.replace(/\s+/g, "_") + "_output", + }); + + // add the output attributes to the content attribute of the DataMapObject + for (let outputAttribute of outputAttributes) { + const attributeContent = dataMapObjectBusinessObject.get( + dataConsts.CONTENT + ); + + const param = bpmnFactory.create(dataConsts.KEY_VALUE_ENTRY, { + name: outputAttribute.name, + value: outputAttribute.value, + }); + attributeContent.push(param); + } + } } + + // create menu entries for the configurations loaded from the QHAna configurations endpoint + let options = createConfigurationsEntries( + element, + "qwm-qhana-service-task", + qhanaServiceConfigs().getQHAnaServiceConfigurations(), + bpmnFactory, + modeling, + commandStack, + replaceElement, + action + ); + + // create a MoreOptionsEntry displaying the configurations entries + return { + ["replace-by-qhana-options"]: createMoreOptionsEntryWithReturn( + element, + "QHAna Service Tasks", + "QHAna Service Tasks", + popupMenu, + options, + "qwm-qhana-service-task" + ), + }; + } } QHAnaReplaceMenuProvider.$inject = [ - 'popupMenu', - 'bpmnReplace', - 'modeling', - 'bpmnFactory', - 'commandStack', - 'translate', - 'elementFactory', - 'create', - 'autoPlace' -]; \ No newline at end of file + "popupMenu", + "bpmnReplace", + "modeling", + "bpmnFactory", + "commandStack", + "translate", + "elementFactory", + "create", + "autoPlace", +]; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceOptions.js b/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceOptions.js index 55de5770..1b80412a 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceOptions.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/menu/QHAnaReplaceOptions.js @@ -1,13 +1,13 @@ -import * as consts from '../QHAnaConstants'; +import * as consts from "../QHAnaConstants"; // QHAna replace options for BPMN tasks export var TASK = [ - { - label: 'QHAna Service Step Task', - actionName: 'replace-with-qhana-service-step-task', - className: 'qwm-qhana-service-step-task', - target: { - type: consts.QHANA_SERVICE_STEP_TASK - } - } -]; \ No newline at end of file + { + label: "QHAna Service Step Task", + actionName: "replace-with-qhana-service-step-task", + className: "qwm-qhana-service-step-task", + target: { + type: consts.QHANA_SERVICE_STEP_TASK, + }, + }, +]; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaPropertiesProvider.js b/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaPropertiesProvider.js index 845b41f0..caf2cffa 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaPropertiesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaPropertiesProvider.js @@ -1,9 +1,9 @@ -import ConfigurationsProperties from '../../../editor/configurations/ConfigurationsProperties'; -import {is} from 'bpmn-js/lib/util/ModelUtil'; -import * as consts from '../QHAnaConstants'; -import * as configConsts from '../../../editor/configurations/Constants'; -import qhanaServiceStepProperties from './QHAnaServiceStepProperties'; -import {instance as qhanaServiceConfigs} from '../configurations/QHAnaConfigurations'; +import ConfigurationsProperties from "../../../editor/configurations/ConfigurationsProperties"; +import { is } from "bpmn-js/lib/util/ModelUtil"; +import * as consts from "../QHAnaConstants"; +import * as configConsts from "../../../editor/configurations/Constants"; +import qhanaServiceStepProperties from "./QHAnaServiceStepProperties"; +import { instance as qhanaServiceConfigs } from "../configurations/QHAnaConfigurations"; const LOW_PRIORITY = 500; @@ -14,44 +14,60 @@ const LOW_PRIORITY = 500; * @param {Function} translate The translate function of the bpmn-js modeler. * @param injector The injector of the bpmn-js modeler which can be used to load dependencies */ -export default function QHAnaPropertiesProvider(propertiesPanel, translate, injector) { +export default function QHAnaPropertiesProvider( + propertiesPanel, + translate, + injector +) { + /** + * Return the groups provided for the given element. + * + * @param element The element the groups are requested for. + * + * @return groups middleware + */ + this.getGroups = function (element) { + return function (groups) { + // add properties group to display the configuration attributes of a QHAna service task + if (is(element, consts.QHANA_SERVICE_TASK)) { + // load configuration which is applied to the element + const selectedConfiguration = + qhanaServiceConfigs().getQHAnaServiceConfiguration( + element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID) + ); - /** - * Return the groups provided for the given element. - * - * @param element The element the groups are requested for. - * - * @return groups middleware - */ - this.getGroups = function (element) { + if (selectedConfiguration) { + // create respective properties group + groups.splice( + 1, + 0, + createQHAnaServiceTaskGroup( + element, + injector, + translate, + selectedConfiguration + ) + ); + } + } - return function (groups) { + // add properties group to display the properties of a QHAna service step task + if (is(element, consts.QHANA_SERVICE_STEP_TASK)) { + groups.splice( + 1, + 0, + createQHAnaServiceStepTaskGroup(element, injector, translate) + ); + } - // add properties group to display the configuration attributes of a QHAna service task - if (is(element, consts.QHANA_SERVICE_TASK)) { - - // load configuration which is applied to the element - const selectedConfiguration = qhanaServiceConfigs().getQHAnaServiceConfiguration(element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID)); - - if (selectedConfiguration) { - // create respective properties group - groups.splice(1, 0, createQHAnaServiceTaskGroup(element, injector, translate, selectedConfiguration)); - } - } - - // add properties group to display the properties of a QHAna service step task - if (is(element, consts.QHANA_SERVICE_STEP_TASK)) { - groups.splice(1, 0, createQHAnaServiceStepTaskGroup(element, injector, translate)); - } - - return groups; - }; + return groups; }; + }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); + propertiesPanel.registerProvider(LOW_PRIORITY, this); } -QHAnaPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'injector']; +QHAnaPropertiesProvider.$inject = ["propertiesPanel", "translate", "injector"]; /** * Create a properties group which contains entries for the properties defined by the given configuration. @@ -62,13 +78,22 @@ QHAnaPropertiesProvider.$inject = ['propertiesPanel', 'translate', 'injector']; * @param configuration The given configuration * @return {{entries: (*), id: string, label}} */ -function createQHAnaServiceTaskGroup(element, injector, translate, configuration) { - - return { - id: 'QHAnaServiceTaskGroupProperties', - label: translate(configuration.groupLabel || 'Configurations Properties'), - entries: ConfigurationsProperties(element, injector, translate, configuration) - }; +function createQHAnaServiceTaskGroup( + element, + injector, + translate, + configuration +) { + return { + id: "QHAnaServiceTaskGroupProperties", + label: translate(configuration.groupLabel || "Configurations Properties"), + entries: ConfigurationsProperties( + element, + injector, + translate, + configuration + ), + }; } /** @@ -80,10 +105,9 @@ function createQHAnaServiceTaskGroup(element, injector, translate, configuration * @return {{entries: ([{component: function(*): VNode<*>, isEdited: function(*): *, id: string, element: *}]|*), id: string, label}} */ function createQHAnaServiceStepTaskGroup(element, injector, translate) { - - return { - id: 'QHAnaServiceStepTaskGroupProperties', - label: translate('Service Step Properties'), - entries: qhanaServiceStepProperties(element, injector, translate) - }; -} \ No newline at end of file + return { + id: "QHAnaServiceStepTaskGroupProperties", + label: translate("Service Step Properties"), + entries: qhanaServiceStepProperties(element, injector, translate), + }; +} diff --git a/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaServiceStepProperties.js b/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaServiceStepProperties.js index 02e278de..73825411 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaServiceStepProperties.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/properties/QHAnaServiceStepProperties.js @@ -1,6 +1,9 @@ -import {isTextFieldEntryEdited, TextFieldEntry} from '@bpmn-io/properties-panel'; -import {useService} from 'bpmn-js-properties-panel'; -import * as consts from '../QHAnaConstants'; +import { + isTextFieldEntryEdited, + TextFieldEntry, +} from "@bpmn-io/properties-panel"; +import { useService } from "bpmn-js-properties-panel"; +import * as consts from "../QHAnaConstants"; /** * Properties group for the properties panel. Contains entries for all attributes for a QHAna service step task. @@ -9,15 +12,14 @@ import * as consts from '../QHAnaConstants'; * @return {[{component: (function(*): preact.VNode), isEdited: ((function(*): *)|*), id: string, element}]} */ export default function (element) { - - return [ - { - id: 'qhanaNextStep', - element, - component: NextStep, - isEdited: isTextFieldEntryEdited - }, - ]; + return [ + { + id: "qhanaNextStep", + element, + component: NextStep, + isEdited: isTextFieldEntryEdited, + }, + ]; } /** @@ -27,31 +29,28 @@ export default function (element) { * @returns {preact.VNode} */ function NextStep(props) { - const { - idPrefix, - element - } = props; + const { idPrefix, element } = props; - const translate = useService('translate'); - const debounce = useService('debounceInput'); - const modeling = useService('modeling'); + const translate = useService("translate"); + const debounce = useService("debounceInput"); + const modeling = useService("modeling"); - const setValue = function (newValue) { - return modeling.updateProperties(element, { - [consts.NEXT_STEP]: newValue - }); - }; + const setValue = function (newValue) { + return modeling.updateProperties(element, { + [consts.NEXT_STEP]: newValue, + }); + }; - const getValue = function () { - return element.businessObject.get(consts.NEXT_STEP); - }; + const getValue = function () { + return element.businessObject.get(consts.NEXT_STEP); + }; - return TextFieldEntry({ - element: element, - id: idPrefix + '-value', - label: translate('Next Step'), - getValue, - setValue, - debounce - }); + return TextFieldEntry({ + element: element, + id: idPrefix + "-value", + label: translate("Next Step"), + getValue, + setValue, + debounce, + }); } diff --git a/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaRenderer.js b/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaRenderer.js index 006d160e..a680ce96 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaRenderer.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaRenderer.js @@ -1,63 +1,62 @@ -import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer'; -import * as consts from '../QHAnaConstants'; -import {drawTaskSVG} from '../../../editor/util/RenderUtilities'; -import {getSVG} from './QHAnaSVGMap'; +import BpmnRenderer from "bpmn-js/lib/draw/BpmnRenderer"; +import * as consts from "../QHAnaConstants"; +import { drawTaskSVG } from "../../../editor/util/RenderUtilities"; +import { getSVG } from "./QHAnaSVGMap"; /** * Custom renderer for rendering the extension elements of the QHAna plugin. */ export default class QHAnaRenderer extends BpmnRenderer { - - constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { - super(config, eventBus, styles, pathMap, canvas, textRenderer, 1001); - - // create handlers to render the QHAna extension elements - this.qhanaHandler = { - [consts.QHANA_SERVICE_TASK]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:Task')(parentGfx, element); - - drawTaskSVG(parentGfx, getSVG(consts.TASK_TYPE_QHANA_SERVICE_TASK)); - - return task; - }, - [consts.QHANA_SERVICE_STEP_TASK]: function (self, parentGfx, element) { - const task = self.renderer('bpmn:Task')(parentGfx, element); - - drawTaskSVG(parentGfx, getSVG(consts.TASK_TYPE_QHANA_SERVICE_STEP_TASK)); - - return task; - }, - }; - } - - renderer(type) { - - return this.handlers[type]; - } - - canRender(element) { - - // only return true if handler for rendering is registered - return this.qhanaHandler[element.type]; - } - - drawShape(parentNode, element) { - - // handle QHAna elements - if (element.type in this.qhanaHandler) { - const h = this.qhanaHandler[element.type]; - - /* jshint -W040 */ - return h(this, parentNode, element); - } + constructor(config, eventBus, styles, pathMap, canvas, textRenderer) { + super(config, eventBus, styles, pathMap, canvas, textRenderer, 1001); + + // create handlers to render the QHAna extension elements + this.qhanaHandler = { + [consts.QHANA_SERVICE_TASK]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:Task")(parentGfx, element); + + drawTaskSVG(parentGfx, getSVG(consts.TASK_TYPE_QHANA_SERVICE_TASK)); + + return task; + }, + [consts.QHANA_SERVICE_STEP_TASK]: function (self, parentGfx, element) { + const task = self.renderer("bpmn:Task")(parentGfx, element); + + drawTaskSVG( + parentGfx, + getSVG(consts.TASK_TYPE_QHANA_SERVICE_STEP_TASK) + ); + + return task; + }, + }; + } + + renderer(type) { + return this.handlers[type]; + } + + canRender(element) { + // only return true if handler for rendering is registered + return this.qhanaHandler[element.type]; + } + + drawShape(parentNode, element) { + // handle QHAna elements + if (element.type in this.qhanaHandler) { + const h = this.qhanaHandler[element.type]; + + /* jshint -W040 */ + return h(this, parentNode, element); } + } } QHAnaRenderer.$inject = [ - 'config', - 'eventBus', - 'styles', - 'pathMap', - 'canvas', - 'textRenderer' -]; \ No newline at end of file + "config", + "eventBus", + "styles", + "pathMap", + "canvas", + "textRenderer", +]; diff --git a/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaSVGMap.js b/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaSVGMap.js index 45dc0020..2be4962d 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaSVGMap.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/rendering/QHAnaSVGMap.js @@ -1,4 +1,4 @@ -import * as qhanaConsts from '../QHAnaConstants'; +import * as qhanaConsts from "../QHAnaConstants"; /** * Return SVG for the given ID @@ -7,21 +7,20 @@ import * as qhanaConsts from '../QHAnaConstants'; * @returns {*} The SVG as an object {tronsform: matrix( Scalingfactor, 0, 0, Scalingfactor, shift X, shift y), svg: svgString} */ export function getSVG(svgId) { + // to insert svgs easily just open them in your browser, copy the outer html and insert it using ctrl + alt + shift + v in intellij to avoid formatting,escaping etc. + // matrix( Scalingfactor, 0, 0, Scalingfactor, shift X, shift y) + // IMPORTANT: ensure that definition Ids for new SVGs are UNIQUE + // viewbox is not required + const svgMap = { + [qhanaConsts.TASK_TYPE_QHANA_SERVICE_TASK]: { + transform: "matrix(0.17, 0, 0, 0.17, 7, 7)", + svg: 'diagnostic-analysis', + }, + [qhanaConsts.TASK_TYPE_QHANA_SERVICE_STEP_TASK]: { + transform: "matrix(0.14, 0, 0, 0.14, 4, 4)", + svg: '', + }, + }; - // to insert svgs easily just open them in your browser, copy the outer html and insert it using ctrl + alt + shift + v in intellij to avoid formatting,escaping etc. - // matrix( Scalingfactor, 0, 0, Scalingfactor, shift X, shift y) - // IMPORTANT: ensure that definition Ids for new SVGs are UNIQUE - // viewbox is not required - const svgMap = { - [qhanaConsts.TASK_TYPE_QHANA_SERVICE_TASK]: { - transform: 'matrix(0.17, 0, 0, 0.17, 7, 7)', - svg: 'diagnostic-analysis' - }, - [qhanaConsts.TASK_TYPE_QHANA_SERVICE_STEP_TASK]: { - transform: 'matrix(0.14, 0, 0, 0.14, 4, 4)', - svg: '' - } - }; - - return svgMap[svgId]; -} \ No newline at end of file + return svgMap[svgId]; +} diff --git a/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-extension.json b/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-extension.json index d2c1bdcb..a5d537a1 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-extension.json +++ b/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-extension.json @@ -8,7 +8,7 @@ "types": [ { "name": "QHAnaServiceTask", - "superClass": [ "bpmn:Task" ], + "superClass": ["bpmn:Task"], "properties": [ { "name": "qhanaIdentifier", @@ -34,7 +34,7 @@ }, { "name": "QHAnaServiceStepTask", - "superClass": [ "bpmn:Task" ], + "superClass": ["bpmn:Task"], "properties": [ { "name": "qhanaNextStep", @@ -46,4 +46,4 @@ ], "enumerations": [], "associations": [] -} \ No newline at end of file +} diff --git a/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-icons.css b/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-icons.css index 8d08d560..e7fb4671 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-icons.css +++ b/components/bpmn-q/modeler-component/extensions/qhana/resources/qhana-icons.css @@ -1,35 +1,35 @@ .qwm-qhana-service-task:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("./icons/diagnostic-pulse-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("./icons/diagnostic-pulse-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .qwm-qhana-service-step-task:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("./icons/qhana-step.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("./icons/qhana-step.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; } .qwm-qhana-update-services:before { - content: ""; - width: 15px; - height: 15px; - background-size: contain; - background-image: url("./icons/update-icon.svg"); - background-repeat: no-repeat; - display: inline-block; - float: left; - margin: 0 6px 0 0; -} \ No newline at end of file + content: ""; + width: 15px; + height: 15px; + background-size: contain; + background-image: url("./icons/update-icon.svg"); + background-repeat: no-repeat; + display: inline-block; + float: left; + margin: 0 6px 0 0; +} diff --git a/components/bpmn-q/modeler-component/extensions/qhana/transformation/QHAnaTransformationHandler.js b/components/bpmn-q/modeler-component/extensions/qhana/transformation/QHAnaTransformationHandler.js index 6c13c7b0..6153d43d 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/transformation/QHAnaTransformationHandler.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/transformation/QHAnaTransformationHandler.js @@ -1,13 +1,16 @@ -import { getXml } from '../../../editor/util/IoUtilities'; -import { createTempModelerFromXml } from '../../../editor/ModelerHandler'; +import { getXml } from "../../../editor/util/IoUtilities"; +import { createTempModelerFromXml } from "../../../editor/ModelerHandler"; import { - addCamundaInputParameter, - getRootProcess, -} from '../../../editor/util/ModellingUtilities'; -import { getAllElementsInProcess, insertShape } from '../../../editor/util/TransformationUtilities'; -import * as consts from '../QHAnaConstants'; -import * as qhanaConsts from '../QHAnaConstants'; -import { layout } from '../../quantme/replacement/layouter/Layouter'; + addCamundaInputParameter, + getRootProcess, +} from "../../../editor/util/ModellingUtilities"; +import { + getAllElementsInProcess, + insertShape, +} from "../../../editor/util/TransformationUtilities"; +import * as consts from "../QHAnaConstants"; +import * as qhanaConsts from "../QHAnaConstants"; +import { layout } from "../../quantme/replacement/layouter/Layouter"; /** * Replace QHAna extensions with camunda bpmn elements so that it complies with the standard @@ -16,77 +19,125 @@ import { layout } from '../../quantme/replacement/layouter/Layouter'; * @returns {Promise<{xml: *, status: string}|{cause: string, status: string}>} */ export async function startQHAnaReplacementProcess(xml) { - let modeler = await createTempModelerFromXml(xml); - let elementRegistry = modeler.get('elementRegistry'); - let modeling = modeler.get('modeling'); - - // get root element of the current diagram - const definitions = modeler.getDefinitions(); - const rootProcess = getRootProcess(definitions); - - console.log(rootProcess); - - if (typeof rootProcess === 'undefined') { - - console.log('Unable to retrieve root process element from definitions!'); - return { status: 'failed', cause: 'Unable to retrieve root process element from definitions!' }; + let modeler = await createTempModelerFromXml(xml); + let elementRegistry = modeler.get("elementRegistry"); + let modeling = modeler.get("modeling"); + + // get root element of the current diagram + const definitions = modeler.getDefinitions(); + const rootProcess = getRootProcess(definitions); + + console.log(rootProcess); + + if (typeof rootProcess === "undefined") { + console.log("Unable to retrieve root process element from definitions!"); + return { + status: "failed", + cause: "Unable to retrieve root process element from definitions!", + }; + } + + // Mark process as executable + rootProcess.isExecutable = true; + + // get all QHAna Service Tasks from the process + const qhanaServiceTasks = getAllElementsInProcess( + rootProcess, + elementRegistry, + consts.QHANA_SERVICE_TASK + ); + console.log( + "Process contains " + + qhanaServiceTasks.length + + " QHAna service tasks to replace..." + ); + + // get all QHAna Service Step Tasks from the process + const qhanaServiceStepTasks = getAllElementsInProcess( + rootProcess, + elementRegistry, + consts.QHANA_SERVICE_STEP_TASK + ); + console.log( + "Process contains " + + qhanaServiceStepTasks.length + + " QHAna service step tasks to replace..." + ); + + // skip transformation if no QHAna service tasks and no QHAna service step tasks exist in the process + if ( + (!qhanaServiceTasks || !qhanaServiceTasks.length) && + (!qhanaServiceStepTasks || !qhanaServiceStepTasks.length) + ) { + return { status: "transformed", xml: xml }; + } + + // replace each qhana:QHAnaServiceTask with a ServiceTask with external implementation + for (let qhanaServiceTask of qhanaServiceTasks) { + let replacementSuccess = false; + console.log( + "Replacing QHAna service task with id %s ", + qhanaServiceTask.element.id + ); + replacementSuccess = await replaceQHAnaServiceTaskByServiceTask( + definitions, + qhanaServiceTask.element, + qhanaServiceTask.parent, + modeler + ); + + if (!replacementSuccess) { + console.log( + "Replacement of QHAna service task with id " + + qhanaServiceTask.element.id + + " failed. Aborting process!" + ); + return { + status: "failed", + cause: + "Replacement of QHAna service task with id " + + qhanaServiceTask.element.id + + " failed. Aborting process!", + }; } - - // Mark process as executable - rootProcess.isExecutable = true; - - // get all QHAna Service Tasks from the process - const qhanaServiceTasks = getAllElementsInProcess(rootProcess, elementRegistry, consts.QHANA_SERVICE_TASK); - console.log('Process contains ' + qhanaServiceTasks.length + ' QHAna service tasks to replace...'); - - // get all QHAna Service Step Tasks from the process - const qhanaServiceStepTasks = getAllElementsInProcess(rootProcess, elementRegistry, consts.QHANA_SERVICE_STEP_TASK); - console.log('Process contains ' + qhanaServiceStepTasks.length + ' QHAna service step tasks to replace...'); - - // skip transformation if no QHAna service tasks and no QHAna service step tasks exist in the process - if ((!qhanaServiceTasks || !qhanaServiceTasks.length) && (!qhanaServiceStepTasks || !qhanaServiceStepTasks.length)) { - return { status: 'transformed', xml: xml }; + } + + // replace each qhana:QHAnaServiceStepTask with an ServiceTask with external implementation + for (let qhanaServiceTask of qhanaServiceStepTasks) { + let replacementSuccess = false; + console.log( + "Replacing QHAna service step task with id %s ", + qhanaServiceTask.element.id + ); + replacementSuccess = await replaceQHAnaServiceStepTaskByServiceTask( + definitions, + qhanaServiceTask.element, + qhanaServiceTask.parent, + modeler + ); + + if (!replacementSuccess) { + console.log( + "Replacement of QHAna service step task with id " + + qhanaServiceTask.element.id + + " failed. Aborting process!" + ); + return { + status: "failed", + cause: + "Replacement of QHAna service step task with id " + + qhanaServiceTask.element.id + + " failed. Aborting process!", + }; } + } - // replace each qhana:QHAnaServiceTask with a ServiceTask with external implementation - for (let qhanaServiceTask of qhanaServiceTasks) { - - let replacementSuccess = false; - console.log('Replacing QHAna service task with id %s ', qhanaServiceTask.element.id); - replacementSuccess = await replaceQHAnaServiceTaskByServiceTask(definitions, qhanaServiceTask.element, qhanaServiceTask.parent, modeler); - - if (!replacementSuccess) { - console.log('Replacement of QHAna service task with id ' + qhanaServiceTask.element.id + ' failed. Aborting process!'); - return { - status: 'failed', - cause: 'Replacement of QHAna service task with id ' + qhanaServiceTask.element.id + ' failed. Aborting process!' - }; - } - } - - // replace each qhana:QHAnaServiceStepTask with an ServiceTask with external implementation - for (let qhanaServiceTask of qhanaServiceStepTasks) { - - let replacementSuccess = false; - console.log('Replacing QHAna service step task with id %s ', qhanaServiceTask.element.id); - replacementSuccess = await replaceQHAnaServiceStepTaskByServiceTask(definitions, qhanaServiceTask.element, qhanaServiceTask.parent, modeler); - - if (!replacementSuccess) { - console.log('Replacement of QHAna service step task with id ' + qhanaServiceTask.element.id + ' failed. Aborting process!'); - return { - status: 'failed', - cause: 'Replacement of QHAna service step task with id ' + qhanaServiceTask.element.id + ' failed. Aborting process!' - }; - } - } - - const transformedXml = await getXml(modeler); - layout(modeling, elementRegistry, rootProcess); - // await saveResultXmlFn(transformedXml); - return { status: 'transformed', xml: transformedXml }; + const transformedXml = await getXml(modeler); + layout(modeling, elementRegistry, rootProcess); + // await saveResultXmlFn(transformedXml); + return { status: "transformed", xml: transformedXml }; } - /** * Replace the given QHAna service task by a BPMN service task. * @@ -96,26 +147,61 @@ export async function startQHAnaReplacementProcess(xml) { * @param modeler The current modeler * @return {Promise} */ -async function replaceQHAnaServiceTaskByServiceTask(definitions, qhanaServiceTask, parentProcess, modeler) { - - const bpmnFactory = modeler.get('bpmnFactory'); - - // create a BPMN service task with implementation external - const topic = 'qhana-plugin.' + qhanaServiceTask.get(qhanaConsts.IDENTIFIER); - const newServiceTask = bpmnFactory.create('bpmn:ServiceTask', { type: 'external', topic: topic }); - - let result = insertShape(definitions, parentProcess, newServiceTask, {}, true, modeler, qhanaServiceTask); - - // set the properties of the QHAna Service Task as inputs of the new Service Task - if (result.success && result.element) { - const newElement = result.element; - addCamundaInputParameter(newElement.businessObject, "qhanaIdentifier", qhanaServiceTask.qhanaIdentifier, bpmnFactory); - addCamundaInputParameter(newElement.businessObject, "qhanaVersion", qhanaServiceTask.qhanaVersion, bpmnFactory); - addCamundaInputParameter(newElement.businessObject, "qhanaName", qhanaServiceTask.qhanaName, bpmnFactory); - addCamundaInputParameter(newElement.businessObject, "qhanaDescription", qhanaServiceTask.qhanaDescription, bpmnFactory); - } - - return result['success']; +async function replaceQHAnaServiceTaskByServiceTask( + definitions, + qhanaServiceTask, + parentProcess, + modeler +) { + const bpmnFactory = modeler.get("bpmnFactory"); + + // create a BPMN service task with implementation external + const topic = "qhana-plugin." + qhanaServiceTask.get(qhanaConsts.IDENTIFIER); + const newServiceTask = bpmnFactory.create("bpmn:ServiceTask", { + type: "external", + topic: topic, + }); + + let result = insertShape( + definitions, + parentProcess, + newServiceTask, + {}, + true, + modeler, + qhanaServiceTask + ); + + // set the properties of the QHAna Service Task as inputs of the new Service Task + if (result.success && result.element) { + const newElement = result.element; + addCamundaInputParameter( + newElement.businessObject, + "qhanaIdentifier", + qhanaServiceTask.qhanaIdentifier, + bpmnFactory + ); + addCamundaInputParameter( + newElement.businessObject, + "qhanaVersion", + qhanaServiceTask.qhanaVersion, + bpmnFactory + ); + addCamundaInputParameter( + newElement.businessObject, + "qhanaName", + qhanaServiceTask.qhanaName, + bpmnFactory + ); + addCamundaInputParameter( + newElement.businessObject, + "qhanaDescription", + qhanaServiceTask.qhanaDescription, + bpmnFactory + ); + } + + return result["success"]; } /** @@ -127,20 +213,40 @@ async function replaceQHAnaServiceTaskByServiceTask(definitions, qhanaServiceTas * @param modeler The current modeler * @return {Promise} */ -async function replaceQHAnaServiceStepTaskByServiceTask(definitions, qhanaServiceTask, parentProcess, modeler) { - - const bpmnFactory = modeler.get('bpmnFactory'); - - // create a BPMN service task with implementation external and the topic defined in the next step attribute - const topic = 'plugin-step.' + consts.NEXT_STEP; - const newServiceTask = bpmnFactory.create('bpmn:ServiceTask', { type: 'external', topic: topic }); - - let result = insertShape(definitions, parentProcess, newServiceTask, {}, true, modeler, qhanaServiceTask); - - // set the properties of the QHAna Service Step Task as inputs of the new Service Task - if (result.success && result.element) { - const newElement = result.element; - addCamundaInputParameter(newElement.businessObject, "qhanaNextStep", qhanaServiceTask.qhanaNextStep, bpmnFactory); - } - return result['success']; +async function replaceQHAnaServiceStepTaskByServiceTask( + definitions, + qhanaServiceTask, + parentProcess, + modeler +) { + const bpmnFactory = modeler.get("bpmnFactory"); + + // create a BPMN service task with implementation external and the topic defined in the next step attribute + const topic = "plugin-step." + consts.NEXT_STEP; + const newServiceTask = bpmnFactory.create("bpmn:ServiceTask", { + type: "external", + topic: topic, + }); + + let result = insertShape( + definitions, + parentProcess, + newServiceTask, + {}, + true, + modeler, + qhanaServiceTask + ); + + // set the properties of the QHAna Service Step Task as inputs of the new Service Task + if (result.success && result.element) { + const newElement = result.element; + addCamundaInputParameter( + newElement.businessObject, + "qhanaNextStep", + qhanaServiceTask.qhanaNextStep, + bpmnFactory + ); + } + return result["success"]; } diff --git a/components/bpmn-q/modeler-component/extensions/qhana/ui/UpdateQHAnaConfigurationsButton.js b/components/bpmn-q/modeler-component/extensions/qhana/ui/UpdateQHAnaConfigurationsButton.js index e2b1c5bb..7c941bdc 100644 --- a/components/bpmn-q/modeler-component/extensions/qhana/ui/UpdateQHAnaConfigurationsButton.js +++ b/components/bpmn-q/modeler-component/extensions/qhana/ui/UpdateQHAnaConfigurationsButton.js @@ -1,15 +1,22 @@ -import React from 'react'; -import {instance as qhanaServiceConfigs} from '../configurations/QHAnaConfigurations'; +import React from "react"; +import { instance as qhanaServiceConfigs } from "../configurations/QHAnaConfigurations"; /** * React button component which updates the loaded QHAna service task configurations when clicked */ export default function UpdateQHAnaConfigurationsButton() { - - return
- -
; + return ( +
+ +
+ ); } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js index f7d48a10..8489a3d5 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/Constants.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/Constants.js @@ -10,73 +10,110 @@ */ // QNames of the QuantME constructs -export const QUANTUM_COMPUTATION_TASK = 'quantme:QuantumComputationTask'; -export const QUANTUM_CIRCUIT_LOADING_TASK = 'quantme:QuantumCircuitLoadingTask'; -export const DATA_PREPARATION_TASK = 'quantme:DataPreparationTask'; -export const ORACLE_EXPANSION_TASK = 'quantme:OracleExpansionTask'; -export const QUANTUM_CIRCUIT_EXECUTION_TASK = 'quantme:QuantumCircuitExecutionTask'; -export const READOUT_ERROR_MITIGATION_TASK = 'quantme:ReadoutErrorMitigationTask'; -export const VARIATIONAL_QUANTUM_ALGORITHM_TASK = 'quantme:VariationalQuantumAlgorithmTask'; -export const WARM_STARTING_TASK = 'quantme:WarmStartingTask'; -export const PARAMETER_OPTIMIZATION_TASK = 'quantme:ParameterOptimizationTask'; -export const RESULT_EVALUATION_TASK = 'quantme:ResultEvaluationTask'; -export const QUANTUM_HARDWARE_SELECTION_SUBPROCESS = 'quantme:QuantumHardwareSelectionSubprocess'; -export const CIRCUIT_CUTTING_SUBPROCESS = 'quantme:CircuitCuttingSubprocess'; +export const QUANTUM_COMPUTATION_TASK = "quantme:QuantumComputationTask"; +export const QUANTUM_CIRCUIT_LOADING_TASK = "quantme:QuantumCircuitLoadingTask"; +export const DATA_PREPARATION_TASK = "quantme:DataPreparationTask"; +export const ORACLE_EXPANSION_TASK = "quantme:OracleExpansionTask"; +export const QUANTUM_CIRCUIT_EXECUTION_TASK = + "quantme:QuantumCircuitExecutionTask"; +export const READOUT_ERROR_MITIGATION_TASK = + "quantme:ReadoutErrorMitigationTask"; +export const VARIATIONAL_QUANTUM_ALGORITHM_TASK = + "quantme:VariationalQuantumAlgorithmTask"; +export const WARM_STARTING_TASK = "quantme:WarmStartingTask"; +export const PARAMETER_OPTIMIZATION_TASK = "quantme:ParameterOptimizationTask"; +export const RESULT_EVALUATION_TASK = "quantme:ResultEvaluationTask"; +export const QUANTUM_HARDWARE_SELECTION_SUBPROCESS = + "quantme:QuantumHardwareSelectionSubprocess"; +export const CIRCUIT_CUTTING_SUBPROCESS = "quantme:CircuitCuttingSubprocess"; // Property names of the QuantME constructs -export const ALGORITHM = 'algorithm'; -export const PROVIDER = 'provider'; -export const PROVIDERS = 'providers'; -export const QUANTUM_CIRCUIT = 'quantumCircuit'; -export const URL = 'url'; -export const ENCODING_SCHEMA = 'encodingSchema'; -export const PROGRAMMING_LANGUAGE = 'programmingLanguage'; -export const ORACLE_ID = 'oracleId'; -export const ORACLE_CIRCUIT = 'oracleCircuit'; -export const ORACLE_URL = 'oracleURL'; -export const QPU = 'qpu'; -export const SHOTS = 'shots'; -export const MAX_AGE = 'maxAge'; -export const SIMULATORS_ALLOWED = 'simulatorsAllowed'; -export const SELECTION_STRATEGY = 'selectionStrategy'; -export const CALIBRATION_METHOD = 'calibrationMethod'; -export const MITIGATION_METHOD = 'mitigationMethod'; -export const DNN_HIDDEN_LAYER = 'dnnHiddenLayer'; -export const NEIGHBORHOOD_RANGE = 'neighborhoodRange'; -export const OBJECTIVE_FUNCTION = 'objectiveFunction'; -export const OPTIMIZER = 'optimizer'; -export const MAX_REM_COSTS = 'maxREMCosts'; -export const MAX_CM_SIZE = 'maxCMSize'; -export const WARM_STARTING_METHOD = 'warmStartingMethod'; -export const QUANTUM_ALGORITHM = 'quantumAlgorithm'; -export const CLASSICAL_ALGORTHM = 'classicalAlgorithm'; -export const REPETITIONS = 'repetitions'; -export const ROUNDED = 'rounded'; -export const COST_FUNCTION = 'costFunction'; -export const ETA = 'eta'; -export const ALPHA = 'alpha'; -export const ALGORITHMIC_PROBLEM = 'algorithmicProblem'; -export const MAX_ITERATIONS = 'maxIterations'; -export const TOLERANCE_THRESHOLD = 'toleranceThreshold'; -export const LEARNING_RATE = 'learningRate'; -export const CUTTING_METHOD = 'cuttingMethod'; -export const MAX_SUBCIRCUIT_WIDTH = 'maxSubCircuitWidth'; -export const MAX_NUMBER_OF_CUTS = 'maxNumberOfCuts'; -export const MAXIMUM_NUM_SUBCIRCUITS = 'maxNumSubCircuits'; +export const ALGORITHM = "algorithm"; +export const PROVIDER = "provider"; +export const PROVIDERS = "providers"; +export const QUANTUM_CIRCUIT = "quantumCircuit"; +export const URL = "url"; +export const ENCODING_SCHEMA = "encodingSchema"; +export const PROGRAMMING_LANGUAGE = "programmingLanguage"; +export const ORACLE_ID = "oracleId"; +export const ORACLE_CIRCUIT = "oracleCircuit"; +export const ORACLE_URL = "oracleURL"; +export const QPU = "qpu"; +export const SHOTS = "shots"; +export const MAX_AGE = "maxAge"; +export const SIMULATORS_ALLOWED = "simulatorsAllowed"; +export const SELECTION_STRATEGY = "selectionStrategy"; +export const CALIBRATION_METHOD = "calibrationMethod"; +export const MITIGATION_METHOD = "mitigationMethod"; +export const DNN_HIDDEN_LAYER = "dnnHiddenLayer"; +export const NEIGHBORHOOD_RANGE = "neighborhoodRange"; +export const OBJECTIVE_FUNCTION = "objectiveFunction"; +export const OPTIMIZER = "optimizer"; +export const MAX_REM_COSTS = "maxREMCosts"; +export const MAX_CM_SIZE = "maxCMSize"; +export const WARM_STARTING_METHOD = "warmStartingMethod"; +export const QUANTUM_ALGORITHM = "quantumAlgorithm"; +export const CLASSICAL_ALGORTHM = "classicalAlgorithm"; +export const REPETITIONS = "repetitions"; +export const ROUNDED = "rounded"; +export const COST_FUNCTION = "costFunction"; +export const ETA = "eta"; +export const ALPHA = "alpha"; +export const ALGORITHMIC_PROBLEM = "algorithmicProblem"; +export const MAX_ITERATIONS = "maxIterations"; +export const TOLERANCE_THRESHOLD = "toleranceThreshold"; +export const LEARNING_RATE = "learningRate"; +export const CUTTING_METHOD = "cuttingMethod"; +export const MAX_SUBCIRCUIT_WIDTH = "maxSubCircuitWidth"; +export const MAX_NUMBER_OF_CUTS = "maxNumberOfCuts"; +export const MAXIMUM_NUM_SUBCIRCUITS = "maxNumSubCircuits"; // endpoint paths of connected services -export const NISQ_ANALYZER_QPU_SELECTION_PATH = 'qpu-selection'; +export const NISQ_ANALYZER_QPU_SELECTION_PATH = "qpu-selection"; // supported selection strategies -export const SELECTION_STRATEGY_SHORTEST_QUEUE_SIZE = 'Shortest-Queue'; +export const SELECTION_STRATEGY_SHORTEST_QUEUE_SIZE = "Shortest-Queue"; export const SELECTION_STRATEGY_LIST = [SELECTION_STRATEGY_SHORTEST_QUEUE_SIZE]; // list of QuantME attributes to check if a given attribute belongs to the extension or not export const QUANTME_ATTRIBUTES = [ - ALGORITHM, PROVIDER, PROVIDERS, QUANTUM_CIRCUIT, URL, ENCODING_SCHEMA, PROGRAMMING_LANGUAGE, ORACLE_ID, ORACLE_CIRCUIT, - ORACLE_URL, QPU, SHOTS, MAX_AGE, SIMULATORS_ALLOWED, SELECTION_STRATEGY, CALIBRATION_METHOD, MITIGATION_METHOD, - DNN_HIDDEN_LAYER, NEIGHBORHOOD_RANGE, OBJECTIVE_FUNCTION, OPTIMIZER, MAX_REM_COSTS, MAX_CM_SIZE, WARM_STARTING_METHOD, - QUANTUM_ALGORITHM, CLASSICAL_ALGORTHM, REPETITIONS, ROUNDED, COST_FUNCTION, ETA, ALPHA, ALGORITHMIC_PROBLEM, - MAX_ITERATIONS, TOLERANCE_THRESHOLD, LEARNING_RATE, CUTTING_METHOD, MAX_SUBCIRCUIT_WIDTH, - MAX_NUMBER_OF_CUTS, MAXIMUM_NUM_SUBCIRCUITS + ALGORITHM, + PROVIDER, + PROVIDERS, + QUANTUM_CIRCUIT, + URL, + ENCODING_SCHEMA, + PROGRAMMING_LANGUAGE, + ORACLE_ID, + ORACLE_CIRCUIT, + ORACLE_URL, + QPU, + SHOTS, + MAX_AGE, + SIMULATORS_ALLOWED, + SELECTION_STRATEGY, + CALIBRATION_METHOD, + MITIGATION_METHOD, + DNN_HIDDEN_LAYER, + NEIGHBORHOOD_RANGE, + OBJECTIVE_FUNCTION, + OPTIMIZER, + MAX_REM_COSTS, + MAX_CM_SIZE, + WARM_STARTING_METHOD, + QUANTUM_ALGORITHM, + CLASSICAL_ALGORTHM, + REPETITIONS, + ROUNDED, + COST_FUNCTION, + ETA, + ALPHA, + ALGORITHMIC_PROBLEM, + MAX_ITERATIONS, + TOLERANCE_THRESHOLD, + LEARNING_RATE, + CUTTING_METHOD, + MAX_SUBCIRCUIT_WIDTH, + MAX_NUMBER_OF_CUTS, + MAXIMUM_NUM_SUBCIRCUITS, ]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js b/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js index 226698da..e1bed46f 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/QuantMEPlugin.js @@ -6,70 +6,71 @@ import OpenToscaTab from "./configTabs/OpenToscaTab"; import NisqAnalyzerTab from "./configTabs/NisqAnalyzerTab"; import QrmDataTab from "./configTabs/QrmDataTab"; import HybridRuntimeTab from "./configTabs/HybridRuntimeTab"; -import {getQRMs} from "./qrm-manager"; -import {startQuantmeReplacementProcess} from "./replacement/QuantMETransformator"; +import { getQRMs } from "./qrm-manager"; +import { startQuantmeReplacementProcess } from "./replacement/QuantMETransformator"; import * as camundaConfig from "../../editor/config/EditorConfigManager"; import * as config from "./framework-config/config-manager"; import TransformationButton from "../../editor/ui/TransformationButton"; -import DataObjectConfigurationsTab from './configurations/DataObjectConfigurationsTab'; +import DataObjectConfigurationsTab from "./configurations/DataObjectConfigurationsTab"; -import quantMEStyles from './styling/quantme.css'; +import quantMEStyles from "./styling/quantme.css"; import QuantMEPluginButton from "./ui/QuantMEPluginButton"; -let quantMEModdleExtension = require('./resources/quantum4bpmn.json'); +let quantMEModdleExtension = require("./resources/quantum4bpmn.json"); /** * Plugin Object of the QuantME extension. Used to register the plugin in the plugin handler of the modeler. */ export default { - buttons: [], - configTabs: [ - { - tabId: 'DataConfigurationEndpointTab', - tabTitle: 'QuantME Data', - configTab: DataObjectConfigurationsTab, - }, - { - tabId: 'OpenTOSCAEndpointTab', - tabTitle: 'OpenTOSCA', - configTab: OpenToscaTab, - }, - { - tabId: 'BPMNTab', - tabTitle: 'Workflow', - configTab: BPMNConfigTab, - }, - { - tabId: 'NISQAnalyzerEndpointTab', - tabTitle: 'NISQ Analyzer', - configTab: NisqAnalyzerTab, - }, - { - tabId: 'QRMDataTab', - tabTitle: 'QRM Data', - configTab: QrmDataTab, - }, - { - tabId: 'HybridRuntimesTab', - tabTitle: 'Hybrid Runtimes', - configTab: HybridRuntimeTab, - } - ], - name: 'quantme', - extensionModule: QuantMEExtensionModule, - moddleDescription: quantMEModdleExtension, - styling: [quantMEStyles], - transformExtensionButton: { - - let currentQRMs = getQRMs(); - return await startQuantmeReplacementProcess(xml, currentQRMs, - { - nisqAnalyzerEndpoint: config.getNisqAnalyzerEndpoint(), - transformationFrameworkEndpoint: config.getTransformationFrameworkEndpoint(), - camundaEndpoint: camundaConfig.getCamundaEndpoint() - } - ); - } - }/>, -}; \ No newline at end of file + buttons: [], + configTabs: [ + { + tabId: "DataConfigurationEndpointTab", + tabTitle: "QuantME Data", + configTab: DataObjectConfigurationsTab, + }, + { + tabId: "OpenTOSCAEndpointTab", + tabTitle: "OpenTOSCA", + configTab: OpenToscaTab, + }, + { + tabId: "BPMNTab", + tabTitle: "Workflow", + configTab: BPMNConfigTab, + }, + { + tabId: "NISQAnalyzerEndpointTab", + tabTitle: "NISQ Analyzer", + configTab: NisqAnalyzerTab, + }, + { + tabId: "QRMDataTab", + tabTitle: "QRM Data", + configTab: QrmDataTab, + }, + { + tabId: "HybridRuntimesTab", + tabTitle: "Hybrid Runtimes", + configTab: HybridRuntimeTab, + }, + ], + name: "quantme", + extensionModule: QuantMEExtensionModule, + moddleDescription: quantMEModdleExtension, + styling: [quantMEStyles], + transformExtensionButton: ( + { + let currentQRMs = getQRMs(); + return await startQuantmeReplacementProcess(xml, currentQRMs, { + nisqAnalyzerEndpoint: config.getNisqAnalyzerEndpoint(), + transformationFrameworkEndpoint: + config.getTransformationFrameworkEndpoint(), + camundaEndpoint: camundaConfig.getCamundaEndpoint(), + }); + }} + /> + ), +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js index ac63d075..8d519a64 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/BPMNConfigTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as config from "../framework-config/config-manager"; /** @@ -10,98 +10,124 @@ import * as config from "../framework-config/config-manager"; * @constructor */ export default function BPMNConfigTab() { + const [transformationFrameworkEndpoint, setTransformationFrameworkEndpoint] = + useState(config.getTransformationFrameworkEndpoint()); + const [scriptSplitterEndpoint, setScriptSplitterEndpoint] = useState( + config.getScriptSplitterEndpoint() + ); + const [scriptSplitterThreshold, setScriptSplitterThreshold] = useState( + config.getScriptSplitterThreshold() + ); - const [transformationFrameworkEndpoint, setTransformationFrameworkEndpoint] = useState(config.getTransformationFrameworkEndpoint()); - const [scriptSplitterEndpoint, setScriptSplitterEndpoint] = useState(config.getScriptSplitterEndpoint()); - const [scriptSplitterThreshold, setScriptSplitterThreshold] = useState(config.getScriptSplitterThreshold()); + const modeler = getModeler(); - const modeler = getModeler(); + const editorActions = modeler.get("editorActions"); + const eventBus = modeler.get("eventBus"); - const editorActions = modeler.get('editorActions'); - const eventBus = modeler.get('eventBus'); - - // register editor action listener for changes in config entries - if (!editorActions._actions.hasOwnProperty('transformationFrameworkEndpointChanged')) { - editorActions.register({ - transformationFrameworkEndpointChanged: function (transformationFrameworkEndpoint) { - modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint; - } - }); - } - if (!editorActions._actions.hasOwnProperty('scriptSplitterEndpointChanged')) { - editorActions.register({ - scriptSplitterEndpointChanged: function (scriptSplitterEndpoint) { - modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint; - eventBus.fire('config.updated', self.modeler.config); - } - }); - } - if (!editorActions._actions.hasOwnProperty('scriptSplitterThresholdChanged')) { - editorActions.register({ - scriptSplitterThresholdChanged: function (scriptSplitterEndpoint) { - modeler.config.scriptSplitterThreshold = scriptSplitterEndpoint; - } - }); - } - - // save changed config entries on close - BPMNConfigTab.prototype.onClose = () => { - modeler.config.transformationFrameworkEndpoint = transformationFrameworkEndpoint; + // register editor action listener for changes in config entries + if ( + !editorActions._actions.hasOwnProperty( + "transformationFrameworkEndpointChanged" + ) + ) { + editorActions.register({ + transformationFrameworkEndpointChanged: function ( + transformationFrameworkEndpoint + ) { + modeler.config.transformationFrameworkEndpoint = + transformationFrameworkEndpoint; + }, + }); + } + if (!editorActions._actions.hasOwnProperty("scriptSplitterEndpointChanged")) { + editorActions.register({ + scriptSplitterEndpointChanged: function (scriptSplitterEndpoint) { modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint; - modeler.config.scriptSplitterThreshold = scriptSplitterThreshold; - config.setTransformationFrameworkEndpoint(transformationFrameworkEndpoint); - config.setScriptSplitterEndpoint(scriptSplitterEndpoint); - config.setScriptSplitterThreshold(scriptSplitterThreshold); - }; + eventBus.fire("config.updated", self.modeler.config); + }, + }); + } + if ( + !editorActions._actions.hasOwnProperty("scriptSplitterThresholdChanged") + ) { + editorActions.register({ + scriptSplitterThresholdChanged: function (scriptSplitterEndpoint) { + modeler.config.scriptSplitterThreshold = scriptSplitterEndpoint; + }, + }); + } + + // save changed config entries on close + BPMNConfigTab.prototype.onClose = () => { + modeler.config.transformationFrameworkEndpoint = + transformationFrameworkEndpoint; + modeler.config.scriptSplitterEndpoint = scriptSplitterEndpoint; + modeler.config.scriptSplitterThreshold = scriptSplitterThreshold; + config.setTransformationFrameworkEndpoint(transformationFrameworkEndpoint); + config.setScriptSplitterEndpoint(scriptSplitterEndpoint); + config.setScriptSplitterThreshold(scriptSplitterThreshold); + }; - return <> -

BPMN related configurations:

- - - - - - - -
QuantME Framework Endpoint - setTransformationFrameworkEndpoint(event.target.value)}/> -
-

Workflow generation:

- - - - - - - - - - - -
Script Splitter Endpoint - setScriptSplitterEndpoint(event.target.value)}/> -
Script Splitter Threshold - setScriptSplitterThreshold(event.target.value)}/> -
- ; + return ( + <> +

BPMN related configurations:

+ + + + + + + +
QuantME Framework Endpoint + + setTransformationFrameworkEndpoint(event.target.value) + } + /> +
+

Workflow generation:

+ + + + + + + + + + + +
Script Splitter Endpoint + + setScriptSplitterEndpoint(event.target.value) + } + /> +
Script Splitter Threshold + + setScriptSplitterThreshold(event.target.value) + } + /> +
+ + ); } BPMNConfigTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.transformationFrameworkEndpoint = config.getTransformationFrameworkEndpoint(); - modeler.config.scriptSplitterEndpoint = config.getScriptSplitterEndpoint(); - modeler.config.scriptSplitterThreshold = config.getScriptSplitterThreshold(); -}; \ No newline at end of file + modeler.config.transformationFrameworkEndpoint = + config.getTransformationFrameworkEndpoint(); + modeler.config.scriptSplitterEndpoint = config.getScriptSplitterEndpoint(); + modeler.config.scriptSplitterThreshold = config.getScriptSplitterThreshold(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js index 7a4b370d..bc18207e 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/HybridRuntimeTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as config from "../framework-config/config-manager"; /** @@ -10,105 +10,133 @@ import * as config from "../framework-config/config-manager"; * @constructor */ export default function HybridRuntimeTab() { + const [qiskitRuntimeHandlerEndpoint, setQiskitRuntimeHandlerEndpoint] = + useState(config.getQiskitRuntimeHandlerEndpoint()); + const [hybridRuntimeProvenance, setHybridRuntimeProvenance] = useState( + config.getHybridRuntimeProvenance() + ); + const [awsRuntimeHandlerEndpoint, setAWSRuntimeHandlerEndpoint] = useState( + config.getAWSRuntimeHandlerEndpoint() + ); - const [qiskitRuntimeHandlerEndpoint, setQiskitRuntimeHandlerEndpoint] = useState(config.getQiskitRuntimeHandlerEndpoint()); - const [hybridRuntimeProvenance, setHybridRuntimeProvenance] = useState(config.getHybridRuntimeProvenance()); - const [awsRuntimeHandlerEndpoint, setAWSRuntimeHandlerEndpoint] = useState(config.getAWSRuntimeHandlerEndpoint()); + let hybridRuntimeProvenanceBoolean = hybridRuntimeProvenance; - let hybridRuntimeProvenanceBoolean = hybridRuntimeProvenance; + const modeler = getModeler(); - const modeler = getModeler(); + const editorActions = modeler.get("editorActions"); + const eventBus = modeler.get("eventBus"); - const editorActions = modeler.get('editorActions'); - const eventBus = modeler.get('eventBus'); + // register editor action listener for changes in config entries + if ( + !editorActions._actions.hasOwnProperty( + "qiskitRuntimeHandlerEndpointChanged" + ) + ) { + editorActions.register({ + qiskitRuntimeHandlerEndpointChanged: function ( + qiskitRuntimeHandlerEndpoint + ) { + self.modeler.config.qiskitRuntimeHandlerEndpoint = + qiskitRuntimeHandlerEndpoint; + eventBus.fire("config.updated", self.modeler.config); + }, + }); + } + if ( + !editorActions._actions.hasOwnProperty("awsRuntimeHandlerEndpointChanged") + ) { + editorActions.register({ + awsRuntimeHandlerEndpointChanged: function (awsRuntimeHandlerEndpoint) { + self.modeler.config.awsRuntimeHandlerEndpoint = + awsRuntimeHandlerEndpoint; + eventBus.fire("config.updated", self.modeler.config); + }, + }); + } + if ( + !editorActions._actions.hasOwnProperty("hybridRuntimeProvenanceChanged") + ) { + editorActions.register({ + hybridRuntimeProvenanceChanged: function (hybridRuntimeProvenance) { + self.modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance; + eventBus.fire("config.updated", self.modeler.config); + }, + }); + } - // register editor action listener for changes in config entries - if (!editorActions._actions.hasOwnProperty('qiskitRuntimeHandlerEndpointChanged')) { - editorActions.register({ - qiskitRuntimeHandlerEndpointChanged: function (qiskitRuntimeHandlerEndpoint) { - self.modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint; - eventBus.fire('config.updated', self.modeler.config); - } - }); - } - if (!editorActions._actions.hasOwnProperty('awsRuntimeHandlerEndpointChanged')) { - editorActions.register({ - awsRuntimeHandlerEndpointChanged: function (awsRuntimeHandlerEndpoint) { - self.modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint; - eventBus.fire('config.updated', self.modeler.config); - } - }); - } - if (!editorActions._actions.hasOwnProperty('hybridRuntimeProvenanceChanged')) { - editorActions.register({ - hybridRuntimeProvenanceChanged: function (hybridRuntimeProvenance) { - self.modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance; - eventBus.fire('config.updated', self.modeler.config); - } - }); - } + // save changed config entries on close + HybridRuntimeTab.prototype.onClose = () => { + modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint; + modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance; + modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint; + config.setQiskitRuntimeHandlerEndpoint(qiskitRuntimeHandlerEndpoint); + config.setAWSRuntimeHandlerEndpoint(awsRuntimeHandlerEndpoint); + config.setHybridRuntimeProvenance(hybridRuntimeProvenance); + }; - // save changed config entries on close - HybridRuntimeTab.prototype.onClose = () => { - modeler.config.qiskitRuntimeHandlerEndpoint = qiskitRuntimeHandlerEndpoint; - modeler.config.hybridRuntimeProvenance = hybridRuntimeProvenance; - modeler.config.awsRuntimeHandlerEndpoint = awsRuntimeHandlerEndpoint; - config.setQiskitRuntimeHandlerEndpoint(qiskitRuntimeHandlerEndpoint); - config.setAWSRuntimeHandlerEndpoint(awsRuntimeHandlerEndpoint); - config.setHybridRuntimeProvenance(hybridRuntimeProvenance); - }; - - return (<> -

Hybrid Runtime Handler Endpoints

- - - - - - - - - - - -
Qiskit Runtime Handler Endpoint: - setQiskitRuntimeHandlerEndpoint(event.target.value)}/> -
AWS Runtime Handler Endpoint: - setAWSRuntimeHandlerEndpoint(event.target.value)}/> -
-

Provenance Collection for Hybrid Runtime

- - - - - - - -
Retrieve Intermediate Results: - { - hybridRuntimeProvenanceBoolean = !hybridRuntimeProvenanceBoolean; - setHybridRuntimeProvenance(hybridRuntimeProvenanceBoolean); - }}/> -
- ); + return ( + <> +

Hybrid Runtime Handler Endpoints

+ + + + + + + + + + + +
Qiskit Runtime Handler Endpoint: + + setQiskitRuntimeHandlerEndpoint(event.target.value) + } + /> +
AWS Runtime Handler Endpoint: + + setAWSRuntimeHandlerEndpoint(event.target.value) + } + /> +
+

Provenance Collection for Hybrid Runtime

+ + + + + + + +
Retrieve Intermediate Results: + { + hybridRuntimeProvenanceBoolean = + !hybridRuntimeProvenanceBoolean; + setHybridRuntimeProvenance(hybridRuntimeProvenanceBoolean); + }} + /> +
+ + ); } HybridRuntimeTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.qiskitRuntimeHandlerEndpoint = config.getQiskitRuntimeHandlerEndpoint(); - modeler.config.hybridRuntimeProvenance = config.getHybridRuntimeProvenance(); - modeler.config.awsRuntimeHandlerEndpoint = config.getAWSRuntimeHandlerEndpoint(); -}; \ No newline at end of file + modeler.config.qiskitRuntimeHandlerEndpoint = + config.getQiskitRuntimeHandlerEndpoint(); + modeler.config.hybridRuntimeProvenance = config.getHybridRuntimeProvenance(); + modeler.config.awsRuntimeHandlerEndpoint = + config.getAWSRuntimeHandlerEndpoint(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js index 45bcdcd7..d8e7a65c 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/NisqAnalyzerTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as config from "../framework-config/config-manager"; /** @@ -10,49 +10,55 @@ import * as config from "../framework-config/config-manager"; * @constructor */ export default function NisqAnalyzerTab() { + const [nisqAnalyzerEndpoint, setNisqAnalyzerEndpoint] = useState( + config.getNisqAnalyzerEndpoint() + ); - const [nisqAnalyzerEndpoint, setNisqAnalyzerEndpoint] = useState(config.getNisqAnalyzerEndpoint()); - - const modeler = getModeler(); - - const editorActions = modeler.get('editorActions'); - - // register editor action listener for changes in config entries - if (!editorActions._actions.hasOwnProperty('nisqAnalyzerEndpointChanged')) { - editorActions.register({ - nisqAnalyzerEndpointChanged: function (nisqAnalyzerEndpoint) { - self.modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; - } - }); - } - - // save changed config entries on close - NisqAnalyzerTab.prototype.onClose = () => { - modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; - config.setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint); - }; - - return <> -

NISQ Analyzer

- - - - - - - -
NISQ Analyzer Endpoint: - setNisqAnalyzerEndpoint(event.target.value)}/> -
- ; + const modeler = getModeler(); + + const editorActions = modeler.get("editorActions"); + + // register editor action listener for changes in config entries + if (!editorActions._actions.hasOwnProperty("nisqAnalyzerEndpointChanged")) { + editorActions.register({ + nisqAnalyzerEndpointChanged: function (nisqAnalyzerEndpoint) { + self.modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; + }, + }); + } + + // save changed config entries on close + NisqAnalyzerTab.prototype.onClose = () => { + modeler.config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; + config.setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint); + }; + + return ( + <> +

NISQ Analyzer

+ + + + + + + +
NISQ Analyzer Endpoint: + + setNisqAnalyzerEndpoint(event.target.value) + } + /> +
+ + ); } NisqAnalyzerTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.nisqAnalyzerEndpoint = config.getNisqAnalyzerEndpoint(); -}; \ No newline at end of file + modeler.config.nisqAnalyzerEndpoint = config.getNisqAnalyzerEndpoint(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/OpenToscaTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/OpenToscaTab.js index 2cf3550b..07111c69 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/OpenToscaTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/OpenToscaTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as config from "../framework-config/config-manager"; /** @@ -10,72 +10,79 @@ import * as config from "../framework-config/config-manager"; * @constructor */ export default function OpenToscaTab() { + const [opentoscaEndpoint, setOpentoscaEndpoint] = useState( + config.getOpenTOSCAEndpoint() + ); + const [wineryEndpoint, setWineryEndpoint] = useState( + config.getWineryEndpoint() + ); - const [opentoscaEndpoint, setOpentoscaEndpoint] = useState(config.getOpenTOSCAEndpoint()); - const [wineryEndpoint, setWineryEndpoint] = useState(config.getWineryEndpoint()); + const modeler = getModeler(); - const modeler = getModeler(); + const editorActions = modeler.get("editorActions"); + const eventBus = modeler.get("eventBus"); - const editorActions = modeler.get('editorActions'); - const eventBus = modeler.get('eventBus'); + // register editor action listener for changes in config entries + if (!editorActions._actions.hasOwnProperty("opentoscaEndpointChanged")) { + editorActions.register({ + opentoscaEndpointChanged: function (opentoscaEndpoint) { + self.modeler.config.opentoscaEndpoint = opentoscaEndpoint; + }, + }); + } + if (!editorActions._actions.hasOwnProperty("wineryEndpointChanged")) { + editorActions.register({ + wineryEndpointChanged: function (wineryEndpoint) { + self.modeler.config.wineryEndpoint = wineryEndpoint; + eventBus.fire("config.updated", self.modeler.config); + }, + }); + } - // register editor action listener for changes in config entries - if (!editorActions._actions.hasOwnProperty('opentoscaEndpointChanged')) { - editorActions.register({ - opentoscaEndpointChanged: function (opentoscaEndpoint) { - self.modeler.config.opentoscaEndpoint = opentoscaEndpoint; - } - }); - } - if (!editorActions._actions.hasOwnProperty('wineryEndpointChanged')) { - editorActions.register({ - wineryEndpointChanged: function (wineryEndpoint) { - self.modeler.config.wineryEndpoint = wineryEndpoint; - eventBus.fire('config.updated', self.modeler.config); - } - }); - } + // save changed config entries on close + OpenToscaTab.prototype.onClose = () => { + modeler.config.opentoscaEndpoint = opentoscaEndpoint; + modeler.config.wineryEndpoint = wineryEndpoint; + config.setOpenTOSCAEndpoint(opentoscaEndpoint); + config.setWineryEndpoint(wineryEndpoint); + }; - // save changed config entries on close - OpenToscaTab.prototype.onClose = () => { - modeler.config.opentoscaEndpoint = opentoscaEndpoint; - modeler.config.wineryEndpoint = wineryEndpoint; - config.setOpenTOSCAEndpoint(opentoscaEndpoint); - config.setWineryEndpoint(wineryEndpoint); - }; - - return <> -

OpenTOSCA

- - - - - - - - - - - -
OpenTOSCA Endpoint: - setOpentoscaEndpoint(event.target.value)}/> -
Winery Endpoint: - setWineryEndpoint(event.target.value)}/> -
- ; + return ( + <> +

OpenTOSCA

+ + + + + + + + + + + +
OpenTOSCA Endpoint: + setOpentoscaEndpoint(event.target.value)} + /> +
Winery Endpoint: + setWineryEndpoint(event.target.value)} + /> +
+ + ); } OpenToscaTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.opentoscaEndpoint = config.getOpenTOSCAEndpoint(); - modeler.config.wineryEndpoint = config.getWineryEndpoint(); -}; \ No newline at end of file + modeler.config.opentoscaEndpoint = config.getOpenTOSCAEndpoint(); + modeler.config.wineryEndpoint = config.getWineryEndpoint(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js index e08c01a2..76c7fb40 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configTabs/QrmDataTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import {getModeler} from "../../../editor/ModelerHandler"; +import React, { useState } from "react"; +import { getModeler } from "../../../editor/ModelerHandler"; import * as config from "../framework-config/config-manager"; /** @@ -10,91 +10,105 @@ import * as config from "../framework-config/config-manager"; * @constructor */ export default function QrmDataTab() { + const [githubRepositoryName, setGithubRepositoryName] = useState( + config.getQRMRepositoryName() + ); + const [githubUsername, setGithubUsername] = useState( + config.getQRMRepositoryUserName() + ); + const [githubRepositoryPath, setGithubRepositoryPath] = useState( + config.getQRMRepositoryPath() + ); - const [githubRepositoryName, setGithubRepositoryName] = useState(config.getQRMRepositoryName()); - const [githubUsername, setGithubUsername] = useState(config.getQRMRepositoryUserName()); - const [githubRepositoryPath, setGithubRepositoryPath] = useState(config.getQRMRepositoryPath()); + const modeler = getModeler(); - const modeler = getModeler(); + const editorActions = modeler.get("editorActions"); - const editorActions = modeler.get('editorActions'); + // register editor action listener for changes in config entries + if (!editorActions._actions.hasOwnProperty("qrmRepoNameChanged")) { + editorActions.register({ + qrmRepoNameChanged: function (qrmRepoName) { + self.modeler.config.githubRepositoryName = qrmRepoName; + }, + }); + } + if (!editorActions._actions.hasOwnProperty("qrmUserNameChanged")) { + editorActions.register({ + qrmUserNameChanged: function (qrmUserName) { + self.modeler.config.githubUsername = qrmUserName; + }, + }); + } + if (!editorActions._actions.hasOwnProperty("qrmRepoPathChanged")) { + editorActions.register({ + qrmRepoPathChanged: function (qrmRepoPath) { + self.modeler.config.githubRepositoryPath = qrmRepoPath; + }, + }); + } - // register editor action listener for changes in config entries - if (!editorActions._actions.hasOwnProperty('qrmRepoNameChanged')) { - editorActions.register({ - qrmRepoNameChanged: function (qrmRepoName) { - self.modeler.config.githubRepositoryName = qrmRepoName; - } - }); - } - if (!editorActions._actions.hasOwnProperty('qrmUserNameChanged')) { - editorActions.register({ - qrmUserNameChanged: function (qrmUserName) { - self.modeler.config.githubUsername = qrmUserName; - } - }); - } - if (!editorActions._actions.hasOwnProperty('qrmRepoPathChanged')) { - editorActions.register({ - qrmRepoPathChanged: function (qrmRepoPath) { - self.modeler.config.githubRepositoryPath = qrmRepoPath; - } - }); - } + // save changed config entries on close + QrmDataTab.prototype.onClose = () => { + modeler.config.githubRepositoryName = githubRepositoryName; + modeler.config.githubUsername = githubUsername; + modeler.config.githubRepositoryPath = githubRepositoryPath; + config.setQRMRepositoryName(githubRepositoryName); + config.setQRMUserName(githubUsername); + config.setQRMRepositoryPath(githubRepositoryPath); + }; - // save changed config entries on close - QrmDataTab.prototype.onClose = () => { - modeler.config.githubRepositoryName = githubRepositoryName; - modeler.config.githubUsername = githubUsername; - modeler.config.githubRepositoryPath = githubRepositoryPath; - config.setQRMRepositoryName(githubRepositoryName); - config.setQRMUserName(githubUsername); - config.setQRMRepositoryPath(githubRepositoryPath); - }; - - return <> -

QRM Data

- - - - - - - - - - - - - - - -
QRM Repository User: - setGithubUsername(event.target.value)}/> -
QRM Repository Name: - setGithubRepositoryName(event.target.value)}/> -
QRM Repository Path: - setGithubRepositoryPath(event.target.value)}/> -
- ; + return ( + <> +

QRM Data

+ + + + + + + + + + + + + + + +
QRM Repository User: + setGithubUsername(event.target.value)} + /> +
QRM Repository Name: + + setGithubRepositoryName(event.target.value) + } + /> +
QRM Repository Path: + + setGithubRepositoryPath(event.target.value) + } + /> +
+ + ); } QrmDataTab.prototype.config = () => { - const modeler = getModeler(); + const modeler = getModeler(); - modeler.config.githubRepositoryName = config.getQRMRepositoryName(); - modeler.config.githubUsername = config.getQRMRepositoryUserName(); - modeler.config.githubRepositoryPath = config.getQRMRepositoryPath(); -}; \ No newline at end of file + modeler.config.githubRepositoryName = config.getQRMRepositoryName(); + modeler.config.githubUsername = config.getQRMRepositoryUserName(); + modeler.config.githubRepositoryPath = config.getQRMRepositoryPath(); +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurations.js b/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurations.js index 71876f83..60f7ddd1 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurations.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurations.js @@ -1,43 +1,41 @@ -import ConfigurationsEndpoint from '../../../editor/configurations/ConfigurationEndpoint'; -import * as dataConsts from '../../data-extension/Constants'; -import * as configManager from '../framework-config/config-manager'; +import ConfigurationsEndpoint from "../../../editor/configurations/ConfigurationEndpoint"; +import * as dataConsts from "../../data-extension/Constants"; +import * as configManager from "../framework-config/config-manager"; /** * Configurations endpoint for custom QuantME data objects which can be applied to DataMapObjects. Loads configurations * from the QuantME data configurations endpoint which can be applied to DataMapObjects. */ class DataObjectConfigurations extends ConfigurationsEndpoint { - - constructor() { - super(configManager.getQuantMEDataConfigurationsEndpoint()); - } - - /** - * Returns all Configurations for DataMapObjects which are saved in this endpoint. - */ - getQuantMEDataConfigurations() { - return this.getConfigurations(dataConsts.DATA_MAP_OBJECT); - } - - /** - * Returns the configuration with the given ID. - * - * @param id The given ID. - * @return {*} - */ - getQuantMEDataConfiguration(id) { - return this.getConfiguration(id); - } - - /** - * Updates the saved configurations by fetching again all plugins from the QuantME endpoint - */ - updateQuantMEDataConfigurations() { - this.fetchConfigurations(); - } + constructor() { + super(configManager.getQuantMEDataConfigurationsEndpoint()); + } + + /** + * Returns all Configurations for DataMapObjects which are saved in this endpoint. + */ + getQuantMEDataConfigurations() { + return this.getConfigurations(dataConsts.DATA_MAP_OBJECT); + } + + /** + * Returns the configuration with the given ID. + * + * @param id The given ID. + * @return {*} + */ + getQuantMEDataConfiguration(id) { + return this.getConfiguration(id); + } + + /** + * Updates the saved configurations by fetching again all plugins from the QuantME endpoint + */ + updateQuantMEDataConfigurations() { + this.fetchConfigurations(); + } } - let configEndpointInstance; /** @@ -46,8 +44,8 @@ let configEndpointInstance; * @return {DataObjectConfigurations} */ export function instance() { - if (!configEndpointInstance) { - configEndpointInstance = new DataObjectConfigurations(); - } - return configEndpointInstance; -} \ No newline at end of file + if (!configEndpointInstance) { + configEndpointInstance = new DataObjectConfigurations(); + } + return configEndpointInstance; +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurationsTab.js b/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurationsTab.js index a92c5c77..eadeae63 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurationsTab.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configurations/DataObjectConfigurationsTab.js @@ -1,5 +1,5 @@ -import React, {useState} from 'react'; -import * as configManager from '../framework-config/config-manager'; +import React, { useState } from "react"; +import * as configManager from "../framework-config/config-manager"; /** * React component specifying a tab for the configuration dialog of the modeler. The tab allows the user to change the @@ -9,32 +9,39 @@ import * as configManager from '../framework-config/config-manager'; * @constructor */ export default function DataObjectConfigurationsTab() { + const [dataConfigurationsEndpoint, setDataConfigurationsEndpoint] = useState( + configManager.getQuantMEDataConfigurationsEndpoint() + ); - const [dataConfigurationsEndpoint, setDataConfigurationsEndpoint] = useState(configManager.getQuantMEDataConfigurationsEndpoint()); + // save the value of the endpoint in the QuantME config + DataObjectConfigurationsTab.prototype.onClose = () => { + configManager.setQuantMEDataConfigurationsEndpoint( + dataConfigurationsEndpoint + ); + }; - // save the value of the endpoint in the QuantME config - DataObjectConfigurationsTab.prototype.onClose = () => { - configManager.setQuantMEDataConfigurationsEndpoint(dataConfigurationsEndpoint); - }; - - return (<> -

QuantME data configuration endpoint:

- - - - - - - -
Data Configurations Endpoint - setDataConfigurationsEndpoint(event.target.value)}/> -
- ); + return ( + <> +

QuantME data configuration endpoint:

+ + + + + + + +
Data Configurations Endpoint + + setDataConfigurationsEndpoint(event.target.value) + } + /> +
+ + ); } -DataObjectConfigurationsTab.prototype.config = () => { -}; \ No newline at end of file +DataObjectConfigurationsTab.prototype.config = () => {}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/configurations/UpdateDataObjectConfigurationsButton.js b/components/bpmn-q/modeler-component/extensions/quantme/configurations/UpdateDataObjectConfigurationsButton.js index 7f96e0b7..d3330fb9 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/configurations/UpdateDataObjectConfigurationsButton.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/configurations/UpdateDataObjectConfigurationsButton.js @@ -1,5 +1,5 @@ -import React from 'react'; -import {instance as quantmeDataObjectConfigs} from './DataObjectConfigurations'; +import React from "react"; +import { instance as quantmeDataObjectConfigs } from "./DataObjectConfigurations"; /** * React button component which updates the loaded configurations of the DataObjectConfigurations endpoint when clicked. @@ -8,11 +8,20 @@ import {instance as quantmeDataObjectConfigs} from './DataObjectConfigurations'; * @constructor */ export default function UpdateDataObjectConfigurationsButton() { - - return
- -
; + return ( +
+ +
+ ); } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/deployment/BindingUtils.js b/components/bpmn-q/modeler-component/extensions/quantme/deployment/BindingUtils.js index 4e8a81cd..d5a1fcaa 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/deployment/BindingUtils.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/deployment/BindingUtils.js @@ -9,8 +9,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -const QUANTME_NAMESPACE_PULL_ENCODED = encodeURIComponent(encodeURIComponent('http://quantil.org/quantme/pull')); -const QUANTME_NAMESPACE_PUSH_ENCODED = encodeURIComponent(encodeURIComponent('http://quantil.org/quantme/push')); +const QUANTME_NAMESPACE_PULL_ENCODED = encodeURIComponent( + encodeURIComponent("http://quantil.org/quantme/pull") +); +const QUANTME_NAMESPACE_PUSH_ENCODED = encodeURIComponent( + encodeURIComponent("http://quantil.org/quantme/push") +); /** * Check whether the given ServiceTask has an attached deployment model that should be bound using pull or push mode @@ -21,22 +25,25 @@ const QUANTME_NAMESPACE_PUSH_ENCODED = encodeURIComponent(encodeURIComponent('ht * or undefined if unable to determine pull or push */ export function getBindingType(serviceTask) { - let urlSplit = serviceTask.deploymentModelUrl.split('servicetemplates/'); - if (urlSplit.length !== 2) { - console.warn('Deployment model url is invalid: %s', serviceTask.deploymentModelUrl); - return undefined; - } - let namespace = urlSplit[1]; + let urlSplit = serviceTask.deploymentModelUrl.split("servicetemplates/"); + if (urlSplit.length !== 2) { + console.warn( + "Deployment model url is invalid: %s", + serviceTask.deploymentModelUrl + ); + return undefined; + } + let namespace = urlSplit[1]; - if (namespace.startsWith(QUANTME_NAMESPACE_PUSH_ENCODED)) { - return 'push'; - } + if (namespace.startsWith(QUANTME_NAMESPACE_PUSH_ENCODED)) { + return "push"; + } - if (namespace.startsWith(QUANTME_NAMESPACE_PULL_ENCODED)) { - return 'pull'; - } + if (namespace.startsWith(QUANTME_NAMESPACE_PULL_ENCODED)) { + return "pull"; + } - return undefined; + return undefined; } /** @@ -48,23 +55,41 @@ export function getBindingType(serviceTask) { * @param modeling the modeling element to adapt properties of the workflow elements * @return {{success: boolean}} true if binding is successful, false otherwise */ -export function bindUsingPull(topicName, serviceTaskId, elementRegistry, modeling) { +export function bindUsingPull( + topicName, + serviceTaskId, + elementRegistry, + modeling +) { + if ( + topicName === undefined || + serviceTaskId === undefined || + elementRegistry === undefined || + modeling === undefined + ) { + console.error( + "Topic name, service task id, element registry, and modeling required for binding using pull!" + ); + return { success: false }; + } - if (topicName === undefined || serviceTaskId === undefined || elementRegistry === undefined || modeling === undefined) { - console.error('Topic name, service task id, element registry, and modeling required for binding using pull!'); - return {success: false}; - } + // retrieve service task to bind + let serviceTask = elementRegistry.get(serviceTaskId); + if (serviceTask === undefined) { + console.error( + "Unable to retrieve corresponding task for id: %s", + serviceTaskId + ); + return { success: false }; + } - // retrieve service task to bind - let serviceTask = elementRegistry.get(serviceTaskId); - if (serviceTask === undefined) { - console.error('Unable to retrieve corresponding task for id: %s', serviceTaskId); - return {success: false}; - } - - // remove deployment model URL and set topic - modeling.updateProperties(serviceTask, {'deploymentModelUrl': undefined, type: 'external', topic: topicName}); - return {success: true}; + // remove deployment model URL and set topic + modeling.updateProperties(serviceTask, { + deploymentModelUrl: undefined, + type: "external", + topic: topicName, + }); + return { success: true }; } /** @@ -76,19 +101,27 @@ export function bindUsingPull(topicName, serviceTaskId, elementRegistry, modelin * @return {{success: boolean}} true if binding is successful, false otherwise */ export function bindUsingPush(csar, serviceTaskId, elementRegistry) { + if ( + csar === undefined || + serviceTaskId === undefined || + elementRegistry === undefined + ) { + console.error( + "CSAR details, service task id, and element registry required for binding using push!" + ); + return { success: false }; + } - if (csar === undefined || serviceTaskId === undefined || elementRegistry === undefined) { - console.error('CSAR details, service task id, and element registry required for binding using push!'); - return {success: false}; - } - - // retrieve service task to bind - let serviceTask = elementRegistry.get(serviceTaskId); - if (serviceTask === undefined) { - console.error('Unable to retrieve corresponding task for id: %s', serviceTaskId); - return {success: false}; - } + // retrieve service task to bind + let serviceTask = elementRegistry.get(serviceTaskId); + if (serviceTask === undefined) { + console.error( + "Unable to retrieve corresponding task for id: %s", + serviceTaskId + ); + return { success: false }; + } - console.warn('Binding using push currently not supported!'); - return {success: false}; + console.warn("Binding using push currently not supported!"); + return { success: false }; } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/deployment/DeploymentUtils.js b/components/bpmn-q/modeler-component/extensions/quantme/deployment/DeploymentUtils.js index 46877eb9..c3811dbc 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/deployment/DeploymentUtils.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/deployment/DeploymentUtils.js @@ -9,8 +9,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {getBindingType} from './BindingUtils'; -import {getFlowElementsRecursively} from '../../../editor/util/ModellingUtilities'; +import { getBindingType } from "./BindingUtils"; +import { getFlowElementsRecursively } from "../../../editor/util/ModellingUtilities"; /** * Get the ServiceTasks of the current workflow that have an attached deployment model to deploy the corresponding service starting from the given root element @@ -19,39 +19,40 @@ import {getFlowElementsRecursively} from '../../../editor/util/ModellingUtilitie * @return the list of ServiceTasks with attached deployment models to deploy the required services */ export function getServiceTasksToDeploy(startElement) { - let csarsToDeploy = []; + let csarsToDeploy = []; - if (startElement === undefined) { - console.warn('Element to start is undefined!'); - return csarsToDeploy; - } + if (startElement === undefined) { + console.warn("Element to start is undefined!"); + return csarsToDeploy; + } - // search for service tasks with assigned deployment model - let flowElements = getFlowElementsRecursively(startElement); - for (let i = 0; i < flowElements.length; i++) { - let flowElement = flowElements[i]; + // search for service tasks with assigned deployment model + let flowElements = getFlowElementsRecursively(startElement); + for (let i = 0; i < flowElements.length; i++) { + let flowElement = flowElements[i]; - if (isDeployableServiceTask(flowElement)) { - console.log('Found deployable service task: ', flowElement); + if (isDeployableServiceTask(flowElement)) { + console.log("Found deployable service task: ", flowElement); - // check if CSAR was already added for another service task - let csarEntry = csarsToDeploy.find(serviceTask => flowElement.deploymentModelUrl === serviceTask.url); - if (csarEntry !== undefined) { - console.log('Adding to existing CSAR entry...'); - csarEntry.serviceTaskIds.push(flowElement.id); - } else { - csarsToDeploy.push( - { - serviceTaskIds: [flowElement.id], - url: flowElement.deploymentModelUrl, - type: getBindingType(flowElement), - csarName: getCSARName(flowElement) - }); - } - } + // check if CSAR was already added for another service task + let csarEntry = csarsToDeploy.find( + (serviceTask) => flowElement.deploymentModelUrl === serviceTask.url + ); + if (csarEntry !== undefined) { + console.log("Adding to existing CSAR entry..."); + csarEntry.serviceTaskIds.push(flowElement.id); + } else { + csarsToDeploy.push({ + serviceTaskIds: [flowElement.id], + url: flowElement.deploymentModelUrl, + type: getBindingType(flowElement), + csarName: getCSARName(flowElement), + }); + } } + } - return csarsToDeploy; + return csarsToDeploy; } /** @@ -61,9 +62,9 @@ export function getServiceTasksToDeploy(startElement) { * @return {*} the CSAR name */ function getCSARName(serviceTask) { - let url = serviceTask.deploymentModelUrl.split('/?csar')[0]; - let urlSplit = url.split('/'); - return urlSplit[urlSplit.length - 1] + '.csar'; + let url = serviceTask.deploymentModelUrl.split("/?csar")[0]; + let urlSplit = url.split("/"); + return urlSplit[urlSplit.length - 1] + ".csar"; } /** @@ -73,5 +74,10 @@ function getCSARName(serviceTask) { * @return {*|boolean} true if the element is a ServiceTask and has an assigned deployment model, false otherwise */ function isDeployableServiceTask(element) { - return element.$type && element.$type === 'bpmn:ServiceTask' && element.deploymentModelUrl && getBindingType(element) !== undefined; + return ( + element.$type && + element.$type === "bpmn:ServiceTask" && + element.deploymentModelUrl && + getBindingType(element) !== undefined + ); } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/deployment/OpenTOSCAUtils.js b/components/bpmn-q/modeler-component/extensions/quantme/deployment/OpenTOSCAUtils.js index e4bddb21..c142d115 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/deployment/OpenTOSCAUtils.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/deployment/OpenTOSCAUtils.js @@ -9,8 +9,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {fetch} from 'whatwg-fetch'; -import {performAjax} from '../utilities/Utilities'; +import { fetch } from "whatwg-fetch"; +import { performAjax } from "../utilities/Utilities"; /** * Upload the CSAR located at the given URL to the connected OpenTOSCA Container and return the corresponding URL and required input parameters @@ -20,56 +20,67 @@ import {performAjax} from '../utilities/Utilities'; * @param url the URL pointing to the CSAR * @param wineryEndpoint the endpoint of the Winery containing the CSAR to upload */ -export async function uploadCSARToContainer(opentoscaEndpoint, csarName, url, wineryEndpoint) { - - if (opentoscaEndpoint === undefined) { - console.error('OpenTOSCA endpoint is undefined. Unable to upload CSARs...'); - return {success: false}; +export async function uploadCSARToContainer( + opentoscaEndpoint, + csarName, + url, + wineryEndpoint +) { + if (opentoscaEndpoint === undefined) { + console.error("OpenTOSCA endpoint is undefined. Unable to upload CSARs..."); + return { success: false }; + } + + try { + if (url.startsWith("{{ wineryEndpoint }}")) { + url = url.replace("{{ wineryEndpoint }}", wineryEndpoint); + } + console.log( + "Checking if CSAR at following URL is already uploaded to the OpenTOSCA Container: ", + url + ); + + // check if CSAR is already uploaded + let getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); + + if (!getCSARResult.success) { + console.log("CSAR is not yet uploaded. Uploading..."); + + let body = { + enrich: "false", + name: csarName, + url: url, + }; + + // upload the CSAR + await fetch(opentoscaEndpoint, { + method: "POST", + body: JSON.stringify(body), + headers: { "Content-Type": "application/json" }, + }); + + // check successful upload and retrieve corresponding url + getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); } - try { - if (url.startsWith('{{ wineryEndpoint }}')) { - url = url.replace('{{ wineryEndpoint }}', wineryEndpoint); - } - console.log('Checking if CSAR at following URL is already uploaded to the OpenTOSCA Container: ', url); - - // check if CSAR is already uploaded - let getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); - - if (!getCSARResult.success) { - console.log('CSAR is not yet uploaded. Uploading...'); - - let body = { - enrich: 'false', - name: csarName, - url: url - }; - - // upload the CSAR - await fetch(opentoscaEndpoint, { - method: 'POST', - body: JSON.stringify(body), - headers: {'Content-Type': 'application/json'} - }); - - // check successful upload and retrieve corresponding url - getCSARResult = await getBuildPlanForCSAR(opentoscaEndpoint, csarName); - } - - if (!getCSARResult.success) { - console.error('Uploading CSAR failed!'); - return {success: false}; - } - - // retrieve input parameters for the build plan - let buildPlanResult = await fetch(getCSARResult.url); - let buildPlanResultJson = await buildPlanResult.json(); - - return {success: true, url: getCSARResult.url, inputParameters: buildPlanResultJson.input_parameters}; - } catch (e) { - console.error('Error while uploading CSAR: ' + e); - return {success: false}; + if (!getCSARResult.success) { + console.error("Uploading CSAR failed!"); + return { success: false }; } + + // retrieve input parameters for the build plan + let buildPlanResult = await fetch(getCSARResult.url); + let buildPlanResultJson = await buildPlanResult.json(); + + return { + success: true, + url: getCSARResult.url, + inputParameters: buildPlanResultJson.input_parameters, + }; + } catch (e) { + console.error("Error while uploading CSAR: " + e); + return { success: false }; + } } /** @@ -80,31 +91,29 @@ export async function uploadCSARToContainer(opentoscaEndpoint, csarName, url, wi * @return the status whether the given CSAR is uploaded and the corresponding build plan link if available */ async function getBuildPlanForCSAR(opentoscaEndpoint, csarName) { - - // get all currently deployed CSARs - let response = await fetch(opentoscaEndpoint); - let responseJson = await response.json(); - - let deployedCSARs = responseJson.csars; - if (deployedCSARs === undefined) { - - // no CSARs available - return {success: false}; + // get all currently deployed CSARs + let response = await fetch(opentoscaEndpoint); + let responseJson = await response.json(); + + let deployedCSARs = responseJson.csars; + if (deployedCSARs === undefined) { + // no CSARs available + return { success: false }; + } + + for (let i = 0; i < deployedCSARs.length; i++) { + let deployedCSAR = deployedCSARs[i]; + if (deployedCSAR.id === csarName) { + console.log("Found uploaded CSAR with id: %s", csarName); + let url = deployedCSAR._links.self.href; + + // retrieve the URl to the build plan required to get the input parameters and to instantiate the CSAR + return getBuildPlanUrl(url); } + } - for (let i = 0; i < deployedCSARs.length; i++) { - let deployedCSAR = deployedCSARs[i]; - if (deployedCSAR.id === csarName) { - console.log('Found uploaded CSAR with id: %s', csarName); - let url = deployedCSAR._links.self.href; - - // retrieve the URl to the build plan required to get the input parameters and to instantiate the CSAR - return getBuildPlanUrl(url); - } - } - - // unable to find CSAR - return {success: false}; + // unable to find CSAR + return { success: false }; } /** @@ -114,25 +123,31 @@ async function getBuildPlanForCSAR(opentoscaEndpoint, csarName) { * @return the URL to the build plan for the given CSAR */ async function getBuildPlanUrl(csarUrl) { - - let response = await fetch(csarUrl + '/servicetemplates'); - let responseJson = await response.json(); - - if (!responseJson.service_templates || responseJson.service_templates.length !== 1) { - console.error('Unable to find service template in CSAR at URL: %s', csarUrl); - return {success: false}; - } - - let buildPlansUrl = responseJson.service_templates[0]._links.self.href + '/buildplans'; - response = await fetch(buildPlansUrl); - responseJson = await response.json(); - - if (!responseJson.plans || responseJson.plans.length !== 1) { - console.error('Unable to find build plan at URL: %s', buildPlansUrl); - return {success: false}; - } - - return {success: true, url: responseJson.plans[0]._links.self.href}; + let response = await fetch(csarUrl + "/servicetemplates"); + let responseJson = await response.json(); + + if ( + !responseJson.service_templates || + responseJson.service_templates.length !== 1 + ) { + console.error( + "Unable to find service template in CSAR at URL: %s", + csarUrl + ); + return { success: false }; + } + + let buildPlansUrl = + responseJson.service_templates[0]._links.self.href + "/buildplans"; + response = await fetch(buildPlansUrl); + responseJson = await response.json(); + + if (!responseJson.plans || responseJson.plans.length !== 1) { + console.error("Unable to find build plan at URL: %s", buildPlansUrl); + return { success: false }; + } + + return { success: true, url: responseJson.plans[0]._links.self.href }; } /** @@ -143,85 +158,96 @@ async function getBuildPlanUrl(csarUrl) { * @return the result of the instance creation (success, endpoint, topic on which the service listens, ...) */ export async function createServiceInstance(csar, camundaEngineEndpoint) { - - let result = {success: false}; - - let inputParameters = csar.inputParameters; - if (csar.type === 'pull') { - - // get special parameters that are required to bind services using external tasks / the pulling pattern - let camundaTopicParam = inputParameters.find((param) => param.name === 'camundaTopic'); - let camundaEndpointParam = inputParameters.find((param) => param.name === 'camundaEndpoint'); - - // abort if parameters are not available - if (camundaTopicParam === undefined || camundaEndpointParam === undefined) { - console.error('Unable to pass topic to poll to service instance creation. Service binding will fail!'); - return result; - } - - // generate topic for the binding - let topicName = makeId(12); - - camundaTopicParam.value = topicName; - camundaEndpointParam.value = camundaEngineEndpoint; - result.topicName = topicName; + let result = { success: false }; + + let inputParameters = csar.inputParameters; + if (csar.type === "pull") { + // get special parameters that are required to bind services using external tasks / the pulling pattern + let camundaTopicParam = inputParameters.find( + (param) => param.name === "camundaTopic" + ); + let camundaEndpointParam = inputParameters.find( + (param) => param.name === "camundaEndpoint" + ); + + // abort if parameters are not available + if (camundaTopicParam === undefined || camundaEndpointParam === undefined) { + console.error( + "Unable to pass topic to poll to service instance creation. Service binding will fail!" + ); + return result; } - // trigger instance creation - let instanceCreationResponse = await fetch(csar.buildPlanUrl + '/instances', { - method: 'POST', - body: JSON.stringify(inputParameters), - headers: {'Content-Type': 'application/json'} - }); - let instanceCreationResponseJson = await instanceCreationResponse.json(); - - // wait for the service instance to be created - await new Promise(r => setTimeout(r, 5000)); - - // get service template instance to poll for completness - let buildPlanResponse = await fetch(csar.buildPlanUrl + '/instances/' + instanceCreationResponseJson); - let buildPlanResponseJson = await buildPlanResponse.json(); - - // retry polling 10 times, creation of the build time takes some time - for (let retry = 0; retry < 10; retry++) { - - // stop retries in case of correct response - if (buildPlanResponseJson._links) { - break; - } - - await new Promise(r => setTimeout(r, 5000)); + // generate topic for the binding + let topicName = makeId(12); + + camundaTopicParam.value = topicName; + camundaEndpointParam.value = camundaEngineEndpoint; + result.topicName = topicName; + } + + // trigger instance creation + let instanceCreationResponse = await fetch(csar.buildPlanUrl + "/instances", { + method: "POST", + body: JSON.stringify(inputParameters), + headers: { "Content-Type": "application/json" }, + }); + let instanceCreationResponseJson = await instanceCreationResponse.json(); + + // wait for the service instance to be created + await new Promise((r) => setTimeout(r, 5000)); + + // get service template instance to poll for completness + let buildPlanResponse = await fetch( + csar.buildPlanUrl + "/instances/" + instanceCreationResponseJson + ); + let buildPlanResponseJson = await buildPlanResponse.json(); + + // retry polling 10 times, creation of the build time takes some time + for (let retry = 0; retry < 10; retry++) { + // stop retries in case of correct response + if (buildPlanResponseJson._links) { + break; + } - console.log('Retry fetching build plan'); + await new Promise((r) => setTimeout(r, 5000)); - buildPlanResponse = await fetch(csar.buildPlanUrl + '/instances/' + instanceCreationResponseJson); - buildPlanResponseJson = await buildPlanResponse.json(); - } + console.log("Retry fetching build plan"); - if (!buildPlanResponseJson._links) { - console.log('Unable to fetch build plans for ' + csar.buildPlanUrl + '/instances/' + instanceCreationResponseJson); - result.success = false; - return result; - } + buildPlanResponse = await fetch( + csar.buildPlanUrl + "/instances/" + instanceCreationResponseJson + ); + buildPlanResponseJson = await buildPlanResponse.json(); + } - let pollingUrl = buildPlanResponseJson._links.service_template_instance.href; + if (!buildPlanResponseJson._links) { + console.log( + "Unable to fetch build plans for " + + csar.buildPlanUrl + + "/instances/" + + instanceCreationResponseJson + ); + result.success = false; + return result; + } - let state = 'CREATING'; - console.log('Polling for finished service instance at URL: %s', pollingUrl); - while (!(state === 'CREATED' || state === 'FAILED')) { + let pollingUrl = buildPlanResponseJson._links.service_template_instance.href; - // wait 5 seconds for next poll - await new Promise(r => setTimeout(r, 5000)); - // poll for current state - let pollingResponse = await fetch(pollingUrl); - let pollingResponseJson = await pollingResponse.json(); - console.log('Polling response: ', pollingResponseJson); + let state = "CREATING"; + console.log("Polling for finished service instance at URL: %s", pollingUrl); + while (!(state === "CREATED" || state === "FAILED")) { + // wait 5 seconds for next poll + await new Promise((r) => setTimeout(r, 5000)); + // poll for current state + let pollingResponse = await fetch(pollingUrl); + let pollingResponseJson = await pollingResponse.json(); + console.log("Polling response: ", pollingResponseJson); - state = pollingResponseJson.state; - } + state = pollingResponseJson.state; + } - result.success = true; - return result; + result.success = true; + return result; } /** @@ -235,47 +261,79 @@ export async function createServiceInstance(csar, camundaEngineEndpoint) { * @param fileName the name of the file to upload as blob * @return the final name of the created ArtifactTemplate */ -export async function createNewArtifactTemplate(wineryEndpoint, localNamePrefix, namespace, type, blob, fileName) { - console.log('Creating new ArtifactTemplate of type: ', type); - - // retrieve the currently available ArtifactTemplates - let getArtifactsResult = await fetch(wineryEndpoint + '/artifacttemplates/'); - let getArtifactsResultJson = await getArtifactsResult.json(); - - console.log(getArtifactsResultJson); - - // get unique name for the ArtifactTemplate - let artifactTemplateLocalName = localNamePrefix; - if (getArtifactsResultJson.some(e => e.id === artifactTemplateLocalName && e.namespace === namespace)) { - let nameOccupied = true; - artifactTemplateLocalName += '-' + makeId(1); - while (nameOccupied) { - if (!getArtifactsResultJson.some(e => e.id === artifactTemplateLocalName && e.namespace === namespace)) { - nameOccupied = false; - } else { - artifactTemplateLocalName += makeId(1); - } - } +export async function createNewArtifactTemplate( + wineryEndpoint, + localNamePrefix, + namespace, + type, + blob, + fileName +) { + console.log("Creating new ArtifactTemplate of type: ", type); + + // retrieve the currently available ArtifactTemplates + let getArtifactsResult = await fetch(wineryEndpoint + "/artifacttemplates/"); + let getArtifactsResultJson = await getArtifactsResult.json(); + + console.log(getArtifactsResultJson); + + // get unique name for the ArtifactTemplate + let artifactTemplateLocalName = localNamePrefix; + if ( + getArtifactsResultJson.some( + (e) => e.id === artifactTemplateLocalName && e.namespace === namespace + ) + ) { + let nameOccupied = true; + artifactTemplateLocalName += "-" + makeId(1); + while (nameOccupied) { + if ( + !getArtifactsResultJson.some( + (e) => e.id === artifactTemplateLocalName && e.namespace === namespace + ) + ) { + nameOccupied = false; + } else { + artifactTemplateLocalName += makeId(1); + } } - console.log('Creating ArtifactTemplate with name: ', artifactTemplateLocalName); - - // create ArtifactTemplate - let artifactCreationResponse = await fetch(wineryEndpoint + '/artifacttemplates/', { - method: 'POST', - body: JSON.stringify({localname: artifactTemplateLocalName, namespace: namespace, type: type}), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'} - }); - let artifactCreationResponseText = await artifactCreationResponse.text(); - - // get URL for the file upload to the ArtifactTemplate - let fileUrl = wineryEndpoint + '/artifacttemplates/' + artifactCreationResponseText + 'files'; - - // upload the blob - const fd = new FormData(); - fd.append('file', blob, fileName); - await performAjax(fileUrl, fd); - - return artifactTemplateLocalName; + } + console.log( + "Creating ArtifactTemplate with name: ", + artifactTemplateLocalName + ); + + // create ArtifactTemplate + let artifactCreationResponse = await fetch( + wineryEndpoint + "/artifacttemplates/", + { + method: "POST", + body: JSON.stringify({ + localname: artifactTemplateLocalName, + namespace: namespace, + type: type, + }), + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + } + ); + let artifactCreationResponseText = await artifactCreationResponse.text(); + + // get URL for the file upload to the ArtifactTemplate + let fileUrl = + wineryEndpoint + + "/artifacttemplates/" + + artifactCreationResponseText + + "files"; + + // upload the blob + const fd = new FormData(); + fd.append("file", blob, fileName); + await performAjax(fileUrl, fd); + + return artifactTemplateLocalName; } /** @@ -286,56 +344,91 @@ export async function createNewArtifactTemplate(wineryEndpoint, localNamePrefix, * @param serviceTemplateNamespace the namespace of the ServiceTemplate to create a new version from * @return the URL to the new version, or an error if the base ServiceTemplate is not available at the given Winery endpoint */ -export async function createNewServiceTemplateVersion(wineryEndpoint, serviceTemplateId, serviceTemplateNamespace) { - console.log('Creating new version of Service Template with ID %s and namespace %s', serviceTemplateId, serviceTemplateNamespace); - - // retrieve the currently available ServiceTemplates - let getTemplatesResult = await fetch(wineryEndpoint + '/servicetemplates/'); - let getTemplatesResultJson = await getTemplatesResult.json(); - - // abort if base service template is not available - if (!getTemplatesResultJson.some(e => e.id === serviceTemplateId && e.namespace === serviceTemplateNamespace)) { - console.log('Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!'); - return {error: 'Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!'}; +export async function createNewServiceTemplateVersion( + wineryEndpoint, + serviceTemplateId, + serviceTemplateNamespace +) { + console.log( + "Creating new version of Service Template with ID %s and namespace %s", + serviceTemplateId, + serviceTemplateNamespace + ); + + // retrieve the currently available ServiceTemplates + let getTemplatesResult = await fetch(wineryEndpoint + "/servicetemplates/"); + let getTemplatesResultJson = await getTemplatesResult.json(); + + // abort if base service template is not available + if ( + !getTemplatesResultJson.some( + (e) => + e.id === serviceTemplateId && e.namespace === serviceTemplateNamespace + ) + ) { + console.log( + "Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!" + ); + return { + error: + "Required base ServiceTemplate for deploying Qiskit Runtime programs not available in Winery!", + }; + } + + // get unique name for the new version + let version = makeId(5); + let nameOccupied = true; + while (nameOccupied) { + if ( + !getTemplatesResultJson.some( + (e) => + e.id === serviceTemplateId + "_" + version + "-w1-wip1" && + e.namespace === serviceTemplateNamespace + ) + ) { + nameOccupied = false; + } else { + version += makeId(1); } - - // get unique name for the new version - let version = makeId(5); - let nameOccupied = true; - while (nameOccupied) { - if (!getTemplatesResultJson.some(e => e.id === serviceTemplateId + '_' + version + '-w1-wip1' && e.namespace === serviceTemplateNamespace)) { - nameOccupied = false; - } else { - version += makeId(1); - } - } - console.log('Using component version: ', version); - - // create ServiceTemplate version - let versionUrl = wineryEndpoint + '/servicetemplates/' + encodeURIComponent(encodeURIComponent(serviceTemplateNamespace)) + '/' + serviceTemplateId; - console.log('Creating new version under URL:', versionUrl); - let versionCreationResponse = await fetch(versionUrl, { - method: 'POST', - body: JSON.stringify({ - version: { - componentVersion: version, currentVersion: false, editable: true, latestVersion: false, - releasable: false, wineryVersion: 1, workInProgressVersion: 1 - } - }), - headers: {'Content-Type': 'application/json', 'Accept': 'application/json'} - }); - - // assemble URL to the created ServiceTemplate version - let versionCreationResponseText = await versionCreationResponse.text(); - return wineryEndpoint + '/servicetemplates/' + versionCreationResponseText; + } + console.log("Using component version: ", version); + + // create ServiceTemplate version + let versionUrl = + wineryEndpoint + + "/servicetemplates/" + + encodeURIComponent(encodeURIComponent(serviceTemplateNamespace)) + + "/" + + serviceTemplateId; + console.log("Creating new version under URL:", versionUrl); + let versionCreationResponse = await fetch(versionUrl, { + method: "POST", + body: JSON.stringify({ + version: { + componentVersion: version, + currentVersion: false, + editable: true, + latestVersion: false, + releasable: false, + wineryVersion: 1, + workInProgressVersion: 1, + }, + }), + headers: { "Content-Type": "application/json", Accept: "application/json" }, + }); + + // assemble URL to the created ServiceTemplate version + let versionCreationResponseText = await versionCreationResponse.text(); + return wineryEndpoint + "/servicetemplates/" + versionCreationResponseText; } function makeId(length) { - let result = ''; - let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - let charactersLength = characters.length; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; + let result = ""; + let characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; } diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js index 11f1a52a..4a62769a 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config-manager.js @@ -10,7 +10,7 @@ */ import defaultConfig from "./config"; -import {getPluginConfig} from '../../../editor/plugin/PluginConfigHandler'; +import { getPluginConfig } from "../../../editor/plugin/PluginConfigHandler"; let config = {}; @@ -18,63 +18,76 @@ let config = {}; * Get the endpoint for Data Object Configurations */ export function getQuantMEDataConfigurationsEndpoint() { - if (config.quantmeDataConfigurationsEndpoint === undefined) { - setQuantMEDataConfigurationsEndpoint( - getPluginConfig('quantme').quantmeDataConfigurationsEndpoint - || defaultConfig.quantmeDataConfigurationsEndpoint); - } - return config.quantmeDataConfigurationsEndpoint; + if (config.quantmeDataConfigurationsEndpoint === undefined) { + setQuantMEDataConfigurationsEndpoint( + getPluginConfig("quantme").quantmeDataConfigurationsEndpoint || + defaultConfig.quantmeDataConfigurationsEndpoint + ); + } + return config.quantmeDataConfigurationsEndpoint; } /** * Set the endpoint for Data Object Configurations */ -export function setQuantMEDataConfigurationsEndpoint(dataConfigurationsEndpoint) { - if (dataConfigurationsEndpoint !== null && dataConfigurationsEndpoint !== undefined) { - config.quantmeDataConfigurationsEndpoint = dataConfigurationsEndpoint; - } +export function setQuantMEDataConfigurationsEndpoint( + dataConfigurationsEndpoint +) { + if ( + dataConfigurationsEndpoint !== null && + dataConfigurationsEndpoint !== undefined + ) { + config.quantmeDataConfigurationsEndpoint = dataConfigurationsEndpoint; + } } /** * Get the NISQ Analyzer endpoint */ export function getNisqAnalyzerEndpoint() { - if (config.nisqAnalyzerEndpoint === undefined) { - setNisqAnalyzerEndpoint( - getPluginConfig('quantme').nisqAnalyzerEndpoint - || defaultConfig.nisqAnalyzerEndpoint); - } - return config.nisqAnalyzerEndpoint; + if (config.nisqAnalyzerEndpoint === undefined) { + setNisqAnalyzerEndpoint( + getPluginConfig("quantme").nisqAnalyzerEndpoint || + defaultConfig.nisqAnalyzerEndpoint + ); + } + return config.nisqAnalyzerEndpoint; } /** * Set the NISQ Analyzer endpoint */ export function setNisqAnalyzerEndpoint(nisqAnalyzerEndpoint) { - if (nisqAnalyzerEndpoint !== null && nisqAnalyzerEndpoint !== undefined) { - config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; - } + if (nisqAnalyzerEndpoint !== null && nisqAnalyzerEndpoint !== undefined) { + config.nisqAnalyzerEndpoint = nisqAnalyzerEndpoint; + } } /** * Get the Transformation Framework endpoint */ export function getTransformationFrameworkEndpoint() { - if (config.transformationFrameworkEndpoint === undefined) { - setTransformationFrameworkEndpoint( - getPluginConfig('quantme').transformationFrameworkEndpoint - || defaultConfig.transformationFrameworkEndpoint); - } - return config.transformationFrameworkEndpoint; + if (config.transformationFrameworkEndpoint === undefined) { + setTransformationFrameworkEndpoint( + getPluginConfig("quantme").transformationFrameworkEndpoint || + defaultConfig.transformationFrameworkEndpoint + ); + } + return config.transformationFrameworkEndpoint; } /** * Set the Transformation Framework endpoint */ -export function setTransformationFrameworkEndpoint(transformationFrameworkEndpoint) { - if (transformationFrameworkEndpoint !== null && transformationFrameworkEndpoint !== undefined) { - config.transformationFrameworkEndpoint = transformationFrameworkEndpoint; - } +export function setTransformationFrameworkEndpoint( + transformationFrameworkEndpoint +) { + if ( + transformationFrameworkEndpoint !== null && + transformationFrameworkEndpoint !== undefined + ) { + config.transformationFrameworkEndpoint = transformationFrameworkEndpoint; + } } /** @@ -83,12 +96,13 @@ export function setTransformationFrameworkEndpoint(transformationFrameworkEndpoi * @return {string} the currently specified endpoint of the OpenTOSCA container */ export function getOpenTOSCAEndpoint() { - if (config.opentoscaEndpoint === undefined) { - setOpenTOSCAEndpoint( - getPluginConfig('quantme').opentoscaEndpoint - || defaultConfig.opentoscaEndpoint); - } - return config.opentoscaEndpoint; + if (config.opentoscaEndpoint === undefined) { + setOpenTOSCAEndpoint( + getPluginConfig("quantme").opentoscaEndpoint || + defaultConfig.opentoscaEndpoint + ); + } + return config.opentoscaEndpoint; } /** @@ -97,9 +111,9 @@ export function getOpenTOSCAEndpoint() { * @param opentoscaEndpoint the endpoint of the OpenTOSCA container */ export function setOpenTOSCAEndpoint(opentoscaEndpoint) { - if (opentoscaEndpoint !== null && opentoscaEndpoint !== undefined) { - config.opentoscaEndpoint = opentoscaEndpoint.replace(/\/$/, ''); - } + if (opentoscaEndpoint !== null && opentoscaEndpoint !== undefined) { + config.opentoscaEndpoint = opentoscaEndpoint.replace(/\/$/, ""); + } } /** @@ -108,12 +122,12 @@ export function setOpenTOSCAEndpoint(opentoscaEndpoint) { * @return {string} the currently specified endpoint of the Winery */ export function getWineryEndpoint() { - if (config.wineryEndpoint === undefined) { - setWineryEndpoint( - getPluginConfig('quantme').wineryEndpoint - || defaultConfig.wineryEndpoint); - } - return config.wineryEndpoint; + if (config.wineryEndpoint === undefined) { + setWineryEndpoint( + getPluginConfig("quantme").wineryEndpoint || defaultConfig.wineryEndpoint + ); + } + return config.wineryEndpoint; } /** @@ -122,9 +136,9 @@ export function getWineryEndpoint() { * @param wineryEndpoint the endpoint of the Winery */ export function setWineryEndpoint(wineryEndpoint) { - if (wineryEndpoint !== null && wineryEndpoint !== undefined) { - config.wineryEndpoint = wineryEndpoint.replace(/\/$/, ''); - } + if (wineryEndpoint !== null && wineryEndpoint !== undefined) { + config.wineryEndpoint = wineryEndpoint.replace(/\/$/, ""); + } } /** @@ -133,12 +147,13 @@ export function setWineryEndpoint(wineryEndpoint) { * @return {string} the specified repository path */ export function getQRMRepositoryPath() { - if (config.githubRepositoryPath === undefined) { - setQRMRepositoryPath( - getPluginConfig('quantme').githubRepositoryPath - || defaultConfig.githubRepositoryPath); - } - return config.githubRepositoryPath; + if (config.githubRepositoryPath === undefined) { + setQRMRepositoryPath( + getPluginConfig("quantme").githubRepositoryPath || + defaultConfig.githubRepositoryPath + ); + } + return config.githubRepositoryPath; } /** @@ -147,9 +162,9 @@ export function getQRMRepositoryPath() { * @param repositoryPath the repository path */ export function setQRMRepositoryPath(repositoryPath) { - if (repositoryPath !== null && repositoryPath !== undefined) { - config.githubRepositoryPath = repositoryPath; - } + if (repositoryPath !== null && repositoryPath !== undefined) { + config.githubRepositoryPath = repositoryPath; + } } /** @@ -158,12 +173,13 @@ export function setQRMRepositoryPath(repositoryPath) { * @return {string} the specified repository name */ export function getQRMRepositoryName() { - if (config.githubRepositoryName === undefined) { - setQRMRepositoryName( - getPluginConfig('quantme').githubRepositoryName - || defaultConfig.githubRepositoryName); - } - return config.githubRepositoryName; + if (config.githubRepositoryName === undefined) { + setQRMRepositoryName( + getPluginConfig("quantme").githubRepositoryName || + defaultConfig.githubRepositoryName + ); + } + return config.githubRepositoryName; } /** @@ -172,9 +188,9 @@ export function getQRMRepositoryName() { * @param repositoryName the repository name */ export function setQRMRepositoryName(repositoryName) { - if (repositoryName !== null && repositoryName !== undefined) { - config.githubRepositoryName = repositoryName; - } + if (repositoryName !== null && repositoryName !== undefined) { + config.githubRepositoryName = repositoryName; + } } /** @@ -183,12 +199,12 @@ export function setQRMRepositoryName(repositoryName) { * @return {string} the specified username */ export function getQRMRepositoryUserName() { - if (config.githubUsername === undefined) { - setQRMUserName( - getPluginConfig('quantme').githubUsername - || defaultConfig.githubUsername); - } - return config.githubUsername; + if (config.githubUsername === undefined) { + setQRMUserName( + getPluginConfig("quantme").githubUsername || defaultConfig.githubUsername + ); + } + return config.githubUsername; } /** @@ -197,9 +213,9 @@ export function getQRMRepositoryUserName() { * @param userName the username */ export function setQRMUserName(userName) { - if (userName !== null && userName !== undefined) { - config.githubUsername = userName; - } + if (userName !== null && userName !== undefined) { + config.githubUsername = userName; + } } /** @@ -208,12 +224,12 @@ export function setQRMUserName(userName) { * @return {string} the specified username */ export function getGithubToken() { - if (config.githubToken === undefined) { - setGithubToken( - getPluginConfig('quantme').githubToken - || defaultConfig.githubToken); - } - return config.githubToken; + if (config.githubToken === undefined) { + setGithubToken( + getPluginConfig("quantme").githubToken || defaultConfig.githubToken + ); + } + return config.githubToken; } /** @@ -222,9 +238,9 @@ export function getGithubToken() { * @param githubToken the username */ export function setGithubToken(githubToken) { - if (githubToken !== null && githubToken !== undefined) { - config.githubToken = githubToken; - } + if (githubToken !== null && githubToken !== undefined) { + config.githubToken = githubToken; + } } /** @@ -233,12 +249,13 @@ export function setGithubToken(githubToken) { * @return {string} the specified endpoint */ export function getQiskitRuntimeHandlerEndpoint() { - if (config.qiskitRuntimeHandlerEndpoint === undefined) { - setQiskitRuntimeHandlerEndpoint( - getPluginConfig('quantme').qiskitRuntimeHandlerEndpoint - || defaultConfig.qiskitRuntimeHandlerEndpoint); - } - return config.qiskitRuntimeHandlerEndpoint; + if (config.qiskitRuntimeHandlerEndpoint === undefined) { + setQiskitRuntimeHandlerEndpoint( + getPluginConfig("quantme").qiskitRuntimeHandlerEndpoint || + defaultConfig.qiskitRuntimeHandlerEndpoint + ); + } + return config.qiskitRuntimeHandlerEndpoint; } /** @@ -247,9 +264,9 @@ export function getQiskitRuntimeHandlerEndpoint() { * @param endpoint the endpoint */ export function setQiskitRuntimeHandlerEndpoint(endpoint) { - if (endpoint !== null && endpoint !== undefined) { - config.qiskitRuntimeHandlerEndpoint = endpoint; - } + if (endpoint !== null && endpoint !== undefined) { + config.qiskitRuntimeHandlerEndpoint = endpoint; + } } /** @@ -258,12 +275,13 @@ export function setQiskitRuntimeHandlerEndpoint(endpoint) { * @return {string} the specified endpoint */ export function getScriptSplitterEndpoint() { - if (config.scriptSplitterEndpoint === undefined) { - setScriptSplitterEndpoint( - getPluginConfig('quantme').scriptSplitterEndpoint - || defaultConfig.scriptSplitterEndpoint); - } - return config.scriptSplitterEndpoint; + if (config.scriptSplitterEndpoint === undefined) { + setScriptSplitterEndpoint( + getPluginConfig("quantme").scriptSplitterEndpoint || + defaultConfig.scriptSplitterEndpoint + ); + } + return config.scriptSplitterEndpoint; } /** @@ -272,9 +290,9 @@ export function getScriptSplitterEndpoint() { * @param endpoint the endpoint */ export function setScriptSplitterEndpoint(endpoint) { - if (endpoint !== null && endpoint !== undefined) { - config.scriptSplitterEndpoint = endpoint; - } + if (endpoint !== null && endpoint !== undefined) { + config.scriptSplitterEndpoint = endpoint; + } } /** @@ -283,12 +301,13 @@ export function setScriptSplitterEndpoint(endpoint) { * @return {int} the specified threshold */ export function getScriptSplitterThreshold() { - if (config.scriptSplitterThreshold === undefined) { - setScriptSplitterThreshold( - getPluginConfig('quantme').scriptSplitterThreshold - || defaultConfig.scriptSplitterThreshold); - } - return config.scriptSplitterThreshold; + if (config.scriptSplitterThreshold === undefined) { + setScriptSplitterThreshold( + getPluginConfig("quantme").scriptSplitterThreshold || + defaultConfig.scriptSplitterThreshold + ); + } + return config.scriptSplitterThreshold; } /** @@ -297,9 +316,9 @@ export function getScriptSplitterThreshold() { * @param threshold the threshold */ export function setScriptSplitterThreshold(threshold) { - if (threshold !== null && threshold !== undefined) { - config.scriptSplitterThreshold = threshold; - } + if (threshold !== null && threshold !== undefined) { + config.scriptSplitterThreshold = threshold; + } } /** @@ -308,12 +327,13 @@ export function setScriptSplitterThreshold(threshold) { * @return {boolean} the current value of the hybrid runtime provenance flag */ export function getHybridRuntimeProvenance() { - if (config.hybridRuntimeProvenance === undefined) { - setHybridRuntimeProvenance( - getPluginConfig('quantme').hybridRuntimeProvenance - || defaultConfig.hybridRuntimeProvenance); - } - return config.hybridRuntimeProvenance; + if (config.hybridRuntimeProvenance === undefined) { + setHybridRuntimeProvenance( + getPluginConfig("quantme").hybridRuntimeProvenance || + defaultConfig.hybridRuntimeProvenance + ); + } + return config.hybridRuntimeProvenance; } /** @@ -322,9 +342,12 @@ export function getHybridRuntimeProvenance() { * @param hybridRuntimeProvenance the new value of the hybrid runtime provenance flag */ export function setHybridRuntimeProvenance(hybridRuntimeProvenance) { - if (hybridRuntimeProvenance !== null && hybridRuntimeProvenance !== undefined) { - config.hybridRuntimeProvenance = hybridRuntimeProvenance; - } + if ( + hybridRuntimeProvenance !== null && + hybridRuntimeProvenance !== undefined + ) { + config.hybridRuntimeProvenance = hybridRuntimeProvenance; + } } /** @@ -333,12 +356,13 @@ export function setHybridRuntimeProvenance(hybridRuntimeProvenance) { * @return {string} the specified endpoint */ export function getAWSRuntimeHandlerEndpoint() { - if (config.awsRuntimeHandlerEndpoint === undefined) { - setAWSRuntimeHandlerEndpoint( - getPluginConfig('quantme').awsRuntimeHandlerEndpoint - || defaultConfig.awsRuntimeHandlerEndpoint); - } - return config.awsRuntimeHandlerEndpoint; + if (config.awsRuntimeHandlerEndpoint === undefined) { + setAWSRuntimeHandlerEndpoint( + getPluginConfig("quantme").awsRuntimeHandlerEndpoint || + defaultConfig.awsRuntimeHandlerEndpoint + ); + } + return config.awsRuntimeHandlerEndpoint; } /** @@ -347,9 +371,9 @@ export function getAWSRuntimeHandlerEndpoint() { * @param endpoint the endpoint */ export function setAWSRuntimeHandlerEndpoint(endpoint) { - if (endpoint !== null && endpoint !== undefined) { - config.awsRuntimeHandlerEndpoint = endpoint; - } + if (endpoint !== null && endpoint !== undefined) { + config.awsRuntimeHandlerEndpoint = endpoint; + } } /** @@ -357,5 +381,5 @@ export function setAWSRuntimeHandlerEndpoint(endpoint) { * by setting this.comfig to an empty js object. */ export function resetConfig() { - config = {}; -} \ No newline at end of file + config = {}; +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js index 05ead47b..49334758 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/config.js @@ -11,19 +11,20 @@ // takes either the environment variables or the default values definded in webpack.config const defaultConfig = { - quantmeDataConfigurationsEndpoint: process.env.DATA_CONFIG, - opentoscaEndpoint: process.env.OPENTOSCA_ENDPOINT, - wineryEndpoint: process.env.WINERY_ENDPOINT, - nisqAnalyzerEndpoint: process.env.NISQ_ANALYZER_ENDPOINT, - transformationFrameworkEndpoint: process.env.TRANSFORMATION_FRAMEWORK_ENDPOINT, - qiskitRuntimeHandlerEndpoint: process.env.QISKIT_RUNTIME_HANDLER_ENDPOINT, - awsRuntimeHandlerEndpoint: process.env.AWS_RUNTIME_HANDLER_ENDPOINT, - scriptSplitterEndpoint: process.env.SCRIPT_SPLITTER_ENDPOINT, - scriptSplitterThreshold: process.env.SCRIPT_SPLITTER_THRESHOLD, - githubRepositoryName: process.env.QRM_REPONAME, - githubUsername: process.env.QRM_USERNAME, - githubRepositoryPath: process.env.QRM_REPOPATH, - githubToken: process.env.GITHUB_TOKEN, - hybridRuntimeProvenance: process.env.PROVENANCE_COLLECTION + quantmeDataConfigurationsEndpoint: process.env.DATA_CONFIG, + opentoscaEndpoint: process.env.OPENTOSCA_ENDPOINT, + wineryEndpoint: process.env.WINERY_ENDPOINT, + nisqAnalyzerEndpoint: process.env.NISQ_ANALYZER_ENDPOINT, + transformationFrameworkEndpoint: + process.env.TRANSFORMATION_FRAMEWORK_ENDPOINT, + qiskitRuntimeHandlerEndpoint: process.env.QISKIT_RUNTIME_HANDLER_ENDPOINT, + awsRuntimeHandlerEndpoint: process.env.AWS_RUNTIME_HANDLER_ENDPOINT, + scriptSplitterEndpoint: process.env.SCRIPT_SPLITTER_ENDPOINT, + scriptSplitterThreshold: process.env.SCRIPT_SPLITTER_THRESHOLD, + githubRepositoryName: process.env.QRM_REPONAME, + githubUsername: process.env.QRM_USERNAME, + githubRepositoryPath: process.env.QRM_REPOPATH, + githubToken: process.env.GITHUB_TOKEN, + hybridRuntimeProvenance: process.env.PROVENANCE_COLLECTION, }; -export default defaultConfig; \ No newline at end of file +export default defaultConfig; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/index.js b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/index.js index 3acdba7f..fc12b5a2 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/framework-config/index.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/framework-config/index.js @@ -8,7 +8,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -import * as configManager from './config-manager'; +import * as configManager from "./config-manager"; const config = configManager; export default config; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnEditorActions.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnEditorActions.js index e2e70672..5d1ac098 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnEditorActions.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnEditorActions.js @@ -1,14 +1,12 @@ -import inherits from 'inherits-browser'; +import inherits from "inherits-browser"; -import EditorActions from 'diagram-js/lib/features/editor-actions/EditorActions'; +import EditorActions from "diagram-js/lib/features/editor-actions/EditorActions"; -import { filter } from 'min-dash'; +import { filter } from "min-dash"; -import { is } from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; -import { - getBBox -} from 'diagram-js/lib/util/Elements'; +import { getBBox } from "diagram-js/lib/util/Elements"; /** * Registers and executes BPMN specific editor actions. @@ -21,48 +19,44 @@ export default function BpmnEditorActions(injector) { inherits(BpmnEditorActions, EditorActions); -BpmnEditorActions.$inject = [ - 'injector' -]; +BpmnEditorActions.$inject = ["injector"]; /** * Register default actions. * * @param {Injector} injector */ -BpmnEditorActions.prototype._registerDefaultActions = function(injector) { - +BpmnEditorActions.prototype._registerDefaultActions = function (injector) { // (0) invoke super method EditorActions.prototype._registerDefaultActions.call(this, injector); // (1) retrieve optional components to integrate with - var canvas = injector.get('canvas', false); - var elementRegistry = injector.get('elementRegistry', false); - var selection = injector.get('selection', false); - var spaceTool = injector.get('spaceTool', false); - var lassoTool = injector.get('lassoTool', false); - var handTool = injector.get('handTool', false); - var globalConnect = injector.get('globalConnect', false); - var distributeElements = injector.get('distributeElements', false); - var alignElements = injector.get('alignElements', false); - var directEditing = injector.get('directEditing', false); - var searchPad = injector.get('searchPad', false); - var modeling = injector.get('modeling', false); - var contextPad = injector.get('contextPad', false); - var eventBus = injector.get('eventBus', false); + var canvas = injector.get("canvas", false); + var elementRegistry = injector.get("elementRegistry", false); + var selection = injector.get("selection", false); + var spaceTool = injector.get("spaceTool", false); + var lassoTool = injector.get("lassoTool", false); + var handTool = injector.get("handTool", false); + var globalConnect = injector.get("globalConnect", false); + var distributeElements = injector.get("distributeElements", false); + var alignElements = injector.get("alignElements", false); + var directEditing = injector.get("directEditing", false); + var searchPad = injector.get("searchPad", false); + var modeling = injector.get("modeling", false); + var contextPad = injector.get("contextPad", false); + var eventBus = injector.get("eventBus", false); // (2) check components and register actions if (canvas && elementRegistry && selection) { - this._registerAction('selectElements', function() { - + this._registerAction("selectElements", function () { // select all elements except for the invisible // root element var rootElement = canvas.getRootElement(); - var elements = elementRegistry.filter(function(element) { + var elements = elementRegistry.filter(function (element) { return element !== rootElement; }); @@ -73,33 +67,33 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (spaceTool) { - this._registerAction('spaceTool', function() { + this._registerAction("spaceTool", function () { spaceTool.toggle(); }); } if (lassoTool) { - this._registerAction('lassoTool', function() { + this._registerAction("lassoTool", function () { lassoTool.toggle(); }); } if (handTool) { - this._registerAction('handTool', function() { + this._registerAction("handTool", function () { handTool.toggle(); }); } if (globalConnect) { - this._registerAction('globalConnectTool', function() { + this._registerAction("globalConnectTool", function () { globalConnect.toggle(); }); } if (selection && distributeElements) { - this._registerAction('distributeElements', function(opts) { + this._registerAction("distributeElements", function (opts) { var currentSelection = selection.get(), - type = opts.type; + type = opts.type; if (currentSelection.length) { distributeElements.trigger(currentSelection, type); @@ -108,14 +102,14 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (selection && alignElements) { - this._registerAction('alignElements', function(opts) { + this._registerAction("alignElements", function (opts) { var currentSelection = selection.get(), - aligneableElements = [], - type = opts.type; + aligneableElements = [], + type = opts.type; if (currentSelection.length) { - aligneableElements = filter(currentSelection, function(element) { - return !is(element, 'bpmn:Lane'); + aligneableElements = filter(currentSelection, function (element) { + return !is(element, "bpmn:Lane"); }); alignElements.trigger(aligneableElements, type); @@ -124,7 +118,7 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (selection && modeling) { - this._registerAction('setColor', function(opts) { + this._registerAction("setColor", function (opts) { var currentSelection = selection.get(); if (currentSelection.length) { @@ -134,7 +128,7 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (selection && directEditing) { - this._registerAction('directEditing', function() { + this._registerAction("directEditing", function () { var currentSelection = selection.get(); if (currentSelection.length) { @@ -144,42 +138,44 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (searchPad) { - this._registerAction('find', function() { + this._registerAction("find", function () { searchPad.toggle(); }); } if (canvas && modeling) { - this._registerAction('deleteElement', function(event) { + this._registerAction("deleteElement", function (event) { let context = {}; let shapes = selection.get(); context.length = shapes.length; context.shapes = shapes; - eventBus.fire('removeSelection', context); + eventBus.fire("removeSelection", context); }); - this._registerAction('undoOperation', function(event) { + this._registerAction("undoOperation", function (event) { let context = {}; - eventBus.fire('undo', context); + eventBus.fire("undo", context); }); - this._registerAction('redoOperation', function(event) { + this._registerAction("redoOperation", function (event) { let context = {}; - eventBus.fire('redo', context); + eventBus.fire("redo", context); }); - this._registerAction('moveToOrigin', function() { + this._registerAction("moveToOrigin", function () { var rootElement = canvas.getRootElement(), - boundingBox, - elements; + boundingBox, + elements; - if (is(rootElement, 'bpmn:Collaboration')) { - elements = elementRegistry.filter(function(element) { - return is(element.parent, 'bpmn:Collaboration'); + if (is(rootElement, "bpmn:Collaboration")) { + elements = elementRegistry.filter(function (element) { + return is(element.parent, "bpmn:Collaboration"); }); } else { - elements = elementRegistry.filter(function(element) { - return element !== rootElement && !is(element.parent, 'bpmn:SubProcess'); + elements = elementRegistry.filter(function (element) { + return ( + element !== rootElement && !is(element.parent, "bpmn:SubProcess") + ); }); } @@ -194,8 +190,8 @@ BpmnEditorActions.prototype._registerDefaultActions = function(injector) { } if (selection && contextPad) { - this._registerAction('replaceElement', function(event) { - contextPad.triggerEntry('replace', 'click', event); + this._registerAction("replaceElement", function (event) { + contextPad.triggerEntry("replace", "click", event); }); } }; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboard.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboard.js index 261dab41..ab39a963 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboard.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboard.js @@ -1,23 +1,21 @@ -import { - isFunction -} from 'min-dash'; +import { isFunction } from "min-dash"; -import inherits from 'inherits-browser'; +import inherits from "inherits-browser"; import { closest as domClosest, event as domEvent, - matches as domMatches -} from 'min-dom'; + matches as domMatches, +} from "min-dom"; import { hasModifier, isCmd, isKey, - isShift -} from 'diagram-js/lib/features/keyboard/KeyboardUtil'; + isShift, +} from "diagram-js/lib/features/keyboard/KeyboardUtil"; -import Keyboard from 'diagram-js/lib/features/keyboard/Keyboard'; +import Keyboard from "diagram-js/lib/features/keyboard/Keyboard"; /** * @typedef {import('../../core/EventBus').default} EventBus @@ -25,10 +23,10 @@ import Keyboard from 'diagram-js/lib/features/keyboard/Keyboard'; * @typedef {({ keyEvent: KeyboardEvent }) => any} Listener */ -var KEYDOWN_EVENT = 'keyboard.keydown', - KEYUP_EVENT = 'keyboard.keyup'; +var KEYDOWN_EVENT = "keyboard.keydown", + KEYUP_EVENT = "keyboard.keyup"; -var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys'; +var HANDLE_MODIFIER_ATTRIBUTE = "input-handle-modified-keys"; var DEFAULT_PRIORITY = 1500; @@ -62,10 +60,7 @@ export default function BpmnKeyboard(injector) { inherits(BpmnKeyboard, Keyboard); -BpmnKeyboard.$inject = [ - 'injector' -]; - +BpmnKeyboard.$inject = ["injector"]; Keyboard.prototype._keydownHandler = function (event) { this._keyHandler(event, KEYDOWN_EVENT); @@ -83,7 +78,7 @@ Keyboard.prototype._keyHandler = function (event, type) { } var context = { - keyEvent: event + keyEvent: event, }; eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context); @@ -111,13 +106,20 @@ Keyboard.prototype._isModifiedKeyIgnored = function (event) { }; Keyboard.prototype._getAllowedModifiers = function (element) { - var modifierContainer = domClosest(element, '[' + HANDLE_MODIFIER_ATTRIBUTE + ']', true); - - if (!modifierContainer || (this._node && !this._node.contains(modifierContainer))) { + var modifierContainer = domClosest( + element, + "[" + HANDLE_MODIFIER_ATTRIBUTE + "]", + true + ); + + if ( + !modifierContainer || + (this._node && !this._node.contains(modifierContainer)) + ) { return []; } - return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(','); + return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(","); }; /** @@ -126,17 +128,16 @@ Keyboard.prototype._getAllowedModifiers = function (element) { * @param {EventTarget} node */ Keyboard.prototype.bind = function (node) { - // make sure that the keyboard is only bound once to the DOM this.unbind(); this._node = node; // bind key events - domEvent.bind(node, 'keydown', this._keydownHandler); - domEvent.bind(node, 'keyup', this._keyupHandler); + domEvent.bind(node, "keydown", this._keydownHandler); + domEvent.bind(node, "keyup", this._keyupHandler); - this._fire('bind'); + this._fire("bind"); }; /** @@ -150,11 +151,11 @@ Keyboard.prototype.unbind = function () { var node = this._node; if (node) { - this._fire('unbind'); + this._fire("unbind"); // unbind key events - domEvent.unbind(node, 'keydown', this._keydownHandler); - domEvent.unbind(node, 'keyup', this._keyupHandler); + domEvent.unbind(node, "keydown", this._keydownHandler); + domEvent.unbind(node, "keyup", this._keyupHandler); } this._node = null; @@ -164,7 +165,7 @@ Keyboard.prototype.unbind = function () { * @param {string} event */ Keyboard.prototype._fire = function (event) { - this._eventBus.fire('keyboard.' + event, { node: this._node }); + this._eventBus.fire("keyboard." + event, { node: this._node }); }; /** @@ -201,10 +202,11 @@ Keyboard.prototype.isCmd = isCmd; Keyboard.prototype.isShift = isShift; Keyboard.prototype.isKey = isKey; - - // helpers /////// function isInput(target) { - return target && (domMatches(target, 'input, textarea') || target.contentEditable === 'true'); -} \ No newline at end of file + return ( + target && + (domMatches(target, "input, textarea") || target.contentEditable === "true") + ); +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js index c186b41f..4f7f1fa6 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/BpmnKeyboardBindings.js @@ -1,7 +1,7 @@ -import inherits from 'inherits-browser'; +import inherits from "inherits-browser"; -import KeyboardBindings from 'diagram-js/lib/features/keyboard/KeyboardBindings'; -import { getModeler } from '../../../editor/ModelerHandler'; +import KeyboardBindings from "diagram-js/lib/features/keyboard/KeyboardBindings"; +import { getModeler } from "../../../editor/ModelerHandler"; /** * @typedef {import('didi').Injector} Injector @@ -20,10 +20,7 @@ export default function BpmnKeyboardBindings(injector) { inherits(BpmnKeyboardBindings, KeyboardBindings); -BpmnKeyboardBindings.$inject = [ - 'injector' -]; - +BpmnKeyboardBindings.$inject = ["injector"]; /** * Register available keyboard bindings. @@ -31,9 +28,16 @@ BpmnKeyboardBindings.$inject = [ * @param {Keyboard} keyboard * @param {BpmnEditorActions} editorActions */ -BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) { +BpmnKeyboardBindings.prototype.registerBindings = function ( + keyboard, + editorActions +) { // inherit default bindings - KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions); + KeyboardBindings.prototype.registerBindings.call( + this, + keyboard, + editorActions + ); /** * Add keyboard binding if respective editor action @@ -43,7 +47,6 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio * @param {Function} fn that implements the key binding */ function addListener(action, fn) { - if (editorActions.isRegistered(action)) { keyboard.addListener(fn); } @@ -51,11 +54,11 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // select all elements // Shift + A - addListener('selectElements', function(context) { + addListener("selectElements", function (context) { var event = context.keyEvent; - if (keyboard.isKey([ 'a', 'A' ], event)) { - editorActions.trigger('selectElements'); + if (keyboard.isKey(["a", "A"], event)) { + editorActions.trigger("selectElements"); return true; } @@ -63,11 +66,11 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // search labels // F - addListener('find', function(context) { + addListener("find", function (context) { var event = context.keyEvent; - if (keyboard.isKey([ 'f', 'F' ], event) && keyboard.isCmd(event)) { - editorActions.trigger('find'); + if (keyboard.isKey(["f", "F"], event) && keyboard.isCmd(event)) { + editorActions.trigger("find"); return true; } @@ -75,15 +78,15 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // activate space tool // S - addListener('spaceTool', function(context) { + addListener("spaceTool", function (context) { var event = context.keyEvent; if (keyboard.hasModifier(event)) { return; } - if (keyboard.isKey([ 's', 'S' ], event)) { - editorActions.trigger('spaceTool'); + if (keyboard.isKey(["s", "S"], event)) { + editorActions.trigger("spaceTool"); return true; } @@ -91,15 +94,15 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // activate lasso tool // L - addListener('lassoTool', function(context) { + addListener("lassoTool", function (context) { var event = context.keyEvent; if (keyboard.hasModifier(event)) { return; } - if (keyboard.isKey([ 'l', 'L' ], event)) { - editorActions.trigger('lassoTool'); + if (keyboard.isKey(["l", "L"], event)) { + editorActions.trigger("lassoTool"); return true; } @@ -107,15 +110,15 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // activate hand tool // H - addListener('handTool', function(context) { + addListener("handTool", function (context) { var event = context.keyEvent; if (keyboard.hasModifier(event)) { return; } - if (keyboard.isKey([ 'h', 'H' ], event)) { - editorActions.trigger('handTool'); + if (keyboard.isKey(["h", "H"], event)) { + editorActions.trigger("handTool"); return true; } @@ -123,23 +126,23 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // activate replace element // R - addListener('replaceElement', function(context) { + addListener("replaceElement", function (context) { var event = context.keyEvent; - if (keyboard.isKey([ 'r', 'R' ], event)) { - editorActions.trigger('replaceElement', event); + if (keyboard.isKey(["r", "R"], event)) { + editorActions.trigger("replaceElement", event); return true; } }); - + // delete selected element // D - addListener('removeSelection', function(context) { + addListener("removeSelection", function (context) { var event = context.keyEvent; - if (keyboard.isKey(['D', 'd'], event)) { - editorActions.trigger('removeSelection'); + if (keyboard.isKey(["D", "d"], event)) { + editorActions.trigger("removeSelection"); return true; } @@ -147,11 +150,11 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // undo operation // U - addListener('undoOperation', function(context) { + addListener("undoOperation", function (context) { var event = context.keyEvent; - if (keyboard.isKey(['z', 'Z'], event) && keyboard.isCmd(event)) { - editorActions.trigger('undo'); + if (keyboard.isKey(["z", "Z"], event) && keyboard.isCmd(event)) { + editorActions.trigger("undo"); return true; } @@ -159,70 +162,68 @@ BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActio // redo operation // R - addListener('redoOperation', function(context) { + addListener("redoOperation", function (context) { var event = context.keyEvent; - if (keyboard.isKey(['y', 'Y'], event) && keyboard.isCmd(event)) { - editorActions.trigger('redo'); + if (keyboard.isKey(["y", "Y"], event) && keyboard.isCmd(event)) { + editorActions.trigger("redo"); return true; } }); - addListener('copy', function(context) { - + addListener("copy", function (context) { // retrieve from local storage - const serializedCopy = localStorage.getItem('bpmnClipboard'); + const serializedCopy = localStorage.getItem("bpmnClipboard"); if (!serializedCopy) { return; } // parse tree, reinstantiating contained objects - const parsedCopy = JSON.parse(serializedCopy, createReviver(getModeler().get('moddle'))); + const parsedCopy = JSON.parse( + serializedCopy, + createReviver(getModeler().get("moddle")) + ); // put into clipboard - getModeler().get('clipboard').set(parsedCopy); + getModeler().get("clipboard").set(parsedCopy); }); /** - * A factory function that returns a reviver to be - * used with JSON#parse to reinstantiate moddle instances. - * - * @param {Moddle} moddle - * - * @return {Function} - */ -function createReviver(moddle) { - - var elCache = {}; - - return function(key, object) { + * A factory function that returns a reviver to be + * used with JSON#parse to reinstantiate moddle instances. + * + * @param {Moddle} moddle + * + * @return {Function} + */ + function createReviver(moddle) { + var elCache = {}; - if (typeof object === 'object' && typeof object.$type === 'string') { + return function (key, object) { + if (typeof object === "object" && typeof object.$type === "string") { + var objectId = object.id; - var objectId = object.id; + if (objectId && elCache[objectId]) { + return elCache[objectId]; + } - if (objectId && elCache[objectId]) { - return elCache[objectId]; - } + var type = object.$type; + var attrs = Object.assign({}, object); - var type = object.$type; - var attrs = Object.assign({}, object); + delete attrs.$type; - delete attrs.$type; + var newEl = moddle.create(type, attrs); - var newEl = moddle.create(type, attrs); + if (objectId) { + elCache[objectId] = newEl; + } - if (objectId) { - elCache[objectId] = newEl; + return newEl; } - return newEl; - } - - return object; - }; -} - -}; \ No newline at end of file + return object; + }; + } +}; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEFactory.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEFactory.js index 3bdc069e..819149f6 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEFactory.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEFactory.js @@ -9,69 +9,68 @@ * SPDX-License-Identifier: Apache-2.0 */ -import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory'; -import { isQuantMETask } from '../utilities/Utilities'; +import BpmnFactory from "bpmn-js/lib/features/modeling/BpmnFactory"; +import { isQuantMETask } from "../utilities/Utilities"; import { - CIRCUIT_CUTTING_SUBPROCESS, - PARAMETER_OPTIMIZATION_TASK, - READOUT_ERROR_MITIGATION_TASK, - RESULT_EVALUATION_TASK, - VARIATIONAL_QUANTUM_ALGORITHM_TASK, - WARM_STARTING_TASK -} from '../Constants'; + CIRCUIT_CUTTING_SUBPROCESS, + PARAMETER_OPTIMIZATION_TASK, + READOUT_ERROR_MITIGATION_TASK, + RESULT_EVALUATION_TASK, + VARIATIONAL_QUANTUM_ALGORITHM_TASK, + WARM_STARTING_TASK, +} from "../Constants"; /** * This class implements functionality when creating shapes representing QuantME tasks */ export default class QuantMEFactory extends BpmnFactory { - constructor(moddle) { - super(moddle); - } - - _ensureId(element) { + constructor(moddle) { + super(moddle); + } - // handle all non QuantME elements as usual - if (!isQuantMETask(element)) { - super._ensureId(element); - return; - } + _ensureId(element) { + // handle all non QuantME elements as usual + if (!isQuantMETask(element)) { + super._ensureId(element); + return; + } - // add an Id to QuantME elements if not already defined - if (!element.id) { - var prefix = (element.$type || '').replace(/^[^:]*:/g, '') + '_'; - element.id = this._model.ids.nextPrefixed(prefix, element); - } + // add an Id to QuantME elements if not already defined + if (!element.id) { + var prefix = (element.$type || "").replace(/^[^:]*:/g, "") + "_"; + element.id = this._model.ids.nextPrefixed(prefix, element); + } - // setting default for selectlist - if (element.$type === READOUT_ERROR_MITIGATION_TASK) { - element.mitigationMethod = 'matrixInversion'; - element.calibrationMethod = 'fullMatrix'; - } + // setting default for selectlist + if (element.$type === READOUT_ERROR_MITIGATION_TASK) { + element.mitigationMethod = "matrixInversion"; + element.calibrationMethod = "fullMatrix"; + } - if (element.$type === CIRCUIT_CUTTING_SUBPROCESS) { - element.cuttingMethod = 'qiskit'; - } + if (element.$type === CIRCUIT_CUTTING_SUBPROCESS) { + element.cuttingMethod = "qiskit"; + } - if (element.$type === WARM_STARTING_TASK) { - element.warmStartingMethod = 'initialStateWarmStartEgger'; - } + if (element.$type === WARM_STARTING_TASK) { + element.warmStartingMethod = "initialStateWarmStartEgger"; + } - if (element.$type === PARAMETER_OPTIMIZATION_TASK) { - element.optimizer = 'cobyla'; - } + if (element.$type === PARAMETER_OPTIMIZATION_TASK) { + element.optimizer = "cobyla"; + } - if (element.$type === RESULT_EVALUATION_TASK) { - element.objectiveFunction = 'expectationValue'; - } + if (element.$type === RESULT_EVALUATION_TASK) { + element.objectiveFunction = "expectationValue"; + } - if (element.$type === VARIATIONAL_QUANTUM_ALGORITHM_TASK) { - element.objectiveFunction = 'expectationValue'; - element.optimizer = 'cobyla'; - element.mitigationMethod = ''; - element.cuttingMethod = ''; - element.warmStartingMethod = ''; - } + if (element.$type === VARIATIONAL_QUANTUM_ALGORITHM_TASK) { + element.objectiveFunction = "expectationValue"; + element.optimizer = "cobyla"; + element.mitigationMethod = ""; + element.cuttingMethod = ""; + element.warmStartingMethod = ""; } + } } -QuantMEFactory.$inject = ['moddle']; \ No newline at end of file +QuantMEFactory.$inject = ["moddle"]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEPathMap.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEPathMap.js index 925ebd17..f151b738 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEPathMap.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEPathMap.js @@ -10,389 +10,405 @@ */ export default function QuantMEPathMap() { + this.quantMEPathMap = { + TASK_TYPE_QUANTUM_COMPUTATION: { + d: + "M 41.60,42.32 " + + "C 41.60,39.45 43.93,37.12 46.80,37.12 " + + "49.67,37.12 52.00,39.45 52.00,42.32 " + + "52.00,45.19 49.67,47.52 46.80,47.52 " + + "43.93,47.52 41.60,45.19 41.60,42.32 " + + "41.60,42.32 41.60,42.32 41.60,42.32 Z " + + "M 20.88,26.36 " + + "C 17.85,31.40 27.00,42.47 41.31,51.07 " + + "55.62,59.67 69.69,62.55 72.72,57.50 " + + "75.75,52.46 66.60,41.40 52.29,32.79 " + + "37.97,24.19 23.91,21.31 20.88,26.36 " + + "20.88,26.36 20.88,26.36 20.88,26.36 Z " + + "M 46.00,11.68 " + + "C 40.12,11.68 35.36,25.22 35.36,41.92 " + + "35.36,58.62 40.12,72.16 46.00,72.16 " + + "51.88,72.16 56.64,58.62 56.64,41.92 " + + "56.64,25.22 51.88,11.68 46.00,11.68 Z " + + "M 72.72,27.90 " + + "C 69.69,22.85 55.62,25.73 41.31,34.33 " + + "27.00,42.93 17.85,54.00 20.88,59.04 " + + "23.91,64.09 37.97,61.21 52.29,52.60 " + + "66.60,44.00 75.75,32.94 72.72,27.90 Z", + }, + TASK_TYPE_CIRCUIT_LOADING: { + d: + "M 18.88,16.64 " + + "C 18.88,16.64 95.43,16.64 95.43,16.64M 18.88,39.52 " + + "C 18.88,39.52 95.43,39.52 95.43,39.52M 18.88,64.00 " + + "C 18.88,64.00 95.43,64.00 95.43,64.00M 42.72,16.64 " + + "C 42.72,16.64 42.72,73.37 42.72,73.37M 33.12,63.92 " + + "C 33.12,58.66 37.42,54.40 42.72,54.40 " + + "48.02,54.40 52.32,58.66 52.32,63.92 " + + "52.32,69.18 48.02,73.44 42.72,73.44 " + + "37.42,73.44 33.12,69.18 33.12,63.92 Z " + + "M 63.84,39.52 " + + "C 63.84,34.22 68.14,29.92 73.44,29.92 " + + "78.74,29.92 83.04,34.22 83.04,39.52 " + + "83.04,44.82 78.74,49.12 73.44,49.12 " + + "68.14,49.12 63.84,44.82 63.84,39.52 Z " + + "M 73.28,30.24 " + + "C 73.28,30.24 73.28,62.78 73.28,62.78", + }, + TASK_TYPE_CIRCUIT_LOADING_FILL: { + d: + "M 36.48,17.36 " + + "C 36.48,13.87 39.35,11.04 42.88,11.04 " + + "46.41,11.04 49.28,13.87 49.28,17.36 " + + "49.28,20.85 46.41,23.68 42.88,23.68 " + + "39.35,23.68 36.48,20.85 36.48,17.36 " + + "36.48,17.36 36.48,17.36 36.48,17.36 Z " + + "M 67.04,63.36 " + + "C 67.04,59.83 69.91,56.96 73.44,56.96 " + + "76.97,56.96 79.84,59.83 79.84,63.36 " + + "79.84,66.89 76.97,69.76 73.44,69.76 " + + "69.91,69.76 67.04,66.89 67.04,63.36 Z", + }, + TASK_TYPE_DATA_PREPARATION: { + d: + "M 76.50,21.50 " + + "C 76.50,21.50 143.25,21.50 143.25,21.50M 76.50,36.50 " + + "C 76.50,36.50 143.25,36.50 143.25,36.50M 76.50,53.50 " + + "C 76.50,53.50 143.25,53.50 143.25,53.50M 109.18,21.82 " + + "C 109.18,21.82 109.09,60.45 109.09,60.45M 102.50,54.00 " + + "C 102.50,50.41 105.41,47.50 109.00,47.50 " + + "112.59,47.50 115.50,50.41 115.50,54.00 " + + "115.50,57.59 112.59,60.50 109.00,60.50 " + + "105.41,60.50 102.50,57.59 102.50,54.00 Z " + + "M 123.50,37.00 " + + "C 123.50,33.41 126.41,30.50 130.00,30.50 " + + "133.59,30.50 136.50,33.41 136.50,37.00 " + + "136.50,40.59 133.59,43.50 130.00,43.50 " + + "126.41,43.50 123.50,40.59 123.50,37.00 Z " + + "M 130.00,31.00 " + + "C 130.00,31.00 129.91,53.00 129.91,53.00", + }, + TASK_TYPE_DATA_PREPARATION_BACKGROUND: { + d: + "M 52.50,23.83" + + "C 52.50,27.88 45.11,31.17 36.00,31.17 " + + "26.89,31.17 19.50,27.88 19.50,23.83", + }, + TASK_TYPE_DATA_PREPARATION_FILL_BLACK: { + d: + "M 19.50,23.83 " + + "C 19.50,19.78 26.89,16.50 36.00,16.50 " + + "45.11,16.50 52.50,19.78 52.50,23.83 " + + "52.50,23.83 52.50,53.17 52.50,53.17 " + + "52.50,57.22 45.11,60.50 36.00,60.50 " + + "26.89,60.50 19.50,57.22 19.50,53.17 " + + "19.50,53.17 19.50,23.83 19.50,23.83 Z " + + "M 66.73,37.73 " + + "C 66.73,37.73 58.45,37.64 58.45,37.64 " + + "58.45,37.64 58.55,35.18 58.55,35.18 " + + "58.55,35.18 66.82,35.18 66.82,35.18 " + + "66.82,35.18 66.73,37.73 66.73,37.73 Z " + + "M 66.60,32.00 " + + "C 66.60,32.00 72.60,36.50 72.60,36.50 " + + "72.60,36.50 66.60,41.01 66.60,41.01 " + + "66.60,41.01 66.60,32.00 66.60,32.00 Z " + + "M 95.50,16.50 " + + "C 95.50,16.50 95.50,26.50 95.50,26.50 " + + "95.50,26.50 84.50,26.50 84.50,26.50 " + + "84.50,26.50 84.50,16.50 84.50,16.50 " + + "84.50,16.50 95.50,16.50 95.50,16.50 Z " + + "M 94.50,48.50 " + + "C 94.50,48.50 94.50,58.50 94.50,58.50 " + + "94.50,58.50 84.50,58.50 84.50,58.50 " + + "84.50,58.50 84.50,48.50 84.50,48.50 " + + "84.50,48.50 94.50,48.50 94.50,48.50 Z", + }, + TASK_TYPE_DATA_PREPARATION_FILL_BACKGROUND: { + d: + "M 95.50,16.50 " + + "C 95.50,16.50 95.50,26.50 95.50,26.50 " + + "95.50,26.50 84.50,26.50 84.50,26.50 " + + "84.50,26.50 84.50,16.50 84.50,16.50 " + + "84.50,16.50 95.50,16.50 95.50,16.50 Z " + + "M 94.50,48.50 " + + "C 94.50,48.50 94.50,58.50 94.50,58.50 " + + "94.50,58.50 84.50,58.50 84.50,58.50 " + + "84.50,58.50 84.50,48.50 84.50,48.50 " + + "84.50,48.50 94.50,48.50 94.50,48.50 Z", + }, + TASK_TYPE_DATA_PREPARATION_DASHED: { + d: + "M 80.50,14.50 " + + "C 80.50,12.84 81.84,11.50 83.50,11.50 " + + "83.50,11.50 95.50,11.50 95.50,11.50 " + + "97.16,11.50 98.50,12.84 98.50,14.50 " + + "98.50,14.50 98.50,59.50 98.50,59.50 " + + "98.50,61.16 97.16,62.50 95.50,62.50 " + + "95.50,62.50 83.50,62.50 83.50,62.50 " + + "81.84,62.50 80.50,61.16 80.50,59.50 " + + "80.50,59.50 80.50,14.50 80.50,14.50 Z", + }, + TASK_TYPE_ORACLE_EXPANSION: { + d: + "M 86.24,15.04 " + + "C 86.24,15.04 149.20,15.04 149.20,15.04M 86.24,33.92 " + + "C 86.24,33.92 149.20,33.92 149.20,33.92M 86.24,54.08 " + + "C 86.24,54.08 149.20,54.08 149.20,54.08M 105.76,15.04 " + + "C 105.76,15.04 105.76,61.21 105.76,61.21M 97.92,53.92 " + + "C 97.92,49.59 101.43,46.08 105.76,46.08 " + + "110.09,46.08 113.60,49.59 113.60,53.92 " + + "113.60,58.25 110.09,61.76 105.76,61.76 " + + "101.43,61.76 97.92,58.25 97.92,53.92 Z " + + "M 123.20,33.92 " + + "C 123.20,29.59 126.71,26.08 131.04,26.08 " + + "135.37,26.08 138.88,29.59 138.88,33.92 " + + "138.88,38.25 135.37,41.76 131.04,41.76 " + + "126.71,41.76 123.20,38.25 123.20,33.92 Z " + + "M 130.88,26.40 " + + "C 130.88,26.40 130.88,53.16 130.88,53.16", + }, + TASK_TYPE_ORACLE_EXPANSION_FILL_BLACK: { + d: + "M 100.80,15.68 " + + "C 100.80,12.76 103.16,10.40 106.08,10.40 " + + "109.00,10.40 111.36,12.76 111.36,15.68 " + + "111.36,18.60 109.00,20.96 106.08,20.96 " + + "103.16,20.96 100.80,18.60 100.80,15.68 Z " + + "M 125.76,53.52 " + + "C 125.76,50.65 128.12,48.32 131.04,48.32 " + + "133.96,48.32 136.32,50.65 136.32,53.52 " + + "136.32,56.39 133.96,58.72 131.04,58.72 " + + "128.12,58.72 125.76,56.39 125.76,53.52 Z", + }, + TASK_TYPE_ORACLE_EXPANSION_BOX: { + d: + "M 16.80,60.96 " + + "C 16.80,60.96 54.40,60.96 54.40,60.96 " + + "54.40,60.96 54.40,25.44 54.40,25.44 " + + "54.40,25.44 16.80,25.44 16.80,25.44 " + + "16.80,25.44 16.80,60.96 16.80,60.96 Z " + + "M 54.40,25.44 " + + "C 54.40,25.44 66.24,13.60 66.24,13.60 " + + "66.24,13.60 66.24,49.12 66.24,49.12 " + + "66.24,49.12 54.40,60.96 54.40,60.96 " + + "54.40,60.96 54.40,25.44 54.40,25.44 Z " + + "M 16.80,25.44 " + + "C 16.80,25.44 28.64,13.60 28.64,13.60 " + + "28.64,13.60 66.24,13.60 66.24,13.60 " + + "66.24,13.60 54.40,25.44 54.40,25.44 " + + "54.40,25.44 16.80,25.44 16.80,25.44 Z", + }, + TASK_TYPE_ORACLE_EXPANSION_ARROW: { + d: + "M 70.80,33.28 " + + "C 70.80,33.28 78.59,33.28 78.59,33.28 " + + "78.59,33.28 78.59,36.00 78.59,36.00 " + + "78.59,36.00 70.80,36.00 70.80,36.00 " + + "70.80,36.00 70.80,33.28 70.80,33.28 Z " + + "M 77.09,29.45 " + + "C 77.09,29.45 81.82,34.36 81.82,34.36 " + + "81.82,34.36 77.09,38.91 77.09,38.91 " + + "77.09,38.91 77.09,29.45 77.09,29.45 Z", + }, + TASK_TYPE_CIRCUIT_EXECUTION: { + d: + "M 41.60,42.32 " + + "C 41.60,39.45 43.93,37.12 46.80,37.12 " + + "49.67,37.12 52.00,39.45 52.00,42.32 " + + "52.00,45.19 49.67,47.52 46.80,47.52 " + + "43.93,47.52 41.60,45.19 41.60,42.32 " + + "41.60,42.32 41.60,42.32 41.60,42.32 Z " + + "M 20.88,26.36 " + + "C 17.85,31.40 27.00,42.47 41.31,51.07 " + + "55.62,59.67 69.69,62.55 72.72,57.50 " + + "75.75,52.46 66.60,41.40 52.29,32.79 " + + "37.97,24.19 23.91,21.31 20.88,26.36 " + + "20.88,26.36 20.88,26.36 20.88,26.36 Z " + + "M 46.00,11.68 " + + "C 40.12,11.68 35.36,25.22 35.36,41.92 " + + "35.36,58.62 40.12,72.16 46.00,72.16 " + + "51.88,72.16 56.64,58.62 56.64,41.92 " + + "56.64,25.22 51.88,11.68 46.00,11.68 Z " + + "M 72.72,27.90 " + + "C 69.69,22.85 55.62,25.73 41.31,34.33 " + + "27.00,42.93 17.85,54.00 20.88,59.04 " + + "23.91,64.09 37.97,61.21 52.29,52.60 " + + "66.60,44.00 75.75,32.94 72.72,27.90 Z", + }, + TASK_TYPE_CIRCUIT_EXECUTION_FILL: { + d: + "M 124.15,33.80 " + + "C 121.33,34.98 119.35,37.74 119.35,40.96 " + + "119.35,45.25 122.87,48.73 127.21,48.73 " + + "131.56,48.73 135.08,45.25 135.08,40.96 " + + "135.08,36.67 131.56,33.19 127.21,33.19 " + + "126.13,33.19 125.09,33.40 124.15,33.80 Z " + + "M 122.72,18.88 " + + "C 122.72,18.88 131.71,18.88 131.71,18.88 " + + "131.71,18.88 131.71,26.31 131.71,26.31 " + + "131.71,26.31 134.64,27.49 134.64,27.49 " + + "134.64,27.49 140.00,22.19 140.00,22.19 " + + "140.00,22.19 146.35,28.47 146.35,28.47 " + + "146.35,28.47 141.00,33.75 141.00,33.75 " + + "141.52,34.53 141.88,35.39 142.16,36.28 " + + "142.16,36.28 149.44,36.28 149.44,36.28 " + + "149.44,36.28 149.44,45.17 149.44,45.17 " + + "149.44,45.17 142.24,45.17 142.24,45.17 " + + "141.89,46.49 141.36,47.73 140.67,48.87 " + + "140.67,48.87 145.80,53.94 145.80,53.94 " + + "145.80,53.94 139.44,60.22 139.44,60.22 " + + "139.44,60.22 133.88,54.72 133.88,54.72 " + + "133.22,55.15 132.48,55.41 131.71,55.61 " + + "131.71,55.61 131.71,63.04 131.71,63.04 " + + "131.71,63.04 122.72,63.04 122.72,63.04 " + + "122.72,63.04 122.72,55.61 122.72,55.61 " + + "122.72,55.61 120.07,54.53 120.07,54.53 " + + "120.07,54.53 114.73,59.82 114.73,59.82 " + + "114.73,59.82 108.37,53.53 108.37,53.53 " + + "108.37,53.53 113.54,48.43 113.54,48.43 " + + "112.94,47.42 112.50,46.32 112.19,45.17 " + + "112.19,45.17 105.12,45.17 105.12,45.17 " + + "105.12,45.17 105.12,36.28 105.12,36.28 " + + "105.12,36.28 112.27,36.28 112.27,36.28 " + + "112.27,36.28 113.55,33.49 113.55,33.49 " + + "113.55,33.49 108.37,28.38 108.37,28.38 " + + "108.37,28.38 114.73,22.09 114.73,22.09 " + + "114.73,22.09 120.07,27.38 120.07,27.38 " + + "120.88,26.89 121.78,26.55 122.72,26.31 " + + "122.72,26.31 122.72,18.88 122.72,18.88 Z " + + "M 77.04,39.20 " + + "C 77.04,39.20 86.45,39.20 86.45,39.20 " + + "86.45,39.20 86.45,41.92 86.45,41.92 " + + "86.45,41.92 77.04,41.92 77.04,41.92 " + + "77.04,41.92 77.04,39.20 77.04,39.20 Z " + + "M 85.18,33.64 " + + "C 85.18,33.64 98.00,40.36 98.00,40.36 " + + "98.00,40.36 85.27,47.36 85.27,47.36 " + + "85.27,47.36 85.18,33.64 85.18,33.64 Z", + }, + TASK_TYPE_ERROR_MITIGATION: { + d: + "M 20.09,71.91 " + + "C 20.09,71.91 20.25,12.50 20.25,12.50 " + + "20.25,12.50 24.88,12.38 24.88,12.38 " + + "24.88,12.38 24.75,67.00 24.75,67.00 " + + "24.75,67.00 79.62,67.25 79.62,67.25 " + + "79.62,67.25 79.70,72.04 79.70,72.04 " + + "79.70,72.04 20.04,71.87 20.04,71.87M 53.25,12.50 " + + "C 53.25,12.50 44.12,12.50 44.12,12.50 " + + "44.12,12.50 43.73,61.91 43.73,61.91 " + + "43.73,61.91 52.88,61.75 52.88,61.75 " + + "52.88,61.75 53.00,12.50 53.00,12.50M 30.38,61.50 " + + "C 30.38,61.50 30.50,31.38 30.50,31.38 " + + "30.50,31.38 40.00,31.38 40.00,31.38 " + + "40.00,31.38 39.73,61.45 39.73,61.45 " + + "39.73,61.45 30.45,61.55 30.45,61.55M 40.36,26.82M 57.12,61.78 " + + "C 57.12,61.78 57.12,30.88 57.12,30.88 " + + "57.12,30.88 66.75,30.88 66.75,30.88 " + + "66.75,30.88 66.75,61.75 66.75,61.75 " + + "66.75,61.75 57.12,61.81 57.12,61.81M 70.06,61.69 " + + "C 70.06,61.69 70.09,46.00 70.09,46.00 " + + "70.09,46.00 79.82,46.00 79.82,46.00 " + + "79.82,46.00 79.82,61.73 79.82,61.73 " + + "79.82,61.73 70.00,61.64 70.00,61.64", + }, + SUBPROCESS_QUANTUM_HARDWARE_SELECTION: { + d: + "M 75.94,17.84 " + + "C 75.94,16.56 76.93,15.52 78.17,15.52 " + + "79.40,15.52 80.40,16.56 80.40,17.84 " + + "80.40,19.12 79.40,20.16 78.17,20.16 " + + "76.93,20.16 75.94,19.12 75.94,17.84 Z " + + "M 66.89,10.91 " + + "C 65.58,13.11 69.55,17.93 75.77,21.68 " + + "81.99,25.43 88.10,26.69 89.42,24.49 " + + "90.74,22.29 86.76,17.47 80.54,13.72 " + + "74.32,9.97 68.21,8.71 66.89,10.91 " + + "66.89,10.91 66.89,10.91 66.89,10.91 Z " + + "M 77.85,4.48 " + + "C 75.29,4.48 73.22,10.39 73.22,17.68 " + + "73.22,24.97 75.29,30.88 77.85,30.88 " + + "80.40,30.88 82.48,24.97 82.48,17.68 " + + "82.48,10.39 80.40,4.48 77.85,4.48 Z " + + "M 89.42,11.58 " + + "C 88.10,9.38 81.99,10.64 75.77,14.39 " + + "69.55,18.14 65.58,22.96 66.89,25.16 " + + "68.21,27.36 74.32,26.10 80.54,22.35 " + + "86.76,18.60 90.74,13.78 89.42,11.58 " + + "89.42,11.58 89.42,11.58 89.42,11.58 Z " + + "M 75.94,49.12 " + + "C 75.94,47.88 76.93,46.88 78.17,46.88 " + + "79.40,46.88 80.40,47.88 80.40,49.12 " + + "80.40,50.36 79.40,51.36 78.17,51.36 " + + "76.93,51.36 75.94,50.36 75.94,49.12 Z " + + "M 66.86,42.16 " + + "C 65.54,44.36 69.52,49.18 75.74,52.93 " + + "81.96,56.68 88.07,57.94 89.39,55.74 " + + "90.71,53.54 86.73,48.72 80.51,44.97 " + + "74.29,41.22 68.18,39.96 66.86,42.16 Z " + + "M 77.77,35.84 " + + "C 75.17,35.84 73.06,41.71 73.06,48.96 " + + "73.06,56.21 75.17,62.08 77.77,62.08 " + + "80.37,62.08 82.48,56.21 82.48,48.96 " + + "82.48,41.71 80.37,35.84 77.77,35.84 Z " + + "M 89.39,42.83 " + + "C 88.07,40.63 81.96,41.89 75.74,45.64 " + + "69.52,49.39 65.54,54.21 66.86,56.41 " + + "68.18,58.61 74.29,57.35 80.51,53.60 " + + "86.73,49.85 90.71,45.03 89.39,42.83 Z " + + "M 4.15,23.20 " + + "C 4.15,23.20 36.33,23.20 36.33,23.20M 4.15,32.96 " + + "C 4.15,32.96 36.33,32.96 36.33,32.96M 4.15,43.20 " + + "C 4.15,43.20 36.33,43.20 36.33,43.20M 14.04,23.20 " + + "C 14.04,23.20 14.04,46.87 14.04,46.87M 10.05,43.20 " + + "C 10.05,40.99 11.87,39.20 14.12,39.20 " + + "16.36,39.20 18.19,40.99 18.19,43.20 " + + "18.19,45.41 16.36,47.20 14.12,47.20 " + + "11.87,47.20 10.05,45.41 10.05,43.20 " + + "10.05,43.20 10.05,43.20 10.05,43.20 Z " + + "M 22.97,32.88 " + + "C 22.97,30.63 24.76,28.80 26.96,28.80 " + + "29.16,28.80 30.95,30.63 30.95,32.88 " + + "30.95,35.13 29.16,36.96 26.96,36.96 " + + "24.76,36.96 22.97,35.13 22.97,32.88 Z " + + "M 26.96,28.96 " + + "C 26.96,28.96 26.96,42.68 26.96,42.68", + }, + SUBPROCESS_QUANTUM_HARDWARE_SELECTION_FILL: { + d: + "M 52.14,21.37 " + + "C 52.14,21.37 52.65,23.09 52.65,23.09 " + + "52.65,23.09 43.96,25.28 43.96,25.26 " + + "43.96,25.24 43.62,23.62 43.62,23.62 " + + "43.62,23.62 52.14,21.38 52.14,21.38 " + + "M 47.87,16.93 " + + "C 47.87,16.93 58.73,20.22 58.73,20.22 " + + "58.73,20.22 51.16,28.70 51.16,28.70 " + + "51.16,28.70 47.87,16.93 47.87,16.93 Z " + + "M 11.65,23.52 " + + "C 11.65,22.02 12.82,20.80 14.28,20.80 " + + "15.73,20.80 16.91,22.02 16.91,23.52 " + + "16.91,25.02 15.73,26.24 14.28,26.24 " + + "12.82,26.24 11.65,25.02 11.65,23.52 Z " + + "M 24.25,42.96 " + + "C 24.25,41.50 25.46,40.32 26.96,40.32 " + + "28.46,40.32 29.67,41.50 29.67,42.96 " + + "29.67,44.42 28.46,45.60 26.96,45.60 " + + "25.46,45.60 24.25,44.42 24.25,42.96 Z " + + "M 52.13,45.69 " + + "C 52.13,45.69 51.27,47.76 51.27,47.76 " + + "51.27,47.76 42.82,44.16 42.82,44.16 " + + "42.82,44.16 43.78,42.22 43.78,42.22 " + + "43.78,42.22 52.13,45.67 52.13,45.67 " + + "M 51.94,25.75 " + + "M 50.92,39.92 " + + "C 50.92,39.92 58.01,48.79 58.01,48.79 " + + "58.01,48.79 47.00,51.50 47.00,51.50 " + + "47.00,51.50 50.92,39.92 50.92,39.92 Z", + }, + }; - this.quantMEPathMap = { - 'TASK_TYPE_QUANTUM_COMPUTATION': { - d: 'M 41.60,42.32 ' + - 'C 41.60,39.45 43.93,37.12 46.80,37.12 ' + - '49.67,37.12 52.00,39.45 52.00,42.32 ' + - '52.00,45.19 49.67,47.52 46.80,47.52 ' + - '43.93,47.52 41.60,45.19 41.60,42.32 ' + - '41.60,42.32 41.60,42.32 41.60,42.32 Z ' + - 'M 20.88,26.36 ' + - 'C 17.85,31.40 27.00,42.47 41.31,51.07 ' + - '55.62,59.67 69.69,62.55 72.72,57.50 ' + - '75.75,52.46 66.60,41.40 52.29,32.79 ' + - '37.97,24.19 23.91,21.31 20.88,26.36 ' + - '20.88,26.36 20.88,26.36 20.88,26.36 Z ' + - 'M 46.00,11.68 ' + - 'C 40.12,11.68 35.36,25.22 35.36,41.92 ' + - '35.36,58.62 40.12,72.16 46.00,72.16 ' + - '51.88,72.16 56.64,58.62 56.64,41.92 ' + - '56.64,25.22 51.88,11.68 46.00,11.68 Z ' + - 'M 72.72,27.90 ' + - 'C 69.69,22.85 55.62,25.73 41.31,34.33 ' + - '27.00,42.93 17.85,54.00 20.88,59.04 ' + - '23.91,64.09 37.97,61.21 52.29,52.60 ' + - '66.60,44.00 75.75,32.94 72.72,27.90 Z' - }, - 'TASK_TYPE_CIRCUIT_LOADING': { - d: 'M 18.88,16.64 ' + - 'C 18.88,16.64 95.43,16.64 95.43,16.64M 18.88,39.52 ' + - 'C 18.88,39.52 95.43,39.52 95.43,39.52M 18.88,64.00 ' + - 'C 18.88,64.00 95.43,64.00 95.43,64.00M 42.72,16.64 ' + - 'C 42.72,16.64 42.72,73.37 42.72,73.37M 33.12,63.92 ' + - 'C 33.12,58.66 37.42,54.40 42.72,54.40 ' + - '48.02,54.40 52.32,58.66 52.32,63.92 ' + - '52.32,69.18 48.02,73.44 42.72,73.44 ' + - '37.42,73.44 33.12,69.18 33.12,63.92 Z ' + - 'M 63.84,39.52 ' + - 'C 63.84,34.22 68.14,29.92 73.44,29.92 ' + - '78.74,29.92 83.04,34.22 83.04,39.52 ' + - '83.04,44.82 78.74,49.12 73.44,49.12 ' + - '68.14,49.12 63.84,44.82 63.84,39.52 Z ' + - 'M 73.28,30.24 ' + - 'C 73.28,30.24 73.28,62.78 73.28,62.78' - }, - 'TASK_TYPE_CIRCUIT_LOADING_FILL': { - d: 'M 36.48,17.36 ' + - 'C 36.48,13.87 39.35,11.04 42.88,11.04 ' + - '46.41,11.04 49.28,13.87 49.28,17.36 ' + - '49.28,20.85 46.41,23.68 42.88,23.68 ' + - '39.35,23.68 36.48,20.85 36.48,17.36 ' + - '36.48,17.36 36.48,17.36 36.48,17.36 Z ' + - 'M 67.04,63.36 ' + - 'C 67.04,59.83 69.91,56.96 73.44,56.96 ' + - '76.97,56.96 79.84,59.83 79.84,63.36 ' + - '79.84,66.89 76.97,69.76 73.44,69.76 ' + - '69.91,69.76 67.04,66.89 67.04,63.36 Z' - }, - 'TASK_TYPE_DATA_PREPARATION': { - d: 'M 76.50,21.50 ' + - 'C 76.50,21.50 143.25,21.50 143.25,21.50M 76.50,36.50 ' + - 'C 76.50,36.50 143.25,36.50 143.25,36.50M 76.50,53.50 ' + - 'C 76.50,53.50 143.25,53.50 143.25,53.50M 109.18,21.82 ' + - 'C 109.18,21.82 109.09,60.45 109.09,60.45M 102.50,54.00 ' + - 'C 102.50,50.41 105.41,47.50 109.00,47.50 ' + - '112.59,47.50 115.50,50.41 115.50,54.00 ' + - '115.50,57.59 112.59,60.50 109.00,60.50 ' + - '105.41,60.50 102.50,57.59 102.50,54.00 Z ' + - 'M 123.50,37.00 ' + - 'C 123.50,33.41 126.41,30.50 130.00,30.50 ' + - '133.59,30.50 136.50,33.41 136.50,37.00 ' + - '136.50,40.59 133.59,43.50 130.00,43.50 ' + - '126.41,43.50 123.50,40.59 123.50,37.00 Z ' + - 'M 130.00,31.00 ' + - 'C 130.00,31.00 129.91,53.00 129.91,53.00' - }, - 'TASK_TYPE_DATA_PREPARATION_BACKGROUND': { - d: 'M 52.50,23.83' + - 'C 52.50,27.88 45.11,31.17 36.00,31.17 ' + - '26.89,31.17 19.50,27.88 19.50,23.83' - }, - 'TASK_TYPE_DATA_PREPARATION_FILL_BLACK': { - d: 'M 19.50,23.83 ' + - 'C 19.50,19.78 26.89,16.50 36.00,16.50 ' + - '45.11,16.50 52.50,19.78 52.50,23.83 ' + - '52.50,23.83 52.50,53.17 52.50,53.17 ' + - '52.50,57.22 45.11,60.50 36.00,60.50 ' + - '26.89,60.50 19.50,57.22 19.50,53.17 ' + - '19.50,53.17 19.50,23.83 19.50,23.83 Z ' + - 'M 66.73,37.73 ' + - 'C 66.73,37.73 58.45,37.64 58.45,37.64 ' + - '58.45,37.64 58.55,35.18 58.55,35.18 ' + - '58.55,35.18 66.82,35.18 66.82,35.18 ' + - '66.82,35.18 66.73,37.73 66.73,37.73 Z ' + - 'M 66.60,32.00 ' + - 'C 66.60,32.00 72.60,36.50 72.60,36.50 ' + - '72.60,36.50 66.60,41.01 66.60,41.01 ' + - '66.60,41.01 66.60,32.00 66.60,32.00 Z ' + - 'M 95.50,16.50 ' + - 'C 95.50,16.50 95.50,26.50 95.50,26.50 ' + - '95.50,26.50 84.50,26.50 84.50,26.50 ' + - '84.50,26.50 84.50,16.50 84.50,16.50 ' + - '84.50,16.50 95.50,16.50 95.50,16.50 Z ' + - 'M 94.50,48.50 ' + - 'C 94.50,48.50 94.50,58.50 94.50,58.50 ' + - '94.50,58.50 84.50,58.50 84.50,58.50 ' + - '84.50,58.50 84.50,48.50 84.50,48.50 ' + - '84.50,48.50 94.50,48.50 94.50,48.50 Z' - }, - 'TASK_TYPE_DATA_PREPARATION_FILL_BACKGROUND': { - d: 'M 95.50,16.50 ' + - 'C 95.50,16.50 95.50,26.50 95.50,26.50 ' + - '95.50,26.50 84.50,26.50 84.50,26.50 ' + - '84.50,26.50 84.50,16.50 84.50,16.50 ' + - '84.50,16.50 95.50,16.50 95.50,16.50 Z ' + - 'M 94.50,48.50 ' + - 'C 94.50,48.50 94.50,58.50 94.50,58.50 ' + - '94.50,58.50 84.50,58.50 84.50,58.50 ' + - '84.50,58.50 84.50,48.50 84.50,48.50 ' + - '84.50,48.50 94.50,48.50 94.50,48.50 Z' - }, - 'TASK_TYPE_DATA_PREPARATION_DASHED': { - d: 'M 80.50,14.50 ' + - 'C 80.50,12.84 81.84,11.50 83.50,11.50 ' + - '83.50,11.50 95.50,11.50 95.50,11.50 ' + - '97.16,11.50 98.50,12.84 98.50,14.50 ' + - '98.50,14.50 98.50,59.50 98.50,59.50 ' + - '98.50,61.16 97.16,62.50 95.50,62.50 ' + - '95.50,62.50 83.50,62.50 83.50,62.50 ' + - '81.84,62.50 80.50,61.16 80.50,59.50 ' + - '80.50,59.50 80.50,14.50 80.50,14.50 Z' - }, - 'TASK_TYPE_ORACLE_EXPANSION': { - d: 'M 86.24,15.04 ' + - 'C 86.24,15.04 149.20,15.04 149.20,15.04M 86.24,33.92 ' + - 'C 86.24,33.92 149.20,33.92 149.20,33.92M 86.24,54.08 ' + - 'C 86.24,54.08 149.20,54.08 149.20,54.08M 105.76,15.04 ' + - 'C 105.76,15.04 105.76,61.21 105.76,61.21M 97.92,53.92 ' + - 'C 97.92,49.59 101.43,46.08 105.76,46.08 ' + - '110.09,46.08 113.60,49.59 113.60,53.92 ' + - '113.60,58.25 110.09,61.76 105.76,61.76 ' + - '101.43,61.76 97.92,58.25 97.92,53.92 Z ' + - 'M 123.20,33.92 ' + - 'C 123.20,29.59 126.71,26.08 131.04,26.08 ' + - '135.37,26.08 138.88,29.59 138.88,33.92 ' + - '138.88,38.25 135.37,41.76 131.04,41.76 ' + - '126.71,41.76 123.20,38.25 123.20,33.92 Z ' + - 'M 130.88,26.40 ' + - 'C 130.88,26.40 130.88,53.16 130.88,53.16' - }, - 'TASK_TYPE_ORACLE_EXPANSION_FILL_BLACK': { - d: 'M 100.80,15.68 ' + - 'C 100.80,12.76 103.16,10.40 106.08,10.40 ' + - '109.00,10.40 111.36,12.76 111.36,15.68 ' + - '111.36,18.60 109.00,20.96 106.08,20.96 ' + - '103.16,20.96 100.80,18.60 100.80,15.68 Z ' + - 'M 125.76,53.52 ' + - 'C 125.76,50.65 128.12,48.32 131.04,48.32 ' + - '133.96,48.32 136.32,50.65 136.32,53.52 ' + - '136.32,56.39 133.96,58.72 131.04,58.72 ' + - '128.12,58.72 125.76,56.39 125.76,53.52 Z' - }, - 'TASK_TYPE_ORACLE_EXPANSION_BOX': { - d: 'M 16.80,60.96 ' + - 'C 16.80,60.96 54.40,60.96 54.40,60.96 ' + - '54.40,60.96 54.40,25.44 54.40,25.44 ' + - '54.40,25.44 16.80,25.44 16.80,25.44 ' + - '16.80,25.44 16.80,60.96 16.80,60.96 Z ' + - 'M 54.40,25.44 ' + - 'C 54.40,25.44 66.24,13.60 66.24,13.60 ' + - '66.24,13.60 66.24,49.12 66.24,49.12 ' + - '66.24,49.12 54.40,60.96 54.40,60.96 ' + - '54.40,60.96 54.40,25.44 54.40,25.44 Z ' + - 'M 16.80,25.44 ' + - 'C 16.80,25.44 28.64,13.60 28.64,13.60 ' + - '28.64,13.60 66.24,13.60 66.24,13.60 ' + - '66.24,13.60 54.40,25.44 54.40,25.44 ' + - '54.40,25.44 16.80,25.44 16.80,25.44 Z' - }, - 'TASK_TYPE_ORACLE_EXPANSION_ARROW': { - d: 'M 70.80,33.28 ' + - 'C 70.80,33.28 78.59,33.28 78.59,33.28 ' + - '78.59,33.28 78.59,36.00 78.59,36.00 ' + - '78.59,36.00 70.80,36.00 70.80,36.00 ' + - '70.80,36.00 70.80,33.28 70.80,33.28 Z ' + - 'M 77.09,29.45 ' + - 'C 77.09,29.45 81.82,34.36 81.82,34.36 ' + - '81.82,34.36 77.09,38.91 77.09,38.91 ' + - '77.09,38.91 77.09,29.45 77.09,29.45 Z' - }, - 'TASK_TYPE_CIRCUIT_EXECUTION': { - d: 'M 41.60,42.32 ' + - 'C 41.60,39.45 43.93,37.12 46.80,37.12 ' + - '49.67,37.12 52.00,39.45 52.00,42.32 ' + - '52.00,45.19 49.67,47.52 46.80,47.52 ' + - '43.93,47.52 41.60,45.19 41.60,42.32 ' + - '41.60,42.32 41.60,42.32 41.60,42.32 Z ' + - 'M 20.88,26.36 ' + - 'C 17.85,31.40 27.00,42.47 41.31,51.07 ' + - '55.62,59.67 69.69,62.55 72.72,57.50 ' + - '75.75,52.46 66.60,41.40 52.29,32.79 ' + - '37.97,24.19 23.91,21.31 20.88,26.36 ' + - '20.88,26.36 20.88,26.36 20.88,26.36 Z ' + - 'M 46.00,11.68 ' + - 'C 40.12,11.68 35.36,25.22 35.36,41.92 ' + - '35.36,58.62 40.12,72.16 46.00,72.16 ' + - '51.88,72.16 56.64,58.62 56.64,41.92 ' + - '56.64,25.22 51.88,11.68 46.00,11.68 Z ' + - 'M 72.72,27.90 ' + - 'C 69.69,22.85 55.62,25.73 41.31,34.33 ' + - '27.00,42.93 17.85,54.00 20.88,59.04 ' + - '23.91,64.09 37.97,61.21 52.29,52.60 ' + - '66.60,44.00 75.75,32.94 72.72,27.90 Z' - }, - 'TASK_TYPE_CIRCUIT_EXECUTION_FILL': { - d: 'M 124.15,33.80 ' + - 'C 121.33,34.98 119.35,37.74 119.35,40.96 ' + - '119.35,45.25 122.87,48.73 127.21,48.73 ' + - '131.56,48.73 135.08,45.25 135.08,40.96 ' + - '135.08,36.67 131.56,33.19 127.21,33.19 ' + - '126.13,33.19 125.09,33.40 124.15,33.80 Z ' + - 'M 122.72,18.88 ' + - 'C 122.72,18.88 131.71,18.88 131.71,18.88 ' + - '131.71,18.88 131.71,26.31 131.71,26.31 ' + - '131.71,26.31 134.64,27.49 134.64,27.49 ' + - '134.64,27.49 140.00,22.19 140.00,22.19 ' + - '140.00,22.19 146.35,28.47 146.35,28.47 ' + - '146.35,28.47 141.00,33.75 141.00,33.75 ' + - '141.52,34.53 141.88,35.39 142.16,36.28 ' + - '142.16,36.28 149.44,36.28 149.44,36.28 ' + - '149.44,36.28 149.44,45.17 149.44,45.17 ' + - '149.44,45.17 142.24,45.17 142.24,45.17 ' + - '141.89,46.49 141.36,47.73 140.67,48.87 ' + - '140.67,48.87 145.80,53.94 145.80,53.94 ' + - '145.80,53.94 139.44,60.22 139.44,60.22 ' + - '139.44,60.22 133.88,54.72 133.88,54.72 ' + - '133.22,55.15 132.48,55.41 131.71,55.61 ' + - '131.71,55.61 131.71,63.04 131.71,63.04 ' + - '131.71,63.04 122.72,63.04 122.72,63.04 ' + - '122.72,63.04 122.72,55.61 122.72,55.61 ' + - '122.72,55.61 120.07,54.53 120.07,54.53 ' + - '120.07,54.53 114.73,59.82 114.73,59.82 ' + - '114.73,59.82 108.37,53.53 108.37,53.53 ' + - '108.37,53.53 113.54,48.43 113.54,48.43 ' + - '112.94,47.42 112.50,46.32 112.19,45.17 ' + - '112.19,45.17 105.12,45.17 105.12,45.17 ' + - '105.12,45.17 105.12,36.28 105.12,36.28 ' + - '105.12,36.28 112.27,36.28 112.27,36.28 ' + - '112.27,36.28 113.55,33.49 113.55,33.49 ' + - '113.55,33.49 108.37,28.38 108.37,28.38 ' + - '108.37,28.38 114.73,22.09 114.73,22.09 ' + - '114.73,22.09 120.07,27.38 120.07,27.38 ' + - '120.88,26.89 121.78,26.55 122.72,26.31 ' + - '122.72,26.31 122.72,18.88 122.72,18.88 Z ' + - 'M 77.04,39.20 ' + - 'C 77.04,39.20 86.45,39.20 86.45,39.20 ' + - '86.45,39.20 86.45,41.92 86.45,41.92 ' + - '86.45,41.92 77.04,41.92 77.04,41.92 ' + - '77.04,41.92 77.04,39.20 77.04,39.20 Z ' + - 'M 85.18,33.64 ' + - 'C 85.18,33.64 98.00,40.36 98.00,40.36 ' + - '98.00,40.36 85.27,47.36 85.27,47.36 ' + - '85.27,47.36 85.18,33.64 85.18,33.64 Z' - }, - 'TASK_TYPE_ERROR_MITIGATION': { - d: 'M 20.09,71.91 ' + - 'C 20.09,71.91 20.25,12.50 20.25,12.50 ' + - '20.25,12.50 24.88,12.38 24.88,12.38 ' + - '24.88,12.38 24.75,67.00 24.75,67.00 ' + - '24.75,67.00 79.62,67.25 79.62,67.25 ' + - '79.62,67.25 79.70,72.04 79.70,72.04 ' + - '79.70,72.04 20.04,71.87 20.04,71.87M 53.25,12.50 ' + - 'C 53.25,12.50 44.12,12.50 44.12,12.50 ' + - '44.12,12.50 43.73,61.91 43.73,61.91 ' + - '43.73,61.91 52.88,61.75 52.88,61.75 ' + - '52.88,61.75 53.00,12.50 53.00,12.50M 30.38,61.50 ' + - 'C 30.38,61.50 30.50,31.38 30.50,31.38 ' + - '30.50,31.38 40.00,31.38 40.00,31.38 ' + - '40.00,31.38 39.73,61.45 39.73,61.45 ' + - '39.73,61.45 30.45,61.55 30.45,61.55M 40.36,26.82M 57.12,61.78 ' + - 'C 57.12,61.78 57.12,30.88 57.12,30.88 ' + - '57.12,30.88 66.75,30.88 66.75,30.88 ' + - '66.75,30.88 66.75,61.75 66.75,61.75 ' + - '66.75,61.75 57.12,61.81 57.12,61.81M 70.06,61.69 ' + - 'C 70.06,61.69 70.09,46.00 70.09,46.00 ' + - '70.09,46.00 79.82,46.00 79.82,46.00 ' + - '79.82,46.00 79.82,61.73 79.82,61.73 ' + - '79.82,61.73 70.00,61.64 70.00,61.64' - }, - 'SUBPROCESS_QUANTUM_HARDWARE_SELECTION': { - d: 'M 75.94,17.84 ' + - 'C 75.94,16.56 76.93,15.52 78.17,15.52 ' + - '79.40,15.52 80.40,16.56 80.40,17.84 ' + - '80.40,19.12 79.40,20.16 78.17,20.16 ' + - '76.93,20.16 75.94,19.12 75.94,17.84 Z ' + - 'M 66.89,10.91 ' + - 'C 65.58,13.11 69.55,17.93 75.77,21.68 ' + - '81.99,25.43 88.10,26.69 89.42,24.49 ' + - '90.74,22.29 86.76,17.47 80.54,13.72 ' + - '74.32,9.97 68.21,8.71 66.89,10.91 ' + - '66.89,10.91 66.89,10.91 66.89,10.91 Z ' + - 'M 77.85,4.48 ' + - 'C 75.29,4.48 73.22,10.39 73.22,17.68 ' + - '73.22,24.97 75.29,30.88 77.85,30.88 ' + - '80.40,30.88 82.48,24.97 82.48,17.68 ' + - '82.48,10.39 80.40,4.48 77.85,4.48 Z ' + - 'M 89.42,11.58 ' + - 'C 88.10,9.38 81.99,10.64 75.77,14.39 ' + - '69.55,18.14 65.58,22.96 66.89,25.16 ' + - '68.21,27.36 74.32,26.10 80.54,22.35 ' + - '86.76,18.60 90.74,13.78 89.42,11.58 ' + - '89.42,11.58 89.42,11.58 89.42,11.58 Z ' + - 'M 75.94,49.12 ' + - 'C 75.94,47.88 76.93,46.88 78.17,46.88 ' + - '79.40,46.88 80.40,47.88 80.40,49.12 ' + - '80.40,50.36 79.40,51.36 78.17,51.36 ' + - '76.93,51.36 75.94,50.36 75.94,49.12 Z ' + - 'M 66.86,42.16 ' + - 'C 65.54,44.36 69.52,49.18 75.74,52.93 ' + - '81.96,56.68 88.07,57.94 89.39,55.74 ' + - '90.71,53.54 86.73,48.72 80.51,44.97 ' + - '74.29,41.22 68.18,39.96 66.86,42.16 Z ' + - 'M 77.77,35.84 ' + - 'C 75.17,35.84 73.06,41.71 73.06,48.96 ' + - '73.06,56.21 75.17,62.08 77.77,62.08 ' + - '80.37,62.08 82.48,56.21 82.48,48.96 ' + - '82.48,41.71 80.37,35.84 77.77,35.84 Z ' + - 'M 89.39,42.83 ' + - 'C 88.07,40.63 81.96,41.89 75.74,45.64 ' + - '69.52,49.39 65.54,54.21 66.86,56.41 ' + - '68.18,58.61 74.29,57.35 80.51,53.60 ' + - '86.73,49.85 90.71,45.03 89.39,42.83 Z ' + - 'M 4.15,23.20 ' + - 'C 4.15,23.20 36.33,23.20 36.33,23.20M 4.15,32.96 ' + - 'C 4.15,32.96 36.33,32.96 36.33,32.96M 4.15,43.20 ' + - 'C 4.15,43.20 36.33,43.20 36.33,43.20M 14.04,23.20 ' + - 'C 14.04,23.20 14.04,46.87 14.04,46.87M 10.05,43.20 ' + - 'C 10.05,40.99 11.87,39.20 14.12,39.20 ' + - '16.36,39.20 18.19,40.99 18.19,43.20 ' + - '18.19,45.41 16.36,47.20 14.12,47.20 ' + - '11.87,47.20 10.05,45.41 10.05,43.20 ' + - '10.05,43.20 10.05,43.20 10.05,43.20 Z ' + - 'M 22.97,32.88 ' + - 'C 22.97,30.63 24.76,28.80 26.96,28.80 ' + - '29.16,28.80 30.95,30.63 30.95,32.88 ' + - '30.95,35.13 29.16,36.96 26.96,36.96 ' + - '24.76,36.96 22.97,35.13 22.97,32.88 Z ' + - 'M 26.96,28.96 ' + - 'C 26.96,28.96 26.96,42.68 26.96,42.68' - }, - 'SUBPROCESS_QUANTUM_HARDWARE_SELECTION_FILL': { - d:'M 52.14,21.37 ' + - 'C 52.14,21.37 52.65,23.09 52.65,23.09 ' + - '52.65,23.09 43.96,25.28 43.96,25.26 ' + - '43.96,25.24 43.62,23.62 43.62,23.62 ' + - '43.62,23.62 52.14,21.38 52.14,21.38 ' + - 'M 47.87,16.93 ' + - 'C 47.87,16.93 58.73,20.22 58.73,20.22 ' + - '58.73,20.22 51.16,28.70 51.16,28.70 ' + - '51.16,28.70 47.87,16.93 47.87,16.93 Z ' + - 'M 11.65,23.52 ' + - 'C 11.65,22.02 12.82,20.80 14.28,20.80 ' + - '15.73,20.80 16.91,22.02 16.91,23.52 ' + - '16.91,25.02 15.73,26.24 14.28,26.24 ' + - '12.82,26.24 11.65,25.02 11.65,23.52 Z ' + - 'M 24.25,42.96 ' + - 'C 24.25,41.50 25.46,40.32 26.96,40.32 ' + - '28.46,40.32 29.67,41.50 29.67,42.96 ' + - '29.67,44.42 28.46,45.60 26.96,45.60 ' + - '25.46,45.60 24.25,44.42 24.25,42.96 Z ' + - 'M 52.13,45.69 ' + - 'C 52.13,45.69 51.27,47.76 51.27,47.76 ' + - '51.27,47.76 42.82,44.16 42.82,44.16 ' + - '42.82,44.16 43.78,42.22 43.78,42.22 ' + - '43.78,42.22 52.13,45.67 52.13,45.67 ' + - 'M 51.94,25.75 ' + - 'M 50.92,39.92 ' + - 'C 50.92,39.92 58.01,48.79 58.01,48.79 ' + - '58.01,48.79 47.00,51.50 47.00,51.50 ' + - '47.00,51.50 50.92,39.92 50.92,39.92 Z' - } - }; - - this.getPath = function getPath(pathId) { - return this.quantMEPathMap[pathId].d; - }; - } \ No newline at end of file + this.getPath = function getPath(pathId) { + return this.quantMEPathMap[pathId].d; + }; +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERenderer.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERenderer.js index 071a7353..0389950b 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERenderer.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMERenderer.js @@ -9,52 +9,63 @@ * SPDX-License-Identifier: Apache-2.0 */ -import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer'; -import * as quantmeReplaceOptions from './QuantMEReplaceOptions'; -import * as consts from '../Constants'; -import { append as svgAppend, attr as svgAttr, create as svgCreate, innerSVG, select as svgSelect } from 'tiny-svg'; -import { getFillColor, getStrokeColor } from 'bpmn-js/lib/draw/BpmnRenderUtil'; -import { getQuantMESVG } from './QuantMESVGMap'; +import BpmnRenderer from "bpmn-js/lib/draw/BpmnRenderer"; +import * as quantmeReplaceOptions from "./QuantMEReplaceOptions"; +import * as consts from "../Constants"; import { - queryAll as domQueryAll -} from 'min-dom'; - + append as svgAppend, + attr as svgAttr, + create as svgCreate, + innerSVG, + select as svgSelect, +} from "tiny-svg"; +import { getFillColor, getStrokeColor } from "bpmn-js/lib/draw/BpmnRenderUtil"; +import { getQuantMESVG } from "./QuantMESVGMap"; +import { queryAll as domQueryAll } from "min-dom"; /** * This class extends the default BPMNRenderer to render the newly introduced QuantME task types */ export default class QuantMERenderer extends BpmnRenderer { - constructor(config, eventBus, styles, pathMap, quantMEPathMap, canvas, textRenderer) { + constructor( + config, + eventBus, + styles, + pathMap, + quantMEPathMap, + canvas, + textRenderer + ) { super(config, eventBus, styles, pathMap, canvas, textRenderer, 1001); var computeStyle = styles.computeStyle; var defaultFillColor = config && config.defaultFillColor, - defaultStrokeColor = config && config.defaultStrokeColor; + defaultStrokeColor = config && config.defaultStrokeColor; function drawTaskSVG(parentGfx, iconID) { var importsvg = getQuantMESVG(iconID); var innerSVGstring = importsvg.svg; var transformDef = importsvg.transform; - const groupDef = svgCreate('g'); + const groupDef = svgCreate("g"); svgAttr(groupDef, { transform: transformDef }); innerSVG(groupDef, innerSVGstring); // set task box opacity to 0 such that icon can be in the background - svgAttr(svgSelect(parentGfx, 'rect'), { 'fill-opacity': 0 }); + svgAttr(svgSelect(parentGfx, "rect"), { "fill-opacity": 0 }); // draw svg in the background parentGfx.prepend(groupDef); } function drawPath(parentGfx, d, attrs) { - attrs = computeStyle(attrs, [ 'no-fill' ], { + attrs = computeStyle(attrs, ["no-fill"], { strokeWidth: 2, - stroke: 'black' + stroke: "black", }); - const path = svgCreate('path'); + const path = svgCreate("path"); svgAttr(path, { d: d }); svgAttr(path, attrs); svgAppend(parentGfx, path); @@ -63,220 +74,255 @@ export default class QuantMERenderer extends BpmnRenderer { } this.quantMeHandlers = { - [consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS]: function(self, parentGfx, element) { - var subprocess = self.renderer('bpmn:SubProcess')(parentGfx, element); - - var pathData = quantMEPathMap.getPath('SUBPROCESS_QUANTUM_HARDWARE_SELECTION'); + [consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS]: function ( + self, + parentGfx, + element + ) { + var subprocess = self.renderer("bpmn:SubProcess")(parentGfx, element); + + var pathData = quantMEPathMap.getPath( + "SUBPROCESS_QUANTUM_HARDWARE_SELECTION" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.5)', + transform: "scale(0.5)", strokeWidth: 1.5, fill: getFillColor(element, defaultFillColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with filled shapes - pathData = quantMEPathMap.getPath('SUBPROCESS_QUANTUM_HARDWARE_SELECTION_FILL'); + pathData = quantMEPathMap.getPath( + "SUBPROCESS_QUANTUM_HARDWARE_SELECTION_FILL" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.5)', + transform: "scale(0.5)", strokeWidth: 1.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); return subprocess; }, - [consts.CIRCUIT_CUTTING_SUBPROCESS]: function(self, parentGfx, element) { - var subprocess = self.renderer('bpmn:SubProcess')(parentGfx, element); - drawTaskSVG(parentGfx, 'SUBPROCESS_TYPE_CIRCUIT_CUTTING'); + [consts.CIRCUIT_CUTTING_SUBPROCESS]: function (self, parentGfx, element) { + var subprocess = self.renderer("bpmn:SubProcess")(parentGfx, element); + drawTaskSVG(parentGfx, "SUBPROCESS_TYPE_CIRCUIT_CUTTING"); return subprocess; }, - [consts.QUANTUM_COMPUTATION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); + [consts.QUANTUM_COMPUTATION_TASK]: function (self, parentGfx, element) { + var task = self.renderer("bpmn:Task")(parentGfx, element); - var pathData = quantMEPathMap.getPath('TASK_TYPE_QUANTUM_COMPUTATION'); + var pathData = quantMEPathMap.getPath("TASK_TYPE_QUANTUM_COMPUTATION"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultFillColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); return task; }, - [consts.QUANTUM_CIRCUIT_LOADING_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); + [consts.QUANTUM_CIRCUIT_LOADING_TASK]: function ( + self, + parentGfx, + element + ) { + var task = self.renderer("bpmn:Task")(parentGfx, element); // create circuit paths without filled shapes - var pathData = quantMEPathMap.getPath('TASK_TYPE_CIRCUIT_LOADING'); + var pathData = quantMEPathMap.getPath("TASK_TYPE_CIRCUIT_LOADING"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultStrokeColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with filled shapes - pathData = quantMEPathMap.getPath('TASK_TYPE_CIRCUIT_LOADING_FILL'); + pathData = quantMEPathMap.getPath("TASK_TYPE_CIRCUIT_LOADING_FILL"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); return task; }, - [consts.DATA_PREPARATION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); + [consts.DATA_PREPARATION_TASK]: function (self, parentGfx, element) { + var task = self.renderer("bpmn:Task")(parentGfx, element); - var pathData = quantMEPathMap.getPath('TASK_TYPE_DATA_PREPARATION'); + var pathData = quantMEPathMap.getPath("TASK_TYPE_DATA_PREPARATION"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultFillColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with filled shapes (black) - pathData = quantMEPathMap.getPath('TASK_TYPE_DATA_PREPARATION_FILL_BLACK'); + pathData = quantMEPathMap.getPath( + "TASK_TYPE_DATA_PREPARATION_FILL_BLACK" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with filled shapes (background color) - pathData = quantMEPathMap.getPath('TASK_TYPE_DATA_PREPARATION_FILL_BACKGROUND'); + pathData = quantMEPathMap.getPath( + "TASK_TYPE_DATA_PREPARATION_FILL_BACKGROUND" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultStrokeColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with dashed shapes - pathData = quantMEPathMap.getPath('TASK_TYPE_DATA_PREPARATION_DASHED'); + pathData = quantMEPathMap.getPath("TASK_TYPE_DATA_PREPARATION_DASHED"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, strokeDasharray: 5, - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create white line for database - pathData = quantMEPathMap.getPath('TASK_TYPE_DATA_PREPARATION_BACKGROUND'); + pathData = quantMEPathMap.getPath( + "TASK_TYPE_DATA_PREPARATION_BACKGROUND" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - stroke: getFillColor(element, '#FFFFFF') + stroke: getFillColor(element, "#FFFFFF"), }); return task; }, - [consts.ORACLE_EXPANSION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); + [consts.ORACLE_EXPANSION_TASK]: function (self, parentGfx, element) { + var task = self.renderer("bpmn:Task")(parentGfx, element); - var pathData = quantMEPathMap.getPath('TASK_TYPE_ORACLE_EXPANSION'); + var pathData = quantMEPathMap.getPath("TASK_TYPE_ORACLE_EXPANSION"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultFillColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); // create circuit paths with filled shapes - pathData = quantMEPathMap.getPath('TASK_TYPE_ORACLE_EXPANSION_FILL_BLACK'); + pathData = quantMEPathMap.getPath( + "TASK_TYPE_ORACLE_EXPANSION_FILL_BLACK" + ); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); // create oracle box - pathData = quantMEPathMap.getPath('TASK_TYPE_ORACLE_EXPANSION_BOX'); + pathData = quantMEPathMap.getPath("TASK_TYPE_ORACLE_EXPANSION_BOX"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, '#FFF') + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, "#FFF"), }); // create arrow - pathData = quantMEPathMap.getPath('TASK_TYPE_ORACLE_EXPANSION_ARROW'); + pathData = quantMEPathMap.getPath("TASK_TYPE_ORACLE_EXPANSION_ARROW"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); return task; }, - [consts.QUANTUM_CIRCUIT_EXECUTION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - - var pathData = quantMEPathMap.getPath('TASK_TYPE_CIRCUIT_EXECUTION'); + [consts.QUANTUM_CIRCUIT_EXECUTION_TASK]: function ( + self, + parentGfx, + element + ) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + + var pathData = quantMEPathMap.getPath("TASK_TYPE_CIRCUIT_EXECUTION"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, fill: getFillColor(element, defaultFillColor), - stroke: getStrokeColor(element, defaultStrokeColor) + stroke: getStrokeColor(element, defaultStrokeColor), }); - pathData = quantMEPathMap.getPath('TASK_TYPE_CIRCUIT_EXECUTION_FILL'); + pathData = quantMEPathMap.getPath("TASK_TYPE_CIRCUIT_EXECUTION_FILL"); drawPath(parentGfx, pathData, { - transform:'scale(0.3)', + transform: "scale(0.3)", strokeWidth: 2.5, - fill: getFillColor(element, '#000000'), - stroke: getStrokeColor(element, defaultStrokeColor) + fill: getFillColor(element, "#000000"), + stroke: getStrokeColor(element, defaultStrokeColor), }); return task; }, - [consts.READOUT_ERROR_MITIGATION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - drawTaskSVG(parentGfx, 'TASK_TYPE_ERROR_MITIGATION'); + [consts.READOUT_ERROR_MITIGATION_TASK]: function ( + self, + parentGfx, + element + ) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + drawTaskSVG(parentGfx, "TASK_TYPE_ERROR_MITIGATION"); return task; }, - [consts.WARM_STARTING_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - drawTaskSVG(parentGfx, 'TASK_TYPE_WARM_STARTING'); + [consts.WARM_STARTING_TASK]: function (self, parentGfx, element) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + drawTaskSVG(parentGfx, "TASK_TYPE_WARM_STARTING"); return task; }, - [consts.PARAMETER_OPTIMIZATION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - drawTaskSVG(parentGfx, 'TASK_TYPE_PARAMETER_OPTIMIZATION'); + [consts.PARAMETER_OPTIMIZATION_TASK]: function ( + self, + parentGfx, + element + ) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + drawTaskSVG(parentGfx, "TASK_TYPE_PARAMETER_OPTIMIZATION"); return task; }, - [consts.RESULT_EVALUATION_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - setTimeout(function() {}, 10000); - drawTaskSVG(parentGfx, 'TASK_TYPE_RESULT_EVALUATION'); + [consts.RESULT_EVALUATION_TASK]: function (self, parentGfx, element) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + setTimeout(function () {}, 10000); + drawTaskSVG(parentGfx, "TASK_TYPE_RESULT_EVALUATION"); return task; }, - [consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK]: function(self, parentGfx, element) { - var task = self.renderer('bpmn:Task')(parentGfx, element); - drawTaskSVG(parentGfx, 'TASK_TYPE_VQA'); + [consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK]: function ( + self, + parentGfx, + element + ) { + var task = self.renderer("bpmn:Task")(parentGfx, element); + drawTaskSVG(parentGfx, "TASK_TYPE_VQA"); return task; - } + }, }; - setTimeout(function() { - + setTimeout(function () { // TODO: pullrequest to change BpmnRenderer.js if issue persists in new Version // extract markers out of task icon svgs when loading a saved diagram // due to restrictions in BpmnRenderer.js that places them in first defs element in svg - var existingDefs = domQueryAll('marker', canvas._svg); + var existingDefs = domQueryAll("marker", canvas._svg); if (existingDefs != null) { var createdNewDefs = false; for (let i = 0; i < existingDefs.length; i++) { - if (existingDefs[i].parentElement.parentElement.nodeName !== 'svg') { + if (existingDefs[i].parentElement.parentElement.nodeName !== "svg") { if (createdNewDefs === false) { - var newDefs = svgCreate('defs'); + var newDefs = svgCreate("defs"); svgAppend(canvas._svg, newDefs); createdNewDefs = true; } @@ -292,7 +338,6 @@ export default class QuantMERenderer extends BpmnRenderer { } canRender(element) { - // default elements can be handled if (super.canRender(element)) { return true; @@ -305,12 +350,11 @@ export default class QuantMERenderer extends BpmnRenderer { } } - console.log('Unable to render element of type: ' + element.type); + console.log("Unable to render element of type: " + element.type); return false; } drawShape(parentNode, element) { - // handle QuantME elements if (element.type in this.quantMeHandlers) { var h = this.quantMeHandlers[element.type]; @@ -325,11 +369,11 @@ export default class QuantMERenderer extends BpmnRenderer { } QuantMERenderer.$inject = [ - 'config', - 'eventBus', - 'styles', - 'pathMap', - 'quantMEPathMap', - 'canvas', - 'textRenderer' -]; \ No newline at end of file + "config", + "eventBus", + "styles", + "pathMap", + "quantMEPathMap", + "canvas", + "textRenderer", +]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceMenuProvider.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceMenuProvider.js index 306ca6cb..794bafa6 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceMenuProvider.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceMenuProvider.js @@ -9,142 +9,170 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as quantmeReplaceOptions from './QuantMEReplaceOptions'; -import {is, isAny} from 'bpmn-js/lib/util/ModelUtil'; +import * as quantmeReplaceOptions from "./QuantMEReplaceOptions"; +import { is, isAny } from "bpmn-js/lib/util/ModelUtil"; import { - createMenuEntries, - createMoreOptionsEntryWithReturn + createMenuEntries, + createMoreOptionsEntryWithReturn, } from "../../../editor/util/PopupMenuUtilities"; -import { - createConfigurationsEntries, -} from '../../../editor/configurations/ConfigurationsUtil'; -import {instance as dataObjectConfigs} from '../configurations/DataObjectConfigurations'; +import { createConfigurationsEntries } from "../../../editor/configurations/ConfigurationsUtil"; +import { instance as dataObjectConfigs } from "../configurations/DataObjectConfigurations"; import * as dataConsts from "../../data-extension/Constants"; -import { filter } from 'min-dash'; -import { isDifferentType } from 'bpmn-js/lib/features/popup-menu/util/TypeUtil'; +import { filter } from "min-dash"; +import { isDifferentType } from "bpmn-js/lib/features/popup-menu/util/TypeUtil"; /** * This class extends the default ReplaceMenuProvider with the newly introduced QuantME task types */ export default class QuantMEReplaceMenuProvider { - constructor(bpmnFactory, popupMenu, modeling, moddle, bpmnReplace, rules, translate, commandStack) { - - this.popupMenu = popupMenu; - this.translate = translate; - this.bpmnReplace = bpmnReplace; - this.replaceElement = bpmnReplace.replaceElement; - this.bpmnFactory = bpmnFactory; - this.modeling = modeling; - this.commandStack = commandStack; - - - popupMenu.registerProvider('bpmn-replace', this); - } - - getPopupMenuEntries(element) { - const self = this; - return function (entries) { - - // do not show entries for extension elements of other plugins, except for DataMapObjects to list the loaded - // configurations for DataMapObjects - if (!(element.type.startsWith('bpmn') || element.type.startsWith('quantme') || is(element, dataConsts.DATA_MAP_OBJECT))) { - return entries; - } - - // add menu entries for the loaded configuration which can be applied to DataMapObjects - if (isAny(element, ['bpmn:DataObjectReference', dataConsts.DATA_MAP_OBJECT])) { - const dataEntries = self.createQuantMEDataEntry(element); - return Object.assign(dataEntries, entries); - } - - // add additional elements to replace tasks - if (is(element, 'bpmn:Task')) { - const quantMETasks = self.createQuantMETasks(element); - return Object.assign(quantMETasks, entries); - } - - // add additional elements to replace subprocesses - if (is(element, 'bpmn:SubProcess')) { - let filteredOptions = filter(quantmeReplaceOptions.SUBPROCESS, isDifferentType(element)); - const subprocessEntries = createMenuEntries(element, filteredOptions, self.translate, self.bpmnReplace.replaceElement); - return Object.assign(subprocessEntries, entries); - } - - return entries; - }; - } - - /** - * Create a MoreOptionsEntry which contains menu entries to replace the current element with all QuantME task types. - * - * @param element The given element - * @return {{'replace-by-more-options': {label: string, className: string, action: Function}}} - */ - createQuantMETasks(element) { - const popupMenu = this.popupMenu; - const translate = this.translate; - const replaceElement = this.bpmnReplace.replaceElement; - let filteredOptions = filter(quantmeReplaceOptions.TASK, isDifferentType(element)); - - // create menu entries for the QuantME task types - let options = createMenuEntries(element, filteredOptions, translate, replaceElement); - - return { - ['replace-by-more-options']: createMoreOptionsEntryWithReturn( - element, - 'QuantME Tasks', - 'QuantME Tasks', - popupMenu, - options, - 'quantme-tasks-icon' - ) - }; - } - - /** - * Creates MoreOptionsEntry for the QuantME data objects configurations. - * - * @param element the given element the menu entries are requested for. - * @return {{'replace-by-quantme-data-options': {label: string, className: string, action: Function}}} - */ - createQuantMEDataEntry(element) { - const bpmnFactory = this.bpmnFactory; - const modeling = this.modeling; - const popupMenu = this.popupMenu; - const replaceElement = this.replaceElement; - const commandStack = this.commandStack; - - // create menu entries to replace the current element by the configuration represented by the menu entry - let options = createConfigurationsEntries( - element, - 'dataflow-data-map-object-icon', - dataObjectConfigs().getQuantMEDataConfigurations(), - bpmnFactory, - modeling, - commandStack, - replaceElement + constructor( + bpmnFactory, + popupMenu, + modeling, + moddle, + bpmnReplace, + rules, + translate, + commandStack + ) { + this.popupMenu = popupMenu; + this.translate = translate; + this.bpmnReplace = bpmnReplace; + this.replaceElement = bpmnReplace.replaceElement; + this.bpmnFactory = bpmnFactory; + this.modeling = modeling; + this.commandStack = commandStack; + + popupMenu.registerProvider("bpmn-replace", this); + } + + getPopupMenuEntries(element) { + const self = this; + return function (entries) { + // do not show entries for extension elements of other plugins, except for DataMapObjects to list the loaded + // configurations for DataMapObjects + if ( + !( + element.type.startsWith("bpmn") || + element.type.startsWith("quantme") || + is(element, dataConsts.DATA_MAP_OBJECT) + ) + ) { + return entries; + } + + // add menu entries for the loaded configuration which can be applied to DataMapObjects + if ( + isAny(element, ["bpmn:DataObjectReference", dataConsts.DATA_MAP_OBJECT]) + ) { + const dataEntries = self.createQuantMEDataEntry(element); + return Object.assign(dataEntries, entries); + } + + // add additional elements to replace tasks + if (is(element, "bpmn:Task")) { + const quantMETasks = self.createQuantMETasks(element); + return Object.assign(quantMETasks, entries); + } + + // add additional elements to replace subprocesses + if (is(element, "bpmn:SubProcess")) { + let filteredOptions = filter( + quantmeReplaceOptions.SUBPROCESS, + isDifferentType(element) ); - - return { - ['replace-by-quantme-data-options']: createMoreOptionsEntryWithReturn( - element, - 'QuantME Data Objects', - 'QuantME Data Objects', - popupMenu, - options, - 'bpmn-icon-task-quantum-computation' - ) - }; - } + const subprocessEntries = createMenuEntries( + element, + filteredOptions, + self.translate, + self.bpmnReplace.replaceElement + ); + return Object.assign(subprocessEntries, entries); + } + + return entries; + }; + } + + /** + * Create a MoreOptionsEntry which contains menu entries to replace the current element with all QuantME task types. + * + * @param element The given element + * @return {{'replace-by-more-options': {label: string, className: string, action: Function}}} + */ + createQuantMETasks(element) { + const popupMenu = this.popupMenu; + const translate = this.translate; + const replaceElement = this.bpmnReplace.replaceElement; + let filteredOptions = filter( + quantmeReplaceOptions.TASK, + isDifferentType(element) + ); + + // create menu entries for the QuantME task types + let options = createMenuEntries( + element, + filteredOptions, + translate, + replaceElement + ); + + return { + ["replace-by-more-options"]: createMoreOptionsEntryWithReturn( + element, + "QuantME Tasks", + "QuantME Tasks", + popupMenu, + options, + "quantme-tasks-icon" + ), + }; + } + + /** + * Creates MoreOptionsEntry for the QuantME data objects configurations. + * + * @param element the given element the menu entries are requested for. + * @return {{'replace-by-quantme-data-options': {label: string, className: string, action: Function}}} + */ + createQuantMEDataEntry(element) { + const bpmnFactory = this.bpmnFactory; + const modeling = this.modeling; + const popupMenu = this.popupMenu; + const replaceElement = this.replaceElement; + const commandStack = this.commandStack; + + // create menu entries to replace the current element by the configuration represented by the menu entry + let options = createConfigurationsEntries( + element, + "dataflow-data-map-object-icon", + dataObjectConfigs().getQuantMEDataConfigurations(), + bpmnFactory, + modeling, + commandStack, + replaceElement + ); + + return { + ["replace-by-quantme-data-options"]: createMoreOptionsEntryWithReturn( + element, + "QuantME Data Objects", + "QuantME Data Objects", + popupMenu, + options, + "bpmn-icon-task-quantum-computation" + ), + }; + } } QuantMEReplaceMenuProvider.$inject = [ - 'bpmnFactory', - 'popupMenu', - 'modeling', - 'moddle', - 'bpmnReplace', - 'rules', - 'translate', - 'commandStack', + "bpmnFactory", + "popupMenu", + "modeling", + "moddle", + "bpmnReplace", + "rules", + "translate", + "commandStack", ]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceOptions.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceOptions.js index 03d6fa68..5fbfac80 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceOptions.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMEReplaceOptions.js @@ -9,122 +9,122 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as consts from '../Constants'; +import * as consts from "../Constants"; export var TASK = [ { - label: 'Quantum Hardware Selection Subprocess', - actionName: 'replace-with-hardware-selection-subprocess', - className: 'bpmn-icon-hardware-selection-subprocess', + label: "Quantum Hardware Selection Subprocess", + actionName: "replace-with-hardware-selection-subprocess", + className: "bpmn-icon-hardware-selection-subprocess", target: { - type: consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS - } + type: consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS, + }, }, { - label: 'Circuit Cutting Subprocess', - actionName: 'replace-with-circuit-cutting-subprocess', - className: 'bpmn-icon-circuit-cutting', + label: "Circuit Cutting Subprocess", + actionName: "replace-with-circuit-cutting-subprocess", + className: "bpmn-icon-circuit-cutting", target: { - type: consts.CIRCUIT_CUTTING_SUBPROCESS - } + type: consts.CIRCUIT_CUTTING_SUBPROCESS, + }, }, { - label: 'Quantum Computation Task', - actionName: 'replace-with-quantum-computation-task', - className: 'bpmn-icon-task-quantum-computation', + label: "Quantum Computation Task", + actionName: "replace-with-quantum-computation-task", + className: "bpmn-icon-task-quantum-computation", target: { - type: consts.QUANTUM_COMPUTATION_TASK - } + type: consts.QUANTUM_COMPUTATION_TASK, + }, }, { - label: 'Quantum Circuit Loading Task', - actionName: 'replace-with-quantum-circuit-loading-task', - className: 'bpmn-icon-circuit-loading', + label: "Quantum Circuit Loading Task", + actionName: "replace-with-quantum-circuit-loading-task", + className: "bpmn-icon-circuit-loading", target: { - type: consts.QUANTUM_CIRCUIT_LOADING_TASK - } + type: consts.QUANTUM_CIRCUIT_LOADING_TASK, + }, }, { - label: 'Data Preparation Task', - actionName: 'replace-with-data-preparation-task', - className: 'bpmn-icon-data-preparation', + label: "Data Preparation Task", + actionName: "replace-with-data-preparation-task", + className: "bpmn-icon-data-preparation", target: { - type: consts.DATA_PREPARATION_TASK - } + type: consts.DATA_PREPARATION_TASK, + }, }, { - label: 'Oracle Expansion Task', - actionName: 'replace-with-oracle-expansion-task', - className: 'bpmn-icon-oracle-expansion', + label: "Oracle Expansion Task", + actionName: "replace-with-oracle-expansion-task", + className: "bpmn-icon-oracle-expansion", target: { - type: consts.ORACLE_EXPANSION_TASK - } + type: consts.ORACLE_EXPANSION_TASK, + }, }, { - label: 'Quantum Circuit Execution Task', - actionName: 'replace-with-quantum-circuit-execution-task', - className: 'bpmn-icon-circuit-execution', + label: "Quantum Circuit Execution Task", + actionName: "replace-with-quantum-circuit-execution-task", + className: "bpmn-icon-circuit-execution", target: { - type: consts.QUANTUM_CIRCUIT_EXECUTION_TASK - } + type: consts.QUANTUM_CIRCUIT_EXECUTION_TASK, + }, }, { - label: 'Readout-Error Mitigation Task', - actionName: 'replace-with-readout-error-mitigation-task', - className: 'bpmn-icon-error-mitigation', + label: "Readout-Error Mitigation Task", + actionName: "replace-with-readout-error-mitigation-task", + className: "bpmn-icon-error-mitigation", target: { - type: consts.READOUT_ERROR_MITIGATION_TASK - } + type: consts.READOUT_ERROR_MITIGATION_TASK, + }, }, { - label: 'Parameter Optimization Task', - actionName: 'replace-with-parameter-optimization-task', - className: 'bpmn-icon-parameter-optimization', + label: "Parameter Optimization Task", + actionName: "replace-with-parameter-optimization-task", + className: "bpmn-icon-parameter-optimization", target: { - type: consts.PARAMETER_OPTIMIZATION_TASK - } + type: consts.PARAMETER_OPTIMIZATION_TASK, + }, }, { - label: 'Result Evaluation Task', - actionName: 'replace-with-result-evaluation-task', - className: 'bpmn-icon-result-evaluation', + label: "Result Evaluation Task", + actionName: "replace-with-result-evaluation-task", + className: "bpmn-icon-result-evaluation", target: { - type: consts.RESULT_EVALUATION_TASK - } + type: consts.RESULT_EVALUATION_TASK, + }, }, { - label: 'Warm-Starting Task', - actionName: 'replace-with-warm-starting-task', - className: 'bpmn-icon-warm-starting', + label: "Warm-Starting Task", + actionName: "replace-with-warm-starting-task", + className: "bpmn-icon-warm-starting", target: { - type: consts.WARM_STARTING_TASK - } + type: consts.WARM_STARTING_TASK, + }, }, { - label: 'Variational Quantum Algorithm Task', - actionName: 'replace-with-variational-quantum-algorithm-task', - className: 'bpmn-icon-variational-quantum-algorithm', + label: "Variational Quantum Algorithm Task", + actionName: "replace-with-variational-quantum-algorithm-task", + className: "bpmn-icon-variational-quantum-algorithm", target: { - type: consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK - } + type: consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK, + }, }, ]; export var SUBPROCESS = [ { - label: 'Quantum Hardware Selection Subprocess', - actionName: 'replace-with-hardware-selection-subprocess', - className: 'bpmn-icon-hardware-selection-subprocess', + label: "Quantum Hardware Selection Subprocess", + actionName: "replace-with-hardware-selection-subprocess", + className: "bpmn-icon-hardware-selection-subprocess", target: { - type: consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS - } + type: consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS, + }, }, { - label: 'Circuit Cutting Subprocess', - actionName: 'replace-with-circuit-cutting-subprocess', - className: 'bpmn-icon-circuit-cutting', + label: "Circuit Cutting Subprocess", + actionName: "replace-with-circuit-cutting-subprocess", + className: "bpmn-icon-circuit-cutting", target: { - type: consts.CIRCUIT_CUTTING_SUBPROCESS - } - } -]; \ No newline at end of file + type: consts.CIRCUIT_CUTTING_SUBPROCESS, + }, + }, +]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMESVGMap.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMESVGMap.js index 75ba342a..3962fcb0 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMESVGMap.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/QuantMESVGMap.js @@ -10,24 +10,46 @@ */ export function getQuantMESVG(svgId) { - // to insert svgs easily just open them in your browser, copy the outer html and insert it using ctrl + alt + shift + v in intellij to avoid formatting,escaping etc. // matrix( Scalingfactor, 0, 0, Scalingfactor, shift X, shift y) // IMPORTANT: ensure that definition Ids for new SVGs are UNIQUE // viewbox is not required const quantMESVGMap = { - 'TASK_TYPE_QUANTUM_COMPUTATION': { svg: '' }, - 'TASK_TYPE_CIRCUIT_LOADING': { svg: '' }, - 'TASK_TYPE_DATA_PREPARATION': { svg: '' }, - 'TASK_TYPE_ORACLE_EXPANSION': { svg: '' }, - 'TASK_TYPE_CIRCUIT_EXECUTION': { svg: '' }, - 'TASK_TYPE_ERROR_MITIGATION': { transform: 'matrix(0.2, 0, 0, 0.2, 5, 5)', svg: '' }, - 'SUBPROCESS_QUANTUM_HARDWARE_SELECTION': { svg: '' }, - 'TASK_TYPE_WARM_STARTING':{ transform: 'matrix(0.22, 0, 0, 0.22, 3, 3)', viewBox: '0 0 189 98', svg: '' }, - 'SUBPROCESS_TYPE_CIRCUIT_CUTTING':{ transform: 'matrix(0.23, 0, 0, 0.23, 4, 3)', viewBox: '0 0 144 144', svg: '' }, - 'TASK_TYPE_PARAMETER_OPTIMIZATION':{ transform: 'matrix(0.22, 0, 0, 0.22, 4, 3)', viewBox: '0 0 125 113', svg: '' }, - 'TASK_TYPE_VQA':{ transform: 'matrix(0.25, 0, 0, 0.25, 3, 2)', viewBox: '0 0 189 98',svg: '' }, - 'TASK_TYPE_RESULT_EVALUATION':{ transform: 'matrix(0.35, 0, 0, 0.35, 4, -3)', viewBox: '0 0 188.96001 98.400002', svg: 'image/svg+xml' } + TASK_TYPE_QUANTUM_COMPUTATION: { svg: "" }, + TASK_TYPE_CIRCUIT_LOADING: { svg: "" }, + TASK_TYPE_DATA_PREPARATION: { svg: "" }, + TASK_TYPE_ORACLE_EXPANSION: { svg: "" }, + TASK_TYPE_CIRCUIT_EXECUTION: { svg: "" }, + TASK_TYPE_ERROR_MITIGATION: { + transform: "matrix(0.2, 0, 0, 0.2, 5, 5)", + svg: '', + }, + SUBPROCESS_QUANTUM_HARDWARE_SELECTION: { svg: "" }, + TASK_TYPE_WARM_STARTING: { + transform: "matrix(0.22, 0, 0, 0.22, 3, 3)", + viewBox: "0 0 189 98", + svg: '', + }, + SUBPROCESS_TYPE_CIRCUIT_CUTTING: { + transform: "matrix(0.23, 0, 0, 0.23, 4, 3)", + viewBox: "0 0 144 144", + svg: '', + }, + TASK_TYPE_PARAMETER_OPTIMIZATION: { + transform: "matrix(0.22, 0, 0, 0.22, 4, 3)", + viewBox: "0 0 125 113", + svg: '', + }, + TASK_TYPE_VQA: { + transform: "matrix(0.25, 0, 0, 0.25, 3, 2)", + viewBox: "0 0 189 98", + svg: '', + }, + TASK_TYPE_RESULT_EVALUATION: { + transform: "matrix(0.35, 0, 0, 0.35, 4, -3)", + viewBox: "0 0 188.96001 98.400002", + svg: 'image/svg+xml', + }, }; return quantMESVGMap[svgId]; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js index 1da7fb14..4e41002e 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/index.js @@ -8,23 +8,32 @@ * * SPDX-License-Identifier: Apache-2.0 */ -import QuantMERenderer from './QuantMERenderer'; -import QuantMEReplaceMenuProvider from './QuantMEReplaceMenuProvider'; -import QuantMEFactory from './QuantMEFactory'; -import QuantMEPathMap from './QuantMEPathMap'; -import QuantMEPropertiesProvider from './properties-provider/QuantMEPropertiesProvider'; -import BpmnKeyboardBinding from './BpmnKeyboardBindings'; -import BpmnEditorActions from './BpmnEditorActions'; -import BpmnKeyboard from './BpmnKeyboard'; +import QuantMERenderer from "./QuantMERenderer"; +import QuantMEReplaceMenuProvider from "./QuantMEReplaceMenuProvider"; +import QuantMEFactory from "./QuantMEFactory"; +import QuantMEPathMap from "./QuantMEPathMap"; +import QuantMEPropertiesProvider from "./properties-provider/QuantMEPropertiesProvider"; +import BpmnKeyboardBinding from "./BpmnKeyboardBindings"; +import BpmnEditorActions from "./BpmnEditorActions"; +import BpmnKeyboard from "./BpmnKeyboard"; export default { - __init__: ['quantMERenderer', 'quantMEReplaceMenu', 'bpmnFactory', 'quantMEPathMap', 'propertiesProvider', 'keyboardBindings', 'editorActions', 'keyboard'], - quantMERenderer: ['type', QuantMERenderer], - quantMEReplaceMenu: ['type', QuantMEReplaceMenuProvider], - bpmnFactory: ['type', QuantMEFactory], - quantMEPathMap: ['type', QuantMEPathMap], - propertiesProvider: ['type', QuantMEPropertiesProvider], - keyboardBindings: ['type', BpmnKeyboardBinding], - editorActions: ['type', BpmnEditorActions], - keyboard: ['type', BpmnKeyboard] + __init__: [ + "quantMERenderer", + "quantMEReplaceMenu", + "bpmnFactory", + "quantMEPathMap", + "propertiesProvider", + "keyboardBindings", + "editorActions", + "keyboard", + ], + quantMERenderer: ["type", QuantMERenderer], + quantMEReplaceMenu: ["type", QuantMEReplaceMenuProvider], + bpmnFactory: ["type", QuantMEFactory], + quantMEPathMap: ["type", QuantMEPathMap], + propertiesProvider: ["type", QuantMEPropertiesProvider], + keyboardBindings: ["type", BpmnKeyboardBinding], + editorActions: ["type", BpmnEditorActions], + keyboard: ["type", BpmnKeyboard], }; diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertiesProvider.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertiesProvider.js index 12c08f73..138b95a8 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertiesProvider.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertiesProvider.js @@ -1,26 +1,26 @@ -import { is } from 'bpmn-js/lib/util/ModelUtil'; +import { is } from "bpmn-js/lib/util/ModelUtil"; import * as consts from "../../Constants"; import * as dataConsts from "../../../data-extension/Constants"; import { - DataPreparationTaskProperties, - HardwareSelectionSubprocessProperties, - OracleExpansionTaskProperties, - QuantumCircuitExecutionTaskProperties, - QuantumCircuitLoadingTaskProperties, - QuantumComputationTaskProperties, - ReadoutErrorMitigationTaskProperties, - CircuitCuttingSubprocessEntries, - ResultEvaluationTaskEntries, - ParameterOptimizationTaskEntries, - VariationalQuantumAlgorithmTaskEntries, - WarmStartingTaskEntries + DataPreparationTaskProperties, + HardwareSelectionSubprocessProperties, + OracleExpansionTaskProperties, + QuantumCircuitExecutionTaskProperties, + QuantumCircuitLoadingTaskProperties, + QuantumComputationTaskProperties, + ReadoutErrorMitigationTaskProperties, + CircuitCuttingSubprocessEntries, + ResultEvaluationTaskEntries, + ParameterOptimizationTaskEntries, + VariationalQuantumAlgorithmTaskEntries, + WarmStartingTaskEntries, } from "./QuantMETaskProperties"; import { ImplementationProps } from "./service-task/ImplementationProps"; import { Group } from "@bpmn-io/properties-panel"; -import { getWineryEndpoint } from '../../framework-config/config-manager'; -import * as configConsts from '../../../../editor/configurations/Constants'; -import { instance as dataObjectConfigs } from '../../configurations/DataObjectConfigurations'; -import ConfigurationsProperties from '../../../../editor/configurations/ConfigurationsProperties'; +import { getWineryEndpoint } from "../../framework-config/config-manager"; +import * as configConsts from "../../../../editor/configurations/Constants"; +import { instance as dataObjectConfigs } from "../../configurations/DataObjectConfigurations"; +import ConfigurationsProperties from "../../../../editor/configurations/ConfigurationsProperties"; const LOW_PRIORITY = 500; @@ -33,61 +33,81 @@ const LOW_PRIORITY = 500; * @param eventBus * @param bpmnFactory */ -export default function QuantMEPropertiesProvider(propertiesPanel, injector, translate, eventBus, bpmnFactory) { - - // subscribe to config updates to retrieve the currently defined Winery endpoint - const self = this; - let wineryEndpoint; - eventBus.on('config.updated', function (config) { - wineryEndpoint = config.wineryEndpoint; - }); - +export default function QuantMEPropertiesProvider( + propertiesPanel, + injector, + translate, + eventBus, + bpmnFactory +) { + // subscribe to config updates to retrieve the currently defined Winery endpoint + const self = this; + let wineryEndpoint; + eventBus.on("config.updated", function (config) { + wineryEndpoint = config.wineryEndpoint; + }); + + /** + * Return the groups provided for the given element. + * + * @param element + * + * @return {(Object[]) => (Object[])} groups middleware + */ + this.getGroups = function (element) { /** - * Return the groups provided for the given element. + * We return a middleware that modifies + * the existing groups. * - * @param element + * @param {Object[]} groups * - * @return {(Object[]) => (Object[])} groups middleware + * @return {Object[]} modified groups */ - this.getGroups = function (element) { - - /** - * We return a middleware that modifies - * the existing groups. - * - * @param {Object[]} groups - * - * @return {Object[]} modified groups - */ - return function (groups) { - - // add properties of QuantME tasks to panel - if (element.type && element.type.startsWith('quantme:')) { - groups.unshift(createQuantMEGroup(element, translate)); - } - - // update ServiceTasks with the deployment extension - if (element.type && element.type === 'bpmn:ServiceTask') { - groups[2] = ImplementationGroup(element, injector, getWineryEndpoint()); - } - - // add properties group for displaying the properties defined by the configurations if a configuration - // is applied to the current element - if (is(element, dataConsts.DATA_MAP_OBJECT)) { - - const selectedConfiguration = dataObjectConfigs().getQuantMEDataConfiguration(element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID)); - if (selectedConfiguration) { - groups.splice(1, 0, createQuantMEDataGroup(element, injector, translate, selectedConfiguration)); - } - } - return groups; - }; + return function (groups) { + // add properties of QuantME tasks to panel + if (element.type && element.type.startsWith("quantme:")) { + groups.unshift(createQuantMEGroup(element, translate)); + } + + // update ServiceTasks with the deployment extension + if (element.type && element.type === "bpmn:ServiceTask") { + groups[2] = ImplementationGroup(element, injector, getWineryEndpoint()); + } + + // add properties group for displaying the properties defined by the configurations if a configuration + // is applied to the current element + if (is(element, dataConsts.DATA_MAP_OBJECT)) { + const selectedConfiguration = + dataObjectConfigs().getQuantMEDataConfiguration( + element.businessObject.get(configConsts.SELECT_CONFIGURATIONS_ID) + ); + if (selectedConfiguration) { + groups.splice( + 1, + 0, + createQuantMEDataGroup( + element, + injector, + translate, + selectedConfiguration + ) + ); + } + } + return groups; }; + }; - propertiesPanel.registerProvider(LOW_PRIORITY, this); + propertiesPanel.registerProvider(LOW_PRIORITY, this); } -QuantMEPropertiesProvider.$inject = ['propertiesPanel', 'injector', 'translate', 'eventBus', 'bpmnFactory']; +QuantMEPropertiesProvider.$inject = [ + "propertiesPanel", + "injector", + "translate", + "eventBus", + "bpmnFactory", +]; /** * Create properties group to display custom QuantME properties in the properties panel. The entries of this group @@ -98,13 +118,12 @@ QuantMEPropertiesProvider.$inject = ['propertiesPanel', 'injector', 'translate', * @return {{entries: ([{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]|[{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]|[{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]|[{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]|[{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]|*|[{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *},{component: function({element: *}): *, isEdited: function(*): *, id: string, element: *}]), id: string, label}} */ function createQuantMEGroup(element, translate) { - - // add required properties to general tab - return { - id: 'quantmeServiceDetails', - label: translate('Details'), - entries: QuantMEProps(element) - }; + // add required properties to general tab + return { + id: "quantmeServiceDetails", + label: translate("Details"), + entries: QuantMEProps(element), + }; } /** @@ -117,22 +136,20 @@ function createQuantMEGroup(element, translate) { * @constructor */ function ImplementationGroup(element, injector, wineryEndpoint) { - const translate = injector.get('translate'); - - const group = { - label: translate('Implementation'), - id: 'CamundaPlatform__Implementation', - component: Group, - entries: [ - ...ImplementationProps({ element, wineryEndpoint, translate }) - ] - }; + const translate = injector.get("translate"); - if (group.entries.length) { - return group; - } + const group = { + label: translate("Implementation"), + id: "CamundaPlatform__Implementation", + component: Group, + entries: [...ImplementationProps({ element, wineryEndpoint, translate })], + }; - return null; + if (group.entries.length) { + return group; + } + + return null; } /** @@ -141,43 +158,40 @@ function ImplementationGroup(element, injector, wineryEndpoint) { * @param element the QuantME element */ function QuantMEProps(element) { - - switch (element.type) { - - case consts.QUANTUM_COMPUTATION_TASK: - return QuantumComputationTaskProperties(element); - - case consts.QUANTUM_CIRCUIT_LOADING_TASK: - return QuantumCircuitLoadingTaskProperties(element); - - case consts.DATA_PREPARATION_TASK: - return DataPreparationTaskProperties(element); - - case consts.ORACLE_EXPANSION_TASK: - return OracleExpansionTaskProperties(element); - - case consts.QUANTUM_CIRCUIT_EXECUTION_TASK: - return QuantumCircuitExecutionTaskProperties(element); - - case consts.READOUT_ERROR_MITIGATION_TASK: - return ReadoutErrorMitigationTaskProperties(element); - - case consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS: - return HardwareSelectionSubprocessProperties(element); - case consts.CIRCUIT_CUTTING_SUBPROCESS: - return CircuitCuttingSubprocessEntries(element); - case consts.RESULT_EVALUATION_TASK: - return ResultEvaluationTaskEntries(element); - case consts.PARAMETER_OPTIMIZATION_TASK: - return ParameterOptimizationTaskEntries(element); - case consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK: - return VariationalQuantumAlgorithmTaskEntries(element); - case consts.WARM_STARTING_TASK: - return WarmStartingTaskEntries(element); - default: - console.log('Unsupported QuantME element of type: ', element.type); - - } + switch (element.type) { + case consts.QUANTUM_COMPUTATION_TASK: + return QuantumComputationTaskProperties(element); + + case consts.QUANTUM_CIRCUIT_LOADING_TASK: + return QuantumCircuitLoadingTaskProperties(element); + + case consts.DATA_PREPARATION_TASK: + return DataPreparationTaskProperties(element); + + case consts.ORACLE_EXPANSION_TASK: + return OracleExpansionTaskProperties(element); + + case consts.QUANTUM_CIRCUIT_EXECUTION_TASK: + return QuantumCircuitExecutionTaskProperties(element); + + case consts.READOUT_ERROR_MITIGATION_TASK: + return ReadoutErrorMitigationTaskProperties(element); + + case consts.QUANTUM_HARDWARE_SELECTION_SUBPROCESS: + return HardwareSelectionSubprocessProperties(element); + case consts.CIRCUIT_CUTTING_SUBPROCESS: + return CircuitCuttingSubprocessEntries(element); + case consts.RESULT_EVALUATION_TASK: + return ResultEvaluationTaskEntries(element); + case consts.PARAMETER_OPTIMIZATION_TASK: + return ParameterOptimizationTaskEntries(element); + case consts.VARIATIONAL_QUANTUM_ALGORITHM_TASK: + return VariationalQuantumAlgorithmTaskEntries(element); + case consts.WARM_STARTING_TASK: + return WarmStartingTaskEntries(element); + default: + console.log("Unsupported QuantME element of type: ", element.type); + } } /** @@ -190,10 +204,14 @@ function QuantMEProps(element) { * @return {{entries: (*), id: string, label}} */ function createQuantMEDataGroup(element, injector, translate, configuration) { - - return { - id: 'QuantMEDataGroupProperties', - label: translate(configuration.groupLabel || 'Data Properties'), - entries: ConfigurationsProperties(element, injector, translate, configuration) - }; -} \ No newline at end of file + return { + id: "QuantMEDataGroupProperties", + label: translate(configuration.groupLabel || "Data Properties"), + entries: ConfigurationsProperties( + element, + injector, + translate, + configuration + ), + }; +} diff --git a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js index 7b329b50..3711abb4 100644 --- a/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js +++ b/components/bpmn-q/modeler-component/extensions/quantme/modeling/properties-provider/QuantMEPropertyEntries.js @@ -11,10 +11,10 @@ import React from "@bpmn-io/properties-panel/preact/compat"; -import { TextFieldEntry, SelectEntry } from '@bpmn-io/properties-panel'; -import * as consts from '../../Constants'; -import { useService } from 'bpmn-js-properties-panel'; -import { HiddenTextFieldEntry } from '../../../../editor/popup/HiddenTextFieldEntry'; +import { TextFieldEntry, SelectEntry } from "@bpmn-io/properties-panel"; +import * as consts from "../../Constants"; +import { useService } from "bpmn-js-properties-panel"; +import { HiddenTextFieldEntry } from "../../../../editor/popup/HiddenTextFieldEntry"; /** * All entries needed to display the different properties introduced through the QuantME task types. One entry represents one @@ -22,1300 +22,1449 @@ import { HiddenTextFieldEntry } from '../../../../editor/popup/HiddenTextFieldEn */ export function AlgorithmEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.algorithm; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - algorithm: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.algorithm; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + algorithm: newValue, + }); + }; + + return ( + + ); } export function ProviderEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.provider; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - provider: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.provider; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + provider: newValue, + }); + }; + + return ( + + ); } export function QuantumCircuitEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.quantumCircuit; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - quantumCircuit: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.quantumCircuit; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + quantumCircuit: newValue, + }); + }; + + return ( + + ); } export function UrlEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.url; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - url: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.url; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + url: newValue, + }); + }; + + return ( + + ); } export function EncodingSchemaEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.encodingSchema; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - encodingSchema: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.encodingSchema; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + encodingSchema: newValue, + }); + }; + + return ( + + ); } export function ProgrammingLanguageEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.programmingLanguage; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - programmingLanguage: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.programmingLanguage; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + programmingLanguage: newValue, + }); + }; + + return ( + + ); } export function OracleIdEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.oracleId; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - oracleId: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.oracleId; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + oracleId: newValue, + }); + }; + + return ( + + ); } export function OracleCircuitEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.oracleCircuit; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - oracleCircuit: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.oracleCircuit; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + oracleCircuit: newValue, + }); + }; + + return ( + + ); } export function OracleURLEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.oracleURL; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - oracleURL: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.oracleURL; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + oracleURL: newValue, + }); + }; + + return ( + + ); } export function QpuEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.qpu; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - qpu: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.qpu; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + qpu: newValue, + }); + }; + + return ( + + ); } export function ShotsEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.shots; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - shots: newValue - }); - }; - - const validate = function (values) { - return values && isNaN(values) ? translate('Shots attribute must contain an Integer!') : ''; - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.shots; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + shots: newValue, + }); + }; + + const validate = function (values) { + return values && isNaN(values) + ? translate("Shots attribute must contain an Integer!") + : ""; + }; + + return ( + + ); } export function MaxAgeEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.maxAge; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - maxAge: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.maxAge; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + maxAge: newValue, + }); + }; + + return ( + + ); } export function ProvidersEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.providers; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - providers: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.providers; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + providers: newValue, + }); + }; + + return ( + + ); } export function SimulatorsAllowedEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.simulatorsAllowed; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - simulatorsAllowed: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.simulatorsAllowed; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + simulatorsAllowed: newValue, + }); + }; + + return ( + + ); } export function SelectionStrategyEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.selectionStrategy; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - selectionStrategy: newValue - }); - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.selectionStrategy; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + selectionStrategy: newValue, + }); + }; + + return ( + + ); } export function CalibrationMethodEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.calibrationMethod; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - calibrationMethod: newValue - }); - }; - - const selectOptions = [ - { value: 'fullMatrix', label: 'Full Matrix' }, - { value: 'tpnm', label: 'TPNM' }, - { value: 'ctmp', label: 'CTMP' }, - { value: 'ddot', label: 'DDOT' }, - { value: 'conditionallyRigorous', label: 'Conditionally Rigorous' }, - { value: 'fuzzyCMeans', label: 'Fuzzy C-Means' }, - { value: 'cumulantCM', label: 'Cumulant CM' }, - { value: 'sclableTMatrix', label: 'Sclable T-Matrix' } - ]; - - const getOptions = function () { - return selectOptions; - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.calibrationMethod; + }; + + const setValue = function (newValue) { + return modeling.updateProperties(element, { + calibrationMethod: newValue, + }); + }; + + const selectOptions = [ + { value: "fullMatrix", label: "Full Matrix" }, + { value: "tpnm", label: "TPNM" }, + { value: "ctmp", label: "CTMP" }, + { value: "ddot", label: "DDOT" }, + { value: "conditionallyRigorous", label: "Conditionally Rigorous" }, + { value: "fuzzyCMeans", label: "Fuzzy C-Means" }, + { value: "cumulantCM", label: "Cumulant CM" }, + { value: "sclableTMatrix", label: "Sclable T-Matrix" }, + ]; + + const getOptions = function () { + return selectOptions; + }; + + return ( + + ); } export function MitigationMethodEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.mitigationMethod; - }; - - const setValue = function (newValue) { - - const bo = element.businessObject; - const isCM = (newValue === 'matrixInversion' || newValue === 'pertubativeREM' || newValue === 'geneticBasedREM' || newValue === 'mthree'); - - // remove CM value if non CM method is selected - if (!isCM) { - return modeling.updateProperties(element, { - mitigationMethod: newValue, - calibrationMethod: undefined - }); - } // set default CM value if CM method is selected and non CM method was selected previously - else if (isCM && !bo.calibrationMethod) { - return modeling.updateProperties(element, { - mitigationMethod: newValue, - calibrationMethod: 'fullMatrix' - }); - } - return modeling.updateProperties(element, { - mitigationMethod: newValue, - }); - }; - - const selectOptions = [ - { value: 'matrixInversion', label: 'Matrix Inversion' }, - { value: 'pertubativeREM', label: 'Pertubative REM' }, - { value: 'mthree', label: 'Mthree' }, - { value: 'geneticBasedREM', label: 'Genetic-Based REM' }, - { value: 'activeREM', label: 'Active REM' }, - { value: 'modelFreeREM', label: 'Model-Free REM' }, - { value: 'hybridREM', label: 'Hybrid REM' }, - { value: 'crosstalkREM', label: 'Crosstalk-Focused REM' }, - { value: 'sim', label: 'SIM' }, - { value: 'aim', label: 'AIM' }, - { value: 'bfa', label: 'BFA' }, - { value: 'truncatedNeumannSeries', label: 'Truncated Neumann Series' }, - { value: 'lsu', label: 'LSU' }, - { value: 'dnnREM', label: 'DNN-Based REM' } - ]; - - const getOptions = function () { - return selectOptions; - }; - - return ; + const modeling = useService("modeling"); + const translate = + useService("translate") || + function (str) { + return str; + }; + const debounce = useService("debounceInput"); + + const getValue = function () { + return element.businessObject.mitigationMethod; + }; + + const setValue = function (newValue) { + const bo = element.businessObject; + const isCM = + newValue === "matrixInversion" || + newValue === "pertubativeREM" || + newValue === "geneticBasedREM" || + newValue === "mthree"; + + // remove CM value if non CM method is selected + if (!isCM) { + return modeling.updateProperties(element, { + mitigationMethod: newValue, + calibrationMethod: undefined, + }); + } // set default CM value if CM method is selected and non CM method was selected previously + else if (isCM && !bo.calibrationMethod) { + return modeling.updateProperties(element, { + mitigationMethod: newValue, + calibrationMethod: "fullMatrix", + }); + } + return modeling.updateProperties(element, { + mitigationMethod: newValue, + }); + }; + + const selectOptions = [ + { value: "matrixInversion", label: "Matrix Inversion" }, + { value: "pertubativeREM", label: "Pertubative REM" }, + { value: "mthree", label: "Mthree" }, + { value: "geneticBasedREM", label: "Genetic-Based REM" }, + { value: "activeREM", label: "Active REM" }, + { value: "modelFreeREM", label: "Model-Free REM" }, + { value: "hybridREM", label: "Hybrid REM" }, + { value: "crosstalkREM", label: "Crosstalk-Focused REM" }, + { value: "sim", label: "SIM" }, + { value: "aim", label: "AIM" }, + { value: "bfa", label: "BFA" }, + { value: "truncatedNeumannSeries", label: "Truncated Neumann Series" }, + { value: "lsu", label: "LSU" }, + { value: "dnnREM", label: "DNN-Based REM" }, + ]; + + const getOptions = function () { + return selectOptions; + }; + + return ( + + ); } export function DNNHiddenLayersEntry({ element }) { - - const modeling = useService('modeling'); - const translate = useService('translate') || function (str) { - return str; - }; - const debounce = useService('debounceInput'); - - const getValue = function () { - return element.businessObject.dnnHiddenLayer; - }; - - const setValue = function (newValue) { - return modeling.updateProperties(element, { - dnnHiddenLayer: newValue - }); - }; - - const hidden = function () { - let mitigationMethod = element.businessObject.mitigationMethod; - console.log('MitigationMethode is now ' + mitigationMethod + ', so this entry has now hide = ' + !(mitigationMethod === 'dnnREM')); - return !(mitigationMethod === 'dnnREM'); - }; - - return