UNPKG

cmmn-js

Version:
449 lines (315 loc) 10.5 kB
'use strict'; var forEach = require('min-dash').forEach, filter = require('min-dash').filter; var replaceOptions = require('../replace/ReplaceOptions'); var isItemCapable = require('../modeling/util/PlanItemDefinitionUtil').isItemCapable; var isAny = require('../modeling/util/ModelingUtil').isAny; var ModelUtil = require('../../util/ModelUtil'), getBusinessObject = ModelUtil.getBusinessObject, getDefinition = ModelUtil.getDefinition, isCasePlanModel = ModelUtil.isCasePlanModel, isRequired = ModelUtil.isRequired, isRepeatable = ModelUtil.isRepeatable, isManualActivation = ModelUtil.isManualActivation, is = ModelUtil.is; var isCollapsed = require('../../util/DiUtil').isCollapsed; /** * Returns true, if an element is from a different type. * discretionary / non discretionary * * @param {djs.model.Base} element * @param {Object} entry * * @return {Boolean} */ function isDifferentType(element, entry) { var businessObject = getBusinessObject(element); return businessObject.$type !== entry.target.type; } /** * Returns true if the definition type is different. * e.g. process task / human task * * @param {djs.model.Base} element * @param {Object} entry * * @return {Boolean} */ function isDifferentDefinitionType(element, entry) { return getDefinition(element).$type !== entry.target.definitionType; } function canReplaceElementByTarget(element, target) { var type = target.type, definitionType = target.definitionType, parent = element.parent, definition = getDefinition(element); if (isPlanItem(element)) { if (type === 'cmmn:DiscretionaryItem' && !isStage(parent)) { return false; } } if (definitionType === 'cmmn:HumanTask') { if (isPlanFragment(element) && !target.isBlocking) { return false; } if (isTask(element) && definition.isBlocking !== target.isBlocking) { return false; } } return true; } /** * This module is an element agnostic replace menu provider for the popup menu. */ function ReplaceMenuProvider(popupMenu, cmmnReplace, cmmnFactory, modeling, rules) { this._popupMenu = popupMenu; this._cmmnReplace = cmmnReplace; this._cmmnFactory = cmmnFactory; this._modeling = modeling; this._rules = rules; this.register(); } ReplaceMenuProvider.$inject = [ 'popupMenu', 'cmmnReplace', 'cmmnFactory', 'modeling', 'rules' ]; /** * Register replace menu provider in the popup menu */ ReplaceMenuProvider.prototype.register = function() { this._popupMenu.registerProvider('cmmn-replace', this); }; /** * Get all entries from replaceOptions for the given element and apply filters * on them. Get for example only elements, which are different from the current one. * * @param {djs.model.Base} element * * @return {Array<Object>} a list of menu entry items */ ReplaceMenuProvider.prototype.getEntries = function(element) { var rules = this._rules; var entries, definition = getDefinition(element); if (!rules.allowed('shape.replace', { element: element })) { return []; } if (isItemCapable(element)) { if (isTask(element)) { entries = filter(replaceOptions.TASK, function(entry) { var target = entry.target; if (canReplaceElementByTarget(element, target)) { // ^ === XOR // returns true if and only of EITHER the type OR the definition type is different return isDifferentType(element, entry) ^ isDifferentDefinitionType(element, entry); } }); return this._createEntries(element, entries); } if (isPlanFragment(element)) { if (!isCollapsed(element)) { entries = filter(replaceOptions.PLAN_FRAGMENT_EXPANDED, function(entry) { var target = entry.target; if (canReplaceElementByTarget(element, target)) { // returns true if the type or the definition type is different (or both) return isDifferentType(element, entry) || isDifferentDefinitionType(element, entry); } }); return this._createEntries(element, entries); } if (isCollapsed(element)) { entries = filter(replaceOptions.TASK, function(entry) { var target = entry.target, definitionType = target.definitionType; if (!canReplaceElementByTarget(element, target)) { return false; } if (definitionType === definition.$type && !target.isCollapsed) { return true; } // ^ === XOR // returns true if and only of EITHER the type OR the definition type is different return isDifferentType(element, entry) ^ isDifferentDefinitionType(element, entry); }); return this._createEntries(element, entries); } } if (isEventListener(element)) { entries = filter(replaceOptions.EVENT_LISTENER, function(entry) { return isDifferentDefinitionType(element, entry); }); return this._createEntries(element, entries); } } if (isCriterion(element)) { entries = filter(replaceOptions.CRITERION, function(entry) { return isDifferentType(element, entry); }); return this._createEntries(element, entries); } return []; }; ReplaceMenuProvider.prototype.getHeaderEntries = function(element) { var headerEntries = []; if (isTask(element)) { headerEntries = headerEntries.concat(this._getBlockingEntry(element)); } if (isStage(element)) { headerEntries = headerEntries.concat(this._getAutoCompleteEntry(element)); } if (ensureSupportRules(element)) { headerEntries = headerEntries.concat(this._getRuleEntries(element)); } return headerEntries; }; /** * Creates an array of menu entry objects for a given element and filters the replaceOptions * according to a filter function. * * @param {djs.model.Base} element * @param {Object} replaceOptions * * @return {Array<Object>} a list of menu items */ ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) { var menuEntries = []; var self = this; forEach(replaceOptions, function(definition) { var entry = self._createMenuEntry(definition, element); menuEntries.push(entry); }); return menuEntries; }; /** * Creates and returns a single menu entry item. * * @param {Object} definition a single replace options definition object * @param {djs.model.Base} element * @param {Function} [action] an action callback function which gets called when * the menu entry is being triggered. * * @return {Object} menu entry item */ ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) { var replaceElement = this._cmmnReplace.replaceElement; var replaceAction = function() { return replaceElement(element, definition.target); }; action = action || replaceAction; var menuEntry = { label: definition.label, className: definition.className, id: definition.actionName, action: action }; return menuEntry; }; ReplaceMenuProvider.prototype._getBlockingEntry = function(element) { var self = this; var definition = getDefinition(element); function toggleBlockingEntry(event, entry) { var blocking = !entry.active; self._modeling.updateControls(element, { isBlocking: blocking }); } var isBlocking = definition.isBlocking; var entries = [ { id: 'toggle-is-blocking', className: 'cmmn-icon-blocking', title: 'Blocking', active: isBlocking, action: toggleBlockingEntry } ]; return entries; }; ReplaceMenuProvider.prototype._getAutoCompleteEntry = function(element) { var self = this; var definition = getDefinition(element); function toggleAutoCompleteEntry(event, entry) { var autoComplete = !entry.active; self._modeling.updateControls(element, { autoComplete: autoComplete }); } var isAutoComplete = definition.autoComplete; var entries = [ { id: 'toggle-auto-complete', className: 'cmmn-icon-auto-complete-marker', title: 'Auto Complete', active: isAutoComplete, action: toggleAutoCompleteEntry } ]; return entries; }; ReplaceMenuProvider.prototype._getRuleEntries = function(element) { var self = this; function toggleControlEntry(control, type) { return function(event, entry) { var value = {}; if (entry.active) { value[control] = undefined; } else { value[control] = self._cmmnFactory.create(type); } self._modeling.updateControls(element, value); }; } var repeatable = isRepeatable(element), required = isRequired(element), manualActivation = isManualActivation(element); var entries = [ { id: 'toggle-required-rule', className: 'cmmn-icon-required-marker', title: 'Required Rule', active: required, action: toggleControlEntry('requiredRule', 'cmmn:RequiredRule') } ]; if (!isMilestone(element)) { entries.push({ id: 'toggle-manual-activation-rule', className: 'cmmn-icon-manual-activation-marker', title: 'Manual Activation Rule', active: manualActivation, action: toggleControlEntry('manualActivationRule', 'cmmn:ManualActivationRule') }); } entries.push({ id: 'toggle-repetition-rule', className: 'cmmn-icon-repetition-marker', title: 'Repetition Rule', active: repeatable, action: toggleControlEntry('repetitionRule', 'cmmn:RepetitionRule') }); return entries; }; module.exports = ReplaceMenuProvider; function ensureSupportRules(element) { var definition = getDefinition(element); return !isCasePlanModel(element) && isAny(definition, [ 'cmmn:Stage', 'cmmn:Milestone', 'cmmn:Task' ]); } function isPlanFragment(element) { return is(getDefinition(element), 'cmmn:PlanFragment'); } function isStage(element) { return is(getDefinition(element), 'cmmn:Stage'); } function isEventListener(element) { return is(getDefinition(element), 'cmmn:EventListener'); } function isMilestone(element) { return is(getDefinition(element), 'cmmn:Milestone'); } function isTask(element) { return is(getDefinition(element), 'cmmn:Task'); } function isCriterion(element) { return is(element, 'cmmn:Criterion'); } function isPlanItem(element) { return is(element, 'cmmn:PlanItem'); }