constructor()

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');
      }
    });
  }