bpmn-js-token-simulation
Advanced tools
Comparing version 0.13.0 to 0.14.0
@@ -9,6 +9,15 @@ # Changelog | ||
## 0.14.0 | ||
* `FEAT`: add support for event sub-process ([#71](https://github.com/bpmn-io/bpmn-js-token-simulation/issues/71)) | ||
* `FEAT`: add support for event-based gateway ([#72](https://github.com/bpmn-io/bpmn-js-token-simulation/issues/72)) | ||
* `FEAT`: support boundary events on tasks | ||
* `FEAT`: rework context pads open/close handling | ||
* `FEAT`: batch simulator element changed events | ||
* `FIX`: correct scope filter integration with context pads | ||
## 0.13.0 | ||
* `FEAT`: allow to toggle explicit mode | ||
* `FEAT`: handle diagram import during simulation | ||
* `FEAT`: handle diagram import during simulation | ||
* `FIX`: do not re-add toggle mode UI on diagram re-import | ||
@@ -15,0 +24,0 @@ |
import { | ||
isAncestor | ||
is | ||
} from '../../util/ElementHelper'; | ||
@@ -13,2 +13,3 @@ | ||
import ExclusiveGatewayHandler from './handler/ExclusiveGatewayHandler'; | ||
import EventBasedGatewayHandler from './handler/EventBasedGatewayHandler'; | ||
import IntermediateCatchEventHandler from './handler/IntermediateCatchEventHandler'; | ||
@@ -35,5 +36,5 @@ import StartEventHandler from './handler/StartEventHandler'; | ||
this._overlayIds = {}; | ||
this._overlaysByElement = new Map(); | ||
this._handlers = {}; | ||
this._handlers = []; | ||
@@ -44,9 +45,13 @@ this.registerHandler('bpmn:ExclusiveGateway', ExclusiveGatewayHandler); | ||
this.registerHandler('bpmn:EventBasedGateway', EventBasedGatewayHandler); | ||
// TODO(nikku): restore or delete | ||
// this.registerHandler('bpmn:SubProcess', ProcessHandler); | ||
this.registerHandler('bpmn:SubProcess', BoundaryEventHandler); | ||
this.registerHandler('bpmn:BoundaryEvent', BoundaryEventHandler); | ||
this.registerHandler('bpmn:StartEvent', StartEventHandler); | ||
this.registerHandler('bpmn:Activity', BoundaryEventHandler); | ||
this.registerHandler('bpmn:Process', StartEventHandler); | ||
this.registerHandler('bpmn:SubProcess', StartEventHandler); | ||
this.registerHandler('bpmn:Participant', StartEventHandler); | ||
eventBus.on(TOGGLE_MODE_EVENT, LOW_PRIORITY, context => { | ||
@@ -85,9 +90,16 @@ const active = context.active; | ||
ContextPads.prototype.registerHandler = function(type, handlerCls) { | ||
var handler = this._injector.instantiate(handlerCls); | ||
const handler = this._injector.instantiate(handlerCls); | ||
if (!this._handlers[type]) { | ||
this._handlers[type] = []; | ||
} | ||
this._handlers.push({ handler, type }); | ||
}; | ||
this._handlers[type].push(handler); | ||
ContextPads.prototype.getHandlers = function(element) { | ||
return ( | ||
this._handlers.filter( | ||
({ type }) => is(element, type) | ||
).map( | ||
({ handler }) => handler | ||
) | ||
); | ||
}; | ||
@@ -108,9 +120,20 @@ | ||
ContextPads.prototype.registerContextPad = function(element, overlayId) { | ||
const overlaysByElement = this._overlaysByElement; | ||
if (!overlaysByElement.has(element)) { | ||
overlaysByElement.set(element, []); | ||
} | ||
const overlayIds = overlaysByElement.get(element); | ||
overlayIds.push(overlayId); | ||
}; | ||
ContextPads.prototype.openElementContextPads = function(element) { | ||
const handlers = this._handlers[element.type] || []; | ||
const contextPads = []; | ||
for (const handler of handlers) { | ||
for (const handler of this.getHandlers(element)) { | ||
const additionalPads = handler.createContextPads(element) || []; | ||
@@ -135,38 +158,45 @@ | ||
this._overlayIds[contextPad.element.id] = overlayId; | ||
this.registerContextPad(element, overlayId); | ||
} | ||
}; | ||
ContextPads.prototype.closeContextPads = function(parent) { | ||
if (!parent) { | ||
parent = this._canvas.getRootElement(); | ||
ContextPads.prototype.closeContextPads = function() { | ||
for (const element of this._overlaysByElement.keys()) { | ||
this.closeElementContextPads(element); | ||
} | ||
this._elementRegistry.forEach(element => { | ||
if (isAncestor(parent, element)) { | ||
this.closeElementContextPads(element); | ||
} | ||
}); | ||
}; | ||
ContextPads.prototype.closeElementContextPads = function(element) { | ||
(element.attachers || []).forEach(attachedElement => { | ||
this.closeElementContextPads(attachedElement); | ||
}); | ||
var overlayId = this._overlayIds[element.id]; | ||
const overlayIds = this._overlaysByElement.get(element) || []; | ||
if (!overlayId) { | ||
return; | ||
for (const overlayId of overlayIds) { | ||
this._overlays.remove(overlayId); | ||
} | ||
this._overlays.remove(overlayId); | ||
delete this._overlayIds[element.id]; | ||
overlayIds.length = 0; | ||
}; | ||
ContextPads.prototype.get = function(element) { | ||
return this._overlayIds[element.id || element]; | ||
}; | ||
ContextPads.$inject = [ | ||
'eventBus', | ||
'elementRegistry', | ||
'overlays', | ||
'injector', | ||
'canvas' | ||
]; | ||
ContextPads.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'injector', 'canvas' ]; | ||
// helpers /////////////// | ||
export function isAncestor(ancestor, descendant) { | ||
do { | ||
if (ancestor === descendant) { | ||
return true; | ||
} | ||
descendant = descendant.parent; | ||
} while (descendant); | ||
return false; | ||
} |
@@ -10,2 +10,3 @@ import { | ||
export default function BoundaryEventHandler(simulator, scopeFilter) { | ||
@@ -17,26 +18,31 @@ this._simulator = simulator; | ||
BoundaryEventHandler.prototype.createContextPads = function(element) { | ||
return element.attachers.map( | ||
attacher => this.createBoundaryContextPad(attacher) | ||
); | ||
}; | ||
if (is(element, 'bpmn:BoundaryEvent')) { | ||
return [ | ||
this.createBoundaryContextPad(element) | ||
]; | ||
} | ||
BoundaryEventHandler.prototype.createBoundaryContextPad = function(element) { | ||
if (is(element, 'bpmn:SubProcess')) { | ||
return element.attachers.map( | ||
attacher => this.createBoundaryContextPad(attacher) | ||
); | ||
} | ||
}; | ||
const scopeElement = element.host; | ||
BoundaryEventHandler.prototype.createBoundaryContextPad = function(boundaryElement) { | ||
if (is(scopeElement, 'bpmn:SubProcess')) { | ||
const scopeElement = boundaryElement.host; | ||
// need nested running scopes on sub-processes | ||
const nestedScope = this._findScope({ | ||
element: scopeElement | ||
}); | ||
const nestedScope = this._findScope({ | ||
element: scopeElement | ||
}); | ||
if (!nestedScope) { | ||
return; | ||
} | ||
} else { | ||
if (!nestedScope) { | ||
return; | ||
// need waiting scope on other elements | ||
const waitingScope = this._findScope({ | ||
waitsOnElement: scopeElement | ||
}); | ||
if (!waitingScope) { | ||
return; | ||
} | ||
} | ||
@@ -52,5 +58,5 @@ | ||
this._simulator.signal({ | ||
element: boundaryElement, | ||
element: element, | ||
scope: this._findScope({ | ||
element: boundaryElement.parent, | ||
element: element.parent, | ||
waitsOnElement: scopeElement | ||
@@ -62,3 +68,3 @@ }) | ||
return { | ||
element: boundaryElement, | ||
element, | ||
html | ||
@@ -65,0 +71,0 @@ }; |
@@ -34,3 +34,5 @@ import { | ||
element, | ||
scope: activeScope | ||
scope: this._findScope({ | ||
waitsOnElement: element | ||
}) | ||
}); | ||
@@ -37,0 +39,0 @@ }); |
@@ -7,8 +7,10 @@ import { | ||
import { | ||
is | ||
is, | ||
getBusinessObject | ||
} from '../../../util/ElementHelper'; | ||
export default function StartEventHandler(simulator) { | ||
export default function StartEventHandler(simulator, scopeFilter) { | ||
this._simulator = simulator; | ||
this._scopeFilter = scopeFilter; | ||
} | ||
@@ -18,4 +20,31 @@ | ||
if (is(element.parent, 'bpmn:SubProcess')) { | ||
return; | ||
const startEvents = findStartEvents(element); | ||
const pads = startEvents.map( | ||
startEvent => this.createStartEventContextPad(startEvent, element) | ||
); | ||
return pads; | ||
}; | ||
StartEventHandler.prototype.createStartEventContextPad = function(element, parent) { | ||
const parentElement = element.parent; | ||
let parentScope; | ||
if (is(parentElement, 'bpmn:SubProcess')) { | ||
if (!isEventSubProcess(parentElement)) { | ||
return; | ||
} | ||
parentScope = this._findScope({ | ||
element: parentElement.parent | ||
}); | ||
// no parent scope for event sub-process | ||
if (!parentScope) { | ||
return; | ||
} | ||
} | ||
@@ -28,17 +57,60 @@ | ||
domEvent.bind(html, 'click', () => { | ||
const parentScope = is(parentElement, 'bpmn:SubProcess') ? this._findScope({ | ||
element: parentElement.parent | ||
}) : null; | ||
this._simulator.signal({ | ||
element | ||
element, | ||
parentScope | ||
}); | ||
}); | ||
return [ | ||
{ | ||
element, | ||
html | ||
} | ||
]; | ||
return { | ||
element, | ||
html | ||
}; | ||
}; | ||
StartEventHandler.prototype._findScope = function(options) { | ||
return ( | ||
this._scopeFilter.findScope(options) || | ||
this._simulator.findScope(options) | ||
); | ||
}; | ||
StartEventHandler.$inject = [ | ||
'simulator' | ||
]; | ||
'simulator', | ||
'scopeFilter' | ||
]; | ||
// helpers ////////////// | ||
function findStartEvents(processElement) { | ||
const startEvents = processElement.businessObject.triggeredByEvent | ||
? [] | ||
: processElement.children.filter(isStartEvent); | ||
const eventSubProcesses = processElement.children.filter(isEventSubProcess); | ||
return eventSubProcesses.reduce((startEvents, subProcessElement) => { | ||
for (const subProcessChild of subProcessElement.children) { | ||
if (isStartEvent(subProcessChild)) { | ||
startEvents.push(subProcessChild); | ||
} | ||
} | ||
return startEvents; | ||
}, startEvents); | ||
} | ||
function isEventSubProcess(element) { | ||
return getBusinessObject(element).triggeredByEvent; | ||
} | ||
function isStartEvent(element) { | ||
return is(element, 'bpmn:StartEvent'); | ||
} |
@@ -16,4 +16,3 @@ import { | ||
'bpmn:InclusiveGateway', | ||
'bpmn:ComplexGateway', | ||
'bpmn:EventBasedGateway' | ||
'bpmn:ComplexGateway' | ||
]; | ||
@@ -20,0 +19,0 @@ |
import { | ||
getBusinessObject | ||
getBusinessObject, | ||
is | ||
} from './ModelUtil'; | ||
@@ -29,17 +30,19 @@ | ||
const childScope = this._simulator.findScope({ | ||
parent: scope, | ||
element: scopeElement | ||
}); | ||
if (is(scopeElement, 'bpmn:SubProcess')) { | ||
const childScope = this._simulator.findScope({ | ||
parent: scope, | ||
element: scopeElement | ||
}); | ||
if (!childScope) { | ||
throw new Error('cancel scope not found'); | ||
if (!childScope) { | ||
throw new Error('cancel scope not found'); | ||
} | ||
this._simulator.destroyScope(childScope, { | ||
reason: 'cancel', | ||
element, | ||
scope | ||
}); | ||
} | ||
this._simulator.destroyScope(childScope, { | ||
reason: 'cancel', | ||
element, | ||
scope | ||
}); | ||
// remove destroyed scope token | ||
@@ -46,0 +49,0 @@ scope.updateTokens(scopeElement, -1); |
import { | ||
isTerminate, | ||
isMessageFlow | ||
isMessageFlow, | ||
isEventSubProcess | ||
} from './ModelUtil'; | ||
@@ -50,3 +51,3 @@ | ||
if (parentScope) { | ||
if (!isEventSubProcess(scope.element) && parentScope) { | ||
this._simulator.exit({ | ||
@@ -53,0 +54,0 @@ element: scope.element, |
@@ -9,2 +9,3 @@ import StartEventBehavior from './StartEventBehavior'; | ||
import ParallelGatewayBehavior from './ParallelGatewayBehavior'; | ||
import EventBasedGatewayBehavior from './EventBasedGatewayBehavior'; | ||
@@ -27,2 +28,3 @@ import ActivityBehavior from './ActivityBehavior'; | ||
'parallelGatewayBehavior', | ||
'eventBasedGatewayBehavior', | ||
'activityBehavior', | ||
@@ -40,2 +42,3 @@ 'subProcessBehavior', | ||
parallelGatewayBehavior: [ 'type', ParallelGatewayBehavior ], | ||
eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ], | ||
activityBehavior: [ 'type', ActivityBehavior ], | ||
@@ -42,0 +45,0 @@ subProcessBehavior: [ 'type', SubProcessBehavior ], |
import { | ||
isLink | ||
isLink, | ||
is | ||
} from './ModelUtil'; | ||
@@ -18,5 +19,19 @@ | ||
IntermediateCatchEventBehavior.prototype.signal = function(context) { | ||
const { | ||
relatedElement, | ||
scope | ||
} = context; | ||
const triggerEventBased = relatedElement && is(relatedElement, 'bpmn:EventBasedGateway'); | ||
if (triggerEventBased) { | ||
scope.updateTokens(relatedElement, -1); | ||
} | ||
const signal = !triggerEventBased; | ||
this._simulator.exit({ | ||
...context, | ||
signal: true | ||
signal | ||
}); | ||
@@ -23,0 +38,0 @@ }; |
@@ -30,2 +30,6 @@ import { | ||
}); | ||
} | ||
export function isEventSubProcess(element) { | ||
return getBusinessObject(element).triggeredByEvent; | ||
} |
@@ -0,1 +1,7 @@ | ||
import { | ||
getBusinessObject, | ||
isEventSubProcess | ||
} from './ModelUtil'; | ||
export default function StartEventBehavior( | ||
@@ -12,2 +18,28 @@ simulator, | ||
StartEventBehavior.prototype.signal = function(context) { | ||
const { | ||
element, | ||
parentScope, | ||
scope | ||
} = context; | ||
const { | ||
parent: parentElement | ||
} = element; | ||
// trigger event sub-process | ||
if (isEventSubProcess(parentElement)) { | ||
if (!parentScope) { | ||
throw new Error('missing <parentScope>'); | ||
} | ||
if (getBusinessObject(element).isInterrupting) { | ||
this._simulator.destroyScope(parentScope, { | ||
reason: 'cancel', | ||
...context | ||
}, [ scope ]); | ||
} | ||
} | ||
this._simulator.exit(context); | ||
@@ -14,0 +46,0 @@ }; |
@@ -19,2 +19,15 @@ import Ids from 'ids'; | ||
const changedElements = new Set(); | ||
on('tick', function() { | ||
for (const element of changedElements) { | ||
emit('elementChanged', { | ||
element | ||
}); | ||
} | ||
changedElements.clear(); | ||
}); | ||
function queue(scope, task) { | ||
@@ -42,2 +55,4 @@ | ||
} | ||
emit('tick'); | ||
} | ||
@@ -70,3 +85,3 @@ | ||
getBehavior(element).signal({ | ||
element, | ||
...context, | ||
scope | ||
@@ -143,2 +158,4 @@ }); | ||
); | ||
elementChanged(element); | ||
}, | ||
@@ -166,2 +183,4 @@ getTokens() { | ||
elementChanged(element); | ||
return scope; | ||
@@ -206,3 +225,3 @@ } | ||
function destroyScope(scope, context=noneContext) { | ||
function destroyScope(scope, context=noneContext, excludeChildren=[]) { | ||
@@ -220,3 +239,3 @@ if (scope.destroyed) { | ||
for (const childScope of scope.children) { | ||
if (!childScope.destroyed) { | ||
if (!childScope.destroyed && !excludeChildren.includes(childScope)) { | ||
destroyScope(childScope, { | ||
@@ -258,5 +277,3 @@ ...context, | ||
function elementChanged(element) { | ||
emit('elementChanged', { | ||
element | ||
}); | ||
changedElements.add(element); | ||
} | ||
@@ -263,0 +280,0 @@ |
@@ -6,2 +6,6 @@ import { | ||
import { | ||
is as __is | ||
} from 'bpmn-js/lib/util/ModelUtil'; | ||
export function is(element, types) { | ||
@@ -16,11 +20,5 @@ if (element.type === 'label') { | ||
var isType = false; | ||
types.forEach(function(type) { | ||
if (type === element.type) { | ||
isType = true; | ||
} | ||
return types.some(function(type) { | ||
return __is(element, type); | ||
}); | ||
return isType; | ||
} | ||
@@ -42,16 +40,2 @@ | ||
return (element && element.businessObject) || element; | ||
} | ||
export function isAncestor(ancestor, descendant) { | ||
let childParent = descendant.parent; | ||
while (childParent) { | ||
if (childParent === ancestor) { | ||
return true; | ||
} | ||
childParent = childParent.parent; | ||
} | ||
return false; | ||
} |
{ | ||
"name": "bpmn-js-token-simulation", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"description": "bpmn-js token simulation extension", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1431275
87
3876