com.smartclient.debug.public.sc.client.application.Workflow.js Maven / Gradle / Ivy
Show all versions of smartgwt Show documentation
/*
* Isomorphic SmartClient
* Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
* Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
* "SmartClient" is a trademark of Isomorphic Software, Inc.
*
* [email protected]
*
* http://smartclient.com/license
*/
// --------------------------------------------------------------------------------------------
//> @class ProcessElement
// A ProcessElement is an abstract superclass for elements involved in a +link{Process}, such
// as a +link{Task} or +link{XORGateway}.
// @visibility workflow
//<
isc.defineClass("ProcessElement");
isc.ProcessElement.addProperties({
//> @attr processElement.ID (String : null : IR)
// Optional ID for this process element, allowing it to be referred to from
// +link{Gateway}s, or as the +link{process.startElement}. See +link{ProcessSequence} and
// +link{Process} to understand when this is required or can be omitted.
//
// Unlike +link{Canvas.ID} a processElement
's is a not a globally unique
// variable, it need only by unique within it's process.
//
// When assigned an ID, a processElement
can be retrieve via
// +link{process.getElement()}.
// @visibility workflow
//<
//> @attr processElement.nextElement (String : null : IR)
// Next +link{process.sequences,sequence} or +link{process.elements,element} to execute
// after this one completes. Sequences are checked first. nextElement
does
// not need to be specified on most elements if you use
// +link{Process.sequences,sequences}.
// @visibility workflow
//<
});
// --------------------------------------------------------------------------------------------
//> @class ProcessSequence
// An Array of +link{ProcessElement}s involved in a +link{Process}. A
// ProcessSequence
is used to reduce the number of explicit
// +link{ProcessElement.ID}s that need to be assigned, by creating an implicit next element -
// the next in the sequence.
//
// A sequence cannot be executed outside of a Process and has no state.
// @visibility workflow
//<
isc.defineClass("ProcessSequence", "ProcessElement");
isc.ProcessSequence.addProperties({
//> @attr processSequence.elements (Array of ProcessElement : null : IR)
// The +link{ProcessElement}s in this sequence.
// @visibility workflow
//<
});
// --------------------------------------------------------------------------------------------
//> @class Task
// A Task is an abstract superclass for +link{Process} and for all Task types that can be
// involved in a Process, such as a +link{ServiceTask}.
//
// @visibility workflow
//<
isc.defineClass("Task", "ProcessElement");
isc.Task.addProperties({
//> @attr task.inputField (String : null : IR)
// Field in the +link{Process.state,process state} which is provided as input data to this
// task.
// See +link{group:taskIO}.
// @visibility workflow
//<
//> @attr task.inputFieldList (Array of String : null : IR)
// List of multiple fields from the +link{Process.state,process state} which are provided
// as input data to this task. See +link{group:taskIO}.
//
// If +link{inputField} is also specified, it will be implicitly added to the
// inputFieldList
if it is not already present.
// @visibility workflow
//<
//> @attr task.outputField (String : null : IR)
// Field in the +link{Process.state,process state} which this task writes outputs to. See
// +link{group:taskIO}.
// @visibility workflow
//<
//> @attr task.outputFieldList (Array of String : null : IR)
// List of multiple fields from the +link{Process.state,process state} which this task will
// write to. See +link{group:taskIO}.
//
// If +link{outputField} is also specified, it will be implicitly added to the
// outputFieldList
if it is not already present.
// @visibility workflow
//<
//> @groupDef taskIO
// Each task has inputs, which can be thought of as copied from the
// +link{process.state,Process state} when the task is started, and outputs, which can be
// thought of as atomically applied to the Process state when a task is completed.
//
// Task can use +link{task.inputField} to specify the field from the Process state that
// should be used as inputs, and +link{task.outputField} to specify the field from the
// Process state that the task should output to.
//
// More complex tasks can take multiple fields from the process state via
// +link{task.inputFieldList} and write to multiple fields of the process state via
// +link{task.outputFieldList}. In this case, the task is said to have an "input Record"
// and/or "output Record", which can be thought of as a copy of the process state Record
// with only the fields listed in the inputFieldList
are copied.
//
// When both inputField
and inputFieldList
are specified, the
// inputField is considered the "primary" input field and will be used automatically by
// various Task subclasses.
// @title Task Input / Output
// @visibility workflow
//<
//> @groupDef taskInputExpression
// In some tasks, the input to the task needs to be passed to a service being called by the
// task, to a user-visible form, or other consumers of the input data.
// A TaskInputExpression can be used to do this declaratively.
//
// A TaskInputExpression is a String prefixed with either "$input" or "$inputRecord",
// followed by an optional dot-separated hierarchical path, which can specify either an
// atomic data value (String, Number) or Record from the input data. For example, if the
// +link{process.state} represented in JSON were:
//
// {
// orderId:5,
// orderItems: [
// {name:"Pencils", quantity:3, itemId:2344}
// ],
// orderUser: { name:"Henry Winkle", address:"...", ... }
// }
//
// .. and a task specified an inputField
of "orderId" and an inputFieldList of
// "orderItems","orderUser", then:
//
// - $input is the value 5
//
- $inputRecord.orderUser.name is "Henry Winkle"
//
- $inputRecord.orderItems[0] is the first orderItems Record ({name:"Pencils", ... })
//
// @title Task Input Expressions
// @visibility workflow
//<
});
// --------------------------------------------------------------------------------------------
//> @class Process
// A instance of Process represents a stateful process executing a series of Tasks,
// which may be:
//
// - user interactions
//
- calls to DataSources (hence: any database or web service)
//
- arbitrary code
//
- other Processes
//
// A Process is stateful in the sense that it maintains +link{process.state,state}
// across the different tasks that are executed. This allows you to maintain context as you
// walk a user through a multi-step business process in your application, which may involve
// multiple operations on multiple entities. Each Task that executes can use the Process state
// as inputs, and can output a result which is stored in the Process state - see
// +link{group:taskIO}.
//
// A Process can have multiple branches, choosing the next Task to execute based on
// +link{Criteria} - see +link{XORGateway} and +link{DecisionGateway}.
//
// Because a Process may return to a previous Task in various situations, the data model of a
// Process is strictly speaking a graph (a set of nodes connected by arbitary
// interlinks). However, most processes have sequences of several tasks in a row, and the
// definition format allows these to be represented as simple Arrays called "sequences",
// specified via +link{process.sequences}. This reduces the need to manually specify IDs and
// interlinks for Tasks that simply proceed to the next task in a sequence.
//
// @visibility workflow
//<
isc.defineClass("Process", "Task");
isc.Process.addProperties({
init : function () {
var res = this.Super("init", arguments);
if (this.autoStart) {
this.start();
}
return res;
},
//> @attr process.sequences (Array of ProcessSequence : null : IR)
// Sequences of ProcessElements. By defining a sequences of elements you can make the
// +link{processElement.nextElement} implicit.
//
// You do not have to explicitly create a +link{ProcessSequence},
// you can instead use the shorthand:
//
// isc.Process.create({
// startElement:"firstSequence",
// sequences: [
// { ID:"something", elements: [ ... ] },
// { ID:"somethingElse", elements: [ ... ] },
// ...
// ]
// ...
// });
//
// .. this is equivalent to ..
//
// isc.Process.create({
// startElement:"firstSequence",
// sequences: [
// isc.ProcessSequence.create({
// ID:"something",
// elements: [ ... ]
// }),
// isc.ProcessSequence.create({
// ID:"somethingElement",
// elements: [ ... ]
// }),
// ...
// ]
// ...
// });
//
//
// @visibility workflow
//<
//> @attr process.elements (Array of ProcessElement : null : IR)
// Elements involved in this Process. You can also group elements into +link{sequences}
// to reduce the need to explicitly define IDs for elements and interlink them.
// @visibility workflow
//<
//> @attr process.startElement (String : null : IR)
// The ID of either a +link{sequences,sequence} or an +link{elements,element} which should
// be the starting point of the process. If not specified, the first sequence is chosen,
// or if there are no sequences, the first element.
// - log a warning and do nothing if there are neither sequences or elements
//
// - an example of how a Process would be defined
// isc.Process.create({
// startElement:"firstSequence",
// sequences: [
// {
// id:"firstSequence",
// elements : [
// isc.ServiceTask.create({ .. }),
// isc.DecisionGateway.create({ .. })
// ]
// },
// {
// id:"errorFlow",
// elements : [ ... ]
//
// }
// ],
// elements: [
// // standalone process elements not part of sequences
// isc.ServiceTask.create({ .. })
// ],
// state : {
// someField:"someValue"
// }
// })
// @visibility workflow
//<
//> @method process.getElement()
// Retrieve a +link{ProcessElement} by it's ID
// @param ID (String) id of the process element
// @return (ProcessElement) the indicated process element, or null if no such element
// exists
// @visibility workflow
//<
getElement : function (ID) {
return this.searchElement(this, ID);
},
searchElement : function (sequence, ID) {
if (sequence.sequences != null) {
for (var i = 0; i < sequence.sequences.length; i++) {
var s = sequence.sequences[i];
if (s.ID == ID) {
return s;
} else if (s.sequences != null || s.elements != null) {
var res = this.searchElement(s, ID);
if (res != null) {
return res;
}
}
}
}
if (sequence.elements != null) {
for (var i = 0; i < sequence.elements.length; i++) {
var e = sequence.elements[i];
if (e.ID == ID) {
return e;
} else if (e.sequences != null || e.elements != null) {
var res = this.searchElement(e, ID);
if (res != null) {
return res;
}
}
}
}
},
//> @attr process.state (Record : null : IRW)
// Current state of a process. As with Records in general, any field of a Record may
// contain a nested Record or Array of Records, so the process state is essentially a
// hierarchical data structure.
//
// @visibility workflow
//<
//> @attr process.autoStart (boolean : false : IR)
// Cause the process to automatically call +link{start()} as soon as it is created.
//<
autoStart: false,
//> @method process.start()
// Starts this task by executing the +link{startElement}.
//<
start : function () {
// Process can be async, so we continue it's execution no matter where we've stopped
if (this.executionStack == null) {
this.executionStack = [];
}
while (this.next() != null) {
var currentTask = this.getFirstTask();
if (currentTask == null) {
// empty sequence
continue;
}
if (currentTask.getClassName() == "ScriptTask") {
if (!this.executeScriptTaskElement(currentTask)){
return;
}
} else if (currentTask.getClassName() == "ServiceTask") {
this.executeServiceTaskElement(currentTask)
return;
}
}
this.finished();
},
//> @method process.finished()
// StringMethod called when a process completes, meaning the process executes a
// ProcessElement with no next element.
// @param state (Record) the final process state
//<
finished : function () {
},
// If user didn't set ID or don't use nextElement property we will take next element
// or sequence based on their order
next : function () {
var currEl = this.executionStack.last();
if (currEl == null) {
// start processing
if (this.startElement != null) {
return this.gotoElement(this, this.startElement);
} else if (this.sequences != null && this.sequences.length > 0) {
this.executionStack.add({el:this, sIndex: 0});
return this.sequences[0];
} else if (this.elements != null && this.elements.length > 0) {
this.executionStack.add({el:this, eIndex: 0});
return this.elements[0];
} else {
isc.logWarn("There are neither sequences or elements. Nothing to execute.");
}
} else {
var el = null;
if (currEl.sIndex != null) {
el = currEl.el.sequences[currEl.sIndex];
} else if (currEl.eIndex != null) {
el = currEl.el.elements[currEl.eIndex];
}
if (el.nextElement != null) {
this.executionStack = [];
var res = this.gotoElement(this, el.nextElement);
return res;
} else {
return this.findNextElement();
}
}
},
gotoElement : function (sequence, ID) {
var elData = {el: sequence};
this.executionStack.add(elData);
if (sequence.sequences != null) {
for (var i = 0; i < sequence.sequences.length; i++) {
var s = sequence.sequences[i];
elData.sIndex = i;
if (s.ID == ID) {
return s;
} else if (s.sequences != null || s.elements != null) {
var res = this.gotoElement(s, ID);
if (res != null) {
return res;
}
}
}
}
delete elData.sIndex;
if (sequence.elements != null) {
for (var i = 0; i < sequence.elements.length; i++) {
var e = sequence.elements[i];
elData.eIndex = i;
if (e.ID == ID) {
return e;
} else if (e.sequences != null || e.elements != null) {
var res = this.gotoElement(e, ID);
if (res != null) {
return res;
}
}
}
}
this.executionStack.removeAt(this.executionStack.length - 1);
},
findNextElement : function () {
var elData = this.executionStack.last();
if (elData.sIndex != null) {
if (elData.sIndex == elData.el.sequences.length - 1) {
this.executionStack.removeAt(this.executionStack.length - 1);
if (elData.el == this) {
return;
} else {
return this.findNextElement();
}
} else {
elData.sIndex++;
return elData.el.sequences[elData.sIndex];
}
}
if (elData.eIndex != null) {
if (elData.eIndex == elData.el.elements.length - 1) {
this.executionStack.removeAt(this.executionStack.length - 1);
if (elData.el == this) {
return;
} else {
return this.findNextElement();
}
} else {
elData.eIndex++;
return elData.el.elements[elData.eIndex];
}
}
},
// recursively search for first non-sequence in element
getFirstTask : function () {
var lastElData = this.executionStack.last();
var el = null;
if (lastElData.sIndex != null) {
el = lastElData.el.sequences[lastElData.sIndex];
} else if (lastElData.eIndex != null) {
el = lastElData.el.elements[lastElData.eIndex];
}
if (el.sequences == null && el.elements == null) {
return el;
}
var elData = {el: el};
this.executionStack.add(elData);
if (el.sequences != null) {
for (var i = 0; i < el.sequences.length; i++) {
elData.sIndex = i
var res = this.getFirstTask(el.sequences[i]);
if (res != null) {
return res;
}
}
}
if (el.elements != null) {
for (var i = 0; i < el.elements.length; i++) {
elData.eIndex = i
var res = this.getFirstTask(el.elements[i]);
if (res != null) {
return res;
}
}
}
this.executionStack.removeAt(this.executionStack.length - 1);
},
executeScriptTaskElement : function (element) {
// process input
var inputData;
var inputRecord;
if (element.inputFieldList != null) {
inputRecord = {};
for (var i = 0; i < element.inputFieldList.length; i++) {
inputRecord[element.inputFieldList[i]] = this.state[element.inputFieldList[i]];
};
}
if (element.inputField != null) {
inputData = this.state[element.inputField];
if (inputRecord != null) {
inputRecord[element.inputField] = inputData;
}
}
element.inputData = inputData;
element.inputRecord = inputRecord;
element.process = this;
try {
var output = element.execute(inputData, inputRecord);
} catch (e) {
isc.logWarn("Error while executing ScriptTask: "+e.toString());
}
if (element.isAsync) {
return false;
}
if (typeof output == 'undefined') {
return true;
}
this.processTaskOutput(element, output);
return true;
},
processTaskOutput : function (task, output) {
// process output
if (task.outputFieldList != null) {
for (var i = 0; i < task.outputFieldList.length; i++) {
if (typeof output[task.outputFieldList[i]] != 'undefined') {
this.state[task.outputFieldList[i]]
= output[task.outputFieldList[i]];
}
};
}
if (task.outputField != null) {
if (task.outputFieldList == null) {
if (typeof output != 'undefined') {
this.state[task.outputField] = output;
}
} else {
if (typeof output[task.outputField] != 'undefined') {
this.state[task.outputField] = output[task.outputField];
}
}
}
},
finishTask : function (task, outputRecord, outputData) {
// ProcessTask use the method to continue process
if (outputRecord == null) {
this.processTaskOutput(task, outputData);
} else {
if (outputData != null) {
outputRecord[task.outputField] = outputData;
}
this.processTaskOutput(task, outputRecord);
}
if (task.isAsync) {
this.start();
}
},
executeServiceTaskElement : function (task) {
var ds = task.dataSource;
if (ds.getClassName == null || ds.getClassName() != "DataSource") {
ds = isc.DataSource.get(ds);
}
var inputData = {};
if (task.inputFieldList != null) {
for (var i = 0; i < task.inputFieldList.length; i++) {
inputData[task.inputFieldList[i]] = this.state[task.inputFieldList[i]];
};
}
if (task.inputField != null) {
inputData[task.inputField] = this.state[task.inputField];
}
var data = null;
if (task.operationType == "fetch") {
if (task.criteria != null) {
data = task.criteria;
this.processCriteriaExpressions(data, task, inputData);
}
if (task.fixedCriteria != null) {
if (data == null) {
data = task.fixedCriteria
} else {
data = isc.DataSource.combineCriteria(data, task.fixedCriteria);
}
}
}
if (data == null) {
data = inputData;
}
var process = this;
ds.performDSOperation(task.operationType, data, function(dsResponse) {
if (dsResponse.data.length == 1) {
if (task.outputFieldList != null) {
for (var i = 0; i < task.outputFieldList.length; i++) {
if (typeof dsResponse.data[0][task.outputFieldList[i]] != 'undefined') {
process.state[task.outputFieldList[i]]
= dsResponse.data[0][task.outputFieldList[i]];
}
};
}
if (task.outputField != null) {
process.state[task.outputField] = dsResponse.data[0][task.outputField];
}
}
process.start();
});
},
processCriteriaExpressions : function (criteria, task, inputData) {
for (var name in criteria) {
if (name == "criteria") {
this.processCriteriaExpressions(criteria.criteria);
} else if (criteria[name].startsWith("$input")) {
var script = "state." + criteria[name].replace("$input", task.inputField);
criteria[name] = isc.Class.evaluate(script, {state: inputData});
} else if (criteria[name].startsWith("$inputRecord")) {
var script = criteria[name].replace("$inputRecord", "state");
criteria[name] = isc.Class.evaluate(script, {state: inputData});
}
}
}
});
// --------------------------------------------------------------------------------------------
//> @class ServiceTask
// A ServiceTask is an element of a +link{Process} which calls a DataSource operation,
// optionally using part of the +link{Process.state,process state} as inputs or storing outputs
// in the process state.
//
// By default a ServiceTask takes the data indicated by +link{task.inputField} and uses it as
// +link{dsRequest.data}. This means the input data becomes +link{Critera} for a "fetch"
// operation, new record values for an "add" operation, etc.
//
// Alternatively, you can set +link{serviceTask.criteria} for a "fetch" operation, or
// +link{serviceTask.values} for other operationTypes. In both cases, you have the ability to
// use simple expressions like $input.fieldName to take portions of the input data and
// use it as part of the criteria or values.
//
// As a special case, if the inputField
is an atomic value (just a String or
// Number rather than a Record) and operationType is "fetch", it will be assumed to be value
// for the primary key field of the target DataSource if +link{serviceTask.criteria,criteria}
// is not explicitly specified
// @visibility workflow
//<
isc.defineClass("ServiceTask", "Task");
isc.ServiceTask.addProperties({
//> @attr serviceTask.dataSource (DataSource or identifier : null : IR)
// DataSource ID or DataSource instance to be used.
// @visibility workflow
//<
//> @attr serviceTask.operationType (DSOperationType : "fetch" : IR)
// Type of operation to invoke
// @visibility workflow
//<
operationType: "fetch"
//> @attr serviceTask.criteria (Criteria : null : IR)
// Criteria (including AdvancedCriteria) to use for a "fetch" operation.
//
// Data values in this criteria prefixed with "$" will be treated as dynamic expressions
// which can access the inputs to this task as $input - see
// +link{group:taskInputExpression}. Specifically, this means that for simple criteria,
// any property value that is a String and is prefixed with "$" will be assumed to be an
// expression, and for AdvancedCriteria, the same treatment will be applied to
// +link{criterion.value}.
//
// If any data value should not be treated as dynamic (for example, a "$" should be taken
// as literal), you can place it in +link{fixedCriteria} instead.
//
// Ignored for any operationType other than "fetch". Update or delete operations should
// place the primary key to update in +link{values}.
// @group taskIO
// @visibility workflow
//<
//> @attr serviceTask.values (Record : null : IR)
// Values to be submitted for "update", "add" and "remove" operations.
//
// Similar to +link{criteria}, data values prefixed with "$" will be treated as a
// +link{group:taskInputExpression}. Use +link{fixedValues} for any values that start with
// "$" but should be treated as a literal.
// @visibility workflow
//<
//> @attr serviceTask.fixedCriteria (Criteria : null : IR)
// Criteria to be submitted as part of the DSRequest, regardless of inputs to the task.
// Will be combined with the data from the +link{task.inputField} or with
// +link{serviceTask.criteria} if specified, via +link{DataSource.combineCriteria()}.
// @visibility workflow
//<
//> @attr serviceTask.fixedValues (Record : null : IR)
// Values to be submitted as part of the DSRequest, regardless of inputs to the task. Will
// be combined with the data from the +link{task.inputField} or with
// +link{serviceTask.values} if specified, via simple copying of fields, with
// fixedValues
overwriting values provided by the inputField
, but
// explicitly specified +link{serviceTask.values} overriding fixedValues
.
// @visibility workflow
//<
});
// --------------------------------------------------------------------------------------------
//> @class ScriptTask
// Task that executes arbitrary code, either synchronous or asynchronous. Override the
// +link{execute()} method to provide custom logic.
// @visibility workflow
//<
isc.defineClass("ScriptTask", "Task");
isc.ScriptTask.addProperties({
//> @method scriptTask.getInputData()
// Get the inputs to this task as specified by +link{task.inputField}.
//
// For a task with a +link{task.inputFieldList,inputFieldList}, use +link{getInputRecord}
// to get access to other inputs.
// @return (Object) input data
// @group taskIO
// @visibility workflow
//<
getInputData : function () {
return this.inputData;
},
//> @method scriptTask.setOutputData()
// Set the task output as specified by +link{task.outputField}.
//
// NOTE: for an +link{script.isAsync,asychronous task}, calling
// setOutputData()
indicates the task is complete. For a task with
// +link{task.outputFieldList,multiple outputs}, call +link{setOutputRecord()} instead.
// @param (Object) task output
// @group taskIO
// @visibility workflow
//<
setOutputData : function (taskOutput) {
this.process.finishTask(this, null, taskOutput);
},
//> @method scriptTask.getInputRecord()
// Get all inputs to the task as specified by the
// +link{task.inputFieldList,inputFieldList}, as a Record.
// @return (Record) input data
// @group taskIO
// @visibility workflow
//<
getInputRecord : function () {
return this.inputRecord;
},
//> @method scriptTask.setOutputRecord()
// Set all outputs of the task as specified by the
// +link{task.outputFieldList,outputFieldList}, by providing a Record.
// @param (Record) task output
// @group taskIO
// @visibility workflow
//<
setOutputRecord : function (outputRecord) {
this.process.finishTask(this, outputRecord);
},
//> @attr scriptTask.isAsync (boolean : false : IR)
// Whether the script task is asynchronous. A synchronous task is expected to return data
// directly from execute() and is considered complete once the execute() method exits.
//
// An asnychronous task is expected to start processing in execute(), and will not be
// considered complete until either +link{setOutputData()} or +link{setOutputRecord} is
// called.
// @visibility workflow
//<
isAsync : false,
//> @method scriptTask.execute()
// Execute the task.
// @param input (Object) the task input
// @param inputRecord (Record) the task input record if an inputFieldList
was
// specified. See +link{group:taskIO}
// @return (Object) the task output. For multiple field output, call
// +link{setOutputRecord()} instead, and return null
// @visibility workflow
//<
execute : function (input, inputRecord) {
}
});
// --------------------------------------------------------------------------------------------
//> @class XORGateway
// Chooses one or another next process element based on AdvancedCriteria applied to
// +link{process.state}.
//
// If the AdvancedCriteria evaluate to true, the +link{nextElement} is chosen, otherwise the
// +link{failureElement}.
//
// Note that "XOR" in XORGateway
means "exclusive or" - only one next element is
// chosen.
// - implementation note: we need to allow the propertyName in simple Criteria or the
// criterion.name in AdvancedCriterion to a be path of the form "orderUser.name". This should
// be a general enhancement applied across the entire Criteria/AdvancedCriteria system.
// @visibility workflow
//<
isc.defineClass("XORGateway", "ProcessElement");
isc.XORGateway.addProperties({
//> @attr xorGateway.criteria (Criteria : IR : IR)
// Simple or +link{AdvancedCriteria} to be applied to the task inputs. These will be
// applied to either the data indicated by the +link{task.inputField} or to the
// "inputRecord" if multiple input fields are declared (see +link{taskIO}).
// @visibility workflow
//<
//> @attr xorGateway.nextElement (String : null : IR)
// ID of the next +link{process.sequences,sequence} or {process.elements,element} to
// procede to if the criteria match the process state. If this gateway is part of a
// +link{process.sequences,sequence} and has a next element in the sequence,
// nextElement
does not need to be specified.
// @visibility workflow
//<
//> @attr xorGateway.failureElement (String : null : IR)
// ID of the next sequence or element to proceed to if the criteria do not match.
// @visibility workflow
//<
});
// --------------------------------------------------------------------------------------------
//> @class DecisionGateway
// Chooses a next element in a +link{Process} by evaluating a series of criteria against the
// +link{process.state} and choosing the element associated with the criteria that matched, or
// a +link{defaultElement} if none of the criteria match.
// @visibility workflow
//<
isc.defineClass("DecisionGateway", "Task");
isc.DecisionGateway.addProperties({
//> @attr decisionGateway.criteriaMap (Object : null : IR)
// A Map from +link{ProcessElement.ID} to Criteria that will cause this ProcessElement to
// be chosen as the next element if the criteria matches.
// @visibility workflow
//<
//> @attr decisionGateway.defaultElement (String : null : IR)
// Next element to pick if no criteria match. If this gateway is part of a
// +link{process.sequences,sequence} and has a next element in the sequence, the
// defaultElement
is assumed to be the next element and does not need to be
// specified.
// @visibility workflow
//<
});