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 2021/03/09 21:50:02 UTC

[incubator-streampipes] branch dev updated (9852d60 -> e700ab5)

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

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


    from 9852d60  [STREAMPIPES-310] Improve export speed of data download
     new 8e5074e  [STREAMPIPES-304] Restrict pipeline canvas to 100% remaining height, add maximize feature
     new f24b1b4  [STREAMPIPES-304] Bump and migrate Jsplumb version
     new 7038392  [STREAMPIPES-304] Improve zoom feature
     new 8c0fc10  [STREAMPIPES-304] Improve pan behaviour
     new 5a48645  [STREAMPIPES-248] Auto-restart orphaned pipelines
     new e700ab5  Merge branch 'dev' of github.com:apache/incubator-streampipes into dev

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../backend/StreamPipesBackendApplication.java     |  30 ++-
 .../api/AbstractPipelineElementResource.java       |  14 ++
 .../api/InvocablePipelineElementResource.java      |  17 +-
 .../container/init/RunningInstances.java           |   4 +
 ui/angular.json                                    |   2 -
 ui/package.json                                    |   6 +-
 .../pipeline-assembly.component.html               | 112 +++++++----
 .../pipeline-assembly.component.scss               | 121 +++++++++++-
 .../pipeline-assembly.component.ts                 | 141 +++++++++----
 .../pipeline-element-options.component.ts          |   8 +-
 .../components/pipeline/pipeline.component.html    |  10 +-
 .../components/pipeline/pipeline.component.ts      |  59 ++++--
 ui/src/app/editor/editor.component.html            |  37 ++--
 ui/src/app/editor/editor.component.scss            |   6 +-
 ui/src/app/editor/editor.component.ts              |  12 +-
 ui/src/app/editor/editor.module.ts                 |  15 +-
 .../filter/enabled-pipeline-element.filter.ts      |  15 ++
 ui/src/app/editor/model/editor.model.ts            |   7 +-
 .../src/app/editor/model/jsplumb.model.ts          |  11 +-
 .../app/editor/services/jsplumb-bridge.service.ts  |  79 +++++---
 .../app/editor/services/jsplumb-config.service.ts  |  48 ++---
 .../editor/services/jsplumb-endpoint.service.ts    |  63 ++++++
 .../app/editor/services/jsplumb-factory.service.ts |  85 ++++++++
 ui/src/app/editor/services/jsplumb.service.ts      | 219 +++++++++------------
 .../app/editor/services/object-provider.service.ts |  24 ++-
 .../services/pipeline-canvas-scrolling.service.ts} |  19 +-
 .../app/editor/services/pipeline-editor.service.ts |  19 +-
 .../services/pipeline-element-dragged.service.ts}  |  15 +-
 .../pipeline-element-recommendation.service.ts     |   8 +-
 .../services/pipeline-positioning.service.ts       | 124 ++++++------
 .../editor/services/pipeline-validation.service.ts |  42 ++--
 .../preview/pipeline-preview.component.ts          |   8 +-
 ui/src/scss/_variables.scss                        |   3 +-
 ui/src/scss/main.scss                              |   6 +-
 .../sp/{pipeline-assembly.scss => jsplumb.scss}    |   3 +
 ui/src/scss/sp/main.scss                           |  33 +---
 36 files changed, 935 insertions(+), 490 deletions(-)
 create mode 100644 ui/src/app/editor/filter/enabled-pipeline-element.filter.ts
 copy streampipes-client/src/main/java/org/apache/streampipes/client/api/SupportsPipelineApi.java => ui/src/app/editor/model/jsplumb.model.ts (82%)
 create mode 100644 ui/src/app/editor/services/jsplumb-endpoint.service.ts
 create mode 100644 ui/src/app/editor/services/jsplumb-factory.service.ts
 copy ui/src/app/{dashboard/services/refresh-dashboard.service.ts => editor/services/pipeline-canvas-scrolling.service.ts} (67%)
 copy ui/src/app/{dashboard/services/refresh-dashboard.service.ts => editor/services/pipeline-element-dragged.service.ts} (70%)
 copy ui/src/scss/sp/{pipeline-assembly.scss => jsplumb.scss} (96%)


[incubator-streampipes] 03/06: [STREAMPIPES-304] Improve zoom feature

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 703839269ab391d1e179fc78871c4cb2d5ec52bf
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Tue Mar 9 20:23:24 2021 +0100

    [STREAMPIPES-304] Improve zoom feature
---
 ui/angular.json                                    |  1 -
 ui/package.json                                    |  3 +-
 .../pipeline-assembly.component.html               | 85 ++++++++++++-------
 .../pipeline-assembly.component.scss               | 94 ++++++++++++++++++++--
 .../pipeline-assembly.component.ts                 | 88 +++++++++++++++-----
 .../components/pipeline/pipeline.component.ts      |  4 +-
 ui/src/app/editor/editor.module.ts                 |  2 -
 ui/src/scss/_variables.scss                        |  3 +-
 ui/src/scss/main.scss                              |  1 -
 9 files changed, 212 insertions(+), 69 deletions(-)

diff --git a/ui/angular.json b/ui/angular.json
index c1ae092..7b233ae 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -37,7 +37,6 @@
               "node_modules/plotly.js/dist/plotly.min.js",
               "node_modules/jquery/dist/jquery.min.js",
               "node_modules/datatables.net/js/jquery.dataTables.js",
-              "node_modules/jquery.panzoom/dist/jquery.panzoom.js",
               "node_modules/jquery-ui-dist/jquery-ui.js",
               "node_modules/quill/dist/quill.js",
             ]
diff --git a/ui/package.json b/ui/package.json
index 8d3a3b9..efc485d 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -35,6 +35,7 @@
     "@fortawesome/fontawesome-free": "5.12.1",
     "@ngx-loading-bar/core": "5.1.0",
     "@ngx-loading-bar/http-client": "5.1.0",
+    "@panzoom/panzoom": "^4.3.2",
     "@stomp/ng2-stompjs": "7.2.0",
     "@swimlane/ngx-charts": "16.0.0",
     "angular-datatables": "^10.0.0",
@@ -58,7 +59,6 @@
     "file-saver": "1.3.8",
     "jquery": "2.1.3",
     "jquery-ui-dist": "1.12.1",
-    "jquery.panzoom": "2.0.5",
     "jshint": "2.11.1",
     "jsplumb": "^2.15.5",
     "jszip": "3.2.1",
@@ -74,7 +74,6 @@
     "ngx-auto-scroll": "^1.1.0",
     "ngx-color-picker": "9.0.0",
     "ngx-echarts": "^6.0.0",
-    "ngx-perfect-scrollbar": "^10.1.0",
     "ngx-quill": "12.0.1",
     "ngx-showdown": "5.1.0",
     "path": "^0.12.7",
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 1eddb4e..bb088c6 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
@@ -24,26 +24,17 @@
                     (click)="submit()" type="submit">
                 <i class="material-icons">save</i>&nbsp;<span>Save pipeline</span>
             </button>
-            <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"
-                    [disabled]="!selectMode"
-                    (click)="toggleSelectMode()">
-                <i class="material-icons">open_with</i>
-            </button>
-            <button mat-button mat-icon-button matTooltip="Select" [matTooltipPosition]="'above'"
-                    [disabled]="selectMode"
-                    (click)="toggleSelectMode()">
-                <i class="material-icons">mode_edit</i>
-            </button>
-            <button mat-button mat-icon-button matTooltip="Zoom In" [matTooltipPosition]="'above'"
-                    [disabled]="currentZoomLevel == 1"
-                    (click)="zoomIn()">
-                <i class="material-icons">zoom_in</i>
-            </button>
-            <button mat-button mat-icon-button matTooltip="Zoom Out" [matTooltipPosition]="'above'"
-                    [disabled]="currentZoomLevel == 0.5"
-                    (click)="zoomOut()">
-                <i class="material-icons">zoom_out</i>
-            </button>
+            <!-- TODO: Use this once copying of elements is supported -->
+<!--            <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"-->
+<!--                    [disabled]="!selectMode"-->
+<!--                    (click)="toggleSelectMode()">-->
+<!--                <i class="material-icons">open_with</i>-->
+<!--            </button>-->
+<!--            <button mat-button mat-icon-button matTooltip="Select" [matTooltipPosition]="'above'"-->
+<!--                    [disabled]="selectMode"-->
+<!--                    (click)="toggleSelectMode()">-->
+<!--                <i class="material-icons">mode_edit</i>-->
+<!--            </button>-->
             <button mat-button mat-icon-button matTooltip="Auto Layout" [matTooltipPosition]="'above'"
                     (click)="autoLayout()">
                 <i class="material-icons">settings_overscan</i>
@@ -102,17 +93,49 @@
         </div>
     </div>
     <div id="outerAssemblyArea" class="outerAssembly sp-blue-border-nopadding">
-        <div id="assembly" class="canvas jtk-surface jtk-surface-no-pan">
-            <pipeline [pipelineValid]="pipelineValid"
-                      [canvasId]="'assembly'"
-                      [rawPipelineModel]="rawPipelineModel"
-                      [allElements]="allElements"
-                      [preview]="false"
-                      [pipelineCached]="pipelineCached"
-                      [pipelineCacheRunning]="pipelineCacheRunning"
-                      (pipelineCachedChanged)="pipelineCached=$event"
-                      (pipelineCacheRunningChanged)="pipelineCacheRunning=$event">
-            </pipeline>
+        <div class="pipeline-canvas-outer">
+            <div class="pan-control">
+                <div class="pan-zoom-control-buttons">
+                    <div class="pan-zoom-button pan-left" (click)="panLeft()">
+                        <i class="material-icons">keyboard_arrow_left</i>
+                    </div>
+                    <div class="pan-zoom-button pan-right" (click)="panRight()">
+                        <i class="material-icons">keyboard_arrow_right</i>
+                    </div>
+                    <div class="pan-zoom-button pan-home" (click)="panHome()">
+                        <i class="material-icons">home</i>
+                    </div>
+                    <div class="pan-zoom-button pan-up" (click)="panUp()">
+                        <i class="material-icons">keyboard_arrow_up</i>
+                    </div>
+                    <div class="pan-zoom-button pan-down" (click)="panDown()">
+                        <i class="material-icons">keyboard_arrow_down</i>
+                    </div>
+                </div>
+            </div>
+            <div class="zoom-control">
+                <div class="pan-zoom-control-buttons">
+                    <div class="pan-zoom-button zoom-in" (click)="zoomIn()">
+                        <i class="material-icons">zoom_in</i>
+                    </div>
+                    <mat-divider class="zoom-divider"></mat-divider>
+                    <div class="pan-zoom-button zoom-out" (click)="zoomOut()">
+                        <i class="material-icons">zoom_out</i>
+                    </div>
+                </div>
+            </div>
+            <div id="assembly" class="canvas">
+                <pipeline [pipelineValid]="pipelineValid"
+                          [canvasId]="'assembly'"
+                          [rawPipelineModel]="rawPipelineModel"
+                          [allElements]="allElements"
+                          [preview]="false"
+                          [pipelineCached]="pipelineCached"
+                          [pipelineCacheRunning]="pipelineCacheRunning"
+                          (pipelineCachedChanged)="pipelineCached=$event"
+                          (pipelineCacheRunningChanged)="pipelineCacheRunning=$event">
+                </pipeline>
+            </div>
         </div>
     </div>
 </div>
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
index 5bbd778..7629335 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
@@ -16,6 +16,8 @@
  *
  */
 
+@import '../../../../scss/_variables.scss';
+
 .mat-spinner-color::ng-deep svg circle{
     stroke: #FFFFFF !important;
 }
@@ -33,24 +35,27 @@
     //position: relative;
     left: 0px;
     top: 0px;
-    overflow: visible;
+    //overflow: visible;
     //box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12);
     //padding: 5px;
-    flex: 1;
-    width: 100%;
+    display: flex;
+    flex: 1 1 0;
+    overflow: hidden;
 }
 
 .pipeline-canvas-outer {
-    flex: 1;
+    position: relative;
+    width: 100%;
+    height: 100%;
 }
 
 .canvas {
-    overflow: auto;
+    //overflow: scroll;
     position: relative;
     left: 0px;
     top: 0px;
-    height: 100%;
-    width: 100%;
+    height: 3000px;
+    width: 3000px;
     z-index: 0;
     margin: -1px;
 
@@ -67,3 +72,78 @@
     overflow: scroll !important;
     cursor:default;
 }
+
+.zoom-control {
+    position: absolute;
+    right: 10px;
+    bottom: 10px;
+    width: 40px;
+    height: 80px;
+    z-index: 10;
+    background: white;
+    box-shadow: 0.175em 0.175em 0 0 rgba(15, 28, 63, 0.125);
+    border-radius: 10px;
+
+}
+
+.pan-control {
+    position: absolute;
+    right: 60px;
+    bottom: 10px;
+    width: 80px;
+    height: 80px;
+    background: white;
+    box-shadow: 0.175em 0.175em 0 0 rgba(15, 28, 63, 0.125);
+    border-radius: 50%;
+    z-index: 10;
+}
+
+.pan-zoom-button {
+    position: absolute;
+    cursor: pointer;
+    color: $sp-color-accent;
+}
+
+.pan-zoom-control-buttons {
+    position: relative;
+}
+
+.zoom-divider {
+    top: 40px;
+    position: relative;
+}
+
+.zoom-in {
+    top: 8px;
+    left: 8px;
+}
+
+.zoom-out {
+    top: 48px;
+    left: 8px;
+}
+
+.pan-up {
+    left: 28px;
+    top: 2px;
+}
+
+.pan-down {
+    left: 28px;
+    top: 58px;
+}
+
+.pan-left {
+    left: -3px;
+    top: 30px;
+}
+
+.pan-right {
+    left: 55px;
+    top: 30px;
+}
+
+.pan-home {
+    left: 28px;
+    top: 30px;
+}
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 8b44161..9ce6213 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
@@ -42,6 +42,7 @@ import {EditorService} from "../../services/editor.service";
 import {PipelineService} from "../../../platform-services/apis/pipeline.service";
 import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scrolling.service";
 import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
+import Panzoom, {PanzoomObject} from "@panzoom/panzoom";
 
 
 @Component({
@@ -84,6 +85,7 @@ export class PipelineAssemblyComponent implements OnInit {
 
     config: any = {};
     @ViewChild("assembly") pipelineCanvas: ElementRef;
+    panzoom: PanzoomObject;
 
     constructor(private JsPlumbFactoryService: JsplumbFactoryService,
                 private PipelinePositioningService: PipelinePositioningService,
@@ -118,20 +120,35 @@ export class PipelineAssemblyComponent implements OnInit {
         });
     }
 
-    ngAfterViewInit() {
-        ($("#assembly") as any).panzoom({
-            disablePan: true,
-            increment: 0.25,
-            minScale: 0.5,
-            maxScale: 1.5,
-            contain: 'invert'
-        });
 
-        $("#assembly").on('panzoomzoom', (e, panzoom, scale) => {
-            this.currentZoomLevel = scale;
-            this.JsplumbBridge.setZoom(scale);
-            this.JsplumbBridge.repaintEverything();
-        });
+    ngAfterViewInit() {
+        const elem = document.getElementById('assembly')
+        this.panzoom = Panzoom(elem, {
+            maxScale: 5,
+            excludeClass: "jtk-draggable",
+            canvas: true,
+            contain: "outside"
+        })
+        //panzoom.pan(10, 10)
+        //panzoom.zoom(2, { animate: true })
+        // ($("#assembly") as any).panzoom({
+        //     disablePan: false,
+        //     increment: 0.25,
+        //     minScale: 0.5,
+        //     maxScale: 1.5,
+        //     contain: 'invert',
+        //     excludeClass: "jtk-managed"
+        // });
+        //
+        // $("#assembly").on('panzoomzoom', (e, panzoom, scale) => {
+        //     this.currentZoomLevel = scale;
+        //     this.JsplumbBridge.setZoom(scale);
+        //     this.JsplumbBridge.repaintEverything();
+        // });
+        //
+        // $("#assembly").on('panzoompan', (e, panzoom, scale) => {
+        //     console.log(e);
+        // });
     }
 
     autoLayout() {
@@ -140,14 +157,6 @@ export class PipelineAssemblyComponent implements OnInit {
     }
 
     toggleSelectMode() {
-        if (this.selectMode) {
-            ($("#assembly") as any).panzoom("option", "disablePan", false);
-            this.selectMode = false;
-        }
-        else {
-            ($("#assembly") as any).panzoom("option", "disablePan", true);
-            this.selectMode = true;
-        }
     }
 
     zoomOut() {
@@ -159,7 +168,10 @@ export class PipelineAssemblyComponent implements OnInit {
     }
 
     doZoom(zoomOut) {
-        ($("#assembly") as any).panzoom("zoom", zoomOut);
+        zoomOut ? this.panzoom.zoomOut() : this.panzoom.zoomIn();
+        this.currentZoomLevel = this.panzoom.getScale();
+        this.JsplumbBridge.setZoom(this.currentZoomLevel);
+        this.JsplumbBridge.repaintEverything();
     }
 
     showClearAssemblyConfirmDialog(event: any) {
@@ -273,4 +285,36 @@ export class PipelineAssemblyComponent implements OnInit {
         this.pipelineCanvasMaximizedEmitter.emit(this.pipelineCanvasMaximized);
     }
 
+    panLeft() {
+        this.pan(100, 0);
+    }
+
+    panRight() {
+        console.log("panning right");
+        this.pan(-100, 0);
+    }
+
+    panUp() {
+        this.pan(0, 100);
+    }
+
+    panDown() {
+        this.pan(0, -100);
+    }
+
+    panHome() {
+        this.panAbsolute(0, 0);
+    }
+
+    pan(xOffset: number, yOffset: number) {
+        let currentPan = this.panzoom.getPan();
+        let panX = Math.min(0, currentPan.x + xOffset);
+        let panY = Math.min(0, currentPan.y + yOffset);
+        let values = this.panzoom.pan(panX, panY);
+    }
+
+    panAbsolute(x: number, y: number) {
+        this.panzoom.pan(x, y);
+    }
+
 }
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index c5486e7..a4a3a34 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -99,8 +99,8 @@ export class PipelineComponent implements OnInit, OnDestroy {
   currentZoomLevel: any;
   TransitionService: any;
 
-  canvasWidth: string = "1000%";
-  canvasHeight: string = "1000%";
+  canvasWidth: string = "100%";
+  canvasHeight: string = "100%";
 
   JsplumbBridge: JsplumbBridge;
 
diff --git a/ui/src/app/editor/editor.module.ts b/ui/src/app/editor/editor.module.ts
index 1794b56..b5c10e0 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -63,7 +63,6 @@ import {PipelineElementTemplateConfigComponent} from "./components/pipeline-elem
 import {EnabledPipelineElementFilter} from "./filter/enabled-pipeline-element.filter";
 import {PipelineElementDraggedService} from "./services/pipeline-element-dragged.service";
 import {PipelineCanvasScrollingService} from "./services/pipeline-canvas-scrolling.service";
-import {PerfectScrollbarModule} from "ngx-perfect-scrollbar";
 import {JsplumbEndpointService} from "./services/jsplumb-endpoint.service";
 import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
 
@@ -83,7 +82,6 @@ import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
         MatProgressSpinnerModule,
         ShowdownModule,
         ReactiveFormsModule,
-        PerfectScrollbarModule
     ],
     declarations: [
         CompatibleElementsComponent,
diff --git a/ui/src/scss/_variables.scss b/ui/src/scss/_variables.scss
index 9c2954e..73382b0 100644
--- a/ui/src/scss/_variables.scss
+++ b/ui/src/scss/_variables.scss
@@ -21,10 +21,11 @@ $sp-color-primary-light: rgb(59, 205, 76);
 $sp-color-accent: rgb(27, 20, 100);
 $sp-color-accent-light-blue: rgb(59, 92, 149);
 $sp-color-accent-light: rgb(156, 156, 156);
+$sp-color-accent-light-transparent: rgba(156, 156, 156, 0.4);
 
 $sp-color-stream: #FFEB3B;
 $sp-color-set: #ffa23b;
 $sp-color-processor: #009688;
 $sp-color-sink: #3F51B5;
 
-$sp-color-error: #B71C1C;
\ No newline at end of file
+$sp-color-error: #B71C1C;
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 020b977..0ff0bdd 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -35,7 +35,6 @@
 //@import '~material-design-icons/iconfont/material-icons.css';
 @import '~quill/dist/quill.snow.css';
 @import '~swagger-ui/dist/swagger-ui.css';
-@import '~perfect-scrollbar/css/perfect-scrollbar.css';
 
 @import '~roboto-fontface/css/roboto/roboto-fontface.css';
 


[incubator-streampipes] 01/06: [STREAMPIPES-304] Restrict pipeline canvas to 100% remaining height, add maximize feature

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8e5074ef46fbd53dc6a0cc4b480c4905021a662f
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Thu Feb 25 23:06:52 2021 +0100

    [STREAMPIPES-304] Restrict pipeline canvas to 100% remaining height, add maximize feature
---
 ui/package.json                                    |  2 +-
 .../pipeline-assembly.component.html               | 40 +++++++++++++---------
 .../pipeline-assembly.component.scss               | 37 +++++++++++++++++++-
 .../pipeline-assembly.component.ts                 | 32 ++++++++++-------
 ui/src/app/editor/editor.component.html            | 37 ++++++++++----------
 ui/src/app/editor/editor.component.scss            |  6 +++-
 ui/src/app/editor/editor.component.ts              | 12 +++----
 ui/src/scss/main.scss                              |  3 +-
 ui/src/scss/sp/main.scss                           | 25 --------------
 9 files changed, 112 insertions(+), 82 deletions(-)

diff --git a/ui/package.json b/ui/package.json
index af7ae1f..ad82c27 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -65,7 +65,7 @@
     "konva": "3.2.4",
     "leaflet": "1.6.0",
     "lodash": "4.17.20",
-    "material-design-icons": "3.0.1",
+    "material-icons": "^0.5.3",
     "ng-file-upload": "9.0.13",
     "ng-pick-datetime": "7.0.0",
     "ng-prettyjson": "0.1.8",
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 6fe56a1..f2424b9 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
@@ -16,38 +16,37 @@
   ~
   -->
 
-<div>
-    <div class="assemblyOptions sp-blue-bg">
+<div fxFlex="100" fxLayout="column">
+    <div class="pipeline-assembly-options sp-blue-bg">
         <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
             <button mat-button matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
-                    style="display:flex;align-items:center;" class="settings-bar-icon-button"
+                    style="display:flex;align-items:center;"
                     [disabled]="!PipelineValidationService.pipelineValid"
                     (click)="submit()" type="submit">
                 <mat-icon>save</mat-icon>&nbsp;<span>Save pipeline</span>
             </button>
-            <button mat-button matTooltip="Pan" [matTooltipPosition]="'above'"
-                    class="settings-bar-icon-button"
+            <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"
                     [disabled]="!selectMode"
                     (click)="toggleSelectMode()">
                 <i class="material-icons">open_with</i>
             </button>
-            <button mat-button matTooltip="Select" [matTooltipPosition]="'above'"
-                    class="settings-bar-icon-button" [disabled]="selectMode"
+            <button mat-button mat-icon-button matTooltip="Select" [matTooltipPosition]="'above'"
+                    [disabled]="selectMode"
                     (click)="toggleSelectMode()">
                 <i class="material-icons">mode_edit</i>
             </button>
-            <button mat-button matTooltip="Zoom In" [matTooltipPosition]="'above'"
-                    class="settings-bar-icon-button" [disabled]="currentZoomLevel == 1"
+            <button mat-button mat-icon-button matTooltip="Zoom In" [matTooltipPosition]="'above'"
+                    [disabled]="currentZoomLevel == 1"
                     (click)="zoomIn()">
                 <i class="material-icons">zoom_in</i>
             </button>
-            <button mat-button matTooltip="Zoom Out" [matTooltipPosition]="'above'"
-                    class="settings-bar-icon-button" [disabled]="currentZoomLevel == 0.5"
+            <button mat-button mat-icon-button matTooltip="Zoom Out" [matTooltipPosition]="'above'"
+                    [disabled]="currentZoomLevel == 0.5"
                     (click)="zoomOut()">
                 <i class="material-icons">zoom_out</i>
             </button>
-            <button mat-button matTooltip="Auto Layout" [matTooltipPosition]="'above'"
-                    class="settings-bar-icon-button" (click)="autoLayout()">
+            <button mat-button mat-icon-button matTooltip="Auto Layout" [matTooltipPosition]="'above'"
+                    (click)="autoLayout()">
                 <i class="material-icons">settings_overscan</i>
             </button>
             <div fxLayout="column" fxLayoutAlign="start center" class="pipeline-cache-block">
@@ -88,8 +87,17 @@
                     </div>
                 </div>
             </div>
-            <button mat-button matTooltip="Clear Assembly Area" [matTooltipPosition]="'above'" [disabled]="EditorService.pipelineAssemblyEmpty"
-                    class="md-icon-button" (click)="showClearAssemblyConfirmDialog($event)">
+            <button mat-button mat-icon-button
+                    [matTooltip]="pipelineCanvasMaximized ? 'Minimize' : 'Maximize'"
+                    [matTooltipPosition]="'above'"
+                    (click)="toggleCanvasMaximized()">
+                <i class="material-icons" *ngIf="!pipelineCanvasMaximized">open_in_full</i>
+                <i class="material-icons" *ngIf="pipelineCanvasMaximized">close_fullscreen</i>
+            </button>
+            <button mat-button mat-icon-button matTooltip="Clear Assembly Area"
+                    [matTooltipPosition]="'above'"
+                    [disabled]="EditorService.pipelineAssemblyEmpty"
+                    (click)="showClearAssemblyConfirmDialog($event)">
                 <i class="material-icons">clear</i>
             </button>
         </div>
@@ -108,4 +116,4 @@
             ></pipeline>
         </div>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
index ea90e47..d252c0d 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
@@ -27,4 +27,39 @@
 .pipeline-cache-block {
     height: 100%;
     margin-left:15px;
-}
\ No newline at end of file
+}
+
+.outerAssembly {
+    position: relative;
+    left: 0px;
+    top: 0px;
+    overflow: visible;
+    //box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12);
+    padding: 5px;
+    height: 100%;
+    flex: 1;
+    width: 100%;
+}
+
+.pipeline-canvas-outer {
+    flex: 1;
+}
+
+.canvas {
+    overflow: visible;
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    height: 300%;
+    width: 300%;
+    z-index: 0;
+    margin: -1px;
+
+    background-color: white;
+    background-image: linear-gradient(90deg, rgba(208, 208, 208, .5) 10%, transparent 0%), linear-gradient(rgba(208, 208, 208, .5) 10%, transparent 0%);
+    background-size: 15px 15px;
+}
+
+.pipeline-assembly-options {
+    color: white;
+}
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 be84a50..6979f0e 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,7 +16,7 @@
  *
  */
 
-import {Component, Input, NgZone, OnInit,} from "@angular/core";
+import {Component, EventEmitter, Input, NgZone, OnInit, Output,} from "@angular/core";
 import {JsplumbBridge} from "../../services/jsplumb-bridge.service";
 import {PipelinePositioningService} from "../../services/pipeline-positioning.service";
 import {PipelineValidationService} from "../../services/pipeline-validation.service";
@@ -40,17 +40,8 @@ import {PipelineService} from "../../../platform-services/apis/pipeline.service"
 })
 export class PipelineAssemblyComponent implements OnInit {
 
-    PipelineEditorService: any;
-    DialogBuilder: any;
-    currentMouseOverElement: any;
-    currentZoomLevel: any;
-    preview: any;
-
     @Input()
     rawPipelineModel: PipelineElementConfig[];
-    selectMode: any;
-    currentPipelineName: any;
-    currentPipelineDescription: any;
 
     @Input()
     currentModifiedPipelineId: any;
@@ -58,6 +49,19 @@ export class PipelineAssemblyComponent implements OnInit {
     @Input()
     allElements: PipelineElementUnion[];
 
+    @Output()
+    pipelineCanvasMaximizedEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+    pipelineCanvasMaximized: boolean = false;
+
+    currentMouseOverElement: any;
+    currentZoomLevel: any;
+    preview: any;
+
+    selectMode: any;
+    currentPipelineName: any;
+    currentPipelineDescription: any;
+
     errorMessagesDisplayed: any = false;
 
     pipelineValid: boolean = false;
@@ -65,7 +69,6 @@ export class PipelineAssemblyComponent implements OnInit {
     pipelineCacheRunning: boolean = false;
     pipelineCached: boolean = false;
 
-
     constructor(private JsplumbBridge: JsplumbBridge,
                 private PipelinePositioningService: PipelinePositioningService,
                 private ObjectProvider: ObjectProvider,
@@ -241,4 +244,9 @@ export class PipelineAssemblyComponent implements OnInit {
         return this.rawPipelineModel.length === 0 || this.rawPipelineModel.every(pe => pe.settings.disabled);
     }
 
-}
\ No newline at end of file
+    toggleCanvasMaximized() {
+        this.pipelineCanvasMaximized = !(this.pipelineCanvasMaximized);
+        this.pipelineCanvasMaximizedEmitter.emit(this.pipelineCanvasMaximized);
+    }
+
+}
diff --git a/ui/src/app/editor/editor.component.html b/ui/src/app/editor/editor.component.html
index 9653198..5d47715 100644
--- a/ui/src/app/editor/editor.component.html
+++ b/ui/src/app/editor/editor.component.html
@@ -17,29 +17,22 @@
   -->
 
 <div fxLayout="column" class="page-container">
-    <div fxLayout="row" class="border" style="padding:0px;background-color:#f6f6f6;">
+    <div fxLayout="row" class="border" style="padding:0px;background-color:#f6f6f6;" *ngIf="!pipelineCanvasMaximized">
         <div fxFlex="100" style="line-height:24px;border-bottom:1px solid #ccc">
             <div fxFlex="100" fxLayout="row">
                 <div fxFlex fxLayoutAlign="start center" [attr.id]="'peType'">
-                    <mat-tab-group [selectedIndex]="selectedIndex" (selectedIndexChange)="selectPipelineElements($event)">
+                    <mat-tab-group [selectedIndex]="selectedIndex"
+                                   (selectedIndexChange)="selectPipelineElements($event)">
                         <mat-tab *ngFor="let tab of tabs" label="{{tab.title}}"></mat-tab>
                     </mat-tab-group>
                 </div>
-                <div fxFlex="30" fxLayout="row" fxLayoutAlign="end center"
-                     style="padding-left:10px;font-size:14px;line-height:24px;border-bottom:1px solid #ccc">
-                    <button mat-button class="md-icon-button" (click)="startCreatePipelineTour()" [matTooltip]="'Tutorial'">
+                <div fxFlex="30" fxLayout="row" fxLayoutAlign="end center" style="padding-left:10px;font-size:14px;line-height:24px;border-bottom:1px solid #ccc">
+                    <button mat-button mat-icon-button (click)="startCreatePipelineTour()"
+                            [matTooltip]="'Tutorial'" style="margin-right: 5px;">
                         <i class="material-icons text-color">
                             school
                         </i>
                     </button>
-                    <button mat-button class="md-icon-button" *ngIf="!minimizedEditorStand"
-                               (click)="toggleEditorStand()" [matTooltip]="'Hide'">
-                        <i class="material-icons text-color">arrow_drop_up</i>
-                    </button>
-                    <button mat-button class="md-icon-button" *ngIf="minimizedEditorStand"
-                               (click)="toggleEditorStand()" [matTooltip]="'Show'">
-                        <i class="material-icons text-color">arrow_drop_down</i>
-                    </button>
                 </div>
             </div>
         </div>
@@ -47,13 +40,19 @@
 
     <div class="fixed-height page-container-padding-inner" fxLayout="column" fxFlex="100">
         <div id="shepherd-test"
-             style="background-color:#f6f6f6;padding:0px;border-bottom:1px solid #ffffff" [hidden]="minimizedEditorStand">
-            <pipeline-element-icon-stand [activeType]="activeType"
-                                         [currentElements]="currentElements"
-                                         *ngIf="allElementsLoaded">
+             style="background-color:#f6f6f6;padding:0px;border-bottom:1px solid #ffffff">
+            <pipeline-element-icon-stand
+                    class="icon-stand-margin"
+                    [activeType]="activeType"
+                    [currentElements]="currentElements"
+                    *ngIf="allElementsLoaded && !pipelineCanvasMaximized">
             </pipeline-element-icon-stand>
         </div>
-        <pipeline-assembly [rawPipelineModel]="rawPipelineModel" [allElements]="allElements"
-                           [currentModifiedPipelineId]="currentModifiedPipelineId"></pipeline-assembly>
+        <pipeline-assembly fxFlex="1"
+                           [rawPipelineModel]="rawPipelineModel"
+                           [allElements]="allElements"
+                           [currentModifiedPipelineId]="currentModifiedPipelineId"
+                           (pipelineCanvasMaximizedEmitter)="togglePipelineCanvasMode($event)">
+        </pipeline-assembly>
     </div>
 </div>
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/editor.component.scss
index 6c14207..8aeccf2 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/editor.component.scss
@@ -28,4 +28,8 @@
 
 .border {
   border: 1px solid #cccccc;
-}
\ No newline at end of file
+}
+
+.icon-stand-margin {
+  margin-bottom: 10px;
+}
diff --git a/ui/src/app/editor/editor.component.ts b/ui/src/app/editor/editor.component.ts
index 7d44d3f..f281b24 100644
--- a/ui/src/app/editor/editor.component.ts
+++ b/ui/src/app/editor/editor.component.ts
@@ -66,13 +66,13 @@ export class EditorComponent implements OnInit {
     elementsLoaded = [false, false, false];
     allElementsLoaded: boolean = false;
 
-    minimizedEditorStand: boolean = false;
-
     requiredStreamForTutorialAppId: any = "org.apache.streampipes.sources.simulator.flowrate1";
     requiredProcessorForTutorialAppId: any = "org.apache.streampipes.processors.filters.jvm.numericalfilter";
     requiredSinkForTutorialAppId: any = "org.apache.streampipes.sinks.internal.jvm.dashboard";
     missingElementsForTutorial: any = [];
 
+    pipelineCanvasMaximized: boolean = false;
+
     isTutorialOpen: boolean = false;
 
     tabs: TabsModel[] = [
@@ -188,10 +188,6 @@ export class EditorComponent implements OnInit {
         this.shepherdService.trigger("select-" +this.activeShorthand);
     }
 
-    toggleEditorStand() {
-        this.minimizedEditorStand = !this.minimizedEditorStand;
-    }
-
     startCreatePipelineTour() {
         if (this.requiredPipelineElementsForTourPresent()) {
             this.shepherdService.startCreatePipelineTour();
@@ -243,4 +239,8 @@ export class EditorComponent implements OnInit {
             return el.appId === appId;
         });
     }
+
+    togglePipelineCanvasMode(maximize: boolean) {
+        this.pipelineCanvasMaximized = maximize;
+    }
 }
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index b922b95..3c96525 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -31,7 +31,8 @@
 @import '~shepherd.js/dist/css/shepherd-theme-default.css';
 @import "~ng-pick-datetime/assets/style/picker.min.css";
 @import '~leaflet/dist/leaflet.css';
-@import '~material-design-icons/iconfont/material-icons.css';
+@import '~material-icons/iconfont/material-icons.css';
+//@import '~material-design-icons/iconfont/material-icons.css';
 @import '~quill/dist/quill.snow.css';
 @import '~swagger-ui/dist/swagger-ui.css';
 
diff --git a/ui/src/scss/sp/main.scss b/ui/src/scss/sp/main.scss
index 945aa26..4185bdb 100644
--- a/ui/src/scss/sp/main.scss
+++ b/ui/src/scss/sp/main.scss
@@ -886,17 +886,6 @@ md-select.md-default-theme .md-select-value.md-select-placeholder, md-select .md
   /* 	margin-top: 1%;  */
 }
 
-.outerAssembly {
-  position: relative;
-  left: 0px;
-  top: 0px;
-  overflow: visible;
-  //box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12);
-  padding: 5px;
-  height: 80vh;
-  width: 100%;
-}
-
 .outer-assembly-preview {
   position: relative;
   overflow: auto;
@@ -911,21 +900,7 @@ md-select.md-default-theme .md-select-value.md-select-placeholder, md-select .md
   background-size: 15px 15px;
 }
 
-.canvas {
-  overflow: visible;
-  position: absolute;
-  left: 0px;
-  top: 0px;
-  height: 300%;
-  width: 300%;
-  z-index: 0;
-  margin: -1px;
 
-  background-color: white;
-  background-image: linear-gradient(90deg, rgba(208, 208, 208, .5) 10%, transparent 0%), linear-gradient(rgba(208, 208, 208, .5) 10%, transparent 0%);
-  background-size: 15px 15px;
-
-}
 
 .canvas-preview-inner {
   position: relative;


[incubator-streampipes] 05/06: [STREAMPIPES-248] Auto-restart orphaned pipelines

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 5a48645f59d619acb2891b2defb77bbe410a33a1
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Tue Mar 9 22:49:29 2021 +0100

    [STREAMPIPES-248] Auto-restart orphaned pipelines
---
 .../backend/StreamPipesBackendApplication.java     | 30 +++++++++++++++++-----
 .../api/AbstractPipelineElementResource.java       | 14 ++++++++++
 .../api/InvocablePipelineElementResource.java      | 17 +++++++++---
 .../container/init/RunningInstances.java           |  4 +++
 4 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesBackendApplication.java b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesBackendApplication.java
index 9ce2c1e..899eb8d 100644
--- a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesBackendApplication.java
+++ b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesBackendApplication.java
@@ -88,21 +88,37 @@ public class StreamPipesBackendApplication {
   }
 
   private void startAllPreviouslyStoppedPipelines() {
-    LOG.info("Checking for pipelines to be started...");
+    LOG.info("Checking for orphaned pipelines...");
+    getAllPipelines()
+            .stream()
+            .filter(Pipeline::isRunning)
+            .forEach(pipeline -> {
+              LOG.info("Restoring orphaned pipeline {}", pipeline.getName());
+              startPipeline(pipeline);
+            });
+
+    LOG.info("Checking for gracefully shut down pipelines to be restarted...");
 
     getAllPipelines()
             .stream()
+            .filter(p -> ! (p.isRunning()))
             .filter(Pipeline::isRestartOnSystemReboot)
             .forEach(pipeline -> {
-              PipelineOperationStatus status = Operations.startPipeline(pipeline);
-              if (status.isSuccess()) {
-                LOG.info("Pipeline {} successfully restarted", status.getPipelineName());
-              } else {
-                LOG.error("Pipeline {} could not be restarted - are all pipeline element containers running?", status.getPipelineName());
-              }
+              startPipeline(pipeline);
               pipeline.setRestartOnSystemReboot(false);
               StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI().updatePipeline(pipeline);
             });
+
+    LOG.info("No more pipelines to restore...");
+  }
+
+  private void startPipeline(Pipeline pipeline) {
+    PipelineOperationStatus status = Operations.startPipeline(pipeline);
+    if (status.isSuccess()) {
+      LOG.info("Pipeline {} successfully restarted", status.getPipelineName());
+    } else {
+      LOG.error("Pipeline {} could not be restarted - are all pipeline element containers running?", status.getPipelineName());
+    }
   }
 
   private List<Pipeline> getAllPipelines() {
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/AbstractPipelineElementResource.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/AbstractPipelineElementResource.java
index 2249acf..9e29b50 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/AbstractPipelineElementResource.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/AbstractPipelineElementResource.java
@@ -193,5 +193,19 @@ public abstract class AbstractPipelineElementResource<D extends Declarer<?>> {
             .build();
   }
 
+  protected <T> Response notModified(T entity) {
+    return Response
+            .notModified()
+            .entity(entity)
+            .build();
+  }
+
+  protected <T> Response noContent(T entity) {
+    return Response
+            .noContent()
+            .entity(entity)
+            .build();
+  }
+
   protected abstract Map<String, D> getElementDeclarers();
 }
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
index 3b6fc68..4530f8a 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
@@ -30,6 +30,8 @@ import org.apache.streampipes.model.staticproperty.Option;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
 import org.apache.streampipes.sdk.extractor.AbstractParameterExtractor;
 import org.apache.streampipes.sdk.extractor.StaticPropertyExtractor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
@@ -39,6 +41,8 @@ import java.util.Map;
 public abstract class InvocablePipelineElementResource<I extends InvocableStreamPipesEntity, D extends Declarer<?>,
         P extends AbstractParameterExtractor<I>> extends AbstractPipelineElementResource<D> {
 
+    private static final Logger LOG = LoggerFactory.getLogger(InvocablePipelineElementResource.class);
+
     protected abstract Map<String, D> getElementDeclarers();
     protected abstract String getInstanceId(String uri, String elementId);
 
@@ -64,9 +68,16 @@ public abstract class InvocablePipelineElementResource<I extends InvocableStream
 
             if (declarer != null) {
                 String runningInstanceId = getInstanceId(graph.getElementId(), elementId);
-                RunningInstances.INSTANCE.add(runningInstanceId, graph, declarer.getClass().newInstance());
-                Response resp = RunningInstances.INSTANCE.getInvocation(runningInstanceId).invokeRuntime(graph);
-                return ok(resp);
+                if (!RunningInstances.INSTANCE.exists(runningInstanceId)) {
+                    RunningInstances.INSTANCE.add(runningInstanceId, graph, declarer.getClass().newInstance());
+                    Response resp = RunningInstances.INSTANCE.getInvocation(runningInstanceId).invokeRuntime(graph);
+                    return ok(resp);
+                } else {
+                    LOG.info("Pipeline element {} with id {} seems to be already running, skipping invocation request.", graph.getName(), runningInstanceId);
+                    Response resp = new Response(graph.getElementId(), true);
+                    return ok(resp);
+                }
+
             }
         } catch (InstantiationException | IllegalAccessException e) {
             e.printStackTrace();
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/init/RunningInstances.java b/streampipes-container/src/main/java/org/apache/streampipes/container/init/RunningInstances.java
index 0f8f651..54fe9c8 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/init/RunningInstances.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/init/RunningInstances.java
@@ -35,6 +35,10 @@ public enum RunningInstances {
         runningInstances.put(id, new ElementInfo<>(description, invocation));
     }
 
+    public boolean exists(String runningInstanceId) {
+        return runningInstances.containsKey(runningInstanceId);
+    }
+
     public InvocableDeclarer getInvocation(String id) {
         ElementInfo<NamedStreamPipesEntity, InvocableDeclarer> result = runningInstances.get(id);
         if (result != null) {


[incubator-streampipes] 06/06: Merge branch 'dev' of github.com:apache/incubator-streampipes into dev

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e700ab5ad56427c6f725723437bfc1b9407edb1f
Merge: 5a48645 9852d60
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Tue Mar 9 22:49:39 2021 +0100

    Merge branch 'dev' of github.com:apache/incubator-streampipes into dev

 .../streampipes/dataexplorer/DataLakeManagementV3.java |  2 +-
 .../AbstractConfigurablePipelineElementBuilder.java    | 18 ++++++++++++++++--
 2 files changed, 17 insertions(+), 3 deletions(-)


[incubator-streampipes] 02/06: [STREAMPIPES-304] Bump and migrate Jsplumb version

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f24b1b4a3ad14203803b8d71c3eb80354f61a39c
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Sun Mar 7 21:37:21 2021 +0100

    [STREAMPIPES-304] Bump and migrate Jsplumb version
---
 ui/angular.json                                    |   1 -
 ui/package.json                                    |   3 +-
 .../pipeline-assembly.component.html               |   9 +-
 .../pipeline-assembly.component.scss               |  18 +-
 .../pipeline-assembly.component.ts                 |  32 +++-
 .../pipeline-element-options.component.ts          |   8 +-
 .../components/pipeline/pipeline.component.html    |  10 +-
 .../components/pipeline/pipeline.component.ts      |  79 +++++---
 ui/src/app/editor/editor.module.ts                 |  17 +-
 .../filter/enabled-pipeline-element.filter.ts      |  15 ++
 ui/src/app/editor/model/editor.model.ts            |   7 +-
 ui/src/app/editor/model/jsplumb.model.ts           |  26 +++
 .../app/editor/services/jsplumb-bridge.service.ts  |  79 +++++---
 .../app/editor/services/jsplumb-config.service.ts  |  48 ++---
 .../editor/services/jsplumb-endpoint.service.ts    |  63 +++++++
 .../app/editor/services/jsplumb-factory.service.ts |  85 +++++++++
 ui/src/app/editor/services/jsplumb.service.ts      | 210 +++++++++------------
 .../app/editor/services/object-provider.service.ts |  24 ++-
 .../services/pipeline-canvas-scrolling.service.ts  |  35 ++++
 .../app/editor/services/pipeline-editor.service.ts |  19 +-
 .../services/pipeline-element-dragged.service.ts   |  31 +++
 .../pipeline-element-recommendation.service.ts     |   8 +-
 .../services/pipeline-positioning.service.ts       | 124 ++++++------
 .../editor/services/pipeline-validation.service.ts |  42 ++---
 .../preview/pipeline-preview.component.ts          |   8 +-
 ui/src/scss/main.scss                              |   4 +-
 ui/src/scss/sp/jsplumb.scss                        |  21 +++
 ui/src/scss/sp/main.scss                           |   8 +-
 28 files changed, 694 insertions(+), 340 deletions(-)

diff --git a/ui/angular.json b/ui/angular.json
index 42cdf7b..c1ae092 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -39,7 +39,6 @@
               "node_modules/datatables.net/js/jquery.dataTables.js",
               "node_modules/jquery.panzoom/dist/jquery.panzoom.js",
               "node_modules/jquery-ui-dist/jquery-ui.js",
-              "node_modules/jsplumb/dist/js/jsPlumb-2.1.3-min.js",
               "node_modules/quill/dist/quill.js",
             ]
           },
diff --git a/ui/package.json b/ui/package.json
index ad82c27..8d3a3b9 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -60,7 +60,7 @@
     "jquery-ui-dist": "1.12.1",
     "jquery.panzoom": "2.0.5",
     "jshint": "2.11.1",
-    "jsplumb": "2.1.3",
+    "jsplumb": "^2.15.5",
     "jszip": "3.2.1",
     "konva": "3.2.4",
     "leaflet": "1.6.0",
@@ -74,6 +74,7 @@
     "ngx-auto-scroll": "^1.1.0",
     "ngx-color-picker": "9.0.0",
     "ngx-echarts": "^6.0.0",
+    "ngx-perfect-scrollbar": "^10.1.0",
     "ngx-quill": "12.0.1",
     "ngx-showdown": "5.1.0",
     "path": "^0.12.7",
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 f2424b9..1eddb4e 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
@@ -20,10 +20,9 @@
     <div class="pipeline-assembly-options sp-blue-bg">
         <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
             <button mat-button matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
-                    style="display:flex;align-items:center;"
                     [disabled]="!PipelineValidationService.pipelineValid"
                     (click)="submit()" type="submit">
-                <mat-icon>save</mat-icon>&nbsp;<span>Save pipeline</span>
+                <i class="material-icons">save</i>&nbsp;<span>Save pipeline</span>
             </button>
             <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"
                     [disabled]="!selectMode"
@@ -103,7 +102,7 @@
         </div>
     </div>
     <div id="outerAssemblyArea" class="outerAssembly sp-blue-border-nopadding">
-        <div id="assembly" class="canvas">
+        <div id="assembly" class="canvas jtk-surface jtk-surface-no-pan">
             <pipeline [pipelineValid]="pipelineValid"
                       [canvasId]="'assembly'"
                       [rawPipelineModel]="rawPipelineModel"
@@ -112,8 +111,8 @@
                       [pipelineCached]="pipelineCached"
                       [pipelineCacheRunning]="pipelineCacheRunning"
                       (pipelineCachedChanged)="pipelineCached=$event"
-                      (pipelineCacheRunningChanged)="pipelineCacheRunning=$event"
-            ></pipeline>
+                      (pipelineCacheRunningChanged)="pipelineCacheRunning=$event">
+            </pipeline>
         </div>
     </div>
 </div>
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
index d252c0d..5bbd778 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
@@ -30,13 +30,12 @@
 }
 
 .outerAssembly {
-    position: relative;
+    //position: relative;
     left: 0px;
     top: 0px;
     overflow: visible;
     //box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 2px 1px -1px rgba(0, 0, 0, .12);
-    padding: 5px;
-    height: 100%;
+    //padding: 5px;
     flex: 1;
     width: 100%;
 }
@@ -46,12 +45,12 @@
 }
 
 .canvas {
-    overflow: visible;
-    position: absolute;
+    overflow: auto;
+    position: relative;
     left: 0px;
     top: 0px;
-    height: 300%;
-    width: 300%;
+    height: 100%;
+    width: 100%;
     z-index: 0;
     margin: -1px;
 
@@ -63,3 +62,8 @@
 .pipeline-assembly-options {
     color: white;
 }
+
+.jtk-surface-nopan {
+    overflow: scroll !important;
+    cursor:default;
+}
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 6979f0e..8b44161 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,7 +16,16 @@
  *
  */
 
-import {Component, EventEmitter, Input, NgZone, OnInit, Output,} 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";
@@ -31,6 +40,8 @@ import {ConfirmDialogComponent} from "../../../core-ui/dialog/confirm-dialog/con
 import {MatDialog} from "@angular/material/dialog";
 import {EditorService} from "../../services/editor.service";
 import {PipelineService} from "../../../platform-services/apis/pipeline.service";
+import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scrolling.service";
+import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
 
 
 @Component({
@@ -52,6 +63,8 @@ export class PipelineAssemblyComponent implements OnInit {
     @Output()
     pipelineCanvasMaximizedEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
 
+    JsplumbBridge: JsplumbBridge;
+
     pipelineCanvasMaximized: boolean = false;
 
     currentMouseOverElement: any;
@@ -69,7 +82,10 @@ export class PipelineAssemblyComponent implements OnInit {
     pipelineCacheRunning: boolean = false;
     pipelineCached: boolean = false;
 
-    constructor(private JsplumbBridge: JsplumbBridge,
+    config: any = {};
+    @ViewChild("assembly") pipelineCanvas: ElementRef;
+
+    constructor(private JsPlumbFactoryService: JsplumbFactoryService,
                 private PipelinePositioningService: PipelinePositioningService,
                 private ObjectProvider: ObjectProvider,
                 public EditorService: EditorService,
@@ -79,7 +95,8 @@ export class PipelineAssemblyComponent implements OnInit {
                 private ShepherdService: ShepherdService,
                 private dialogService: DialogService,
                 private dialog: MatDialog,
-                private ngZone: NgZone) {
+                private ngZone: NgZone,
+                private pipelineCanvasScrollingService: PipelineCanvasScrollingService) {
 
         this.selectMode = true;
         this.currentZoomLevel = 1;
@@ -87,11 +104,18 @@ export class PipelineAssemblyComponent implements OnInit {
     }
 
     ngOnInit(): void {
+        this.JsplumbBridge = this.JsPlumbFactoryService.getJsplumbBridge(false);
         if (this.currentModifiedPipelineId) {
             this.displayPipelineById();
         } else {
             this.checkAndDisplayCachedPipeline();
         }
+        this.pipelineCanvasScrollingService.canvasScrollYSubject.subscribe(position => {
+            console.log("scrolling to" + position);
+            console.log(this.pipelineCanvas.nativeElement.scrollHeight);
+            console.log(this.pipelineCanvas.nativeElement.scrollTop);
+            this.pipelineCanvas.nativeElement.scrollTop = this.pipelineCanvas.nativeElement.scrollHeight;
+        });
     }
 
     ngAfterViewInit() {
@@ -231,7 +255,7 @@ export class PipelineAssemblyComponent implements OnInit {
             this.EditorService.makePipelineAssemblyEmpty(false);
             this.ngZone.run(() => {
                 this.pipelineValid = this.PipelineValidationService
-                    .isValidPipeline(this.rawPipelineModel.filter(pe => !(pe.settings.disabled)));
+                    .isValidPipeline(this.rawPipelineModel.filter(pe => !(pe.settings.disabled)), false);
             });
         });
     }
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 6d20d18..4c89316 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
@@ -36,6 +36,7 @@ import {CompatibleElementsComponent} from "../../dialog/compatible-elements/comp
 import {Tuple2} from "../../../core-model/base/Tuple2";
 import { cloneDeep } from "lodash";
 import {Observable, Subscription} from "rxjs";
+import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
 
 @Component({
   selector: 'pipeline-element-options',
@@ -79,11 +80,13 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
 
   pipelineElementConfiguredObservable: Subscription;
 
+  JsplumbBridge: JsplumbBridge;
+
   constructor(private ObjectProvider: ObjectProvider,
               private PipelineElementRecommendationService: PipelineElementRecommendationService,
               private DialogService: DialogService,
               private EditorService: EditorService,
-              private JsplumbBridge: JsplumbBridge,
+              private JsplumbFactoryService: JsplumbFactoryService,
               private JsplumbService: JsplumbService,
               private PipelineValidationService: PipelineValidationService,
               private RestApi: RestApi) {
@@ -91,6 +94,7 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
     this.possibleElements = [];
     this.recommendedElements = [];
     this.recommendationsShown = false;
+    this.JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(false);
   }
 
   ngOnInit() {
@@ -187,4 +191,4 @@ export class PipelineElementOptionsComponent implements OnInit, OnDestroy {
   ngOnDestroy(): void {
     this.pipelineElementConfiguredObservable.unsubscribe();
   }
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.html b/ui/src/app/editor/components/pipeline/pipeline.component.html
index fbcd494..b3736fa 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.html
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.html
@@ -16,10 +16,12 @@
   ~
   -->
 
-<div *ngFor="let pipelineElement of rawPipelineModel" style="width:100%;height:100%;z-index:1">
-    <div *ngIf="pipelineElement.settings.disabled == undefined || !pipelineElement.settings.disabled" style="width:100%;height:100%;z-index:1">
+<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('')">
+          (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">
@@ -45,4 +47,4 @@
         </pipeline-element-options>
     </span>
 </div>
-</div>
\ No newline at end of file
+
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index dd83082..c5486e7 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -23,12 +23,12 @@ import {JsplumbBridge} from "../../services/jsplumb-bridge.service";
 import {ShepherdService} from "../../../services/tour/shepherd.service";
 import {
   ChangeDetectorRef,
-  Component,
+  Component, ElementRef,
   EventEmitter,
   Input,
-  NgZone,
+  NgZone, OnDestroy,
   OnInit,
-  Output
+  Output, ViewChild
 } from "@angular/core";
 import {
   InvocablePipelineElementUnion,
@@ -37,9 +37,9 @@ import {
 } from "../../model/editor.model";
 import {
   CustomOutputStrategy,
-  DataProcessorInvocation, ErrorMessage,
-  Pipeline,
-  SpDataStream
+  DataProcessorInvocation, DataSinkInvocation, ErrorMessage,
+  Pipeline, SpDataSet,
+  SpDataStream, SpDataStreamUnion
 } from "../../../core-model/gen/streampipes-model";
 import {ObjectProvider} from "../../services/object-provider.service";
 import {CustomizeComponent} from "../../dialog/customize/customize.component";
@@ -51,13 +51,17 @@ import {MatchingErrorComponent} from "../../dialog/matching-error/matching-error
 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 {Subject} from "rxjs";
+import {PipelineElementDraggedService} from "../../services/pipeline-element-dragged.service";
+import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scrolling.service";
+import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
 
 @Component({
   selector: 'pipeline',
   templateUrl: './pipeline.component.html',
   styleUrls: ['./pipeline.component.css']
 })
-export class PipelineComponent implements OnInit {
+export class PipelineComponent implements OnInit, OnDestroy {
 
   @Input()
   pipelineValid: boolean;
@@ -88,25 +92,32 @@ export class PipelineComponent implements OnInit {
 
   availablePipelineElementCache: PipelineElementUnion[];
 
-  plumbReady: any;
+  plumbReady: boolean;
   currentMouseOverElement: string;
   currentPipelineModel: Pipeline;
   idCounter: any;
   currentZoomLevel: any;
   TransitionService: any;
 
-  // remove later
+  canvasWidth: string = "1000%";
+  canvasHeight: string = "1000%";
+
+  JsplumbBridge: JsplumbBridge;
+
+  //@ViewChild('outerCanvas') canvasRef: ElementRef;
 
   constructor(private JsplumbService: JsplumbService,
               private PipelineEditorService: PipelineEditorService,
-              private JsplumbBridge: JsplumbBridge,
+              private JsplumbFactoryService: JsplumbFactoryService,
               private ObjectProvider: ObjectProvider,
               private EditorService: EditorService,
               private ShepherdService: ShepherdService,
               private PipelineValidationService: PipelineValidationService,
               private dialogService: DialogService,
               private dialog: MatDialog,
-              private ngZone: NgZone) {
+              private ngZone: NgZone,
+              private pipelineElementDraggedService: PipelineElementDraggedService,
+              private pipelineCanvasScrollingService: PipelineCanvasScrollingService) {
     this.plumbReady = false;
     this.currentMouseOverElement = "";
     this.currentPipelineModel = new Pipeline();
@@ -116,21 +127,39 @@ export class PipelineComponent implements OnInit {
   }
 
   ngOnInit() {
+    // this.pipelineElementDraggedService.pipelineElementMovedSubject.subscribe(position => {
+    //   console.log(position);
+    //   console.log(this.canvasHeight);
+    //   console.log(this.canvasRef.nativeElement.offsetHeight);
+    //   console.log(this.canvasRef.nativeElement.scrollHeight);
+    //   if ((position.y + 200) > this.canvasRef.nativeElement.offsetHeight) {
+    //     this.canvasHeight = this.canvasRef.nativeElement.offsetHeight +10 + "px";
+    //     this.pipelineCanvasScrollingService.canvasScrollYSubject.next(position.y);
+    //   }
+    //   if ((position.x + 200) > this.canvasRef.nativeElement.offsetWidth) {
+    //     this.canvasWidth = this.canvasRef.nativeElement.offsetWidth + 100 + "px";
+    //   }
+    // });
+    this.JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(this.preview);
     this.JsplumbBridge.setContainer(this.canvasId);
     this.initAssembly();
     this.initPlumb();
   }
 
+  ngAfterViewInit() {
+  }
+
   validatePipeline() {
     this.ngZone.run(() => {
       this.pipelineValid = this.PipelineValidationService
-          .isValidPipeline(this.rawPipelineModel.filter(pe => !(pe.settings.disabled)));
+          .isValidPipeline(this.rawPipelineModel.filter(pe => !(pe.settings.disabled)), this.preview);
     });
   }
 
   ngOnDestroy() {
     this.JsplumbBridge.deleteEveryEndpoint();
     this.plumbReady = false;
+    //this.pipelineElementDraggedService.pipelineElementMovedSubject.unsubscribe();
   }
 
   updateMouseover(elementId) {
@@ -203,18 +232,22 @@ export class PipelineComponent implements OnInit {
             this.rawPipelineModel.push(pipelineElementConfig);
             if (ui.draggable.hasClass('set')) {
               setTimeout(() => {
-                this.JsplumbService.setDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload, true, false);
+                this.EditorService.updateDataSet(pipelineElementConfig.payload).subscribe(data => {
+                  (pipelineElementConfig.payload as SpDataSet).eventGrounding = data.eventGrounding;
+                  (pipelineElementConfig.payload as SpDataSet).datasetInvocationId = data.invocationId;
+                  this.JsplumbService.dataStreamDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as SpDataSet, true, false);
+                });
               }, 0);
             }
             else if (ui.draggable.hasClass('stream')) {
               this.checkTopicModel(pipelineElementConfig);
             } else if (ui.draggable.hasClass('sepa')) {
                 setTimeout(() => {
-                  this.JsplumbService.sepaDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload, true, false);
+                  this.JsplumbService.dataProcessorDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataProcessorInvocation, true, false);
                 }, 10);
             } else if (ui.draggable.hasClass('action')) {
                 setTimeout(() => {
-                  this.JsplumbService.actionDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload, true, false);
+                  this.JsplumbService.dataSinkDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataSinkInvocation, true, false);
                 }, 10);
             }
             if (this.ShepherdService.isTourActive()) {
@@ -232,7 +265,10 @@ export class PipelineComponent implements OnInit {
 
   checkTopicModel(pipelineElementConfig: PipelineElementConfig) {
       setTimeout(() => {
-        this.JsplumbService.streamDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload, true, false);
+        this.JsplumbService.dataStreamDropped(pipelineElementConfig.payload.dom,
+            pipelineElementConfig.payload as SpDataStream,
+            true,
+            false);
       }, 10);
 
     var streamDescription = pipelineElementConfig.payload as SpDataStream;
@@ -260,7 +296,7 @@ export class PipelineComponent implements OnInit {
 
   initPlumb() {
 
-    this.JsplumbService.prepareJsplumb();
+    //this.JsplumbService.prepareJsplumb();
 
     this.JsplumbBridge.unbind("connection");
 
@@ -306,8 +342,7 @@ export class PipelineComponent implements OnInit {
               info.targetEndpoint.setType("token");
               this.validatePipeline();
               this.modifyPipeline(pipelineModificationMessage.pipelineModifications);
-              var sourceEndpoint = this.JsplumbBridge.selectEndpoints({element: info.targetEndpoint.elementId});
-              if (this.PipelineEditorService.isFullyConnected(pe)) {
+              if (this.JsplumbService.isFullyConnected(pe, this.preview)) {
                 let payload = pe.payload as InvocablePipelineElementUnion;
                 if ((payload.staticProperties && payload.staticProperties.length > 0) || this.isCustomOutput(pe)) {
                   this.showCustomizeDialog({a: false, b: pe});
@@ -411,7 +446,9 @@ export class PipelineComponent implements OnInit {
       if (c) {
         pipelineElementInfo.b.settings.openCustomize = false;
         (pipelineElementInfo.b.payload as InvocablePipelineElementUnion).configured = true;
-        this.JsplumbService.activateEndpoint(pipelineElementInfo.b.payload.dom, pipelineElementInfo.b.settings.completed);
+        if (!(pipelineElementInfo.b.payload instanceof DataSinkInvocation)) {
+          this.JsplumbBridge.activateEndpoint("out-" + pipelineElementInfo.b.payload.dom, pipelineElementInfo.b.settings.completed);
+        }
         this.JsplumbBridge.getSourceEndpoint(pipelineElementInfo.b.payload.dom).setType("token");
         this.triggerPipelineCacheUpdate();
         this.announceConfiguredElement(pipelineElementInfo.b);
@@ -425,4 +462,4 @@ export class PipelineComponent implements OnInit {
   }
 
 
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/editor/editor.module.ts b/ui/src/app/editor/editor.module.ts
index 511678d..1794b56 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -60,6 +60,12 @@ import {PropertySelectionComponent} from "./components/output-strategy/property-
 import {UserDefinedOutputStrategyComponent} from "./components/output-strategy/user-defined-output/user-defined-output.component";
 import {ConnectModule} from "../connect/connect.module";
 import {PipelineElementTemplateConfigComponent} from "./components/pipeline-element-template-config/pipeline-element-template-config.component";
+import {EnabledPipelineElementFilter} from "./filter/enabled-pipeline-element.filter";
+import {PipelineElementDraggedService} from "./services/pipeline-element-dragged.service";
+import {PipelineCanvasScrollingService} from "./services/pipeline-canvas-scrolling.service";
+import {PerfectScrollbarModule} from "ngx-perfect-scrollbar";
+import {JsplumbEndpointService} from "./services/jsplumb-endpoint.service";
+import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
 
 @NgModule({
     imports: [
@@ -76,13 +82,15 @@ import {PipelineElementTemplateConfigComponent} from "./components/pipeline-elem
         FormsModule,
         MatProgressSpinnerModule,
         ShowdownModule,
-        ReactiveFormsModule
+        ReactiveFormsModule,
+        PerfectScrollbarModule
     ],
     declarations: [
         CompatibleElementsComponent,
         CustomizeComponent,
         CustomOutputStrategyComponent,
         EditorComponent,
+        EnabledPipelineElementFilter,
         HelpComponent,
         MatchingErrorComponent,
         MissingElementsForTutorialComponent,
@@ -104,16 +112,19 @@ import {PipelineElementTemplateConfigComponent} from "./components/pipeline-elem
     providers: [
         EditorService,
         SemanticTypeUtilsService,
-        JsplumbBridge,
+        JsplumbFactoryService,
+        JsplumbEndpointService,
         JsplumbService,
         JsplumbConfigService,
         ObjectProvider,
+        PipelineCanvasScrollingService,
+        PipelineElementDraggedService,
         PipelineEditorService,
         PipelinePositioningService,
         PipelineValidationService,
         PipelineElementRecommendationService,
         ImageChecker,
-        SafeCss
+        SafeCss,
     ],
   exports: [
     EditorComponent,
diff --git a/ui/src/app/editor/filter/enabled-pipeline-element.filter.ts b/ui/src/app/editor/filter/enabled-pipeline-element.filter.ts
new file mode 100644
index 0000000..8abb26d
--- /dev/null
+++ b/ui/src/app/editor/filter/enabled-pipeline-element.filter.ts
@@ -0,0 +1,15 @@
+import {Pipe, PipeTransform} from "@angular/core";
+import {PipelineElementConfig} from "../model/editor.model";
+
+@Pipe({
+  name: 'enabledPipelineElement',
+  pure: false
+})
+export class EnabledPipelineElementFilter implements PipeTransform {
+  transform(items: PipelineElementConfig[]): any {
+    if (!items) {
+      return items;
+    }
+    return items.filter(item => item.settings.disabled == undefined || !item.settings.disabled);
+  }
+}
diff --git a/ui/src/app/editor/model/editor.model.ts b/ui/src/app/editor/model/editor.model.ts
index 692d7c3..ab103db 100644
--- a/ui/src/app/editor/model/editor.model.ts
+++ b/ui/src/app/editor/model/editor.model.ts
@@ -29,6 +29,11 @@ export type PipelineElementHolder = {
   [key: string]: Array<PipelineElementUnion>;
 };
 
+export interface PipelineElementPosition {
+  x: number;
+  y: number;
+}
+
 export interface PipelineElementConfig {
   type: string,
   settings: {
@@ -80,4 +85,4 @@ export const PIPELINE_ELEMENT_TOKEN = new InjectionToken<{}>('pipelineElement');
 export type PipelineElementIdentifier = "org.apache.streampipes.model.SpDataStream"
     | "org.apache.streampipes.model.SpDataSet"
     | "org.apache.streampipes.model.graph.DataProcessorInvocation"
-    | "org.apache.streampipes.model.graph.DataSinkInvocation";
\ No newline at end of file
+    | "org.apache.streampipes.model.graph.DataSinkInvocation";
diff --git a/ui/src/app/editor/model/jsplumb.model.ts b/ui/src/app/editor/model/jsplumb.model.ts
new file mode 100644
index 0000000..3d9dddc
--- /dev/null
+++ b/ui/src/app/editor/model/jsplumb.model.ts
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+export interface JsplumbSettings {
+  dotRadius: number,
+  lineWidth: number,
+  arrowWidth: number,
+  arrowLength: number,
+  arrowLineWidth: number,
+  curviness: number
+}
diff --git a/ui/src/app/editor/services/jsplumb-bridge.service.ts b/ui/src/app/editor/services/jsplumb-bridge.service.ts
index 82631d1..ca94753 100644
--- a/ui/src/app/editor/services/jsplumb-bridge.service.ts
+++ b/ui/src/app/editor/services/jsplumb-bridge.service.ts
@@ -16,107 +16,130 @@
  *
  */
 
-import {Injectable} from "@angular/core";
+import {jsPlumbInstance} from 'jsplumb'
 
-declare const jsPlumb: any;
-
-@Injectable()
 export class JsplumbBridge {
 
-    constructor() {
+    constructor(private jsPlumbInstance: jsPlumbInstance) {
+    }
+
+    activateEndpoint(endpointId: string, endpointEnabled: boolean) {
+        let endpoint = this.getEndpointById(endpointId);
+        endpoint.setEnabled(endpointEnabled);
+    }
+
+    activateEndpointWithType(endpointId: string, endpointEnabled: boolean, endpointType: string) {
+        this.activateEndpoint(endpointId, endpointEnabled);
+        this.setEndpointType(endpointId, endpointType);
+    }
+
+    setEndpointType(endpointId: string, endpointType: string) {
+        let endpoint = this.getEndpointById(endpointId);
+        // @ts-ignore
+        endpoint.setType(endpointType);
+    }
+
+    getEndpointById(endpointId: string) {
+        return this.jsPlumbInstance.getEndpoint(endpointId);
     }
 
     setZoom(scale) {
-        jsPlumb.setZoom(scale);
+        this.jsPlumbInstance.setZoom(scale);
     }
 
     repaintEverything() {
-        jsPlumb.repaintEverything(true);
+        this.jsPlumbInstance.repaintEverything(true);
     }
 
     deleteEveryEndpoint() {
-        jsPlumb.deleteEveryEndpoint();
+        this.jsPlumbInstance.deleteEveryEndpoint();
     }
 
     setContainer(container) {
-        jsPlumb.setContainer(container);
+        this.jsPlumbInstance.setContainer(container);
     }
 
     unbind(element) {
-        jsPlumb.unbind(element);
+        this.jsPlumbInstance.unbind(element);
     }
 
     bind(event, fn) {
-        return jsPlumb.bind(event, fn);
+        return this.jsPlumbInstance.bind(event, fn);
     }
 
     // TODO: Overloading Functions?
     selectEndpoints(endpoint?) {
         if (endpoint === undefined) {
-            return jsPlumb.selectEndpoints();
+            // @ts-ignore
+            return this.jsPlumbInstance.selectEndpoints();
         }
-        return jsPlumb.selectEndpoints(endpoint);
+        // @ts-ignore
+        return this.jsPlumbInstance.selectEndpoints(endpoint);
     }
 
     selectEndpointsById(id) {
-        return jsPlumb.selectEndpoints({source: id});
+        // @ts-ignore
+        return this.jsPlumbInstance.selectEndpoints({source: id});
     }
 
     getSourceEndpoint(id) {
-        return jsPlumb.selectEndpoints({source: id});
+        // @ts-ignore
+        return this.jsPlumbInstance.selectEndpoints({source: id});
     }
 
     getTargetEndpoint(id) {
-        return jsPlumb.selectEndpoints({target: id});
+        // @ts-ignore
+        return this.jsPlumbInstance.selectEndpoints({target: id});
     }
 
     getEndpointCount(id) {
-        return jsPlumb.selectEndpoints({element: id}).length;
+        // @ts-ignore
+        return this.jsPlumbInstance.selectEndpoints({element: id}).length;
     }
 
     detach(connection) {
-        jsPlumb.detach(connection);
+        this.jsPlumbInstance.deleteConnection(connection);
     }
 
     getConnections(filter) {
-        return jsPlumb.getConnections(filter);
+        return this.jsPlumbInstance.getConnections(filter, {});
     }
 
     addEndpoint(element, options) {
-        return jsPlumb.addEndpoint(element, options);
+        return this.jsPlumbInstance.addEndpoint(element, options);
     }
 
     connect(connection) {
-        jsPlumb.connect(connection);
+        this.jsPlumbInstance.connect(connection);
     }
 
     removeAllEndpoints(element) {
-        jsPlumb.removeAllEndpoints(element);
+        this.jsPlumbInstance.removeAllEndpoints(element);
     }
 
     registerEndpointTypes(typeInfo) {
-        jsPlumb.registerEndpointTypes(typeInfo);
+        this.jsPlumbInstance.registerEndpointTypes(typeInfo);
     }
 
     draggable(element, option) {
-        jsPlumb.draggable(element, option);
+        this.jsPlumbInstance.draggable(element, option);
     }
 
     // TODO: Overloading Functions?
     setSuspendDrawing(bool1, bool2?) {
         if (bool2 === undefined) {
-            jsPlumb.setSuspendDrawing(bool1);
+            this.jsPlumbInstance.setSuspendDrawing(bool1);
         } else {
-            jsPlumb.setSuspendDrawing(bool1, bool2);
+            this.jsPlumbInstance.setSuspendDrawing(bool1, bool2);
         }
     }
 
     getAllConnections() {
-        return jsPlumb.getAllConnections();
+        return this.jsPlumbInstance.getAllConnections();
     }
 
     reset() {
-        jsPlumb.reset();
+        this.jsPlumbInstance.reset();
     }
 }
 
diff --git a/ui/src/app/editor/services/jsplumb-config.service.ts b/ui/src/app/editor/services/jsplumb-config.service.ts
index c18d55b..44de61c 100644
--- a/ui/src/app/editor/services/jsplumb-config.service.ts
+++ b/ui/src/app/editor/services/jsplumb-config.service.ts
@@ -17,6 +17,7 @@
  */
 
 import {Injectable} from "@angular/core";
+import {JsplumbSettings} from "../model/jsplumb.model";
 
 @Injectable()
 export class JsplumbConfigService {
@@ -33,28 +34,33 @@ export class JsplumbConfigService {
     }
 
     makeConfig(settings) {
-        let config = {};
-        config['streamEndpointOptions'] = this.makeStreamEndpointOptions(settings);
-        config['sepaEndpointOptions'] = this.makeSepaEndpointOptions(settings);
-        config['leftTargetPointOptions'] = this.makeLeftTargetPointOptions(settings);
+        let config = {} as any;
+        config.streamEndpointOptions = this.makeStreamEndpointOptions(settings);
+        config.sepaEndpointOptions = this.makeSepaEndpointOptions(settings);
+        config.leftTargetPointOptions = this.makeLeftTargetPointOptions(settings);
         return config;
     }
 
-    makeSettings(dotRadius, lineWidth, arrowWidth, arrowLength, arrowLineWidth, curviness) {
-        let settings = {};
-        settings['dotRadius'] = dotRadius;
-        settings['lineWidth'] = lineWidth;
-        settings['arrowWidth'] = arrowWidth;
-        settings['arrowLength'] = arrowLength;
-        settings['arrowLineWidth'] = arrowLineWidth;
-        settings['curviness'] = curviness;
+    makeSettings(dotRadius: number,
+                 lineWidth: number,
+                 arrowWidth: number,
+                 arrowLength: number,
+                 arrowLineWidth: number,
+                 curviness: number) {
+        let settings = {} as JsplumbSettings;
+        settings.dotRadius = dotRadius;
+        settings.lineWidth = lineWidth;
+        settings.arrowWidth = arrowWidth;
+        settings.arrowLength = arrowLength;
+        settings.arrowLineWidth = arrowLineWidth;
+        settings.curviness = curviness;
         return settings;
     }
 
-    makeStreamEndpointOptions(settings) {
+    makeStreamEndpointOptions(settings: JsplumbSettings) {
         return {
             endpoint: ["Dot", {radius: settings.dotRadius}],
-            connectorStyle: {strokeStyle: "#BDBDBD", outlineColor: "#9E9E9E", lineWidth: settings.lineWidth},
+            connectorStyle: {stroke: "#BDBDBD", outlineStroke: "#BDBDBD", strokeWidth: settings.lineWidth},
             connector: ["Bezier", {curviness: settings.curviness}],
             isSource: true,
             maxConnections: -1,
@@ -64,8 +70,8 @@ export class JsplumbConfigService {
                 ["Arrow", {
                     width: settings.arrowWidth, length: settings.arrowLength, location: 0.5, id: "arrow", paintStyle: {
                         fillStyle: "#BDBDBD",
-                        strokeStyle: "#9E9E9E",
-                        lineWidth: settings.arrowLineWidth
+                        stroke: "#9E9E9E",
+                        strokeWidth: settings.arrowLineWidth
                     }
                 }],
             ]
@@ -76,7 +82,7 @@ export class JsplumbConfigService {
         return {
             endpoint: ["Dot", {radius: settings.dotRadius}],
             connectorStyle: {
-                strokeStyle: "#BDBDBD", outlineColor: "#9E9E9E", lineWidth: settings.lineWidth
+                stroke: "#BDBDBD", outlineStroke: "#9E9E9E", strokeWidth: settings.lineWidth
             },
             connector: ["Bezier", {curviness: settings.curviness}],
             isSource: true,
@@ -86,9 +92,9 @@ export class JsplumbConfigService {
             connectorOverlays: [
                 ["Arrow", {
                     width: settings.arrowWidth, length: settings.arrowLength, location: 0.5, id: "arrow", paintStyle: {
-                        fillStyle: "#BDBDBD",
-                        strokeStyle: "#9E9E9E",
-                        lineWidth: settings.arrowLineWidth
+                        fill: "#BDBDBD",
+                        stroke: "#9E9E9E",
+                        strokeWidth: settings.arrowLineWidth
                     }
                 }],
             ],
@@ -108,5 +114,3 @@ export class JsplumbConfigService {
     }
 
 }
-
-//JsplumbConfigService.$inject = [];
\ No newline at end of file
diff --git a/ui/src/app/editor/services/jsplumb-endpoint.service.ts b/ui/src/app/editor/services/jsplumb-endpoint.service.ts
new file mode 100644
index 0000000..e3e7be3
--- /dev/null
+++ b/ui/src/app/editor/services/jsplumb-endpoint.service.ts
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {Injectable} from "@angular/core";
+import {JsplumbConfigService} from "./jsplumb-config.service";
+
+@Injectable()
+export class JsplumbEndpointService {
+
+  constructor(private jsplumbConfigService: JsplumbConfigService) {
+
+  }
+
+  getJsplumbConfig(preview): any {
+    return preview ? this.jsplumbConfigService.getPreviewConfig() : this.jsplumbConfigService.getEditorConfig();
+  }
+
+  getStreamEndpoint(preview: boolean,
+                    pipelineElementDomId: string) {
+    let jsplumbConfig = this.getJsplumbConfig(preview);
+    let config = jsplumbConfig.streamEndpointOptions;
+    config.uuid = "out-" + pipelineElementDomId;
+    return config;
+  }
+
+  getInputEndpoint(preview, pipelineElementDomId, index): any {
+    let jsplumbConfig = this.getJsplumbConfig(preview);
+    let inConfig = jsplumbConfig.leftTargetPointOptions;
+    inConfig.uuid = "in-" + index + "-" + pipelineElementDomId;
+    return inConfig;
+  }
+
+  getOutputEndpoint(preview, pipelineElementDomId): any {
+    let jsplumbConfig = this.getJsplumbConfig(preview);
+    let outConfig = jsplumbConfig.sepaEndpointOptions;
+    outConfig.uuid = "out-" + pipelineElementDomId;
+    return outConfig;
+  }
+
+  getNewTargetPoint(preview, x, y, pipelineElementDomId, index): any {
+    let inConfig = this.getInputEndpoint(preview, pipelineElementDomId, index);
+    inConfig.type = "empty";
+    inConfig.anchor = [x, y, -1, 0];
+    inConfig.isTarget = true;
+
+    return inConfig;
+  }
+}
diff --git a/ui/src/app/editor/services/jsplumb-factory.service.ts b/ui/src/app/editor/services/jsplumb-factory.service.ts
new file mode 100644
index 0000000..ff16482
--- /dev/null
+++ b/ui/src/app/editor/services/jsplumb-factory.service.ts
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {jsPlumb, jsPlumbInstance} from "jsplumb";
+import {JsplumbBridge} from "./jsplumb-bridge.service";
+import {Injectable} from "@angular/core";
+
+@Injectable()
+export class JsplumbFactoryService {
+
+  pipelineEditorInstance: jsPlumbInstance;
+  pipelinePreviewInstance: jsPlumbInstance;
+
+  pipelineEditorBridge: JsplumbBridge;
+  pipelinePreviewBridge: JsplumbBridge;
+
+  constructor() {
+    this.pipelineEditorInstance = this.makePipelineEditorInstance();
+    this.pipelinePreviewInstance = this.makePipelinePreviewInstance();
+
+    this.pipelineEditorBridge = new JsplumbBridge(this.pipelineEditorInstance);
+    this.pipelinePreviewBridge = new JsplumbBridge(this.pipelinePreviewInstance);
+
+    this.prepareJsplumb(this.pipelineEditorInstance);
+    this.prepareJsplumb(this.pipelinePreviewInstance);
+  }
+
+  getJsplumbBridge(previewConfig: boolean): JsplumbBridge {
+    return previewConfig ? this.pipelineEditorBridge : this.pipelinePreviewBridge;
+  }
+
+  makePipelineEditorInstance(): jsPlumbInstance {
+    return jsPlumb.getInstance();
+  }
+
+  makePipelinePreviewInstance(): jsPlumbInstance {
+    return jsPlumb.getInstance();
+  }
+
+  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
+        }
+      }
+    });
+  }
+}
diff --git a/ui/src/app/editor/services/jsplumb.service.ts b/ui/src/app/editor/services/jsplumb.service.ts
index 96190da..4196480 100644
--- a/ui/src/app/editor/services/jsplumb.service.ts
+++ b/ui/src/app/editor/services/jsplumb.service.ts
@@ -19,63 +19,45 @@
 import {JsplumbConfigService} from "./jsplumb-config.service";
 import {JsplumbBridge} from "./jsplumb-bridge.service";
 import {Injectable} from "@angular/core";
-import {PipelineElementConfig, PipelineElementUnion} from "../model/editor.model";
+import {
+    InvocablePipelineElementUnion,
+    PipelineElementConfig,
+    PipelineElementUnion
+} from "../model/editor.model";
 import {PipelineElementTypeUtils} from "../utils/editor.utils";
 import {
     DataProcessorInvocation,
     DataSinkInvocation,
+    Pipeline,
     SpDataSet,
-    SpDataStream
+    SpDataStream,
+    SpDataStreamUnion
 } from "../../core-model/gen/streampipes-model";
-import {EditorService} from "./editor.service";
+import {PipelineElementDraggedService} from "./pipeline-element-dragged.service";
+import {JsplumbEndpointService} from "./jsplumb-endpoint.service";
+import {JsplumbFactoryService} from "./jsplumb-factory.service";
 
 @Injectable()
 export class JsplumbService {
 
-    idCounter: any;
+    idCounter: number = 0;
 
     constructor(private JsplumbConfigService: JsplumbConfigService,
-                private JsplumbBridge: JsplumbBridge,
-                private EditorService: EditorService) {
-        this.idCounter = 0;
-    }
-
-    prepareJsplumb() {
-        this.JsplumbBridge.registerEndpointTypes({
-            "empty": {
-                paintStyle: {
-                    fillStyle: "white",
-                    strokeStyle: "#9E9E9E",
-                    lineWidth: 2
-                }
-            },
-            "token": {
-                paintStyle: {
-                    fillStyle: "#BDBDBD",
-                    strokeStyle: "#9E9E9E",
-                    lineWidth: 2
-                },
-                hoverPaintStyle: {
-                    fillStyle: "#BDBDBD",
-                    strokeStyle: "#4CAF50",
-                    lineWidth: 4
-                }
-            },
-            "highlight": {
-                paintStyle: {
-                    fillStyle: "white",
-                    strokeStyle: "#4CAF50",
-                    lineWidth: 4
-                }
-            }
-        });
+                private JsplumbFactory: JsplumbFactoryService,
+                private jsplumbEndpointService: JsplumbEndpointService,
+                private pipelineElementDraggedService: PipelineElementDraggedService) {
     }
 
-    activateEndpoint(endpointId, endpointEnabled) {
-        this.JsplumbBridge.selectEndpointsById(endpointId).setEnabled(endpointEnabled);
+    isFullyConnected(pipelineElementConfig: PipelineElementConfig,
+                     previewConfig: boolean) {
+        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;
     }
 
-    makeRawPipeline(pipelineModel, isPreview) {
+    makeRawPipeline(pipelineModel: Pipeline,
+                    isPreview: boolean) {
         return pipelineModel
             .streams
             .map(s => this.toConfig(s, "stream", isPreview))
@@ -83,59 +65,69 @@ export class JsplumbService {
             .concat(pipelineModel.actions.map(s => this.toConfig(s, "action", isPreview)));
     }
 
-    toConfig(pe, type, isPreview) {
-        pe.type = type;
+    toConfig(pe: PipelineElementUnion,
+             type: string,
+             isPreview: boolean) {
+        (pe as any).type = type;
         return this.createNewPipelineElementConfig(pe, {x: 100, y: 100}, isPreview, true);
     }
 
-
-    createElement(pipelineModel, pipelineElement, pipelineElementDomId) {
+    createElement(pipelineModel: PipelineElementConfig[],
+                  pipelineElement: InvocablePipelineElementUnion,
+                  pipelineElementDomId: string) {
         var pipelineElementDom = $("#" + pipelineElementDomId);
         var pipelineElementConfig = this.createNewPipelineElementConfigWithFixedCoordinates(pipelineElementDom, pipelineElement, false);
         pipelineModel.push(pipelineElementConfig);
         setTimeout(() => {
-            this.createAssemblyElement(pipelineElementConfig.payload.dom, pipelineElementConfig.payload, pipelineElementDom);
+            this.createAssemblyElement(pipelineElementConfig.payload.dom,
+                pipelineElementConfig.payload as InvocablePipelineElementUnion,
+                pipelineElementDom,
+                false);
         });
     }
 
-    createAssemblyElement($newElementId, json, $parentElement) {
+    createAssemblyElement(pipelineElementDomId: string,
+                          pipelineElement: InvocablePipelineElementUnion,
+                          $parentElement,
+                          previewConfig: boolean) {
         var $target;
-        if (json.belongsTo.indexOf("sepa") > 0) { //Sepa Element
-            $target = this.sepaDropped($newElementId, json, true, false);
-            this.connectNodes($parentElement, $target);
+        if (pipelineElement.belongsTo.indexOf("sepa") > 0) { //Sepa Element
+            $target = this.dataProcessorDropped(pipelineElementDomId, pipelineElement as DataProcessorInvocation, true, false);
+            this.connectNodes($parentElement, $target, previewConfig);
         } else {
-            $target = this.actionDropped($newElementId, json, true, false);
-            this.connectNodes($parentElement, $target);
+            $target = this.dataSinkDropped(pipelineElementDomId, pipelineElement, true, false);
+            this.connectNodes($parentElement, $target, previewConfig);
         }
     }
 
-    connectNodes($parentElement, $target) {
+    connectNodes($parentElement, $target, previewConfig: boolean) {
         var options;
+        let jsplumbBridge = this.getBridge(previewConfig);
         if ($parentElement.hasClass("stream")) {
             // TODO: getJsplumbConfig depends on isPreview. Not implemented yet
-            options = this.getJsplumbConfig(true).streamEndpointOptions;
+            options = this.jsplumbEndpointService.getJsplumbConfig(true).streamEndpointOptions;
         } else {
             // TODO: getJsplumbConfig depends on isPreview. Not implemented yet
-            options = this.getJsplumbConfig(true).sepaEndpointOptions;
+            options = this.jsplumbEndpointService.getJsplumbConfig(true).sepaEndpointOptions;
         }
         var sourceEndPoint;
-        if (this.JsplumbBridge.selectEndpoints({source: $parentElement}).length > 0) {
-            if (!(this.JsplumbBridge.selectEndpoints({source: $parentElement}).get(0).isFull())) {
-                sourceEndPoint = this.JsplumbBridge.selectEndpoints({source: $parentElement}).get(0)
+        if (jsplumbBridge.selectEndpoints({source: $parentElement}).length > 0) {
+            if (!(jsplumbBridge.selectEndpoints({source: $parentElement}).get(0).isFull())) {
+                sourceEndPoint = jsplumbBridge.selectEndpoints({source: $parentElement}).get(0)
             } else {
-                sourceEndPoint = this.JsplumbBridge.addEndpoint($parentElement, options);
+                sourceEndPoint = jsplumbBridge.addEndpoint($parentElement, options);
             }
         } else {
-            sourceEndPoint = this.JsplumbBridge.addEndpoint($parentElement, options);
+            sourceEndPoint = jsplumbBridge.addEndpoint($parentElement, options);
         }
 
-        var targetEndPoint = this.JsplumbBridge.selectEndpoints({target: $target}).get(0);
+        var targetEndPoint = jsplumbBridge.selectEndpoints({target: $target}).get(0);
 
-        this.JsplumbBridge.connect({source: sourceEndPoint, target: targetEndPoint, detachable: true});
-        this.JsplumbBridge.repaintEverything();
+        jsplumbBridge.connect({source: sourceEndPoint, target: targetEndPoint, detachable: true});
+        jsplumbBridge.repaintEverything();
     }
 
-    createNewPipelineElementConfigWithFixedCoordinates($parentElement, json, isPreview) {
+    createNewPipelineElementConfigWithFixedCoordinates($parentElement, json, isPreview): PipelineElementConfig {
         var x = $parentElement.position().left;
         var y = $parentElement.position().top;
         var coord = {'x': x + 200, 'y': y};
@@ -183,7 +175,7 @@ export class JsplumbService {
         }
     }
 
-    makeId(count) {
+    makeId(count: number) {
         var text = "";
         var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
@@ -193,82 +185,64 @@ export class JsplumbService {
         return text;
     }
 
-    streamDropped(pipelineElementDomId, json, endpoints, preview) {
+    dataStreamDropped(pipelineElementDomId: string,
+                  pipelineElement: SpDataStreamUnion,
+                  endpoints: boolean,
+                  preview: boolean) {
+        let jsplumbBridge = this.getBridge(preview);
         if (endpoints) {
             if (!preview) {
-                this.JsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent'});
+                jsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent',
+                    // drag: (e => {
+                    // this.pipelineElementDraggedService.notify({x: e.pos[0], y: e.pos[1]});
+                    // })
+                });
             }
 
-            this.JsplumbBridge.addEndpoint(pipelineElementDomId, this.getStreamEndpoint(preview, pipelineElementDomId));
+            let endpointOptions = this.jsplumbEndpointService.getStreamEndpoint(preview, pipelineElementDomId);
+            jsplumbBridge.addEndpoint(pipelineElementDomId, endpointOptions);
         }
         return pipelineElementDomId;
     };
 
-    setDropped($newElement, json, endpoints, preview) {
-        this.EditorService.updateDataSet(json).subscribe(data => {
-            json.eventGrounding = data.eventGrounding;
-            json.datasetInvocationId = data.invocationId;
-            this.streamDropped($newElement, json, endpoints, preview);
-        });
-    }
-
-    sepaDropped(pipelineElementDomId, json, endpoints, preview) {
-        this.actionDropped(pipelineElementDomId, json, endpoints, preview);
+    dataProcessorDropped(pipelineElementDomId: string,
+                pipelineElement: DataProcessorInvocation,
+                endpoints: boolean,
+                preview: boolean): string {
+        let jsplumbBridge = this.getBridge(preview);
+        this.dataSinkDropped(pipelineElementDomId, pipelineElement, endpoints, preview);
         if (endpoints) {
-            this.JsplumbBridge.addEndpoint(pipelineElementDomId, this.getOutputEndpoint(preview, pipelineElementDomId));
+            jsplumbBridge.addEndpoint(pipelineElementDomId,
+                this.jsplumbEndpointService.getOutputEndpoint(preview, pipelineElementDomId));
         }
         return pipelineElementDomId;
     };
 
-    actionDropped(pipelineElementDomId, json, endpoints, preview) {
+    dataSinkDropped(pipelineElementDomId: string,
+                  pipelineElement: InvocablePipelineElementUnion,
+                  endpoints: boolean,
+                  preview: boolean): string {
+        let jsplumbBridge = this.getBridge(preview);
         if (!preview) {
-            this.JsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent'});
+            jsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent'});
         }
 
         if (endpoints) {
-            if (json.inputStreams.length < 2) { //1 InputNode
-                this.JsplumbBridge.addEndpoint(pipelineElementDomId, this.getInputEndpoint(preview, pipelineElementDomId, 0));
+            if (pipelineElement.inputStreams.length < 2) { //1 InputNode
+                jsplumbBridge.addEndpoint(pipelineElementDomId,
+                    this.jsplumbEndpointService.getInputEndpoint(preview, pipelineElementDomId, 0));
             } else {
-                this.JsplumbBridge.addEndpoint(pipelineElementDomId, this.getNewTargetPoint(preview, 0, 0.25, pipelineElementDomId, 0));
-                this.JsplumbBridge.addEndpoint(pipelineElementDomId, this.getNewTargetPoint(preview, 0, 0.75, pipelineElementDomId, 1));
+                jsplumbBridge.addEndpoint(pipelineElementDomId,
+                    this.jsplumbEndpointService.getNewTargetPoint(preview, 0, 0.25, pipelineElementDomId, 0));
+                jsplumbBridge.addEndpoint(pipelineElementDomId,
+                    this.jsplumbEndpointService.getNewTargetPoint(preview, 0, 0.75, pipelineElementDomId, 1));
             }
         }
         return pipelineElementDomId;
     };
 
-    getJsplumbConfig(preview): any {
-        return preview ? this.JsplumbConfigService.getPreviewConfig() : this.JsplumbConfigService.getEditorConfig();
+    getBridge(previewConfig: boolean): JsplumbBridge {
+        return this.JsplumbFactory.getJsplumbBridge(previewConfig);
     }
 
-    getStreamEndpoint(preview, pipelineElementDomId) {
-        var jsplumbConfig = this.getJsplumbConfig(preview);
-        let config = jsplumbConfig.streamEndpointOptions;
-        config.uuid = "out-" + pipelineElementDomId;
-        return config;
-    }
-
-    getInputEndpoint(preview, pipelineElementDomId, index): any {
-        var jsplumbConfig = this.getJsplumbConfig(preview);
-        let inConfig = jsplumbConfig.leftTargetPointOptions;
-        inConfig.uuid = "in-" + index + "-" + pipelineElementDomId;
-        return inConfig;
-    }
-
-    getOutputEndpoint(preview, pipelineElementDomId): any {
-        var jsplumbConfig = this.getJsplumbConfig(preview);
-        let outConfig = jsplumbConfig.sepaEndpointOptions;
-        outConfig.uuid = "out-" + pipelineElementDomId;
-        return outConfig;
-    }
-
-    getNewTargetPoint(preview, x, y, pipelineElementDomId, index): any {
-        let inConfig = this.getInputEndpoint(preview, pipelineElementDomId, index);
-        inConfig.type = "empty";
-        inConfig.anchor = [x, y, -1, 0];
-        inConfig.isTarget = true;
-
-        return inConfig;
-    }
 }
-
-//JsplumbService.$inject = ['JsplumbConfigService', 'JsplumbBridge', '$timeout', 'RestApi'];
diff --git a/ui/src/app/editor/services/object-provider.service.ts b/ui/src/app/editor/services/object-provider.service.ts
index 1562e55..5e951c7 100644
--- a/ui/src/app/editor/services/object-provider.service.ts
+++ b/ui/src/app/editor/services/object-provider.service.ts
@@ -18,17 +18,17 @@
 
 import {Injectable} from "@angular/core";
 import {RestApi} from "../../services/rest-api.service";
-import {JsplumbBridge} from "./jsplumb-bridge.service";
 import {InvocablePipelineElementUnion, PipelineElementConfig} from "../model/editor.model";
-import {Pipeline} from "../../core-model/gen/streampipes-model";
+import {DataSinkInvocation, Pipeline} from "../../core-model/gen/streampipes-model";
 import {EditorService} from "./editor.service";
+import {JsplumbFactoryService} from "./jsplumb-factory.service";
 
 @Injectable()
 export class ObjectProvider {
 
     constructor(private RestApi: RestApi,
-                private JsplumbBridge: JsplumbBridge,
-                private EditorService: EditorService) {
+                private EditorService: EditorService,
+                private JsplumbFactoryService: JsplumbFactoryService) {
     }
 
     prepareElement(pipelineElement: InvocablePipelineElementUnion) {
@@ -57,6 +57,19 @@ export class ObjectProvider {
         return pipeline;
     }
 
+    hasConnectedPipelineElement(pipelineElementDomId: string,
+                                rawPipelineModel: PipelineElementConfig[]) {
+        let pipelineElement = this.findElement(pipelineElementDomId, rawPipelineModel);
+        if (pipelineElement.payload instanceof DataSinkInvocation) {
+            return false;
+        } else {
+            return rawPipelineModel
+                .filter(pe => !pe.settings.disabled && pe.payload.connectedTo)
+                .find(pe => (pe.payload.connectedTo.indexOf(pipelineElementDomId) > -1))
+                != undefined;
+        }
+    }
+
     findElement(elementId, rawPipelineModel: PipelineElementConfig[]): PipelineElementConfig {
         let result = {} as PipelineElementConfig;
         rawPipelineModel.forEach(pe => {
@@ -68,12 +81,13 @@ export class ObjectProvider {
     }
 
     addElementNew(pipeline, currentPipelineElements: PipelineElementConfig[]): Pipeline {
+        let JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(false);
         currentPipelineElements.forEach(pe => {
             if (pe.settings.disabled == undefined || !(pe.settings.disabled)) {
                 if (pe.type === 'sepa' || pe.type === 'action') {
                     let payload = pe.payload;
                     payload = this.prepareElement(payload as InvocablePipelineElementUnion);
-                    let connections = this.JsplumbBridge.getConnections({
+                    let connections = JsplumbBridge.getConnections({
                         target: $("#" + payload.dom)
                     });
                     for (let i = 0; i < connections.length; i++) {
diff --git a/ui/src/app/editor/services/pipeline-canvas-scrolling.service.ts b/ui/src/app/editor/services/pipeline-canvas-scrolling.service.ts
new file mode 100644
index 0000000..4eef96e
--- /dev/null
+++ b/ui/src/app/editor/services/pipeline-canvas-scrolling.service.ts
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {Injectable} from "@angular/core";
+import {Subject} from "rxjs";
+
+@Injectable()
+export class PipelineCanvasScrollingService {
+
+  public canvasScrollXSubject: Subject<number> = new Subject<number>();
+  public canvasScrollYSubject: Subject<number> = new Subject<number>();
+
+  public notifyX(position: number): void {
+    this.canvasScrollXSubject.next(position);
+  }
+
+  public notifyY(position: number): void {
+    this.canvasScrollYSubject.next(position);
+  }
+}
diff --git a/ui/src/app/editor/services/pipeline-editor.service.ts b/ui/src/app/editor/services/pipeline-editor.service.ts
index c630e14..fea88eb 100644
--- a/ui/src/app/editor/services/pipeline-editor.service.ts
+++ b/ui/src/app/editor/services/pipeline-editor.service.ts
@@ -17,13 +17,11 @@
  */
 
 import {Injectable} from "@angular/core";
-import {JsplumbBridge} from "./jsplumb-bridge.service";
-import {InvocablePipelineElementUnion, PipelineElementConfig} from "../model/editor.model";
 
 @Injectable()
 export class PipelineEditorService {
 
-    constructor(private JsplumbBridge: JsplumbBridge) {
+    constructor() {
     }
 
     getCoordinates(ui, currentZoomLevel) {
@@ -36,19 +34,6 @@ export class PipelineEditorService {
         };
     }
 
-    isConnected(element) {
-        if (this.JsplumbBridge.getConnections({source: element}).length < 1 && this.JsplumbBridge.getConnections({target: element}).length < 1) {
-            return false;
-        }
-        return true;
-    }
-
-    isFullyConnected(pipelineElementConfig: PipelineElementConfig) {
-        let payload = pipelineElementConfig.payload as InvocablePipelineElementUnion;
-        return payload.inputStreams == null ||
-            this.JsplumbBridge.getConnections({target: $("#" +payload.dom)}).length == payload.inputStreams.length;
-    }
-
     getDropPositionY(helper, currentZoomLevel) {
         var newTop;
         var helperPos = helper.offset();
@@ -66,5 +51,3 @@ export class PipelineEditorService {
     }
 
 }
-
-//PipelineEditorService.$inject = ['JsplumbBridge'];
\ No newline at end of file
diff --git a/ui/src/app/editor/services/pipeline-element-dragged.service.ts b/ui/src/app/editor/services/pipeline-element-dragged.service.ts
new file mode 100644
index 0000000..e209ee4
--- /dev/null
+++ b/ui/src/app/editor/services/pipeline-element-dragged.service.ts
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {Injectable} from "@angular/core";
+import {Subject} from "rxjs";
+import {PipelineElementPosition} from "../model/editor.model";
+
+@Injectable()
+export class PipelineElementDraggedService {
+
+  public pipelineElementMovedSubject: Subject<PipelineElementPosition> = new Subject<PipelineElementPosition>();
+
+  public notify(position: PipelineElementPosition): void {
+    this.pipelineElementMovedSubject.next(position);
+  }
+}
diff --git a/ui/src/app/editor/services/pipeline-element-recommendation.service.ts b/ui/src/app/editor/services/pipeline-element-recommendation.service.ts
index 3696e3e..d8df30c 100644
--- a/ui/src/app/editor/services/pipeline-element-recommendation.service.ts
+++ b/ui/src/app/editor/services/pipeline-element-recommendation.service.ts
@@ -20,9 +20,9 @@ import {Injectable} from "@angular/core";
 import {EditorService} from "./editor.service";
 import {PipelineElementUnion} from "../model/editor.model";
 import {
-    InvocableStreamPipesEntity,
-    PipelineElementRecommendation,
-    SpDataStream
+  InvocableStreamPipesEntity,
+  PipelineElementRecommendation,
+  SpDataStream
 } from "../../core-model/gen/streampipes-model";
 
 @Injectable()
@@ -61,4 +61,4 @@ export class PipelineElementRecommendationService {
                 .filter(pe => (pe instanceof SpDataStream && pe.elementId === belongsTo)
                     || (pe instanceof InvocableStreamPipesEntity && pe.belongsTo === belongsTo));
     }
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/editor/services/pipeline-positioning.service.ts b/ui/src/app/editor/services/pipeline-positioning.service.ts
index 2963125..ef03614 100644
--- a/ui/src/app/editor/services/pipeline-positioning.service.ts
+++ b/ui/src/app/editor/services/pipeline-positioning.service.ts
@@ -16,120 +16,118 @@
  *
  */
 
-//import * from 'lodash';
 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";
-
-declare const jsPlumb: any;
+import {
+    DataProcessorInvocation,
+    DataSinkInvocation,
+    SpDataStream
+} from "../../core-model/gen/streampipes-model";
+import {JsplumbFactoryService} from "./jsplumb-factory.service";
+import {ObjectProvider} from "./object-provider.service";
 
 @Injectable()
 export class PipelinePositioningService {
 
-
     constructor(private JsplumbService: JsplumbService,
                 private JsplumbConfigService: JsplumbConfigService,
-                private JsplumbBridge: JsplumbBridge) {
+                private JsplumbFactoryService: JsplumbFactoryService,
+                private ObjectProvider: ObjectProvider) {
     }
 
-    displayPipeline(rawPipelineModel: PipelineElementConfig[], targetCanvas, isPreview, autoLayout) {
-        var jsplumbConfig = isPreview ? this.JsplumbConfigService.getPreviewConfig() : this.JsplumbConfigService.getEditorConfig();
-
-        for (var i = 0; i < rawPipelineModel.length; i++) {
-            var currentPe = rawPipelineModel[i];
+    displayPipeline(rawPipelineModel: PipelineElementConfig[],
+                    targetCanvas,
+                    previewConfig: boolean,
+                    autoLayout: boolean) {
+        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") {
-                    this.JsplumbService.streamDropped(currentPe.payload.dom, currentPe.payload, true, isPreview);
+                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.sepaDropped(currentPe.payload.dom, currentPe.payload, true, isPreview);
+                    this.JsplumbService.dataProcessorDropped(currentPe.payload.dom, currentPe.payload as DataProcessorInvocation, true, previewConfig);
                 }
                 if (currentPe.type === "action") {
-                    this.JsplumbService.actionDropped(currentPe.payload.dom, currentPe.payload, true, isPreview);
+                    this.JsplumbService.dataSinkDropped(currentPe.payload.dom, currentPe.payload as DataSinkInvocation, true, previewConfig);
                 }
             }
-        }
+        });
 
-        this.connectPipelineElements(rawPipelineModel, !isPreview, jsplumbConfig);
+        this.connectPipelineElements(rawPipelineModel, previewConfig, jsplumbConfig, jsPlumbBridge);
         if (autoLayout) {
-            this.layoutGraph(targetCanvas, "span[id^='jsplumb']", isPreview ? 75 : 110, isPreview);
+            this.layoutGraph(targetCanvas, "span[id^='jsplumb']", previewConfig ? 75 : 110, previewConfig);
         }
-        this.JsplumbBridge.repaintEverything();
+        jsPlumbBridge.repaintEverything();
     }
 
     layoutGraph(canvas, nodeIdentifier, dimension, isPreview) {
+        let jsPlumbBridge = this.JsplumbFactoryService.getJsplumbBridge(isPreview);
         var g = new dagre.graphlib.Graph();
         g.setGraph({rankdir: "LR", ranksep: isPreview ? "50" : "100"});
         g.setDefaultEdgeLabel(function () {
             return {};
         });
-        var nodes = $(canvas).find(nodeIdentifier).get();
 
-        for (var i = 0; i < nodes.length; i++) {
-            var n = nodes[i];
+        var nodes = $(canvas).find(nodeIdentifier).get();
+        nodes.forEach((n, index) => {
             g.setNode(n.id, {label: n.id, width: dimension, height: dimension});
-        }
-        var edges = this.JsplumbBridge.getAllConnections();
-        for (var i = 0; i < edges.length; i++) {
-            var c = edges[i];
-            g.setEdge(c.source.id, c.target.id);
-        }
+        });
+
+        var edges = jsPlumbBridge.getAllConnections();
+        edges.forEach(edge => {
+            g.setEdge(edge.source.id, edge.target.id);
+        });
+
         dagre.layout(g);
         g.nodes().forEach(v => {
-            $("#" + v).css("left", g.node(v).x + "px");
-            $("#" + v).css("top", g.node(v).y + "px");
+            $(`#${v}`).css("left", g.node(v).x + "px");
+            $(`#${v}`).css("top", g.node(v).y + "px");
         });
     }
 
-    connectPipelineElements(rawPipelineModel: PipelineElementConfig[], detachable, jsplumbConfig) {
+    connectPipelineElements(rawPipelineModel: PipelineElementConfig[],
+                            previewConfig: boolean,
+                            jsplumbConfig: any,
+                            jsPlumbBridge: JsplumbBridge) {
         var source, target;
-
-        this.JsplumbBridge.setSuspendDrawing(true);
+        jsPlumbBridge.setSuspendDrawing(true);
         for (var i = 0; i < rawPipelineModel.length; i++) {
             var pe = rawPipelineModel[i];
 
-            if (pe.type == "sepa") {
-                if (pe.payload.connectedTo) {
-                    for (var j = 0, connection; connection = pe.payload.connectedTo[j]; j++) {
+            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;
 
-                        var options;
-                        var id = "#" + source;
-                        if ($(id).hasClass("sepa")) {
-                            options = jsplumbConfig.sepaEndpointOptions;
-                        } else {
-                            options = jsplumbConfig.streamEndpointOptions;
-                        }
-
                         let sourceEndpointId = "out-" + connection;
-                        let targetEndpointId = "in-" + j + "-" + pe.payload.dom;
-                        this.JsplumbBridge.connect(
-                            {uuids: [sourceEndpointId, targetEndpointId], detachable: detachable}
+                        let inTargetEndpointId = "in-" + index + "-" + pe.payload.dom;
+                        jsPlumbBridge.connect(
+                            {
+                                uuids: [sourceEndpointId, inTargetEndpointId],
+                                detachable: !previewConfig
+                            }
                         );
-                    }
-                }
-            } else if (pe.type == "action") {
-                target = pe.payload.dom;
+                        jsPlumbBridge.activateEndpointWithType(sourceEndpointId, true, "token");
+                        jsPlumbBridge.activateEndpointWithType(inTargetEndpointId, true, "token");
 
-                if (pe.payload.connectedTo) {
-                    for (var j = 0, connection; connection = pe.payload.connectedTo[j]; j++) {
-                        source = connection;
-                        let sourceEndpointId = "out-" + connection;
-                        let targetEndpointId = "in-" + j + "-" + target;
-                        this.JsplumbBridge.connect(
-                            {uuids: [sourceEndpointId, targetEndpointId], detachable: detachable}
-                        );
-                    }
+                        if (!(pe.payload instanceof DataSinkInvocation) && !(this.ObjectProvider.hasConnectedPipelineElement(pe.payload.dom, rawPipelineModel))) {
+                            let outTargetEndpointId = "out-" + pe.payload.dom;
+                            jsPlumbBridge.activateEndpointWithType(outTargetEndpointId, true, "token");
+                        }
+                    });
                 }
             }
         }
-        this.JsplumbBridge.setSuspendDrawing(false, true);
+        jsPlumbBridge.setSuspendDrawing(false, true);
     }
-
 }
-
-//PipelinePositioningService.$inject = ['JsplumbService', 'JsplumbConfigService', 'JsplumbBridge'];
\ No newline at end of file
diff --git a/ui/src/app/editor/services/pipeline-validation.service.ts b/ui/src/app/editor/services/pipeline-validation.service.ts
index 53f7bb3..24a3df2 100644
--- a/ui/src/app/editor/services/pipeline-validation.service.ts
+++ b/ui/src/app/editor/services/pipeline-validation.service.ts
@@ -21,6 +21,7 @@ import {JsplumbBridge} from "./jsplumb-bridge.service";
 import {Injectable} from "@angular/core";
 import {PipelineElementConfig} from "../model/editor.model";
 import {DataProcessorInvocation, DataSinkInvocation} from "../../core-model/gen/streampipes-model";
+import {JsplumbFactoryService} from "./jsplumb-factory.service";
 
 @Injectable()
 export class PipelineValidationService {
@@ -36,10 +37,11 @@ export class PipelineValidationService {
         {title: "Did you configure all elements?", content: "There's a pipeline element which is missing some configuration."},
     ];
 
-    constructor(private JsplumbBridge: JsplumbBridge) {
+    constructor(private JsplumbFactoryService: JsplumbFactoryService) {
     }
 
-    isValidPipeline(rawPipelineModel) {
+    isValidPipeline(rawPipelineModel, previewConfig: boolean) {
+        let jsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(previewConfig);
         let streamInAssembly = this.isStreamInAssembly(rawPipelineModel);
         let sepaInAssembly = this.isSepaInAssembly(rawPipelineModel);
         let actionInAssembly = this.isActionInAssembly(rawPipelineModel);
@@ -48,12 +50,12 @@ export class PipelineValidationService {
         let allElementsConfigured = true;
 
         if (streamInAssembly && (sepaInAssembly || actionInAssembly)) {
-            allElementsConnected = this.allElementsConnected(rawPipelineModel);
+            allElementsConnected = this.allElementsConnected(rawPipelineModel, jsplumbBridge);
             allElementsConfigured = this.allElementsConfigured(rawPipelineModel);
         }
 
         if (streamInAssembly && actionInAssembly && allElementsConnected) {
-            onlyOnePipelineCreated = this.onlyOnePipelineCreated(rawPipelineModel);
+            onlyOnePipelineCreated = this.onlyOnePipelineCreated(rawPipelineModel, jsplumbBridge);
         }
 
         if (!this.isEmptyPipeline(rawPipelineModel)) {
@@ -96,12 +98,12 @@ export class PipelineValidationService {
             .every(config => config.settings.completed);
     }
 
-    allElementsConnected(rawPipelineModel) {
-        let g = this.makeGraph(rawPipelineModel);
-        return g.nodes().every(node => this.isFullyConnected(g, node, rawPipelineModel));
+    allElementsConnected(rawPipelineModel, jsplumbBridge) {
+        let g = this.makeGraph(rawPipelineModel, jsplumbBridge);
+        return g.nodes().every(node => this.isFullyConnected(g, node));
     }
 
-    isFullyConnected(g, node, rawPipelineModel) {
+    isFullyConnected(g, node) {
         var nodeProperty = g.node(node);
         return g.outEdges(node).length >= nodeProperty.endpointCount;
     }
@@ -118,8 +120,8 @@ export class PipelineValidationService {
         return this.isInAssembly(rawPipelineModel, "sepa");
     }
 
-    onlyOnePipelineCreated(rawPipelineModel) {
-        let g = this.makeGraph(rawPipelineModel);
+    onlyOnePipelineCreated(rawPipelineModel, jsplumbBridge: JsplumbBridge) {
+        let g = this.makeGraph(rawPipelineModel, jsplumbBridge);
         let tarjan = dagre.graphlib.alg.tarjan(g);
 
         return tarjan.length == 1;
@@ -135,7 +137,7 @@ export class PipelineValidationService {
         return isElementInAssembly;
     }
 
-    makeGraph(rawPipelineModel: PipelineElementConfig[]) {
+    makeGraph(rawPipelineModel: PipelineElementConfig[], jsplumbBridge: JsplumbBridge) {
         var g = new dagre.graphlib.Graph();
         g.setGraph({rankdir: "LR"});
         g.setDefaultEdgeLabel(function () {
@@ -151,28 +153,20 @@ export class PipelineValidationService {
                     label: n.id,
                     type: elementOptions.type,
                     name: elementOptions.payload.name,
-                    endpointCount: this.JsplumbBridge.getEndpointCount(n.id)
+                    endpointCount: jsplumbBridge.getEndpointCount(n.id)
                 });
             }
         }
-        var edges = this.JsplumbBridge.getAllConnections();
-        for (var i = 0; i < edges.length; i++) {
+        var edges = jsplumbBridge.getAllConnections();
+        edges.forEach((edge, i) => {
             var c = edges[i];
             g.setEdge(c.source.id, c.target.id);
             g.setEdge(c.target.id, c.source.id);
-        }
+        });
         return g;
     }
 
     getElementOptions(id, rawPipelineModel: PipelineElementConfig[]) {
-        var pipelineElement;
-        rawPipelineModel.forEach(pe => {
-           if (pe.payload.dom === id) {
-               pipelineElement = pe;
-           }
-        });
-        return pipelineElement;
+        return rawPipelineModel.find(pe => pe.payload.dom === id);
     }
 }
-
-//PipelineValidationService.$inject=['ObjectProvider', 'JsplumbBridge'];
\ No newline at end of file
diff --git a/ui/src/app/pipeline-details/components/preview/pipeline-preview.component.ts b/ui/src/app/pipeline-details/components/preview/pipeline-preview.component.ts
index 1fe08db..9125e2f 100644
--- a/ui/src/app/pipeline-details/components/preview/pipeline-preview.component.ts
+++ b/ui/src/app/pipeline-details/components/preview/pipeline-preview.component.ts
@@ -21,8 +21,8 @@ import {Pipeline} from "../../../core-model/gen/streampipes-model";
 import {PipelineElementConfig, PipelineElementUnion} from "../../../editor/model/editor.model";
 import {PipelinePositioningService} from "../../../editor/services/pipeline-positioning.service";
 import {JsplumbService} from "../../../editor/services/jsplumb.service";
-import {JsplumbBridge} from "../../../editor/services/jsplumb-bridge.service";
 import {ObjectProvider} from "../../../editor/services/object-provider.service";
+import {JsplumbFactoryService} from "../../../editor/services/jsplumb-factory.service";
 
 @Component({
     selector: 'pipeline-preview',
@@ -44,7 +44,7 @@ export class PipelinePreviewComponent implements OnInit {
 
     constructor(private PipelinePositioningService: PipelinePositioningService,
                 private JsplumbService: JsplumbService,
-                private JsplumbBridge: JsplumbBridge,
+                private JsplumbFactoryService: JsplumbFactoryService,
                 private ObjectProvider: ObjectProvider) {
         this.PipelinePositioningService = PipelinePositioningService;
         this.ObjectProvider = ObjectProvider;
@@ -58,7 +58,7 @@ export class PipelinePreviewComponent implements OnInit {
                 this.PipelinePositioningService.displayPipeline(this.rawPipelineModel, elid, true, true);
                 var existingEndpointIds = [];
                 setTimeout(() => {
-                    this.JsplumbBridge.selectEndpoints().each(endpoint => {
+                    this.JsplumbFactoryService.getJsplumbBridge(true).selectEndpoints().each(endpoint => {
                         if (existingEndpointIds.indexOf(endpoint.element.id) === -1) {
                             $(endpoint.element).click(() => {
                                 let payload = this.ObjectProvider.findElement(endpoint.element.id, this.rawPipelineModel).payload;
@@ -71,4 +71,4 @@ export class PipelinePreviewComponent implements OnInit {
             });
         });
     }
-}
\ No newline at end of file
+}
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 3c96525..020b977 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/dist/css/jsplumb.css';
+@import '~jsplumb/css/jsplumbtoolkit-defaults.css';
 @import '~ng-prettyjson/dist/ng-prettyjson.min.css';
 @import '~prismjs/themes/prism.css';
 @import '~angular-loading-bar/build/loading-bar.min.css';
@@ -35,6 +35,7 @@
 //@import '~material-design-icons/iconfont/material-icons.css';
 @import '~quill/dist/quill.snow.css';
 @import '~swagger-ui/dist/swagger-ui.css';
+@import '~perfect-scrollbar/css/perfect-scrollbar.css';
 
 @import '~roboto-fontface/css/roboto/roboto-fontface.css';
 
@@ -60,6 +61,7 @@
 @import './sp/pipeline-assembly.scss';
 @import './sp/widgets.scss';
 @import './sp/progress-bar.scss';
+@import './sp/jsplumb';
 
 @import './sp/input.ng1';
 @import './sp/documentation.ng1';
diff --git a/ui/src/scss/sp/jsplumb.scss b/ui/src/scss/sp/jsplumb.scss
new file mode 100644
index 0000000..9d828b5
--- /dev/null
+++ b/ui/src/scss/sp/jsplumb.scss
@@ -0,0 +1,21 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+.jtk-endpoint {
+  z-index: 5;
+}
diff --git a/ui/src/scss/sp/main.scss b/ui/src/scss/sp/main.scss
index 4185bdb..3d1044e 100644
--- a/ui/src/scss/sp/main.scss
+++ b/ui/src/scss/sp/main.scss
@@ -22,6 +22,10 @@ md-progress-linear.md-accent .md-container {
   background-color: rgb(168, 168, 168);
 }
 
+.stream-endpoint {
+  background: green;
+}
+
 .gu-mirror {
   display: none; /* This fixes a bug with the dragular framework */
 }
@@ -517,10 +521,6 @@ md-select.md-default-theme .md-select-value.md-select-placeholder, md-select .md
   border: 2px solid $sp-color-accent !important;
 }
 
-.jsplumb-endpoint {
-  z-index: 5;
-}
-
 .popover {
   border: 1px solid $sp-color-accent;
   box-shadow: 0 0px 0px;


[incubator-streampipes] 04/06: [STREAMPIPES-304] Improve pan behaviour

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8c0fc103a7ea627661e4bf85745460696e6635aa
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Tue Mar 9 21:59:01 2021 +0100

    [STREAMPIPES-304] Improve pan behaviour
---
 .../pipeline-assembly.component.html               |  4 +-
 .../pipeline-assembly.component.ts                 | 51 +++++++++-------------
 .../components/pipeline/pipeline.component.ts      | 20 +--------
 ui/src/app/editor/services/jsplumb.service.ts      | 23 +++++-----
 4 files changed, 36 insertions(+), 62 deletions(-)

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 bb088c6..ce6c419 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
@@ -93,7 +93,7 @@
         </div>
     </div>
     <div id="outerAssemblyArea" class="outerAssembly sp-blue-border-nopadding">
-        <div class="pipeline-canvas-outer">
+        <div class="pipeline-canvas-outer" #outerCanvas>
             <div class="pan-control">
                 <div class="pan-zoom-control-buttons">
                     <div class="pan-zoom-button pan-left" (click)="panLeft()">
@@ -124,7 +124,7 @@
                     </div>
                 </div>
             </div>
-            <div id="assembly" class="canvas">
+            <div id="assembly" class="canvas" #assembly>
                 <pipeline [pipelineValid]="pipelineValid"
                           [canvasId]="'assembly'"
                           [rawPipelineModel]="rawPipelineModel"
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 9ce6213..385d5c3 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
@@ -43,6 +43,7 @@ import {PipelineService} from "../../../platform-services/apis/pipeline.service"
 import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scrolling.service";
 import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
 import Panzoom, {PanzoomObject} from "@panzoom/panzoom";
+import {PipelineElementDraggedService} from "../../services/pipeline-element-dragged.service";
 
 
 @Component({
@@ -84,7 +85,7 @@ export class PipelineAssemblyComponent implements OnInit {
     pipelineCached: boolean = false;
 
     config: any = {};
-    @ViewChild("assembly") pipelineCanvas: ElementRef;
+    @ViewChild("outerCanvas") pipelineCanvas: ElementRef;
     panzoom: PanzoomObject;
 
     constructor(private JsPlumbFactoryService: JsplumbFactoryService,
@@ -98,11 +99,10 @@ export class PipelineAssemblyComponent implements OnInit {
                 private dialogService: DialogService,
                 private dialog: MatDialog,
                 private ngZone: NgZone,
-                private pipelineCanvasScrollingService: PipelineCanvasScrollingService) {
+                private pipelineElementDraggedService: PipelineElementDraggedService) {
 
         this.selectMode = true;
         this.currentZoomLevel = 1;
-
     }
 
     ngOnInit(): void {
@@ -112,15 +112,24 @@ export class PipelineAssemblyComponent implements OnInit {
         } else {
             this.checkAndDisplayCachedPipeline();
         }
-        this.pipelineCanvasScrollingService.canvasScrollYSubject.subscribe(position => {
-            console.log("scrolling to" + position);
-            console.log(this.pipelineCanvas.nativeElement.scrollHeight);
-            console.log(this.pipelineCanvas.nativeElement.scrollTop);
-            this.pipelineCanvas.nativeElement.scrollTop = this.pipelineCanvas.nativeElement.scrollHeight;
+        this.pipelineElementDraggedService.pipelineElementMovedSubject.subscribe(position => {
+            let offsetHeight = this.pipelineCanvas.nativeElement.offsetHeight;
+            let offsetWidth = this.pipelineCanvas.nativeElement.offsetWidth;
+            let currentPan = this.panzoom.getPan();
+            let xOffset = 0;
+            let yOffset = 0;
+            if ((position.y + currentPan.y) > (offsetHeight - 100)) {
+              yOffset = -10;
+            }
+            if ((position.x + currentPan.x) > (offsetWidth - 100)) {
+              xOffset = -10;
+            }
+            if (xOffset < 0 || yOffset < 0) {
+              this.pan(xOffset, yOffset);
+            }
         });
     }
 
-
     ngAfterViewInit() {
         const elem = document.getElementById('assembly')
         this.panzoom = Panzoom(elem, {
@@ -129,26 +138,6 @@ export class PipelineAssemblyComponent implements OnInit {
             canvas: true,
             contain: "outside"
         })
-        //panzoom.pan(10, 10)
-        //panzoom.zoom(2, { animate: true })
-        // ($("#assembly") as any).panzoom({
-        //     disablePan: false,
-        //     increment: 0.25,
-        //     minScale: 0.5,
-        //     maxScale: 1.5,
-        //     contain: 'invert',
-        //     excludeClass: "jtk-managed"
-        // });
-        //
-        // $("#assembly").on('panzoomzoom', (e, panzoom, scale) => {
-        //     this.currentZoomLevel = scale;
-        //     this.JsplumbBridge.setZoom(scale);
-        //     this.JsplumbBridge.repaintEverything();
-        // });
-        //
-        // $("#assembly").on('panzoompan', (e, panzoom, scale) => {
-        //     console.log(e);
-        // });
     }
 
     autoLayout() {
@@ -231,7 +220,7 @@ export class PipelineAssemblyComponent implements OnInit {
             pipeline._id = this.currentModifiedPipelineId;
         }
 
-        const dialogRef = this.dialogService.open(SavePipelineComponent,{
+        this.dialogService.open(SavePipelineComponent,{
             panelType: PanelType.SLIDE_IN_PANEL,
             title: "Save pipeline",
             data: {
@@ -310,7 +299,7 @@ export class PipelineAssemblyComponent implements OnInit {
         let currentPan = this.panzoom.getPan();
         let panX = Math.min(0, currentPan.x + xOffset);
         let panY = Math.min(0, currentPan.y + yOffset);
-        let values = this.panzoom.pan(panX, panY);
+        this.panzoom.pan(panX, panY);
     }
 
     panAbsolute(x: number, y: number) {
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index a4a3a34..22ad8c0 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -104,8 +104,6 @@ export class PipelineComponent implements OnInit, OnDestroy {
 
   JsplumbBridge: JsplumbBridge;
 
-  //@ViewChild('outerCanvas') canvasRef: ElementRef;
-
   constructor(private JsplumbService: JsplumbService,
               private PipelineEditorService: PipelineEditorService,
               private JsplumbFactoryService: JsplumbFactoryService,
@@ -115,9 +113,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
               private PipelineValidationService: PipelineValidationService,
               private dialogService: DialogService,
               private dialog: MatDialog,
-              private ngZone: NgZone,
-              private pipelineElementDraggedService: PipelineElementDraggedService,
-              private pipelineCanvasScrollingService: PipelineCanvasScrollingService) {
+              private ngZone: NgZone) {
     this.plumbReady = false;
     this.currentMouseOverElement = "";
     this.currentPipelineModel = new Pipeline();
@@ -127,19 +123,6 @@ export class PipelineComponent implements OnInit, OnDestroy {
   }
 
   ngOnInit() {
-    // this.pipelineElementDraggedService.pipelineElementMovedSubject.subscribe(position => {
-    //   console.log(position);
-    //   console.log(this.canvasHeight);
-    //   console.log(this.canvasRef.nativeElement.offsetHeight);
-    //   console.log(this.canvasRef.nativeElement.scrollHeight);
-    //   if ((position.y + 200) > this.canvasRef.nativeElement.offsetHeight) {
-    //     this.canvasHeight = this.canvasRef.nativeElement.offsetHeight +10 + "px";
-    //     this.pipelineCanvasScrollingService.canvasScrollYSubject.next(position.y);
-    //   }
-    //   if ((position.x + 200) > this.canvasRef.nativeElement.offsetWidth) {
-    //     this.canvasWidth = this.canvasRef.nativeElement.offsetWidth + 100 + "px";
-    //   }
-    // });
     this.JsplumbBridge = this.JsplumbFactoryService.getJsplumbBridge(this.preview);
     this.JsplumbBridge.setContainer(this.canvasId);
     this.initAssembly();
@@ -159,7 +142,6 @@ export class PipelineComponent implements OnInit, OnDestroy {
   ngOnDestroy() {
     this.JsplumbBridge.deleteEveryEndpoint();
     this.plumbReady = false;
-    //this.pipelineElementDraggedService.pipelineElementMovedSubject.unsubscribe();
   }
 
   updateMouseover(elementId) {
diff --git a/ui/src/app/editor/services/jsplumb.service.ts b/ui/src/app/editor/services/jsplumb.service.ts
index 4196480..8bdcf2e 100644
--- a/ui/src/app/editor/services/jsplumb.service.ts
+++ b/ui/src/app/editor/services/jsplumb.service.ts
@@ -191,13 +191,7 @@ export class JsplumbService {
                   preview: boolean) {
         let jsplumbBridge = this.getBridge(preview);
         if (endpoints) {
-            if (!preview) {
-                jsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent',
-                    // drag: (e => {
-                    // this.pipelineElementDraggedService.notify({x: e.pos[0], y: e.pos[1]});
-                    // })
-                });
-            }
+            this.makeDraggableIfNotPreview(pipelineElementDomId, jsplumbBridge, preview);
 
             let endpointOptions = this.jsplumbEndpointService.getStreamEndpoint(preview, pipelineElementDomId);
             jsplumbBridge.addEndpoint(pipelineElementDomId, endpointOptions);
@@ -223,9 +217,7 @@ export class JsplumbService {
                   endpoints: boolean,
                   preview: boolean): string {
         let jsplumbBridge = this.getBridge(preview);
-        if (!preview) {
-            jsplumbBridge.draggable(pipelineElementDomId, {containment: 'parent'});
-        }
+        this.makeDraggableIfNotPreview(pipelineElementDomId, jsplumbBridge, preview);
 
         if (endpoints) {
             if (pipelineElement.inputStreams.length < 2) { //1 InputNode
@@ -245,4 +237,15 @@ export class JsplumbService {
         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]});
+                })
+            });
+        }
+    }
 }