flowblocks
Version:
Reusable flow diagram blocks
962 lines (829 loc) • 39.7 kB
JavaScript
const jointjs = require("jointjs")
const DEFAULTS = require('./defaults')
const fromstring = require('from-string');
class Block {
constructor(options) {
this.options = {
defaultSize: DEFAULTS.SIZE,
defaultPosition: DEFAULTS.POSITION,
defaultPositionDelta: DEFAULTS.POSITION_DELTA
};
this.Model = {};
this.View = {};
Object.assign(this.options, options);
this._initialize();
}
_initialize() {
jointjs.shapes.flowblocks = {};
this.Model = jointjs.shapes.devs.Model.define('flowblocks.Block', {
// now model fields
name: '',
icon: './resources/img/svg/agave.svg',
status: 'ERROR', // OK, ERROR,
statusMsg: 'OK',
blockId: undefined,
debug: false, // debug mode when blockId is presented
errors: [], // array of block errors that are the cause for the ERROR status of the block
configurables: [], // configurable values {i: id, v: value}
_validationFunction: undefined, // validation function
_configurablesDefinitions: [], // array of configurables definitions: (id:string, label: string, placeholder:string, type:string, required:boolean)
_style: undefined,
_defaultStyle: DEFAULTS.STYLE,
_styles: DEFAULTS.STYLES,
// stores number of ports of this element that are already connected
_portsConnected: 0,
// type of element
_type: undefined,
// template name used to create this element
_template: undefined,
// array of ports and elements that are connected to each port
_portConnections: [], // {port: whether connected to in or out port, id: connected element id , bId: connected element block id, type: connected element type, }
// now presentation fields
attrs: {
rect: {
'fill': 'rgb(211, 55, 255)'
},
body: {
fill: '#ffffff',
stroke: '#000000'
},
link: {
refWidth: '100%',
refHeight: '100%',
xlinkShow: 'new',
cursor: 'pointer'
},
'.status-err': {
'refHeight': '25%',
'fill': 'rgb(204, 41, 0)',
'refY': '75%'
},
'.fb-icon-rect': {
'ref-width': '100%',
'fill': '#3DB5FF'
},
'.fb-icon-image': {
'ref': '.fb-icon-rect'
},
'.fb-status-rect': {
'ref-width': '100%',
'fill': 'rgb(209, 226, 208)'
},
'.fb-status-text': {
'ref': '.fb-status-rect',
'text-anchor': 'start',
'fill': 'black',
'y-alignment': 'middle'
},
'.fb-label-rect': {
'ref-width': '100%',
'fill': 'rgb(255, 230, 206)'
},
'.fb-validation-rect': {
'fill': '#d63031'
},
'.fb-label-text': {
'ref': '.fb-label-rect',
'text-anchor': 'start',
'fill': 'black',
'y-alignment': 'middle'
},
'.fb-type-label-text': {
'text-anchor': 'start',
'fill': 'black',
'y-alignment': 'middle'
}
// label: {
// fill: '#ffa500'
// }
}
// defaults - object that contains properties to be assigned to every constructed instance of the subtype.
// Used for specifying default attributes.
}, {
// proto props - object that contains properties to be assigned on the subtype prototype.
// Intended for properties intrinsic to the subtype, not usually modified. Used for specifying shape markup.
markup: [
'<g class="rotatable">',
'<rect class="body"/>',
'<rect class="fb-icon-rect"/>',
'<image class="fb-icon-image" href="//resources/img/svg/agave.svg" />',
'<rect class="fb-label-rect"/>',
'<text class="fb-label-text">Label</text>',
'<rect class="fb-status-rect"/>',
'<text class="fb-status-text"></text>',
'<rect class="fb-validation-rect"/>',
'<text class="fb-type-label-text"></text>',
'</g>'
].join(''),
initialize: function () {
this.on('change:name change:icon change:status change:errors change:statusMsg change:size change:_type', function () {
this._updateMyModel();
this.trigger('flowblocks-block-update');
}, this);
// this.on('all',function(eName, thing){
// console.log(eName, thing);
// })
//this.updateRectangles();
this._updateMyModel();
jointjs.shapes.devs.Model.prototype.initialize.apply(this, arguments);
//joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments);
},
/**
* Block available public operations
*/
api: function () {
var api = [
"element.set('name','my label');",
"element.set('position', {x:30, y:10});",
"element.set('size', {width:50, height: 50});",
"element.set('icon', 'https://cdn.jsdelivr.net/npm/flowblocks-icons@1.0.8/i/vase.svg');",
"element.style({titleBarColor: '#FADB50'});",
"element.getStatus();",
"element.freePorts();",
];
return api;
},
/**
* Returns status of the block and eventuall errors connected with the block
*/
getStatus(){
return {
valid: this.get('status') == 'OK',
errors: this.get('errors')
}
},
/**
* Validation function input:
* var blockData = {
blockId: this.get('blockId'),
type: this.get('_type'),
configurables: Object.assign({},this.get('configurables')),
connections: portConnectionsCopy
}
* Validation function output:
* Array of errors, each error must have a form of:
* code: error code,
* cId: element/object id that generated this error (for instance port id)
* msg: error message to be displayed
* @param {*} validationFunction
*/
applyValidation(validationFunction){
if(!validationFunction) return;
this.set('_validationFunction', validationFunction);
this.set('_validationSource', validationFunction.toString());
},
/**
* Used during import, reinstantiates and binds custom validation
* functions to blocks
*/
_reApplyValidation(validationFunctionFromTypes){
if(validationFunctionFromTypes){
this.set('_validationFunction', validationFunctionFromTypes);
return;
}
if(this.get('_validationSource')){
this.set('_validationFunction', new Function("return " + this.get('_validationSource'))());
}
},
/**
* Gets style definition. When style is already a style definition then nothing changes. Otherwise
* either default style definition is applied or a style definition with given name is returned.
* @param {*} style
*/
_getStyle(style){
var returnStyle = undefined;
// when no style provided then return default
if(!style){
returnStyle = this.get('_defaultStyle');
} else if (typeof style === 'string' || style instanceof String) {
// when style name provided then load style definition
returnStyle = this.get('_styles')[style.toLocaleLowerCase()];
} else {
// when style object provided return the style definition that
// is provided in object
returnStyle = style;
}
return returnStyle;
},
_getPortGroup(groupType){
var ports = this.attributes.ports || {};
var groups = ports.groups || {};
var portGroup = groups[groupType];
return portGroup;
},
/**
* Applies style for the block.
* @param {*} style Either name of the available preset styles or style specification
*/
style(style) {
var calulatedStyle = this._getStyle(style);
if(calulatedStyle){
this.set('_style', calulatedStyle);
if (calulatedStyle.bodyColor)
this.attr('.fb-icon-rect/fill', calulatedStyle.bodyColor)
if (calulatedStyle.titleBarColor)
this.attr('.fb-label-rect/fill', calulatedStyle.titleBarColor)
if (calulatedStyle.statusBarColor)
this.attr('.fb-status-rect/fill', calulatedStyle.statusBarColor)
if (calulatedStyle.portInColor) {
this.getPorts().forEach(port => {
if(port.group == 'in'){
this.portProp(port.id, 'attrs/.port-body/fill', calulatedStyle.portInColor);
// .port-body
// FIXME hmm not elegant and probably may break in the future
var portGroup = this._getPortGroup('in');
if(portGroup && portGroup.attrs){
var groupPortBody = portGroup.attrs['.port-body'] || {};
groupPortBody.fill = calulatedStyle.portInColor;
}
}
})
}
if (calulatedStyle.portOutColor){
this.getPorts().forEach(port => {
if(port.group == 'out'){
//this.portProp(port.id, 'attrs/circle/fill', style.portOutColor);
this.portProp(port.id, 'attrs/.port-body/fill', calulatedStyle.portOutColor);
// FIXME hmm not elegant and probably may break in the future
var portGroup = this._getPortGroup('out');
if(portGroup && portGroup.attrs){
var groupPortBody = portGroup.attrs['.port-body'] || {};
groupPortBody.fill = calulatedStyle.portOutColor;
}
}
})
}
}
},
/**
* Resets block configurables values.
* Validation is applied to check if all configurables meet validation criteria.
* @param {*} configurables Array of configurables values {i: name/id of the configurable, v: values (string)}
*/
setConfigurables(configurables){
this.set('configurables', configurables);
// treat name in a special way - block name is populated from the name configurable
const name = this.getConfigurable("name")
this.set("name", name);
// redraw and recalculate status
this._recalculateStatus();
},
/**
* Sets block configurable to given value. If necessary updates existing value.
* @param {*} name name/id of the configurable
* @param {*} value value to be set
*/
setConfigurable(name, value){
var configurable = this.getConfigurable(name);
if(configurable){
var configurables = this.get('configurables');
configurables.forEach(item=>{
if(item.i == name){
item.v = value
}
})
this.setConfigurables(configurables);
}else{
// also add default configurablesDefinition
this.addConfigurableDefinition({
id: name,
label: name,
placeholder:"",
type: 'TEXT',
required: true
})
// no configurable found, adding new
var configurables = this.get('configurables');
configurables.push({
i: name,
v: value
})
this.setConfigurables(configurables);
}
},
/**
* Adds or replaces configurable definition entry for given definition.id
* @param {*} definition configurable definition (id:string, label: string, placeholder:string, type:string, required:boolean)
*/
addConfigurableDefinition(definition){
const configurablesDefinitions = this.get("_configurablesDefinitions");
//(id:string, label: string, placeholder:string, type:string, required:boolean)
const configurableConfiguration = configurablesDefinitions.find((item)=>{return item.id == definition.id})
if(configurableConfiguration){
// entry exists so replace
configurablesDefinitions.map((item)=>{
return item.id == definition.id?definition:item;
})
}else{
configurablesDefinitions.push(definition);
}
this.set("_configurablesDefinitions", configurablesDefinitions);
},
/**
* Returns array of free ports of element.
* One can filter by portType ('in' or 'out')
* @param {*} portType When provided only free ports of given type are returned
* @returns Array of ports that are free in given block
*/
freePorts(portType) {
var ports = this.getPorts().filter(port=>{
if(portType){
return port.group == portType;
} else
return true;
});
var usedPorts = this.get('_portConnections');
// ports that are not used and can be connected
var freePorts = [];
ports.forEach(element => {
// find if current port is occupied
var usedPort = usedPorts.find(uPort => {
return uPort.port == element.id;
})
if (!usedPort) {
freePorts.push(element);
}
})
return freePorts;
},
_dumpConnections() {
if (this.get('debug'))
console.log('Connections[' + this.get('blockId') + ']: ', JSON.stringify(this.get('_portConnections')));
},
_statusToString(){
var msg = 'Block validation state: '+this.get('status');
this.get('errors').forEach(error=>{
msg += " | "+error.msg
})
return msg;
},
/**
* Validates if all ports are connected
*/
_basePortsValidation(){
var freePorts = this.freePorts();
if (freePorts.length > 0){
freePorts.forEach(port=>{
this.get('errors').push({
code: 'PORT_NOT_CONNECTED',
cId: port.id,
msg: 'Port ['+port.id+'] is not connected'
})
})
}
},
/**
* Validates if all required configurables are set
*/
_baseConfigurablesValidation(){
var self = this;
this.get('_configurablesDefinitions').filter(item=>{
return item.required;
}).forEach(requiredItem=>{
var actualConfigurable = self.getConfigurable(requiredItem.id);
if(!actualConfigurable){
this.get('errors').push({
code: 'FIELD_REQUIRED',
cId: requiredItem.id,
msg: 'Field ['+requiredItem.id+'] is required'
})
}
})
},
_baseStatusValidation(){
this._basePortsValidation();
this._baseConfigurablesValidation();
},
/**
* Reads block configurable with given name and returns it in its proper data type
* @param {*} name Name of the configurable to be returned
* @returns Configurable value (casted to the type) or undefined when no such configurable found
*/
getConfigurable(name){
var item = undefined;
Object.entries(this.get('configurables')).forEach(entry=>{
if(entry[1].i == name)
item = entry[1].v;
})
return fromstring.parse(item);
},
_customValidation(){
var self = this;
// Object.assign({}, A1);
var portConnectionsCopy = Object.assign({}, this.get('_portConnections'));
var blockData = {
id: this.id,
blockId: this.get('blockId'),
type: this.get('_type'),
configurables: Object.assign({},this.get('configurables')),
connections: portConnectionsCopy,
configurable: function (name) {
var item = undefined;
Object.entries(this.configurables).forEach(entry=>{
if(entry[1].i == name)
item = entry[1].v;
})
return fromstring.parse(item);
},
connection: function (port) {
var item = undefined;
Object.entries(this.connections).forEach(entry=>{
if(entry[1].port == port)
item = entry[1];
})
return item;
},
toArray: function(input){
var stringRepresentation = input || '[]';
var arrayObject = undefined;
try{
var arrayObject = fromstring.parse(stringRepresentation);
}catch(e){
}
return Array.isArray(arrayObject)?arrayObject:[];
}
}
// var configurable = function (name) {
// var item = undefined;
// return Object.entries(blockData.configurables).forEach(entry=>{
// if(entry[1].i == name)
// item = entry[1];
// })
// return item;
// }
// configurable.blockData = blockData;
// var connection = function (port) {
// var item = undefined;
// return Object.entries(blockData.connections).forEach(entry=>{
// if(entry[1].i == name)
// item = entry[1];
// })
// return item;
// }
// connection.blockData = blockData;
if(this.get('_validationFunction')){
// todo if new types definition is provided use the new definition for validation instead of the saved one
var errorsArray = this.get('_validationFunction').call(self, blockData);
errorsArray.forEach(error=>{
this.get('errors').push({
code: error.code,
cId: error.cId,
msg: error.msg
})
})
}
},
// add helper functions
/**
* Revalidates block
* One that wants to retrieve block status shall call getStatus().
*/
_recalculateStatus() {
// reset status
this.set('errors',[]);
this.set('status', 'OK');
this.set('statusMsg', 'OK');
this._baseStatusValidation();
this._customValidation();
// console.log('Errors from validation ', this.get('blockId'), this.get('errors'));
if(this.get('errors').length>0){
this.set('status', 'ERROR');
this.attr('.fb-validation-rect/fill', this.get('_style').validationERRORColor)
this.set('statusMsg', 'INVALID');
} else {
this.set('errors',[])// reset errors array
this.set('status', 'OK');
this.attr('.fb-validation-rect/fill', this.get('_style').validationOKColor)
this.set('statusMsg', 'OK');
}
// use validation function
// console.log(this.get('blockId'), freePorts.length, this.get('status'));
},
_handleDelete(blockToBeDeleted) {
var blockConnections = this.get('_portConnections').filter(block=>{
return block.id != blockToBeDeleted
})
this.set('_portConnections',blockConnections);
this._recalculateStatus();
},
_handleDisconnect(block, port, linkId) {
// console.log(this.get('blockId'),block.get('blockId'), port, linkId);
if(block==undefined)
return;
var recordToRemove = this.get('_portConnections').find(element => {
return element.port == port && element.id == block.id && element.linkId == linkId;
})
var idxToRemove = this.get('_portConnections').findIndex(element => {
return element.port == port && element.id == block.id && element.linkId == linkId;
})
if (idxToRemove >= 0)
this.get('_portConnections').splice(idxToRemove, 1);
// console.log('Disconnected', this.get('blockId'), port, recordToRemove ? recordToRemove.bId: undefined);
this._recalculateStatus();
},
_handleConnectFrom(participant, port, targetPort, linkId) {
//console.log('ConnectFrom', port, participant);
// {port: whether connected to in or out port, id: connected element id , bId: connected element block id, type: connected element type, }
var item = {
port: port,
id: participant.get('id'),
bId: participant.get('blockId'),
type: participant.get('_type'),
targetPort: targetPort,
linkId: linkId
}
this.get('_portConnections').push(item);
// console.log('Connect', this.get('blockId'), participant.get('blockId'), port);
this._recalculateStatus();
},
_handleConnectTo(participant, port, targetPort, linkId) {
// console.log('ConnectTo', port, participant);
// {port: whether connected to in or out port, id: connected element id , bId: connected element block id, type: connected element type, }
var item = {
port: port,
id: participant.get('id'),
bId: participant.get('blockId'),
type: participant.get('_type'),
targetPort: targetPort,
linkId: linkId
}
this.get('_portConnections').push(item);
// console.log('Connect', this.get('blockId'), participant.get('blockId'), port);
this._recalculateStatus();
},
_recalculateRectWithLabel: function (classSelectorPrefix, label, elementHeight, fontSize, baseSize, positionY) {
var attrs = this.get('attrs');
// section height
var partHeight = elementHeight * baseSize.height;
var partFontSize = fontSize * partHeight;
var fontY = positionY + partHeight / 2;
var fontX = 0.1 * baseSize.width;
this.attr(classSelectorPrefix + '-rect/height', partHeight);
// attrs[classSelectorPrefix+'-rect'].height = partHeight;
this.attr(classSelectorPrefix + '-rect/transform', 'translate(0,' + positionY + ')');
// attrs[classSelectorPrefix+'-rect'].transform = 'translate(0,' + positionY + ')';
this.attr(classSelectorPrefix + '-text/font-size', partFontSize);
// attrs[classSelectorPrefix+'-text']['font-size'] = partFontSize;
this.attr(classSelectorPrefix + '-text/transform', 'translate(' + fontX + ',' + fontY + ')');
// attrs[classSelectorPrefix+'-text'].transform = 'translate(' + fontX + ',' + fontY + ')';
this.attr(classSelectorPrefix + '-text/text', label);
return partHeight;
},
_recalculateValidationRect: function (classSelectorPrefix, elementHeight, elementWidth, baseSize, positionY) {
var attrs = this.get('attrs');
// section height
var partHeight = elementHeight * baseSize.height;
var positionX = (1.0-elementWidth) * baseSize.width;
var partWidth = elementWidth * baseSize.width;
this.attr(classSelectorPrefix + '-rect/height', partHeight);
this.attr(classSelectorPrefix + '-rect/width', partWidth);
this.attr(classSelectorPrefix + '-rect/transform', 'translate('+positionX+',' + positionY + ')');
// this.attr(classSelectorPrefix + '-rect/title', 'Block validation state: '+this.get('status'));
this.attr(classSelectorPrefix + '-rect/title', this._statusToString());
return partHeight;
},
_recalculateRectWithIcon: function (classSelectorPrefix, iconHref, elementHeight, iconSize, baseSize, positionY) {
var partHeight = elementHeight * baseSize.height;
this.attr(classSelectorPrefix + '-rect/height', partHeight);
this.attr(classSelectorPrefix + '-rect/transform', 'translate(0,' + positionY + ')');
var iconHeight = iconSize * partHeight;
var iconX = baseSize.width / 2 - iconHeight / 2;
var iconY = positionY + partHeight / 2 - iconHeight / 2;
this.attr(classSelectorPrefix + '-image/height', iconHeight);
this.attr(classSelectorPrefix + '-image/transform', 'translate(' + iconX + ',' + iconY + ')');
this.attr(classSelectorPrefix + '-image/href', iconHref);
return partHeight;
},
_enableRemoval(paper){
var view = this.findView(paper);
// console.log(view.getBBox().width, this.getBBox().width)
// console.log(view.getBBox().width, this.getBBox().width)
// var dx = view.getBBox().width-this.getBBox().width;
var ports = this.getPorts();
var hasIn = false;
var hasOut = false;
ports.forEach(port=>{
if(port.group == 'out')
hasOut = true;
if(port.group == 'in')
hasIn = true;
})
// if(hasIn&&hasOut){
// dx = -dx/2;
// }else if(hasOut){
// dx = -dx;
// }else{
// dx=0
// }
var nx = '0%';
if(hasIn&&hasOut)
nx = '70%'
else if (hasOut)
nx = '62%'
else
nx = '100%'
var removeButton = new joint.elementTools.Remove({
focusOpacity: 0.5,
rotate: true,
x: nx,
// y: '0%',
// offset: { x: dx, y: 0 }
});
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [removeButton]
});
view.addTools(toolsView);
view.hideTools();
},
_recalculateTypeLabel: function(classSelectorPrefix, label, baseSize, positionY){
var fontSize = baseSize.height*DEFAULTS.LABEL.FONT.SIZE;
var fontX = 0;
var fontY = positionY+fontSize;
this.attr(classSelectorPrefix + '-text/font-size', fontSize);
this.attr(classSelectorPrefix + '-text/transform', 'translate(' + fontX + ',' + fontY + ')');
this.attr(classSelectorPrefix + '-text/text', label);
this.attr(classSelectorPrefix + '-text/font-family', DEFAULTS.LABEL.FONT.FAMILY);
this.attr(classSelectorPrefix + '-text/font-weight', DEFAULTS.LABEL.FONT.WEIGHT);
},
_updateMyModel: function () {
var self = this;
var offsetY = 0;
var field = {
width: this.get('size').width,
height: this.get('size').height,
icon: this.get('icon'),
name: this.get('debug') ? this.get('name') + '[' + this.get('blockId') + ']' : this.get('name'),
statusMessage: this.get('statusMsg'),
status: this.get('status'),
type: this.get('_type')
}
offsetY += self._recalculateRectWithLabel('.fb-label', field.name, 0.2, 0.6, field, offsetY);
offsetY += self._recalculateRectWithIcon('.fb-icon', field.icon, 0.6, 0.8, field, offsetY);
var previousOffsetY = offsetY;
offsetY += self._recalculateRectWithLabel('.fb-status', field.statusMessage, 0.2, 0.3, field, offsetY);
self._recalculateValidationRect('.fb-validation', 0.2, 0.15, field, previousOffsetY);
self._recalculateTypeLabel('.fb-type-label',field.type, field, offsetY);
}
}, {
// static props - object that contains properties to be assigned on the subtype constructor.
// Not very common, used mostly for alternative constructor functions.
})
jointjs.shapes.flowblocks.BlockView = jointjs.dia.ElementView.extend({
initialize: function () {
jointjs.dia.ElementView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'flowblocks-block-update', function () {
this.update();
this.resize();
});
}
});
this.View = jointjs.shapes.flowblocks.BlockView;
}
createBlank(blockId, typeName, template, statusDefinition, style, validation, configurablesDefinitionArray) {
var factories = {
PassThrough: this.createPassThroughElement,
Start: this.createStartElement,
Split: this.createSplitElement,
Split3: this.createSplit3Element,
Split4: this.createSplit4Element,
Split5: this.createSplit5Element,
Join: this.createJoinElement,
End: this.createSinkElement,
Mixer: this.createMixerElement
}
if (factories[template]) {
var block = factories[template].call(this, '');
// set id
block.set('blockId',blockId);
block.set('_type', typeName);
block.set('_template', template);
block.set('_configurablesDefinitions', configurablesDefinitionArray);
block.applyValidation(validation);
// apply style
block.style(style);
// calculate initial status of block
block._recalculateStatus();
return block;
} else {
throw new Error('Unsuported template: ' + template);
}
}
_createBaseOptions(){
var options = {
position: this.options.defaultPosition,
size: this.options.defaultSize,
ports: {
groups: {
'in': {
attrs: {
'.port-body': {
fill: '#16A085',
magnet: 'passive'
}
}
},
'out': {
attrs: {
'.port-body': {
fill: '#E74C3C'
}
}
}
},
},
attrs: {
'.label': { text: 'Model', 'ref-x': .5, 'ref-y': .2 },
rect: { fill: '#2ECC71' }
}
}
return options;
}
/**
* Element with a single input and a dual output
* @param {*} name
* @param {*} statusDefinition
*/
createSplitElement(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1']
options.outPorts = ['out1', 'out2']
var newBlock = new this.Model(options);
return newBlock;
}
createSplit3Element(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1']
options.outPorts = ['out1', 'out2', 'out3']
var newBlock = new this.Model(options);
return newBlock;
}
createSplit4Element(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1']
options.outPorts = ['out1', 'out2', 'out3', 'out4']
var newBlock = new this.Model(options);
return newBlock;
}
createSplit5Element(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1']
options.outPorts = ['out1', 'out2', 'out3', 'out4', 'out5']
var newBlock = new this.Model(options);
return newBlock;
}
/**
* Element with a double input and a single output
* @param {*} name
* @param {*} statusDefinition
*/
createJoinElement(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1', 'in2'];
options.outPorts = ['out1'];
var newBlock = new this.Model(options);
return newBlock;
}
createMixerElement(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1', 'in2'];
options.outPorts = ['out1', 'out2'];
var newBlock = new this.Model(options);
return newBlock;
}
/**
* Element with a single input and a single output
* @param {*} name
* @param {*} statusDefinition
*/
createPassThroughElement(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1'];
options.outPorts = ['out1'];
var newBlock = new this.Model(options);
return newBlock;
}
/**
* Starting element
* @param {*} name
* @param {*} statusDefinition
*/
createStartElement(name) {
var options = this._createBaseOptions();
options.outPorts = ['out1'];
var newBlock = new this.Model(options);
return newBlock;
}
/**
* Finish (sink) element
* @param {*} name
* @param {*} statusDefinition
*/
createSinkElement(name) {
var options = this._createBaseOptions();
options.inPorts = ['in1'];
var newBlock = new this.Model(options);
return newBlock;
}
}
module.exports = new Block({});