in src/visual/Visualizer.js [137:252]
constructor(element, readOnly = false) {
const graph = new joint.dia.Graph();
if (!(element instanceof Element)) {
element = document.getElementById(element);
}
this._readOnly = readOnly;
const paper = new Paper({
el: element,
width: element.offsetWidth,
height: element.offsetHeight,
model: graph,
defaultLink: new VisualLink(),
elementView: VisualWorkflowView,
interactive: val => !this._elementsPanning && (!this._readOnly || !(val.model instanceof VisualLink)),
clickThreshold: 1,
});
/**
* JointJS paper object.
* @type {joint.dia.Paper}
*/
this.paper = paper;
/**
* Array of currently selected states.
* @type {Array}
*/
this.selection = [];
/** Public member to access zoom object
* @type {Zoom}
*/
this.zoom = new Zoom(paper);
this._dragStartPosition = null;
this._elementsPanning = false;
this._handlePanning();
this._handleSelection();
this._selectionEnabled = true;
this._graph = graph;
this._children = {};
this._declarations = {};
this._timer = null;
this._step = null;
this.clear();
const validateMagnet = this.paper.options.validateMagnet;
this.paper.options.validateMagnet = (cellView, magnet) => {
if (this._readOnly) {
return false;
}
const args = [cellView, magnet];
return validateMagnet.apply(this.paper, args);
};
const validateConnection = this.paper.options.validateConnection;
this.paper.options.validateConnection = (cellViewS, magnetS, cellViewT, magnetT, end, linkView) => {
if (this._readOnly) {
return false;
}
if (this._isChildSubWorkflow(cellViewS.model.step)) return false;
if (this._isChildSubWorkflow(cellViewT.model.step)) return false;
const args = [cellViewS, magnetS, cellViewT, magnetT, end, linkView];
if (!validateConnection.apply(this.paper, args)) {
return false;
}
const source = cellViewS.model;
const target = cellViewT.model;
const sourceIsAncestor = target.isEmbeddedIn(source, { deep: true });
const targetIsAncestor = source.isEmbeddedIn(target, { deep: true });
const sourcePortIsInput = magnetS.getAttribute('port-group') === 'in';
const targetPortIsInput = magnetT.getAttribute('port-group') === 'in';
// one may only link step's input if it is an ancestor
// and it is connected to descendants input
if (sourcePortIsInput && (!sourceIsAncestor || !targetPortIsInput)) {
return false;
}
// output port may only be a target port if the source port
// is the target port of a descendant
if (!targetPortIsInput && (!targetIsAncestor || sourcePortIsInput)) {
return false;
}
// one may not connect ancestor's/descendant's output
// with descendant's/ancestor's input
if ((sourceIsAncestor || targetIsAncestor) && !sourcePortIsInput && targetPortIsInput) {
return false;
}
const targetStep = target.step;
const targetPortName = magnetT.attributes.port.value;
const targetGroup = targetPortIsInput ? targetStep.i : targetStep.o;
return _.size(targetGroup[targetPortName].inputs) === 0;
};
graph.on('change:position', (cell) => {
let parentId = cell.get('parent');
while (parentId) {
const parent = graph.getCell(parentId);
parent.fit();
parentId = parent.get('parent');
}
});
}