You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/01/03 21:17:56 UTC

[incubator-streampipes] 01/01: [STREAMPIPES-499] Bump Jsplumb dependency and migrate code

This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch STREAMPIPES-494
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit e939a0e54d3624c8a4ec0cf7a3d0538d9da4f400
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Mon Jan 3 22:06:26 2022 +0100

    [STREAMPIPES-499] Bump Jsplumb dependency and migrate code
---
 ui/package.json                                    |   8 +-
 .../pipeline-assembly.component.html               |   6 +-
 .../pipeline-assembly.component.ts                 |  20 +-
 .../pipeline-element-options.component.ts          |  87 +++---
 .../components/pipeline/pipeline.component.html    |  74 ++---
 .../components/pipeline/pipeline.component.ts      | 198 +++++++------
 .../app/editor/services/jsplumb-bridge.service.ts  |  37 ++-
 .../app/editor/services/jsplumb-config.service.ts  |  87 ++++--
 .../app/editor/services/jsplumb-factory.service.ts | 113 ++++----
 ui/src/app/editor/services/jsplumb.service.ts      |  66 ++---
 .../app/editor/services/object-provider.service.ts |  10 +-
 .../services/pipeline-positioning.service.ts       | 305 +++++++++++----------
 .../editor/services/pipeline-validation.service.ts |  26 +-
 ui/src/scss/main.scss                              |   2 +-
 14 files changed, 521 insertions(+), 518 deletions(-)

diff --git a/ui/package.json b/ui/package.json
index 7da712e..da51b0d 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -39,7 +39,12 @@
     "@fortawesome/fontawesome-free": "5.12.1",
     "@ngx-loading-bar/core": "5.1.2",
     "@ngx-loading-bar/http-client": "5.1.2",
-    "@panzoom/panzoom": "^4.3.2",
+    "@jsplumb/core": "^5.3.1",
+    "@jsplumb/browser-ui": "^5.3.1",
+    "@jsplumb/connector-bezier": "^5.3.1",
+    "@jsplumb/util": "^5.3.1",
+    "@jsplumb/common": "^5.3.1",
+    "@panzoom/panzoom": "^4.4.3",
     "@stomp/ng2-stompjs": "7.2.0",
     "@swimlane/ngx-charts": "16.0.0",
     "angular-datatables": "^12.0.2",
@@ -65,7 +70,6 @@
     "jquery": "2.1.3",
     "jquery-ui-dist": "1.12.1",
     "jshint": "^2.13.1",
-    "jsplumb": "^2.15.5",
     "jszip": "3.2.1",
     "konva": "3.2.4",
     "leaflet": "1.6.0",
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
index 3463fcb..e32b4d1 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
@@ -121,8 +121,7 @@
                     </div>
                 </div>
             </div>
-            <div id="assembly" class="canvas" #assembly>
-                <pipeline #pipelineComponent
+                <pipeline class="canvas jtk-surface" id="assembly" #pipelineComponent
                           [pipelineValid]="pipelineValid"
                           [canvasId]="'assembly'"
                           [rawPipelineModel]="rawPipelineModel"
@@ -132,9 +131,8 @@
                           [pipelineCanvasMetadata]="pipelineCanvasMetadata"
                           [pipelineCacheRunning]="pipelineCacheRunning"
                           (pipelineCachedChanged)="pipelineCached=$event"
-                          (pipelineCacheRunningChanged)="pipelineCacheRunning=$event">
+                          (pipelineCacheRunningChanged)="pipelineCacheRunning=$event" style="position:absolute;">
                 </pipeline>
-            </div>
         </div>
     </div>
 </div>
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
index 079d03f..9658e17 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
@@ -16,16 +16,7 @@
  *
  */
 
-import {
-    Component,
-    ElementRef,
-    EventEmitter,
-    Input,
-    NgZone,
-    OnInit,
-    Output,
-    ViewChild,
-} from '@angular/core';
+import { Component, ElementRef, EventEmitter, Input, NgZone, OnInit, Output, ViewChild, } from '@angular/core';
 import { JsplumbBridge } from '../../services/jsplumb-bridge.service';
 import { PipelinePositioningService } from '../../services/pipeline-positioning.service';
 import { PipelineValidationService } from '../../services/pipeline-validation.service';
@@ -116,7 +107,6 @@ export class PipelineAssemblyComponent implements OnInit {
     }
 
     ngOnInit(): void {
-        this.JsplumbBridge = this.JsPlumbFactoryService.getJsplumbBridge(false);
         if (this.currentModifiedPipelineId) {
             this.displayPipelineById();
         } else {
@@ -141,23 +131,21 @@ export class PipelineAssemblyComponent implements OnInit {
     }
 
     ngAfterViewInit() {
+        this.JsplumbBridge = this.JsPlumbFactoryService.getJsplumbBridge(this.preview);
         const elem = document.getElementById('assembly');
         this.panzoom = Panzoom(elem, {
             maxScale: 5,
-            excludeClass: 'jtk-draggable',
+            excludeClass: "sp-no-pan",
             canvas: true,
             contain: 'outside'
         });
     }
 
     autoLayout() {
-        this.PipelinePositioningService.layoutGraph('#assembly', 'span[id^=\'jsplumb\']', 110, false);
+        this.PipelinePositioningService.layoutGraph('#assembly', 'div[id^=\'jsplumb\']', 110, false);
         this.JsplumbBridge.repaintEverything();
     }
 
-    toggleSelectMode() {
-    }
-
     zoomOut() {
         this.doZoom(true);
     }
diff --git a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
index 9de810a..1d131a0 100644
--- a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
+++ b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts
@@ -16,27 +16,23 @@
  *
  */
 
-import {JsplumbBridge} from "../../services/jsplumb-bridge.service";
-import {JsplumbService} from "../../services/jsplumb.service";
-import {PipelineValidationService} from "../../services/pipeline-validation.service";
-import {RestApi} from "../../../services/rest-api.service";
-import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from "@angular/core";
-import {PipelineElementRecommendationService} from "../../services/pipeline-element-recommendation.service";
-import {ObjectProvider} from "../../services/object-provider.service";
-import {
-  InvocablePipelineElementUnion,
-  PipelineElementConfig,
-  PipelineElementUnion
-} from "../../model/editor.model";
-import {SpDataStream, WildcardTopicDefinition} from "../../../core-model/gen/streampipes-model";
-import {EditorService} from "../../services/editor.service";
-import {PanelType} from "../../../core-ui/dialog/base-dialog/base-dialog.model";
-import {DialogService} from "../../../core-ui/dialog/base-dialog/base-dialog.service";
-import {CompatibleElementsComponent} from "../../dialog/compatible-elements/compatible-elements.component";
-import {Tuple2} from "../../../core-model/base/Tuple2";
-import { cloneDeep } from "lodash";
-import {Observable, Subscription} from "rxjs";
-import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
+import { JsplumbBridge } from '../../services/jsplumb-bridge.service';
+import { JsplumbService } from '../../services/jsplumb.service';
+import { PipelineValidationService } from '../../services/pipeline-validation.service';
+import { RestApi } from '../../../services/rest-api.service';
+import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
+import { PipelineElementRecommendationService } from '../../services/pipeline-element-recommendation.service';
+import { ObjectProvider } from '../../services/object-provider.service';
+import { InvocablePipelineElementUnion, PipelineElementConfig, PipelineElementUnion } from '../../model/editor.model';
+import { SpDataStream, WildcardTopicDefinition } from '../../../core-model/gen/streampipes-model';
+import { EditorService } from '../../services/editor.service';
+import { PanelType } from '../../../core-ui/dialog/base-dialog/base-dialog.model';
+import { DialogService } from '../../../core-ui/dialog/base-dialog/base-dialog.service';
+import { CompatibleElementsComponent } from '../../dialog/compatible-elements/compatible-elements.component';
+import { Tuple2 } from '../../../core-model/base/Tuple2';
+import { cloneDeep } from 'lodash';
+import { Subscription } from 'rxjs';
+import { JsplumbFactoryService } from '../../services/jsplumb-factory.service';
 
 @Component({
   selector: 'pipeline-element-options',
@@ -83,25 +79,25 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
 
   JsplumbBridge: JsplumbBridge;
 
-  constructor(private ObjectProvider: ObjectProvider,
-              private PipelineElementRecommendationService: PipelineElementRecommendationService,
-              private DialogService: DialogService,
-              private EditorService: EditorService,
-              private JsplumbFactoryService: JsplumbFactoryService,
-              private JsplumbService: JsplumbService,
-              private PipelineValidationService: PipelineValidationService,
-              private RestApi: RestApi) {
+  constructor(private objectProvider: ObjectProvider,
+              private pipelineElementRecommendationService: PipelineElementRecommendationService,
+              private dialogService: DialogService,
+              private editorService: EditorService,
+              private jsplumbFactoryService: JsplumbFactoryService,
+              private jsplumbService: JsplumbService,
+              private pipelineValidationService: PipelineValidationService,
+              private restApi: RestApi) {
     this.recommendationsAvailable = false;
     this.possibleElements = [];
     this.recommendedElements = [];
     this.recommendationsShown = false;
-    this.JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(false);
+    this.JsplumbBridge = this.jsplumbFactoryService.getJsplumbBridge(false);
   }
 
   ngOnInit() {
-    this.pipelineElementConfiguredObservable = this.EditorService.pipelineElementConfigured$.subscribe(pipelineElementDomId => {
+    this.pipelineElementConfiguredObservable = this.editorService.pipelineElementConfigured$.subscribe(pipelineElementDomId => {
       this.pipelineElement.settings.openCustomize = false;
-      this.RestApi.updateCachedPipeline(this.rawPipelineModel);
+      this.restApi.updateCachedPipeline(this.rawPipelineModel);
       if (pipelineElementDomId === this.pipelineElement.payload.dom) {
         this.initRecs(this.pipelineElement.payload.dom);
       }
@@ -120,39 +116,39 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
   }
 
   customizeElement(pipelineElement: PipelineElementConfig) {
-    let restrictedEditMode = ! (this.isRootElement());
-    let customizeInfo = {a: restrictedEditMode, b: pipelineElement} as Tuple2<Boolean, PipelineElementConfig>;
+    const restrictedEditMode = ! (this.isRootElement());
+    const customizeInfo = {a: restrictedEditMode, b: pipelineElement} as Tuple2<Boolean, PipelineElementConfig>;
     this.customize.emit(customizeInfo);
   }
 
   openHelpDialog() {
-    this.EditorService.openHelpDialog(this.pipelineElement.payload);
+    this.editorService.openHelpDialog(this.pipelineElement.payload);
   }
 
   openCustomizeStreamDialog() {
-    //this.EditorDialogManager.showCustomizeStreamDialog(this.pipelineElement.payload);
+    // this.EditorDialogManager.showCustomizeStreamDialog(this.pipelineElement.payload);
   }
 
   initRecs(pipelineElementDomId) {
-    let clonedModel: PipelineElementConfig[] = cloneDeep(this.rawPipelineModel);
+    const clonedModel: PipelineElementConfig[] = cloneDeep(this.rawPipelineModel);
     clonedModel.forEach(pe => {
       if (pe.payload.dom === pipelineElementDomId && (pe.type !== 'stream'  && pe.type !== 'set')) {
         pe.settings.completed = false;
         (pe.payload as InvocablePipelineElementUnion).configured = false;
       }
-    })
-    var currentPipeline = this.ObjectProvider.makePipeline(clonedModel);
-    this.EditorService.recommendPipelineElement(currentPipeline).subscribe((result) => {
+    });
+    const currentPipeline = this.objectProvider.makePipeline(clonedModel);
+    this.editorService.recommendPipelineElement(currentPipeline).subscribe((result) => {
       if (result.success) {
-        this.possibleElements = cloneDeep(this.PipelineElementRecommendationService.collectPossibleElements(this.allElements, result.possibleElements));
-        this.recommendedElements = cloneDeep(this.PipelineElementRecommendationService.populateRecommendedList(this.allElements, result.recommendedElements));
+        this.possibleElements = cloneDeep(this.pipelineElementRecommendationService.collectPossibleElements(this.allElements, result.possibleElements));
+        this.recommendedElements = cloneDeep(this.pipelineElementRecommendationService.populateRecommendedList(this.allElements, result.recommendedElements));
         this.recommendationsAvailable = true;
       }
     });
   }
 
   openPossibleElementsDialog() {
-    const dialogRef = this.DialogService.open(CompatibleElementsComponent,{
+    const dialogRef = this.dialogService.open(CompatibleElementsComponent, {
       panelType: PanelType.SLIDE_IN_PANEL,
       title: 'Compatible Elements',
       data: {
@@ -173,12 +169,11 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
   }
 
   isRootElement() {
-    return this.JsplumbBridge.getConnections({source: this.pipelineElement.payload.dom}).length === 0;
+    return this.JsplumbBridge.getConnections({source: document.getElementById(this.pipelineElement.payload.dom)}).length === 0;
   }
 
   isConfigured() {
-    if (this.isDataSource) return true;
-    else {
+    if (this.isDataSource) { return true; } else {
       return (this.pipelineElement.payload as InvocablePipelineElementUnion).configured;
     }
   }
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.html b/ui/src/app/editor/components/pipeline/pipeline.component.html
index d7cce7f..571ddc9 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.html
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.html
@@ -16,43 +16,45 @@
   ~
   -->
 
-<div style="z-index: 1" [ngStyle]="{width: canvasWidth, height: canvasHeight}" #outerCanvas>
-    <span id="{{pipelineElement.payload.dom}}" style="{{getElementCss(pipelineElement.settings)}}"
-          (click)="updateOptionsClick(pipelineElement.payload.dom)"
-          (mouseenter)="updateMouseover(pipelineElement.payload.dom)"
-          (mouseleave)="updateMouseover('')"
-          *ngFor="let pipelineElement of rawPipelineModel | enabledPipelineElement">
-        <span style="z-index:5;"
-              [ngClass]="getElementCssClasses(pipelineElement)">
-            <div class="pipeline-element-progress-container sp-fade" *ngIf="pipelineElement.settings.loadingStatus">
-                 <mat-spinner [mode]="'indeterminate'" class="pipeline-element-progress" [diameter]="40" color="accent"></mat-spinner>
-            </div>
-            <div class="pipeline-element-loading-container sp-fade-opacity"
-                 *ngIf="pipelineElement.settings.loadingStatus"></div>
-            <div class="pipeline-element-configuration-invalid {{pipelineElement.type === 'stream' ? 'pi-stream' : 'pi-processor'}}"
-                 *ngIf="!pipelineElement.settings.completed">
-                <i class="material-icons pipeline-element-configuration-invalid-icon">
+<div [attr.data-jtk-managed]="pipelineElement.payload.dom"
+     id="{{pipelineElement.payload.dom}}"
+     style="{{getElementCss(pipelineElement.settings)}}"
+     (click)="updateOptionsClick(pipelineElement.payload.dom)"
+     (mouseenter)="updateMouseover(pipelineElement.payload.dom)"
+     (mouseleave)="updateMouseover('')"
+     *ngFor="let pipelineElement of rawPipelineModel | enabledPipelineElement" class="sp-no-pan">
+    <div style="z-index:5;"
+         [ngClass]="getElementCssClasses(pipelineElement)">
+        <div class="pipeline-element-progress-container sp-fade" *ngIf="pipelineElement.settings.loadingStatus">
+            <mat-spinner [mode]="'indeterminate'" class="pipeline-element-progress" [diameter]="40"
+                         color="accent"></mat-spinner>
+        </div>
+        <div class="pipeline-element-loading-container sp-fade-opacity"
+             *ngIf="pipelineElement.settings.loadingStatus"></div>
+        <div class="pipeline-element-configuration-invalid {{pipelineElement.type === 'stream' ? 'pi-stream' : 'pi-processor'}}"
+             *ngIf="!pipelineElement.settings.completed">
+            <i class="material-icons pipeline-element-configuration-invalid-icon">
                 warning
-                </i>
-            </div>
-            <pipeline-element [pipelineElement]="pipelineElement.payload" [preview]="preview"></pipeline-element>
-        </span>
-         <pipeline-element-options *ngIf="!preview" (delete)="handleDeleteOption($event)"
-                                   (customize)="showCustomizeDialog($event)"
-                                   [currentMouseOverElement]="currentMouseOverElement"
-                                   [pipelineValid]="pipelineValid"
-                                   [allElements]="allElements"
-                                   [pipelineElement]="pipelineElement"
-                                   [rawPipelineModel]="rawPipelineModel"
-                                   [pipelineElementId]="(pipelineElement.type == 'stream' || pipelineElement.type == 'set') ? pipelineElement.payload.elementId : pipelineElement.payload.belongsTo"
-                                   [internalId]="pipelineElement.payload.dom"
-                                   [attr.data-cy]="'sp-pe-menu-' + pipelineElement.payload.name.toLowerCase().replaceAll(' ', '_')">
-        </pipeline-element-options>
-        <pipeline-element-preview *ngIf="previewModeActive && (pipelinePreview.supportedPipelineElementDomIds.indexOf(pipelineElement.payload.dom) > -1)"
-                                  [previewId]="pipelinePreview.previewId"
-                                  [pipelineElementDomId]="pipelineElement.payload.dom">
+            </i>
+        </div>
+        <pipeline-element [pipelineElement]="pipelineElement.payload" [preview]="preview"></pipeline-element>
+    </div>
+    <pipeline-element-options *ngIf="!preview" (delete)="handleDeleteOption($event)"
+                              (customize)="showCustomizeDialog($event)"
+                              [currentMouseOverElement]="currentMouseOverElement"
+                              [pipelineValid]="pipelineValid"
+                              [allElements]="allElements"
+                              [pipelineElement]="pipelineElement"
+                              [rawPipelineModel]="rawPipelineModel"
+                              [pipelineElementId]="(pipelineElement.type == 'stream' || pipelineElement.type == 'set') ? pipelineElement.payload.elementId : pipelineElement.payload.belongsTo"
+                              [internalId]="pipelineElement.payload.dom"
+                              [attr.data-cy]="'sp-pe-menu-' + pipelineElement.payload.name.toLowerCase().replaceAll(' ', '_')">
+    </pipeline-element-options>
+    <pipeline-element-preview
+            *ngIf="previewModeActive && (pipelinePreview.supportedPipelineElementDomIds.indexOf(pipelineElement.payload.dom) > -1)"
+            [previewId]="pipelinePreview.previewId"
+            [pipelineElementDomId]="pipelineElement.payload.dom">
 
-        </pipeline-element-preview>
-    </span>
+    </pipeline-element-preview>
 </div>
 
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index 08288fc..f353df0 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -16,17 +16,13 @@
  *
  */
 
-import {PipelineValidationService} from "../../services/pipeline-validation.service";
-import {JsplumbService} from "../../services/jsplumb.service";
-import {PipelineEditorService} from "../../services/pipeline-editor.service";
-import {JsplumbBridge} from "../../services/jsplumb-bridge.service";
-import {ShepherdService} from "../../../services/tour/shepherd.service";
-import {Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output} from "@angular/core";
-import {
-  InvocablePipelineElementUnion,
-  PipelineElementConfig,
-  PipelineElementUnion
-} from "../../model/editor.model";
+import { PipelineValidationService } from '../../services/pipeline-validation.service';
+import { JsplumbService } from '../../services/jsplumb.service';
+import { PipelineEditorService } from '../../services/pipeline-editor.service';
+import { JsplumbBridge } from '../../services/jsplumb-bridge.service';
+import { ShepherdService } from '../../../services/tour/shepherd.service';
+import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from '@angular/core';
+import { InvocablePipelineElementUnion, PipelineElementConfig, PipelineElementUnion } from '../../model/editor.model';
 import {
   CustomOutputStrategy,
   DataProcessorInvocation,
@@ -36,20 +32,22 @@ import {
   PipelinePreviewModel,
   SpDataSet,
   SpDataStream
-} from "../../../core-model/gen/streampipes-model";
-import {ObjectProvider} from "../../services/object-provider.service";
-import {CustomizeComponent} from "../../dialog/customize/customize.component";
-import {PanelType} from "../../../core-ui/dialog/base-dialog/base-dialog.model";
-import {DialogService} from "../../../core-ui/dialog/base-dialog/base-dialog.service";
-import {EditorService} from "../../services/editor.service";
-import {MatchingResultMessage} from "../../../core-model/gen/streampipes-model-client";
-import {MatchingErrorComponent} from "../../dialog/matching-error/matching-error.component";
-import {Tuple2} from "../../../core-model/base/Tuple2";
-import {ConfirmDialogComponent} from "../../../core-ui/dialog/confirm-dialog/confirm-dialog.component";
-import {MatDialog} from "@angular/material/dialog";
-import {forkJoin} from "rxjs";
-import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
-import {PipelinePositioningService} from "../../services/pipeline-positioning.service";
+} from '../../../core-model/gen/streampipes-model';
+import { ObjectProvider } from '../../services/object-provider.service';
+import { CustomizeComponent } from '../../dialog/customize/customize.component';
+import { PanelType } from '../../../core-ui/dialog/base-dialog/base-dialog.model';
+import { DialogService } from '../../../core-ui/dialog/base-dialog/base-dialog.service';
+import { EditorService } from '../../services/editor.service';
+import { MatchingResultMessage } from '../../../core-model/gen/streampipes-model-client';
+import { MatchingErrorComponent } from '../../dialog/matching-error/matching-error.component';
+import { Tuple2 } from '../../../core-model/base/Tuple2';
+import { ConfirmDialogComponent } from '../../../core-ui/dialog/confirm-dialog/confirm-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
+import { forkJoin } from 'rxjs';
+import { JsplumbFactoryService } from '../../services/jsplumb-factory.service';
+import { PipelinePositioningService } from '../../services/pipeline-positioning.service';
+import { EVENT_CONNECTION_ABORT, EVENT_CONNECTION_DRAG } from '@jsplumb/browser-ui';
+import { EVENT_CONNECTION, EVENT_CONNECTION_DETACHED, EVENT_CONNECTION_MOVED } from '@jsplumb/core';
 
 @Component({
   selector: 'pipeline',
@@ -88,21 +86,18 @@ export class PipelineComponent implements OnInit, OnDestroy {
   @Output()
   pipelineCacheRunningChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
 
-  availablePipelineElementCache: PipelineElementUnion[];
-
   plumbReady: boolean;
   currentMouseOverElement: string;
   currentPipelineModel: Pipeline;
   idCounter: any;
   currentZoomLevel: any;
-  TransitionService: any;
 
-  canvasWidth: string = "100%";
-  canvasHeight: string = "100%";
+  canvasWidth = '100%';
+  canvasHeight = '100%';
 
   JsplumbBridge: JsplumbBridge;
 
-  previewModeActive: boolean = false;
+  previewModeActive = false;
   pipelinePreview: PipelinePreviewModel;
 
   constructor(private JsplumbService: JsplumbService,
@@ -115,9 +110,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
               private PipelineValidationService: PipelineValidationService,
               private dialogService: DialogService,
               private dialog: MatDialog,
-              private ngZone: NgZone,) {
+              private ngZone: NgZone, ) {
     this.plumbReady = false;
-    this.currentMouseOverElement = "";
+    this.currentMouseOverElement = '';
     this.currentPipelineModel = new Pipeline();
     this.idCounter = 0;
 
@@ -126,14 +121,10 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   ngOnInit() {
     this.JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(this.preview);
-    this.JsplumbBridge.setContainer(this.canvasId);
     this.initAssembly();
     this.initPlumb();
   }
 
-  ngAfterViewInit() {
-  }
-
   validatePipeline() {
     setTimeout(() => {
       this.ngZone.run(() => {
@@ -145,7 +136,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   ngOnDestroy() {
     this.deletePipelineElementPreview(false);
-    this.JsplumbBridge.deleteEveryEndpoint();
+    this.JsplumbFactoryService.destroy(this.preview);
     this.plumbReady = false;
   }
 
@@ -155,23 +146,23 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   updateOptionsClick(elementId) {
     if (this.currentMouseOverElement == elementId) {
-      this.currentMouseOverElement = "";
+      this.currentMouseOverElement = '';
     } else {
       this.currentMouseOverElement = elementId;
     }
   }
 
   getElementCss(currentPipelineElementSettings) {
-    return "position:absolute;"
-        + (this.preview ? "width:75px;" : "width:110px;")
-        + (this.preview ? "height:75px;" : "height:110px;")
-        + "left: " + currentPipelineElementSettings.position.x + "px; "
-        + "top: " + currentPipelineElementSettings.position.y + "px; "
+    return 'position:absolute;'
+        + (this.preview ? 'width:75px;' : 'width:110px;')
+        + (this.preview ? 'height:75px;' : 'height:110px;')
+        + 'left: ' + currentPipelineElementSettings.position.x + 'px; '
+        + 'top: ' + currentPipelineElementSettings.position.y + 'px; ';
   }
 
   getElementCssClasses(currentPipelineElement) {
-    return currentPipelineElement.type + " " + (currentPipelineElement.settings.openCustomize ? "" : "")
-        + currentPipelineElement.settings.connectable + " "
+    return currentPipelineElement.type + ' ' + (currentPipelineElement.settings.openCustomize ? '' : '')
+        + currentPipelineElement.settings.connectable + ' '
         + currentPipelineElement.settings.displaySettings;
   }
 
@@ -191,9 +182,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
     this.dialog.open(ConfirmDialogComponent, {
       width: '500px',
       data: {
-        "title": "Currently, it is not possible to mix data streams and data sets in a single pipeline.",
-        "confirmAndCancel": false,
-        "okTitle": "Ok",
+        'title': 'Currently, it is not possible to mix data streams and data sets in a single pipeline.',
+        'confirmAndCancel': false,
+        'okTitle': 'Ok',
       },
     });
   }
@@ -202,17 +193,16 @@ export class PipelineComponent implements OnInit, OnDestroy {
     return this.allElements.find(a => a.elementId === elementId);
   }
 
-
   initAssembly() {
     ($('#assembly') as any).droppable({
-      tolerance: "fit",
+      tolerance: 'fit',
       drop: (element, ui) => {
-        let pipelineElementId = ui.draggable.data("pe");
-        let pipelineElement: PipelineElementUnion = this.findPipelineElementByElementId(pipelineElementId);
+        const pipelineElementId = ui.draggable.data('pe');
+        const pipelineElement: PipelineElementUnion = this.findPipelineElementByElementId(pipelineElementId);
         if (ui.draggable.hasClass('draggable-icon')) {
           this.EditorService.makePipelineAssemblyEmpty(false);
-          let newElementId = pipelineElement.elementId + ":" + this.JsplumbService.makeId(5);
-          let pipelineElementConfig = this.JsplumbService.createNewPipelineElementConfig(pipelineElement,
+          const newElementId = pipelineElement.elementId + ':' + this.JsplumbService.makeId(5);
+          const pipelineElementConfig = this.JsplumbService.createNewPipelineElementConfig(pipelineElement,
               this.PipelineEditorService.getCoordinates(ui, this.currentZoomLevel),
               false,
               false,
@@ -242,7 +232,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
               }, 10);
             }
             if (this.ShepherdService.isTourActive()) {
-              this.ShepherdService.trigger("drop-" + pipelineElementConfig.type);
+              this.ShepherdService.trigger('drop-' + pipelineElementConfig.type);
             }
           }
         }
@@ -251,7 +241,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
         this.triggerPipelineCacheUpdate();
       }
 
-    }); //End #assembly.droppable()
+    }); // End #assembly.droppable()
   }
 
   checkTopicModel(pipelineElementConfig: PipelineElementConfig) {
@@ -262,12 +252,12 @@ export class PipelineComponent implements OnInit, OnDestroy {
           false);
     }, 10);
 
-    var streamDescription = pipelineElementConfig.payload as SpDataStream;
+    const streamDescription = pipelineElementConfig.payload as SpDataStream;
     if (streamDescription
         .eventGrounding
         .transportProtocols[0]
-        .topicDefinition["@class"] === "org.apache.streampipes.model.grounding.WildcardTopicDefinition") {
-      //this.EditorDialogManager.showCustomizeStreamDialog(streamDescription);
+        .topicDefinition['@class'] === 'org.apache.streampipes.model.grounding.WildcardTopicDefinition') {
+      // this.EditorDialogManager.showCustomizeStreamDialog(streamDescription);
     }
   }
 
@@ -288,54 +278,56 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   initPlumb() {
 
-    //this.JsplumbService.prepareJsplumb();
+    // this.JsplumbService.prepareJsplumb();
 
-    this.JsplumbBridge.unbind("connection");
+    this.JsplumbBridge.unbind(EVENT_CONNECTION);
 
-    this.JsplumbBridge.bind("connectionMoved", (info, originalEvent) => {
-      var pe = this.ObjectProvider.findElement(info.newTargetEndpoint.elementId, this.rawPipelineModel);
-      var oldPe = this.ObjectProvider.findElement(info.originalTargetEndpoint.elementId, this.rawPipelineModel);
+    this.JsplumbBridge.bind(EVENT_CONNECTION_MOVED, (info) => {
+      const pe = this.ObjectProvider.findElement(info.newTargetEndpoint.elementId, this.rawPipelineModel);
+      const oldPe = this.ObjectProvider.findElement(info.originalTargetEndpoint.elementId, this.rawPipelineModel);
       (oldPe.payload as InvocablePipelineElementUnion).configured = false;
       (pe.payload as InvocablePipelineElementUnion).configured = false;
     });
 
-    this.JsplumbBridge.bind("connectionDetached", (info, originalEvent) => {
-      var pe = this.ObjectProvider.findElement(info.targetEndpoint.elementId, this.rawPipelineModel);
+    this.JsplumbBridge.bind(EVENT_CONNECTION_DETACHED, (info) => {
+      const pe = this.ObjectProvider.findElement(info.targetEndpoint.elementId, this.rawPipelineModel);
       (pe.payload as InvocablePipelineElementUnion).configured = false;
       pe.settings.openCustomize = true;
-      info.targetEndpoint.setType("empty");
+      info.targetEndpoint.setType('empty');
+      this.JsplumbBridge.repaintEverything();
       this.validatePipeline();
     });
 
-    this.JsplumbBridge.bind("connectionDrag", connection => {
-      this.JsplumbBridge.selectEndpoints().each(function (endpoint) {
+    this.JsplumbBridge.bind(EVENT_CONNECTION_DRAG, () => {
+      this.JsplumbBridge.selectEndpoints().each(endpoint => {
         if (endpoint.isTarget && endpoint.connections.length === 0) {
-          endpoint.setType("highlight");
+          endpoint.setType('highlight');
         }
       });
-
+      this.JsplumbBridge.repaintEverything();
     });
-    this.JsplumbBridge.bind("connectionAborted", connection => {
+    this.JsplumbBridge.bind(EVENT_CONNECTION_ABORT, () => {
       this.JsplumbBridge.selectEndpoints().each(endpoint => {
         if (endpoint.isTarget && endpoint.connections.length === 0) {
-          endpoint.setType("empty");
+          endpoint.setType('empty');
         }
       });
-    })
+      this.JsplumbBridge.repaintEverything();
+    });
 
-    this.JsplumbBridge.bind("connection", (info, originalEvent) => {
-      var pe = this.ObjectProvider.findElement(info.target.id, this.rawPipelineModel);
+    this.JsplumbBridge.bind(EVENT_CONNECTION, (info) => {
+      const pe = this.ObjectProvider.findElement(info.target.id, this.rawPipelineModel);
       if (pe.settings.openCustomize) {
         this.currentPipelineModel = this.ObjectProvider.makePipeline(this.rawPipelineModel);
         pe.settings.loadingStatus = true;
         this.ObjectProvider.updatePipeline(this.currentPipelineModel)
             .subscribe(pipelineModificationMessage => {
               pe.settings.loadingStatus = false;
-              info.targetEndpoint.setType("token");
+              info.targetEndpoint.setType('token');
               this.validatePipeline();
               this.modifyPipeline(pipelineModificationMessage.pipelineModifications);
               if (this.JsplumbService.isFullyConnected(pe, this.preview)) {
-                let payload = pe.payload as InvocablePipelineElementUnion;
+                const payload = pe.payload as InvocablePipelineElementUnion;
                 if ((payload.staticProperties && payload.staticProperties.length > 0) || this.isCustomOutput(pe)) {
                   this.showCustomizeDialog({a: false, b: pe});
                 } else {
@@ -348,7 +340,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
               pe.settings.loadingStatus = false;
               this.JsplumbBridge.detach(info.connection);
               if (Array.isArray(status.error)) {
-                let matchingResultMessage = (status.error as any[]).map(e => MatchingResultMessage.fromData(e as MatchingResultMessage));
+                const matchingResultMessage = (status.error as any[]).map(e => MatchingResultMessage.fromData(e as MatchingResultMessage));
                 this.showMatchingErrorDialog(matchingResultMessage);
               } else {
                 this.showErrorDialog(status.error.title, status.error.description);
@@ -357,7 +349,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
       }
     });
 
-    window.onresize = (event) => {
+    window.onresize = () => {
       this.JsplumbBridge.repaintEverything();
     };
 
@@ -369,9 +361,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
   modifyPipeline(pipelineModifications) {
     if (pipelineModifications) {
       pipelineModifications.forEach(modification => {
-        var id = modification.domId;
-        if (id !== "undefined") {
-          var pe = this.ObjectProvider.findElement(id, this.rawPipelineModel);
+        const id = modification.domId;
+        if (id !== 'undefined') {
+          const pe = this.ObjectProvider.findElement(id, this.rawPipelineModel);
           (pe.payload as InvocablePipelineElementUnion).staticProperties = modification.staticProperties;
           (pe.payload as DataProcessorInvocation).outputStrategies = modification.outputStrategies;
           (pe.payload as InvocablePipelineElementUnion).inputStreams = modification.inputStreams;
@@ -381,7 +373,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
   }
 
   isCustomOutput(pe) {
-    var custom = false;
+    let custom = false;
     pe.payload.outputStrategies.forEach(strategy => {
       if (strategy instanceof CustomOutputStrategy) {
         custom = true;
@@ -395,11 +387,11 @@ export class PipelineComponent implements OnInit, OnDestroy {
       this.pipelineCacheRunning = true;
       this.pipelineCacheRunningChanged.emit(this.pipelineCacheRunning);
       this.PipelinePositioningService.collectPipelineElementPositions(this.pipelineCanvasMetadata, this.rawPipelineModel);
-      let updateCachedPipeline = this.EditorService.updateCachedPipeline(this.rawPipelineModel);
-      let updateCachedCanvasMetadata = this.EditorService.updateCachedCanvasMetadata(this.pipelineCanvasMetadata);
-      forkJoin([updateCachedPipeline, updateCachedCanvasMetadata]).subscribe(msg => {
+      const updateCachedPipeline = this.EditorService.updateCachedPipeline(this.rawPipelineModel);
+      const updateCachedCanvasMetadata = this.EditorService.updateCachedCanvasMetadata(this.pipelineCanvasMetadata);
+      forkJoin([updateCachedPipeline, updateCachedCanvasMetadata]).subscribe(() => {
         this.pipelineCacheRunning = false;
-        this.pipelineCacheRunningChanged.emit(this.pipelineCacheRunning)
+        this.pipelineCacheRunningChanged.emit(this.pipelineCacheRunning);
         this.pipelineCached = true;
         this.pipelineCachedChanged.emit(this.pipelineCached);
       });
@@ -410,10 +402,10 @@ export class PipelineComponent implements OnInit, OnDestroy {
     this.dialog.open(ConfirmDialogComponent, {
       width: '500px',
       data: {
-        "title": title,
-        "subtitle": description,
-        "okTitle": "Ok",
-        "confirmAndCancel": false
+        'title': title,
+        'subtitle': description,
+        'okTitle': 'Ok',
+        'confirmAndCancel': false
       },
     });
   }
@@ -421,9 +413,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
   showMatchingErrorDialog(matchingResultMessage: MatchingResultMessage[]) {
     this.dialogService.open(MatchingErrorComponent, {
       panelType: PanelType.STANDARD_PANEL,
-      title: "Invalid Connection",
+      title: 'Invalid Connection',
       data: {
-        "matchingResultMessage": matchingResultMessage
+        'matchingResultMessage': matchingResultMessage
       }
     });
   }
@@ -431,11 +423,11 @@ export class PipelineComponent implements OnInit, OnDestroy {
   showCustomizeDialog(pipelineElementInfo: Tuple2<Boolean, PipelineElementConfig>) {
     const dialogRef = this.dialogService.open(CustomizeComponent, {
       panelType: PanelType.SLIDE_IN_PANEL,
-      title: "Customize " + pipelineElementInfo.b.payload.name,
-      width: "50vw",
+      title: 'Customize ' + pipelineElementInfo.b.payload.name,
+      width: '50vw',
       data: {
-        "pipelineElement": pipelineElementInfo.b,
-        "restrictedEditMode": pipelineElementInfo.a
+        'pipelineElement': pipelineElementInfo.b,
+        'restrictedEditMode': pipelineElementInfo.a
       }
     });
 
@@ -444,9 +436,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
         pipelineElementInfo.b.settings.openCustomize = false;
         (pipelineElementInfo.b.payload as InvocablePipelineElementUnion).configured = true;
         if (!(pipelineElementInfo.b.payload instanceof DataSinkInvocation)) {
-          this.JsplumbBridge.activateEndpoint("out-" + pipelineElementInfo.b.payload.dom, pipelineElementInfo.b.settings.completed);
+          this.JsplumbBridge.activateEndpoint('out-' + pipelineElementInfo.b.payload.dom, pipelineElementInfo.b.settings.completed);
         }
-        this.JsplumbBridge.getSourceEndpoint(pipelineElementInfo.b.payload.dom).setType("token");
+        this.JsplumbBridge.getSourceEndpoint(pipelineElementInfo.b.payload.dom).toggleType('token');
         this.triggerPipelineCacheUpdate();
         this.announceConfiguredElement(pipelineElementInfo.b);
         if (this.previewModeActive) {
@@ -463,7 +455,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   initiatePipelineElementPreview() {
     if (!this.previewModeActive) {
-      let pipeline = this.ObjectProvider.makePipeline(this.rawPipelineModel);
+      const pipeline = this.ObjectProvider.makePipeline(this.rawPipelineModel);
       this.EditorService.initiatePipelinePreview(pipeline).subscribe(response => {
         this.pipelinePreview = response;
         this.previewModeActive = true;
@@ -475,7 +467,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   deletePipelineElementPreview(resume: boolean) {
     if (this.previewModeActive) {
-      this.EditorService.deletePipelinePreviewRequest(this.pipelinePreview.previewId).subscribe(response => {
+      this.EditorService.deletePipelinePreviewRequest(this.pipelinePreview.previewId).subscribe(() => {
         this.previewModeActive = false;
         if (resume) {
           this.initiatePipelineElementPreview();
diff --git a/ui/src/app/editor/services/jsplumb-bridge.service.ts b/ui/src/app/editor/services/jsplumb-bridge.service.ts
index ca94753..6a3eb77 100644
--- a/ui/src/app/editor/services/jsplumb-bridge.service.ts
+++ b/ui/src/app/editor/services/jsplumb-bridge.service.ts
@@ -16,16 +16,17 @@
  *
  */
 
-import {jsPlumbInstance} from 'jsplumb'
+import { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
+import { SelectOptions } from "@jsplumb/core";
 
 export class JsplumbBridge {
 
-    constructor(private jsPlumbInstance: jsPlumbInstance) {
+    constructor(private jsPlumbInstance: BrowserJsPlumbInstance) {
     }
 
     activateEndpoint(endpointId: string, endpointEnabled: boolean) {
         let endpoint = this.getEndpointById(endpointId);
-        endpoint.setEnabled(endpointEnabled);
+        endpoint.enabled = endpointEnabled;
     }
 
     activateEndpointWithType(endpointId: string, endpointEnabled: boolean, endpointType: string) {
@@ -48,15 +49,17 @@ export class JsplumbBridge {
     }
 
     repaintEverything() {
-        this.jsPlumbInstance.repaintEverything(true);
+        this.jsPlumbInstance.repaintEverything();
     }
 
     deleteEveryEndpoint() {
-        this.jsPlumbInstance.deleteEveryEndpoint();
+        // TODO
+        //this.jsPlumbInstance.destroy();
+        //this.jsPlumbInstance.deleteEveryEndpoint();
     }
 
     setContainer(container) {
-        this.jsPlumbInstance.setContainer(container);
+        this.jsPlumbInstance.setContainer(document.getElementById(container));
     }
 
     unbind(element) {
@@ -94,19 +97,21 @@ export class JsplumbBridge {
 
     getEndpointCount(id) {
         // @ts-ignore
-        return this.jsPlumbInstance.selectEndpoints({element: id}).length;
+        return this.jsPlumbInstance.selectEndpoints({element: document.getElementById(id)}).length;
     }
 
     detach(connection) {
         this.jsPlumbInstance.deleteConnection(connection);
     }
 
-    getConnections(filter) {
-        return this.jsPlumbInstance.getConnections(filter, {});
+    getConnections(filter: SelectOptions<Element>) {
+        return this.jsPlumbInstance.getConnections(filter);
     }
 
-    addEndpoint(element, options) {
-        return this.jsPlumbInstance.addEndpoint(element, options);
+    addEndpoint(pipelineElementDomId: string,
+                options: any) {
+        options.cssClass = "sp-no-pan";
+        return this.jsPlumbInstance.addEndpoint(document.getElementById(pipelineElementDomId), options);
     }
 
     connect(connection) {
@@ -114,17 +119,13 @@ export class JsplumbBridge {
     }
 
     removeAllEndpoints(element) {
-        this.jsPlumbInstance.removeAllEndpoints(element);
+        this.jsPlumbInstance.removeAllEndpoints(document.getElementById(element));
     }
 
     registerEndpointTypes(typeInfo) {
         this.jsPlumbInstance.registerEndpointTypes(typeInfo);
     }
 
-    draggable(element, option) {
-        this.jsPlumbInstance.draggable(element, option);
-    }
-
     // TODO: Overloading Functions?
     setSuspendDrawing(bool1, bool2?) {
         if (bool2 === undefined) {
@@ -135,12 +136,10 @@ export class JsplumbBridge {
     }
 
     getAllConnections() {
-        return this.jsPlumbInstance.getAllConnections();
+        return this.jsPlumbInstance.getConnections();
     }
 
     reset() {
         this.jsPlumbInstance.reset();
     }
 }
-
-//JsplumbBridge.$inject = [];
diff --git a/ui/src/app/editor/services/jsplumb-config.service.ts b/ui/src/app/editor/services/jsplumb-config.service.ts
index 44de61c..2ddacd9 100644
--- a/ui/src/app/editor/services/jsplumb-config.service.ts
+++ b/ui/src/app/editor/services/jsplumb-config.service.ts
@@ -16,8 +16,10 @@
  *
  */
 
-import {Injectable} from "@angular/core";
-import {JsplumbSettings} from "../model/jsplumb.model";
+import { Injectable } from "@angular/core";
+import { JsplumbSettings } from "../model/jsplumb.model";
+import { BezierConnector } from "@jsplumb/connector-bezier";
+import { EndpointTypeDescriptor } from "@jsplumb/core";
 
 @Injectable()
 export class JsplumbConfigService {
@@ -33,6 +35,37 @@ export class JsplumbConfigService {
         return this.makeConfig(this.makeSettings(6, 2, 15, 15, 1, 40));
     }
 
+    getEndpointTypeConfig(): Record<string, EndpointTypeDescriptor> {
+        return {
+            "empty": {
+                paintStyle: {
+                    fill: "white",
+                    stroke: "#9E9E9E",
+                    strokeWidth: 2,
+                }
+            },
+            "token": {
+                paintStyle: {
+                    fill: "#BDBDBD",
+                    stroke: "#9E9E9E",
+                    strokeWidth: 2
+                },
+                hoverPaintStyle: {
+                    fill: "#BDBDBD",
+                    stroke: "#4CAF50",
+                    strokeWidth: 4,
+                }
+            },
+            "highlight": {
+                paintStyle: {
+                    fill: "white",
+                    stroke: "#4CAF50",
+                    strokeWidth: 4
+                }
+            }
+        }
+    }
+
     makeConfig(settings) {
         let config = {} as any;
         config.streamEndpointOptions = this.makeStreamEndpointOptions(settings);
@@ -59,45 +92,29 @@ export class JsplumbConfigService {
 
     makeStreamEndpointOptions(settings: JsplumbSettings) {
         return {
-            endpoint: ["Dot", {radius: settings.dotRadius}],
+            endpoint: {type: "Dot", options: {radius: settings.dotRadius}},
             connectorStyle: {stroke: "#BDBDBD", outlineStroke: "#BDBDBD", strokeWidth: settings.lineWidth},
-            connector: ["Bezier", {curviness: settings.curviness}],
-            isSource: true,
+            connector: {type: BezierConnector.type, options: {curviness: settings.curviness}},
+            source: true,
+            type: "token",
             maxConnections: -1,
             anchor: "Right",
-            type: "token",
-            connectorOverlays: [
-                ["Arrow", {
-                    width: settings.arrowWidth, length: settings.arrowLength, location: 0.5, id: "arrow", paintStyle: {
-                        fillStyle: "#BDBDBD",
-                        stroke: "#9E9E9E",
-                        strokeWidth: settings.arrowLineWidth
-                    }
-                }],
-            ]
+            connectorOverlays: this.defaultConnectorOverlay(settings)
         }
     }
 
     makeSepaEndpointOptions(settings) {
         return {
-            endpoint: ["Dot", {radius: settings.dotRadius}],
+            endpoint: {type: "Dot", options: {radius: settings.dotRadius}},
             connectorStyle: {
                 stroke: "#BDBDBD", outlineStroke: "#9E9E9E", strokeWidth: settings.lineWidth
             },
-            connector: ["Bezier", {curviness: settings.curviness}],
-            isSource: true,
+            connector: {type: BezierConnector.type, options: {curviness: settings.curviness}},
+            source: true,
             maxConnections: -1,
             anchor: "Right",
             type: "empty",
-            connectorOverlays: [
-                ["Arrow", {
-                    width: settings.arrowWidth, length: settings.arrowLength, location: 0.5, id: "arrow", paintStyle: {
-                        fill: "#BDBDBD",
-                        stroke: "#9E9E9E",
-                        strokeWidth: settings.arrowLineWidth
-                    }
-                }],
-            ],
+            connectorOverlays: this.defaultConnectorOverlay(settings),
             parameters: {
                 endpointType: "output"
             }
@@ -106,11 +123,23 @@ export class JsplumbConfigService {
 
     makeLeftTargetPointOptions(settings) {
         return {
-            endpoint: ["Dot", {radius: settings.dotRadius}],
+            endpoint: {type: "Dot", options: {radius: settings.dotRadius}},
             type: "empty",
             anchor: "Left",
-            isTarget: true
+            target: true
         }
     }
 
+    defaultConnectorOverlay(settings) {
+        return [{
+            type: "Arrow", options: {
+                width: settings.arrowWidth, length: settings.arrowLength, location: 0.5, id: "arrow", paintStyle: {
+                    fillStyle: "#BDBDBD",
+                    stroke: "#9E9E9E",
+                    strokeWidth: settings.arrowLineWidth
+                }
+            }
+        }];
+    }
+
 }
diff --git a/ui/src/app/editor/services/jsplumb-factory.service.ts b/ui/src/app/editor/services/jsplumb-factory.service.ts
index ff16482..6671a82 100644
--- a/ui/src/app/editor/services/jsplumb-factory.service.ts
+++ b/ui/src/app/editor/services/jsplumb-factory.service.ts
@@ -16,70 +16,75 @@
  *
  */
 
-import {jsPlumb, jsPlumbInstance} from "jsplumb";
-import {JsplumbBridge} from "./jsplumb-bridge.service";
-import {Injectable} from "@angular/core";
+import { JsPlumbInstance } from "@jsplumb/core";
+import { JsplumbBridge } from "./jsplumb-bridge.service";
+import { Injectable } from "@angular/core";
+import { BrowserJsPlumbInstance, ContainmentType, newInstance } from "@jsplumb/browser-ui";
+import { PipelineElementDraggedService } from "./pipeline-element-dragged.service";
+import { JsplumbConfigService } from "./jsplumb-config.service";
 
 @Injectable()
 export class JsplumbFactoryService {
 
-  pipelineEditorInstance: jsPlumbInstance;
-  pipelinePreviewInstance: jsPlumbInstance;
+    pipelineEditorInstance: BrowserJsPlumbInstance;
+    pipelinePreviewInstance: BrowserJsPlumbInstance;
 
-  pipelineEditorBridge: JsplumbBridge;
-  pipelinePreviewBridge: JsplumbBridge;
+    pipelineEditorBridge: JsplumbBridge;
+    pipelinePreviewBridge: JsplumbBridge;
 
-  constructor() {
-    this.pipelineEditorInstance = this.makePipelineEditorInstance();
-    this.pipelinePreviewInstance = this.makePipelinePreviewInstance();
+    constructor(private pipelineElementDraggedService: PipelineElementDraggedService,
+                private jsplumbConfigService: JsplumbConfigService) {
+    }
 
-    this.pipelineEditorBridge = new JsplumbBridge(this.pipelineEditorInstance);
-    this.pipelinePreviewBridge = new JsplumbBridge(this.pipelinePreviewInstance);
-
-    this.prepareJsplumb(this.pipelineEditorInstance);
-    this.prepareJsplumb(this.pipelinePreviewInstance);
-  }
+    getJsplumbBridge(previewConfig: boolean): JsplumbBridge {
+        if (!previewConfig) {
+            if (!this.pipelineEditorBridge) {
+                this.pipelineEditorInstance = this.makePipelineEditorInstance();
+                this.prepareJsplumb(this.pipelineEditorInstance);
+                this.pipelineEditorBridge = new JsplumbBridge(this.pipelineEditorInstance);
+            }
+            return this.pipelineEditorBridge;
+        } else {
+            if (!this.pipelinePreviewBridge) {
+                this.pipelinePreviewInstance = this.makePipelinePreviewInstance();
+                this.prepareJsplumb(this.pipelinePreviewInstance);
+                this.pipelinePreviewBridge = new JsplumbBridge(this.pipelinePreviewInstance);
+            }
+            return this.pipelinePreviewBridge;
+        }
+    }
 
-  getJsplumbBridge(previewConfig: boolean): JsplumbBridge {
-    return previewConfig ? this.pipelineEditorBridge : this.pipelinePreviewBridge;
-  }
+    makePipelineEditorInstance(): BrowserJsPlumbInstance {
+        return newInstance({
+            container: document.getElementById("assembly"), dragOptions: {
+                containment: ContainmentType.parent,
+                cursor: 'pointer',
+                zIndex: 2000,
+                drag: params => {
+                    this.pipelineElementDraggedService.notify({x: params.pos.x, y: params.pos.y});
+                }
+            },
+        });
+    }
 
-  makePipelineEditorInstance(): jsPlumbInstance {
-    return jsPlumb.getInstance();
-  }
+    makePipelinePreviewInstance(): BrowserJsPlumbInstance {
+        return newInstance({
+            container: document.getElementById("assembly-preview"),
+            elementsDraggable: false
+        });
+    }
 
-  makePipelinePreviewInstance(): jsPlumbInstance {
-    return jsPlumb.getInstance();
-  }
+    prepareJsplumb(jsplumbInstance: JsPlumbInstance) {
+        jsplumbInstance.registerEndpointTypes(this.jsplumbConfigService.getEndpointTypeConfig());
+    }
 
-  prepareJsplumb(jsplumbInstance: jsPlumbInstance) {
-    jsplumbInstance.registerEndpointTypes({
-      "empty": {
-        paintStyle: {
-          fill: "white",
-          stroke: "#9E9E9E",
-          strokeWidth: 2,
-        }
-      },
-      "token": {
-        paintStyle: {
-          fill: "#BDBDBD",
-          stroke: "#9E9E9E",
-          strokeWidth: 2
-        },
-        hoverPaintStyle: {
-          fill: "#BDBDBD",
-          stroke: "#4CAF50",
-          strokeWidth: 4,
-        }
-      },
-      "highlight": {
-        paintStyle: {
-          fill: "white",
-          stroke: "#4CAF50",
-          strokeWidth: 4
+    destroy(preview: boolean) {
+        if (preview) {
+            this.pipelinePreviewInstance.destroy();
+            this.pipelinePreviewBridge = undefined;
+        } else {
+            this.pipelineEditorInstance.destroy();
+            this.pipelineEditorBridge = undefined;
         }
-      }
-    });
-  }
+    }
 }
diff --git a/ui/src/app/editor/services/jsplumb.service.ts b/ui/src/app/editor/services/jsplumb.service.ts
index 4a30738..7ac0e1c 100644
--- a/ui/src/app/editor/services/jsplumb.service.ts
+++ b/ui/src/app/editor/services/jsplumb.service.ts
@@ -16,15 +16,11 @@
  *
  */
 
-import {JsplumbConfigService} from "./jsplumb-config.service";
-import {JsplumbBridge} from "./jsplumb-bridge.service";
-import {Injectable} from "@angular/core";
-import {
-    InvocablePipelineElementUnion,
-    PipelineElementConfig,
-    PipelineElementUnion
-} from "../model/editor.model";
-import {PipelineElementTypeUtils} from "../utils/editor.utils";
+import { JsplumbConfigService } from "./jsplumb-config.service";
+import { JsplumbBridge } from "./jsplumb-bridge.service";
+import { Injectable } from "@angular/core";
+import { InvocablePipelineElementUnion, PipelineElementConfig, PipelineElementUnion } from "../model/editor.model";
+import { PipelineElementTypeUtils } from "../utils/editor.utils";
 import {
     DataProcessorInvocation,
     DataSinkInvocation,
@@ -33,9 +29,8 @@ import {
     SpDataStream,
     SpDataStreamUnion
 } from "../../core-model/gen/streampipes-model";
-import {PipelineElementDraggedService} from "./pipeline-element-dragged.service";
-import {JsplumbEndpointService} from "./jsplumb-endpoint.service";
-import {JsplumbFactoryService} from "./jsplumb-factory.service";
+import { JsplumbEndpointService } from "./jsplumb-endpoint.service";
+import { JsplumbFactoryService } from "./jsplumb-factory.service";
 import { EditorService } from './editor.service';
 
 @Injectable()
@@ -46,8 +41,7 @@ export class JsplumbService {
     constructor(private JsplumbConfigService: JsplumbConfigService,
                 private JsplumbFactory: JsplumbFactoryService,
                 private jsplumbEndpointService: JsplumbEndpointService,
-                private editorService: EditorService,
-                private pipelineElementDraggedService: PipelineElementDraggedService) {
+                private editorService: EditorService) {
     }
 
     isFullyConnected(pipelineElementConfig: PipelineElementConfig,
@@ -55,7 +49,7 @@ export class JsplumbService {
         let jsplumbBridge = this.JsplumbFactory.getJsplumbBridge(previewConfig);
         let payload = pipelineElementConfig.payload as InvocablePipelineElementUnion;
         return payload.inputStreams == null ||
-          jsplumbBridge.getConnections({target: $("#" +payload.dom)}).length == payload.inputStreams.length;
+          jsplumbBridge.getConnections({target: document.getElementById(payload.dom)}).length == payload.inputStreams.length;
     }
 
     makeRawPipeline(pipelineModel: Pipeline,
@@ -107,7 +101,7 @@ export class JsplumbService {
     createElement(pipelineModel: PipelineElementConfig[],
                   pipelineElement: InvocablePipelineElementUnion,
                   pipelineElementDomId: string) {
-        var pipelineElementDom = $("#" + pipelineElementDomId);
+        let pipelineElementDom = $("#" + pipelineElementDomId);
 
         let pipelineElementConfig = this.createNewPipelineElementConfigWithFixedCoordinates(pipelineElementDom, pipelineElement, false);
         pipelineModel.push(pipelineElementConfig);
@@ -123,7 +117,7 @@ export class JsplumbService {
                           pipelineElement: InvocablePipelineElementUnion,
                           $parentElement,
                           previewConfig: boolean) {
-        var $target;
+        let $target;
         if (pipelineElement instanceof DataProcessorInvocation) {
             $target = this.dataProcessorDropped(pipelineElementDomId, pipelineElement as DataProcessorInvocation, true, false);
             this.connectNodes($parentElement, $target, previewConfig);
@@ -136,7 +130,7 @@ export class JsplumbService {
     connectNodes($parentElement,
                  $target,
                  previewConfig: boolean) {
-        var options;
+        let options;
         let jsplumbBridge = this.getBridge(previewConfig);
         if ($parentElement.hasClass("stream")) {
             // TODO: getJsplumbConfig depends on isPreview. Not implemented yet
@@ -145,7 +139,7 @@ export class JsplumbService {
             // TODO: getJsplumbConfig depends on isPreview. Not implemented yet
             options = this.jsplumbEndpointService.getJsplumbConfig(true).sepaEndpointOptions;
         }
-        var sourceEndPoint;
+        let sourceEndPoint;
         if (jsplumbBridge.selectEndpoints({source: $parentElement}).length > 0) {
             if (!(jsplumbBridge.selectEndpoints({source: $parentElement}).get(0).isFull())) {
                 sourceEndPoint = jsplumbBridge.selectEndpoints({source: $parentElement}).get(0)
@@ -156,15 +150,15 @@ export class JsplumbService {
             sourceEndPoint = jsplumbBridge.addEndpoint($parentElement, options);
         }
 
-        var targetEndPoint = jsplumbBridge.selectEndpoints({target: $target}).get(0);
+        let targetEndPoint = jsplumbBridge.selectEndpoints({target: $target}).get(0);
 
         jsplumbBridge.connect({source: sourceEndPoint, target: targetEndPoint, detachable: true});
         jsplumbBridge.repaintEverything();
     }
 
     createNewPipelineElementConfigWithFixedCoordinates($parentElement, json, isPreview): PipelineElementConfig {
-        var x = $parentElement.position().left;
-        var y = $parentElement.position().top;
+        let x = $parentElement.position().left;
+        let y = $parentElement.position().top;
         return this.createNewPipelineElementConfigAtPosition(x, y, json, isPreview);
     }
 
@@ -232,10 +226,10 @@ export class JsplumbService {
     }
 
     makeId(count: number) {
-        var text = "";
-        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        let text = "";
+        const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
-        for (var i = 0; i < count; i++)
+        for (let i = 0; i < count; i++)
             text += possible.charAt(Math.floor(Math.random() * possible.length));
 
         return text;
@@ -253,20 +247,15 @@ export class JsplumbService {
             return this.dataProcessorDropped(pipelineElementDomId, pipelineElement, endpoints, preview);
         } else if (pipelineElement instanceof DataSinkInvocation) {
             return this.dataSinkDropped(pipelineElementDomId, pipelineElement, endpoints, preview);
-        };
+        }
     }
 
     dataSetDropped(pipelineElementDomId: string,
                    pipelineElement: SpDataSet,
                    endpoints: boolean,
                    preview: boolean) {
-
-
-
         let jsplumbBridge = this.getBridge(preview);
-
         if (endpoints) {
-            this.makeDraggableIfNotPreview(pipelineElementDomId, jsplumbBridge, preview);
             let endpointOptions = this.jsplumbEndpointService.getStreamEndpoint(preview, pipelineElementDomId);
             jsplumbBridge.addEndpoint(pipelineElementDomId, endpointOptions);
         }
@@ -280,7 +269,6 @@ export class JsplumbService {
                       preview: boolean) {
         let jsplumbBridge = this.getBridge(preview);
         if (endpoints) {
-            this.makeDraggableIfNotPreview(pipelineElementDomId, jsplumbBridge, preview);
             let endpointOptions = this.jsplumbEndpointService.getStreamEndpoint(preview, pipelineElementDomId);
             jsplumbBridge.addEndpoint(pipelineElementDomId, endpointOptions);
         }
@@ -305,8 +293,6 @@ export class JsplumbService {
                     endpoints: boolean,
                     preview: boolean): string {
         let jsplumbBridge = this.getBridge(preview);
-        this.makeDraggableIfNotPreview(pipelineElementDomId, jsplumbBridge, preview);
-
         if (endpoints) {
             if (pipelineElement.inputStreams.length < 2) { //1 InputNode
                 jsplumbBridge.addEndpoint(pipelineElementDomId,
@@ -324,16 +310,4 @@ export class JsplumbService {
     getBridge(previewConfig: boolean): JsplumbBridge {
         return this.JsplumbFactory.getJsplumbBridge(previewConfig);
     }
-
-    makeDraggableIfNotPreview(pipelineElementDomId: string,
-                              jsplumbBridge: JsplumbBridge,
-                              previewConfig: boolean) {
-        if (!previewConfig) {
-            jsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent',
-                drag: (e => {
-                    this.pipelineElementDraggedService.notify({x: e.pos[0], y: e.pos[1]});
-                })
-            });
-        }
-    }
 }
diff --git a/ui/src/app/editor/services/object-provider.service.ts b/ui/src/app/editor/services/object-provider.service.ts
index ff64406..e644181 100644
--- a/ui/src/app/editor/services/object-provider.service.ts
+++ b/ui/src/app/editor/services/object-provider.service.ts
@@ -71,13 +71,7 @@ export class ObjectProvider {
     }
 
     findElement(elementId, rawPipelineModel: PipelineElementConfig[]): PipelineElementConfig {
-        let result = {} as PipelineElementConfig;
-        rawPipelineModel.forEach(pe => {
-            if (pe.payload.dom === elementId) {
-                result = pe;
-            }
-        });
-        return result;
+        return rawPipelineModel.find(pe => pe.payload.dom === elementId) || {} as PipelineElementConfig;
     }
 
     addElementNew(pipeline, currentPipelineElements: PipelineElementConfig[]): Pipeline {
@@ -88,7 +82,7 @@ export class ObjectProvider {
                     let payload = pe.payload;
                     payload = this.prepareElement(payload as InvocablePipelineElementUnion);
                     let connections = JsplumbBridge.getConnections({
-                        target: $("#" + payload.dom)
+                        target: document.getElementById(payload.dom)
                     });
                     for (let i = 0; i < connections.length; i++) {
                         payload.connectedTo.push(connections[i].sourceId);
diff --git a/ui/src/app/editor/services/pipeline-positioning.service.ts b/ui/src/app/editor/services/pipeline-positioning.service.ts
index bf41d28..8e6b682 100644
--- a/ui/src/app/editor/services/pipeline-positioning.service.ts
+++ b/ui/src/app/editor/services/pipeline-positioning.service.ts
@@ -17,163 +17,186 @@
  */
 
 import * as dagre from "dagre";
-import {JsplumbBridge} from "./jsplumb-bridge.service";
-import {JsplumbConfigService} from "./jsplumb-config.service";
-import {JsplumbService} from "./jsplumb.service";
-import {Injectable} from "@angular/core";
-import {PipelineElementConfig} from "../model/editor.model";
+import { JsplumbBridge } from "./jsplumb-bridge.service";
+import { JsplumbConfigService } from "./jsplumb-config.service";
+import { JsplumbService } from "./jsplumb.service";
+import { Injectable } from "@angular/core";
+import { PipelineElementConfig } from "../model/editor.model";
 import {
-    DataProcessorInvocation,
-    DataSinkInvocation,
-    PipelineCanvasMetadata,
-    PipelineElementMetadata,
-    SpDataStream
+  DataProcessorInvocation,
+  DataSinkInvocation,
+  PipelineCanvasMetadata,
+  PipelineElementMetadata,
+  SpDataStream
 } from "../../core-model/gen/streampipes-model";
-import {JsplumbFactoryService} from "./jsplumb-factory.service";
-import {ObjectProvider} from "./object-provider.service";
+import { JsplumbFactoryService } from "./jsplumb-factory.service";
+import { ObjectProvider } from "./object-provider.service";
+import { Connection } from "@jsplumb/core";
 
 @Injectable()
 export class PipelinePositioningService {
 
-    constructor(private JsplumbService: JsplumbService,
-                private JsplumbConfigService: JsplumbConfigService,
-                private JsplumbFactoryService: JsplumbFactoryService,
-                private ObjectProvider: ObjectProvider) {
-    }
+  constructor(private JsplumbService: JsplumbService,
+              private JsplumbConfigService: JsplumbConfigService,
+              private JsplumbFactoryService: JsplumbFactoryService,
+              private ObjectProvider: ObjectProvider) {
+  }
+
+  collectPipelineElementPositions(pipelineCanvasMetadata: PipelineCanvasMetadata,
+                                  rawPipelineModel: PipelineElementConfig[]): PipelineCanvasMetadata {
+    rawPipelineModel.forEach(pe => {
+      this.collectPipelineElementPosition(pe.payload.dom, pipelineCanvasMetadata);
+    });
+    return pipelineCanvasMetadata;
+  }
 
-    collectPipelineElementPositions(pipelineCanvasMetadata: PipelineCanvasMetadata,
-                                    rawPipelineModel: PipelineElementConfig[]): PipelineCanvasMetadata {
-        rawPipelineModel.forEach(pe => {
-           this.collectPipelineElementPosition(pe.payload.dom, pipelineCanvasMetadata);
-        });
-        return pipelineCanvasMetadata;
+  collectPipelineElementPosition(domId: string,
+                                 pipelineCanvasMetadata: PipelineCanvasMetadata) {
+    let elementRef = $(`#${domId}`);
+    if (elementRef && elementRef.position()) {
+      let leftPos = elementRef.position().left;
+      let topPos = elementRef.position().top;
+      if (!pipelineCanvasMetadata.pipelineElementMetadata) {
+        pipelineCanvasMetadata.pipelineElementMetadata = {};
+      }
+      if (!pipelineCanvasMetadata.pipelineElementMetadata[domId]) {
+        pipelineCanvasMetadata.pipelineElementMetadata[domId] = new PipelineElementMetadata();
+      }
+      pipelineCanvasMetadata.pipelineElementMetadata[domId].position = {
+        x: leftPos,
+        y: topPos
+      };
     }
+  }
 
-    collectPipelineElementPosition(domId: string,
-                                   pipelineCanvasMetadata: PipelineCanvasMetadata) {
-        let elementRef = $(`#${domId}`);
-        if (elementRef && elementRef.position()) {
-            let leftPos = elementRef.position().left;
-            let topPos = elementRef.position().top;
-            if (!pipelineCanvasMetadata.pipelineElementMetadata) {
-                pipelineCanvasMetadata.pipelineElementMetadata = {};
-            }
-            if (!pipelineCanvasMetadata.pipelineElementMetadata[domId]) {
-                pipelineCanvasMetadata.pipelineElementMetadata[domId] = new PipelineElementMetadata();
-            }
-            pipelineCanvasMetadata.pipelineElementMetadata[domId].position = {
-                x: leftPos,
-                y: topPos
-            };
+  displayPipeline(rawPipelineModel: PipelineElementConfig[],
+                  targetCanvas,
+                  previewConfig: boolean,
+                  autoLayout: boolean,
+                  pipelineCanvasMetadata?: PipelineCanvasMetadata) {
+    let jsPlumbBridge = this.JsplumbFactoryService.getJsplumbBridge(previewConfig);
+
+    let jsplumbConfig = previewConfig ?
+      this.JsplumbConfigService.getPreviewConfig() :
+      this.JsplumbConfigService.getEditorConfig();
+
+    rawPipelineModel.forEach(currentPe => {
+      if (!currentPe.settings.disabled) {
+        if (currentPe.type === "stream" || currentPe.type === "set") {
+          this.JsplumbService.dataStreamDropped(
+            currentPe.payload.dom,
+            currentPe.payload as SpDataStream,
+            true,
+            previewConfig
+          );
+        }
+        if (currentPe.type === "sepa") {
+          this.JsplumbService.dataProcessorDropped(
+            currentPe.payload.dom,
+            currentPe.payload as DataProcessorInvocation,
+            true,
+            previewConfig
+          );
+        }
+        if (currentPe.type === "action") {
+          this.JsplumbService.dataSinkDropped(
+            currentPe.payload.dom,
+            currentPe.payload as DataSinkInvocation,
+            true,
+            previewConfig
+          );
         }
+      }
+    });
+
+    this.connectPipelineElements(rawPipelineModel, previewConfig, jsplumbConfig, jsPlumbBridge);
+    if (autoLayout) {
+      this.layoutGraph(
+        targetCanvas,
+        "div[id^='jsplumb']",
+        previewConfig ? 75 : 110,
+        previewConfig
+      );
+    } else if (pipelineCanvasMetadata) {
+      this.layoutGraphFromCanvasMetadata(pipelineCanvasMetadata);
     }
+    jsPlumbBridge.repaintEverything();
+  }
 
-    displayPipeline(rawPipelineModel: PipelineElementConfig[],
-                    targetCanvas,
-                    previewConfig: boolean,
-                    autoLayout: boolean,
-                    pipelineCanvasMetadata?: PipelineCanvasMetadata) {
-        let jsPlumbBridge = this.JsplumbFactoryService.getJsplumbBridge(previewConfig);
-        let jsplumbConfig = previewConfig ? this.JsplumbConfigService.getPreviewConfig() : this.JsplumbConfigService.getEditorConfig();
-        rawPipelineModel.forEach(currentPe => {
-            if (!currentPe.settings.disabled) {
-                if (currentPe.type === "stream" || currentPe.type === "set") {
-                    this.JsplumbService.dataStreamDropped(currentPe.payload.dom,
-                        currentPe.payload as SpDataStream,
-                        true,
-                        previewConfig);
-                }
-                if (currentPe.type === "sepa") {
-                    this.JsplumbService.dataProcessorDropped(currentPe.payload.dom, currentPe.payload as DataProcessorInvocation, true, previewConfig);
-                }
-                if (currentPe.type === "action") {
-                    this.JsplumbService.dataSinkDropped(currentPe.payload.dom, currentPe.payload as DataSinkInvocation, true, previewConfig);
-                }
-            }
-        });
+  layoutGraph(canvasId: string,
+              nodeIdentifier: string,
+              dimension: number,
+              previewConfig: boolean) {
 
-        this.connectPipelineElements(rawPipelineModel, previewConfig, jsplumbConfig, jsPlumbBridge);
-        if (autoLayout) {
-            this.layoutGraph(targetCanvas, "span[id^='jsplumb']", previewConfig ? 75 : 110, previewConfig);
-        } else if (pipelineCanvasMetadata)  {
-            this.layoutGraphFromCanvasMetadata(pipelineCanvasMetadata);
+    let jsPlumbBridge = this.JsplumbFactoryService.getJsplumbBridge(previewConfig);
+    let g = new dagre.graphlib.Graph();
+    g.setGraph({rankdir: "LR", ranksep: previewConfig ? "50" : "100"});
+    g.setDefaultEdgeLabel(function () {
+      return {}
+    });
+    let nodes = $(canvasId).find(nodeIdentifier).get();
+    nodes.forEach((n) => {
+      g.setNode(n.id, {label: n.id, width: dimension, height: dimension});
+    });
+
+    let edges = jsPlumbBridge.getAllConnections() as Connection[];
+    edges.forEach(edge => {
+      g.setEdge(edge.source.id, edge.target.id);
+    });
+
+    dagre.layout(g);
+    g.nodes().forEach(v => {
+      let elementRef = $(`#${v}`);
+      elementRef.css("left", g.node(v).x + "px");
+      elementRef.css("top", g.node(v).y + "px");
+    });
+  }
+
+  layoutGraphFromCanvasMetadata(pipelineCanvasMetadata: PipelineCanvasMetadata) {
+    Object.entries(pipelineCanvasMetadata.pipelineElementMetadata).forEach(
+      ([key, value]) => {
+        let elementRef = $(`#${key}`);
+        if (elementRef) {
+          elementRef.css("left", value.position.x + "px");
+          elementRef.css("top", value.position.y + "px");
         }
-        jsPlumbBridge.repaintEverything();
-    }
+      }
+    );
+  }
 
-    layoutGraph(canvasId: string,
-                nodeIdentifier: string,
-                dimension: number,
-                previewConfig: boolean) {
-        let jsPlumbBridge = this.JsplumbFactoryService.getJsplumbBridge(previewConfig);
-        let g = new dagre.graphlib.Graph();
-        g.setGraph({rankdir: "LR", ranksep: previewConfig ? "50" : "100"});
-        g.setDefaultEdgeLabel(function () {
-            return {};
-        });
-
-        let nodes = $(canvasId).find(nodeIdentifier).get();
-        nodes.forEach((n) => {
-            g.setNode(n.id, {label: n.id, width: dimension, height: dimension});
-        });
-
-        let edges = jsPlumbBridge.getAllConnections();
-        edges.forEach(edge => {
-            g.setEdge(edge.source.id, edge.target.id);
-        });
-
-        dagre.layout(g);
-        g.nodes().forEach(v => {
-            let elementRef = $(`#${v}`);
-            elementRef.css("left", g.node(v).x + "px");
-            elementRef.css("top", g.node(v).y + "px");
-        });
-    }
+  connectPipelineElements(rawPipelineModel: PipelineElementConfig[],
+                          previewConfig: boolean,
+                          jsplumbConfig: any,
+                          jsPlumbBridge: JsplumbBridge) {
+    let source, target;
+    jsPlumbBridge.setSuspendDrawing(true);
+    rawPipelineModel.forEach(pe => {
+      if (pe.type == "sepa" || pe.type == "action") {
+        if (!(pe.settings.disabled) && pe.payload.connectedTo) {
+          pe.payload.connectedTo.forEach((connection, index) => {
+            source = connection;
+            target = pe.payload.dom;
 
-    layoutGraphFromCanvasMetadata(pipelineCanvasMetadata: PipelineCanvasMetadata) {
-        Object.entries(pipelineCanvasMetadata.pipelineElementMetadata).forEach(
-            ([key, value]) => {
-                let elementRef = $(`#${key}`);
-                if (elementRef) {
-                    elementRef.css("left", value.position.x + "px");
-                    elementRef.css("top", value.position.y + "px");
-                }
-            }
-        );
-    }
+            let sourceEndpointId = "out-" + connection;
+            let inTargetEndpointId = "in-" + index + "-" + pe.payload.dom;
+            jsPlumbBridge.connect(
+              {
+                uuids: [sourceEndpointId, inTargetEndpointId],
+                detachable: !previewConfig
+              }
+            );
+            jsPlumbBridge.activateEndpointWithType(sourceEndpointId, true, "token");
+            jsPlumbBridge.activateEndpointWithType(inTargetEndpointId, true, "token");
 
-    connectPipelineElements(rawPipelineModel: PipelineElementConfig[],
-                            previewConfig: boolean,
-                            jsplumbConfig: any,
-                            jsPlumbBridge: JsplumbBridge) {
-        let source, target;
-        jsPlumbBridge.setSuspendDrawing(true);
-        rawPipelineModel.forEach(pe => {
-            if (pe.type == "sepa" || pe.type == "action") {
-                if (!(pe.settings.disabled) && pe.payload.connectedTo) {
-                    pe.payload.connectedTo.forEach((connection, index) => {
-                        source = connection;
-                        target = pe.payload.dom;
-
-                        let sourceEndpointId = "out-" + connection;
-                        let inTargetEndpointId = "in-" + index + "-" + pe.payload.dom;
-                        jsPlumbBridge.connect(
-                            {
-                                uuids: [sourceEndpointId, inTargetEndpointId],
-                                detachable: !previewConfig
-                            }
-                        );
-                        jsPlumbBridge.activateEndpointWithType(sourceEndpointId, true, "token");
-                        jsPlumbBridge.activateEndpointWithType(inTargetEndpointId, true, "token");
-
-                        if (!(pe.payload instanceof DataSinkInvocation) && !(this.ObjectProvider.hasConnectedPipelineElement(pe.payload.dom, rawPipelineModel))) {
-                            let outTargetEndpointId = "out-" + pe.payload.dom;
-                            jsPlumbBridge.activateEndpointWithType(outTargetEndpointId, true, "token");
-                        }
-                    });
-                }
+            if (!(pe.payload instanceof DataSinkInvocation) &&
+              !(this.ObjectProvider.hasConnectedPipelineElement(pe.payload.dom, rawPipelineModel))) {
+              let outTargetEndpointId = "out-" + pe.payload.dom;
+              jsPlumbBridge.activateEndpointWithType(outTargetEndpointId, true, "token");
             }
-        });
-        jsPlumbBridge.setSuspendDrawing(false, true);
-    }
+          });
+        }
+      }
+    });
+    jsPlumbBridge.setSuspendDrawing(false, true);
+  }
 }
diff --git a/ui/src/app/editor/services/pipeline-validation.service.ts b/ui/src/app/editor/services/pipeline-validation.service.ts
index 3ce8951..b3329fe 100644
--- a/ui/src/app/editor/services/pipeline-validation.service.ts
+++ b/ui/src/app/editor/services/pipeline-validation.service.ts
@@ -23,6 +23,7 @@ import { PipelineElementConfig } from '../model/editor.model';
 import { DataProcessorInvocation, DataSinkInvocation } from '../../core-model/gen/streampipes-model';
 import { JsplumbFactoryService } from './jsplumb-factory.service';
 import { UserErrorMessage } from '../../core-model/base/UserErrorMessage';
+import { Connection } from "@jsplumb/core";
 
 @Injectable()
 export class PipelineValidationService {
@@ -31,11 +32,11 @@ export class PipelineValidationService {
     pipelineValid = false;
 
     availableErrorMessages: UserErrorMessage[] = [
-      new UserErrorMessage('Did you add a data stream?', 'Any pipeline needs at least one data stream.'),
-      new UserErrorMessage('Did you add a data sink?', 'Any pipeline needs at least one data sink.'),
-      new UserErrorMessage('Did you connect all elements?', 'No orphaned elements are allowed within a pipeline, make sure to connect all elements.'),
-      new UserErrorMessage('Separate pipelines', 'It seems you\'ve created more than one pipeline at once. Create only one pipeline at a time!'),
-      new UserErrorMessage('Did you configure all elements?', 'There\'s a pipeline element which is missing some configuration.')
+        new UserErrorMessage('Did you add a data stream?', 'Any pipeline needs at least one data stream.'),
+        new UserErrorMessage('Did you add a data sink?', 'Any pipeline needs at least one data sink.'),
+        new UserErrorMessage('Did you connect all elements?', 'No orphaned elements are allowed within a pipeline, make sure to connect all elements.'),
+        new UserErrorMessage('Separate pipelines', 'It seems you\'ve created more than one pipeline at once. Create only one pipeline at a time!'),
+        new UserErrorMessage('Did you configure all elements?', 'There\'s a pipeline element which is missing some configuration.')
     ];
 
     constructor(private jsplumbFactoryService: JsplumbFactoryService) {
@@ -61,10 +62,10 @@ export class PipelineValidationService {
 
         if (!this.isEmptyPipeline(rawPipelineModel)) {
             this.buildErrorMessages(streamInAssembly,
-              actionInAssembly,
-              allElementsConnected,
-              onlyOnePipelineCreated,
-              allElementsConfigured);
+                actionInAssembly,
+                allElementsConnected,
+                onlyOnePipelineCreated,
+                allElementsConfigured);
         } else {
             this.errorMessages = [];
         }
@@ -76,7 +77,7 @@ export class PipelineValidationService {
 
     isEmptyPipeline(rawPipelineModel) {
         return !this.isActionInAssembly(rawPipelineModel) &&
-          !this.isStreamInAssembly(rawPipelineModel) && !this.isInAssembly(rawPipelineModel, 'sepa');
+            !this.isStreamInAssembly(rawPipelineModel) && !this.isInAssembly(rawPipelineModel, 'sepa');
     }
 
     buildErrorMessages(streamInAssembly, actionInAssembly, allElementsConnected, onlyOnePipelineCreated, allElementsConfigured) {
@@ -149,8 +150,7 @@ export class PipelineValidationService {
         g.setDefaultEdgeLabel(function () {
             return {};
         });
-        const nodes = $('#assembly').find('span[id^=\'jsplumb\']').get();
-
+        const nodes = $('#assembly').find('div[id^=\'jsplumb\']').get();
         for (let i = 0; i < nodes.length; i++) {
             const n = nodes[i];
             const elementOptions = this.getElementOptions(n.id, rawPipelineModel);
@@ -163,7 +163,7 @@ export class PipelineValidationService {
                 });
             }
         }
-        const edges = jsplumbBridge.getAllConnections();
+        const edges = jsplumbBridge.getAllConnections() as Connection[];
         edges.forEach((edge, i) => {
             const c = edges[i];
             g.setEdge(c.source.id, c.target.id);
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 4ae48bf..8fcfa66 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -24,7 +24,7 @@
 @import '~bootstrap/dist/css/bootstrap.css';
 @import '~@fortawesome/fontawesome-free/css/all.css';
 @import '~angular-ui-tree/dist/angular-ui-tree.min.css';
-@import '~jsplumb/css/jsplumbtoolkit-defaults.css';
+@import '~@jsplumb/browser-ui/css/jsplumbtoolkit.css';
 @import '~ng-prettyjson/dist/ng-prettyjson.min.css';
 @import '~prismjs/themes/prism.css';
 @import '~angular-loading-bar/build/loading-bar.min.css';