mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2024-12-20 05:27:56 +00:00
commit
e254395459
@ -151,7 +151,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gns3-web-ui-e2e": {
|
"gns3-web-ui-e2e": {
|
||||||
"root": "",
|
"root": "e2e",
|
||||||
"sourceRoot": "e2e",
|
"sourceRoot": "e2e",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"architect": {
|
"architect": {
|
||||||
|
@ -6,26 +6,6 @@ import { CdkTableModule } from "@angular/cdk/table";
|
|||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import {
|
|
||||||
MatButtonModule,
|
|
||||||
MatCardModule,
|
|
||||||
MatMenuModule,
|
|
||||||
MatToolbarModule,
|
|
||||||
MatIconModule,
|
|
||||||
MatFormFieldModule,
|
|
||||||
MatInputModule,
|
|
||||||
MatTableModule,
|
|
||||||
MatDialogModule,
|
|
||||||
MatProgressBarModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
MatSnackBarModule,
|
|
||||||
MatCheckboxModule,
|
|
||||||
MatListModule,
|
|
||||||
MatExpansionModule,
|
|
||||||
MatSortModule,
|
|
||||||
MatSelectModule,
|
|
||||||
MatTooltipModule
|
|
||||||
} from '@angular/material';
|
|
||||||
|
|
||||||
import { D3Service } from 'd3-ng2-service';
|
import { D3Service } from 'd3-ng2-service';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
@ -52,14 +32,13 @@ import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.
|
|||||||
import { ProgressDialogComponent } from './common/progress-dialog/progress-dialog.component';
|
import { ProgressDialogComponent } from './common/progress-dialog/progress-dialog.component';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
import { CreateSnapshotDialogComponent, ProjectMapComponent } from './components/project-map/project-map.component';
|
import { ProjectMapComponent } from './components/project-map/project-map.component';
|
||||||
import { ServersComponent, AddServerDialogComponent } from './components/servers/servers.component';
|
import { ServersComponent, AddServerDialogComponent } from './components/servers/servers.component';
|
||||||
import { NodeContextMenuComponent } from './components/project-map/node-context-menu/node-context-menu.component';
|
import { NodeContextMenuComponent } from './components/project-map/node-context-menu/node-context-menu.component';
|
||||||
import { StartNodeActionComponent } from './components/project-map/node-context-menu/actions/start-node-action/start-node-action.component';
|
import { StartNodeActionComponent } from './components/project-map/node-context-menu/actions/start-node-action/start-node-action.component';
|
||||||
import { StopNodeActionComponent } from './components/project-map/node-context-menu/actions/stop-node-action/stop-node-action.component';
|
import { StopNodeActionComponent } from './components/project-map/node-context-menu/actions/stop-node-action/stop-node-action.component';
|
||||||
import { ApplianceComponent } from './components/appliance/appliance.component';
|
import { ApplianceComponent } from './components/appliance/appliance.component';
|
||||||
import { ApplianceListDialogComponent } from './components/appliance/appliance-list-dialog/appliance-list-dialog.component';
|
import { ApplianceListDialogComponent } from './components/appliance/appliance-list-dialog/appliance-list-dialog.component';
|
||||||
import { NodeSelectInterfaceComponent } from './components/project-map/node-select-interface/node-select-interface.component';
|
|
||||||
import { CartographyModule } from './cartography/cartography.module';
|
import { CartographyModule } from './cartography/cartography.module';
|
||||||
import { ToasterService } from './services/toaster.service';
|
import { ToasterService } from './services/toaster.service';
|
||||||
import { ProjectWebServiceHandler } from "./handlers/project-web-service-handler";
|
import { ProjectWebServiceHandler } from "./handlers/project-web-service-handler";
|
||||||
@ -67,7 +46,7 @@ import { LinksDataSource } from "./cartography/datasources/links-datasource";
|
|||||||
import { NodesDataSource } from "./cartography/datasources/nodes-datasource";
|
import { NodesDataSource } from "./cartography/datasources/nodes-datasource";
|
||||||
import { SymbolsDataSource } from "./cartography/datasources/symbols-datasource";
|
import { SymbolsDataSource } from "./cartography/datasources/symbols-datasource";
|
||||||
import { SelectionManager } from "./cartography/managers/selection-manager";
|
import { SelectionManager } from "./cartography/managers/selection-manager";
|
||||||
import { InRectangleHelper } from "./cartography/components/map/helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "./cartography/helpers/in-rectangle-helper";
|
||||||
import { DrawingsDataSource } from "./cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "./cartography/datasources/drawings-datasource";
|
||||||
import { MoveLayerDownActionComponent } from './components/project-map/node-context-menu/actions/move-layer-down-action/move-layer-down-action.component';
|
import { MoveLayerDownActionComponent } from './components/project-map/node-context-menu/actions/move-layer-down-action/move-layer-down-action.component';
|
||||||
import { MoveLayerUpActionComponent } from './components/project-map/node-context-menu/actions/move-layer-up-action/move-layer-up-action.component';
|
import { MoveLayerUpActionComponent } from './components/project-map/node-context-menu/actions/move-layer-up-action/move-layer-up-action.component';
|
||||||
@ -84,6 +63,10 @@ import { environment } from "../environments/environment";
|
|||||||
import { RavenState } from "./common/error-handlers/raven-state-communicator";
|
import { RavenState } from "./common/error-handlers/raven-state-communicator";
|
||||||
import { ServerDiscoveryComponent } from "./components/servers/server-discovery/server-discovery.component";
|
import { ServerDiscoveryComponent } from "./components/servers/server-discovery/server-discovery.component";
|
||||||
import { ServerDatabase } from './services/server.database';
|
import { ServerDatabase } from './services/server.database';
|
||||||
|
import { CreateSnapshotDialogComponent } from './components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component';
|
||||||
|
import { SnapshotsComponent } from './components/snapshots/snapshots.component';
|
||||||
|
import { SnapshotMenuItemComponent } from './components/snapshots/snapshot-menu-item/snapshot-menu-item.component';
|
||||||
|
import { MATERIAL_IMPORTS } from './material.imports';
|
||||||
|
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
@ -105,6 +88,8 @@ if (environment.production) {
|
|||||||
ServersComponent,
|
ServersComponent,
|
||||||
AddServerDialogComponent,
|
AddServerDialogComponent,
|
||||||
CreateSnapshotDialogComponent,
|
CreateSnapshotDialogComponent,
|
||||||
|
SnapshotMenuItemComponent,
|
||||||
|
SnapshotsComponent,
|
||||||
ProjectsComponent,
|
ProjectsComponent,
|
||||||
DefaultLayoutComponent,
|
DefaultLayoutComponent,
|
||||||
ProgressDialogComponent,
|
ProgressDialogComponent,
|
||||||
@ -113,7 +98,6 @@ if (environment.production) {
|
|||||||
StopNodeActionComponent,
|
StopNodeActionComponent,
|
||||||
ApplianceComponent,
|
ApplianceComponent,
|
||||||
ApplianceListDialogComponent,
|
ApplianceListDialogComponent,
|
||||||
NodeSelectInterfaceComponent,
|
|
||||||
MoveLayerDownActionComponent,
|
MoveLayerDownActionComponent,
|
||||||
MoveLayerUpActionComponent,
|
MoveLayerUpActionComponent,
|
||||||
ProjectMapShortcutsComponent,
|
ProjectMapShortcutsComponent,
|
||||||
@ -130,28 +114,11 @@ if (environment.production) {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
CdkTableModule,
|
CdkTableModule,
|
||||||
MatButtonModule,
|
|
||||||
MatMenuModule,
|
|
||||||
MatCardModule,
|
|
||||||
MatToolbarModule,
|
|
||||||
MatIconModule,
|
|
||||||
MatFormFieldModule,
|
|
||||||
MatInputModule,
|
|
||||||
MatTableModule,
|
|
||||||
MatDialogModule,
|
|
||||||
MatProgressBarModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
MatSnackBarModule,
|
|
||||||
MatCheckboxModule,
|
|
||||||
MatListModule,
|
|
||||||
MatExpansionModule,
|
|
||||||
MatSortModule,
|
|
||||||
MatSelectModule,
|
|
||||||
MatTooltipModule,
|
|
||||||
CartographyModule,
|
CartographyModule,
|
||||||
HotkeyModule.forRoot(),
|
HotkeyModule.forRoot(),
|
||||||
PersistenceModule,
|
PersistenceModule,
|
||||||
NgxElectronModule
|
NgxElectronModule,
|
||||||
|
...MATERIAL_IMPORTS
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SettingsService,
|
SettingsService,
|
||||||
|
@ -1,12 +1,71 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MapComponent } from './components/map/map.component';
|
import { MapComponent } from './components/map/map.component';
|
||||||
|
import { CssFixer } from './helpers/css-fixer';
|
||||||
|
import { FontFixer } from './helpers/font-fixer';
|
||||||
|
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
|
||||||
|
import { SvgToDrawingConverter } from './helpers/svg-to-drawing-converter';
|
||||||
|
import { QtDasharrayFixer } from './helpers/qt-dasharray-fixer';
|
||||||
|
import { LayersManager } from './managers/layers-manager';
|
||||||
|
import { MapChangeDetectorRef } from './services/map-change-detector-ref';
|
||||||
|
import { GraphLayout } from './widgets/graph-layout';
|
||||||
|
import { LinksWidget } from './widgets/links';
|
||||||
|
import { NodesWidget } from './widgets/nodes';
|
||||||
|
import { DrawingsWidget } from './widgets/drawings';
|
||||||
|
import { DrawingLineWidget } from './widgets/drawing-line';
|
||||||
|
import { SelectionTool } from './tools/selection-tool';
|
||||||
|
import { MovingTool } from './tools/moving-tool';
|
||||||
|
import { LayersWidget } from './widgets/layers';
|
||||||
|
import { LinkWidget } from './widgets/link';
|
||||||
|
import { InterfaceStatusWidget } from './widgets/interface-status';
|
||||||
|
import { InterfaceLabelWidget } from './widgets/interface-label';
|
||||||
|
import { EllipseDrawingWidget } from './widgets/drawings/ellipse-drawing';
|
||||||
|
import { ImageDrawingWidget } from './widgets/drawings/image-drawing';
|
||||||
|
import { RectDrawingWidget } from './widgets/drawings/rect-drawing';
|
||||||
|
import { TextDrawingWidget } from './widgets/drawings/text-drawing';
|
||||||
|
import { LineDrawingWidget } from './widgets/drawings/line-drawing';
|
||||||
|
import { Context } from './models/context';
|
||||||
|
import { DrawLinkToolComponent } from './components/draw-link-tool/draw-link-tool.component';
|
||||||
|
import { NodeSelectInterfaceComponent } from './components/node-select-interface/node-select-interface.component';
|
||||||
|
import { MatMenuModule, MatIconModule } from '@angular/material';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatIconModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
MapComponent,
|
||||||
|
DrawLinkToolComponent,
|
||||||
|
NodeSelectInterfaceComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CssFixer,
|
||||||
|
FontFixer,
|
||||||
|
MultiLinkCalculatorHelper,
|
||||||
|
SvgToDrawingConverter,
|
||||||
|
QtDasharrayFixer,
|
||||||
|
LayersManager,
|
||||||
|
MapChangeDetectorRef,
|
||||||
|
GraphLayout,
|
||||||
|
LinksWidget,
|
||||||
|
NodesWidget,
|
||||||
|
DrawingsWidget,
|
||||||
|
DrawingLineWidget,
|
||||||
|
SelectionTool,
|
||||||
|
MovingTool,
|
||||||
|
LayersWidget,
|
||||||
|
LinkWidget,
|
||||||
|
InterfaceStatusWidget,
|
||||||
|
InterfaceLabelWidget,
|
||||||
|
EllipseDrawingWidget,
|
||||||
|
ImageDrawingWidget,
|
||||||
|
LineDrawingWidget,
|
||||||
|
RectDrawingWidget,
|
||||||
|
TextDrawingWidget,
|
||||||
|
Context,
|
||||||
],
|
],
|
||||||
declarations: [MapComponent],
|
|
||||||
exports: [MapComponent]
|
exports: [MapComponent]
|
||||||
})
|
})
|
||||||
export class CartographyModule { }
|
export class CartographyModule { }
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DrawLinkToolComponent } from './draw-link-tool.component';
|
||||||
|
|
||||||
|
describe('DrawLinkToolComponent', () => {
|
||||||
|
let component: DrawLinkToolComponent;
|
||||||
|
let fixture: ComponentFixture<DrawLinkToolComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ DrawLinkToolComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawLinkToolComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('should create', () => {
|
||||||
|
// expect(component).toBeTruthy();
|
||||||
|
// });
|
||||||
|
});
|
@ -0,0 +1,66 @@
|
|||||||
|
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { Port } from '../../../models/port';
|
||||||
|
import { DrawingLineWidget } from '../../widgets/drawing-line';
|
||||||
|
import { Node } from '../../models/node';
|
||||||
|
import { NodesWidget, NodeEvent } from '../../widgets/nodes';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component';
|
||||||
|
|
||||||
|
|
||||||
|
export class LinkCreated {
|
||||||
|
constructor(
|
||||||
|
public sourceNode: Node,
|
||||||
|
public sourcePort: Port,
|
||||||
|
public targetNode: Node,
|
||||||
|
public targetPort: Port
|
||||||
|
){}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-draw-link-tool',
|
||||||
|
templateUrl: './draw-link-tool.component.html',
|
||||||
|
styleUrls: ['./draw-link-tool.component.scss']
|
||||||
|
})
|
||||||
|
export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
||||||
|
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
|
||||||
|
|
||||||
|
@Output('linkCreated') linkCreated = new EventEmitter<LinkCreated>();
|
||||||
|
|
||||||
|
private onNodeClicked: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingLineTool: DrawingLineWidget,
|
||||||
|
private nodesWidget: NodesWidget
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.onNodeClicked = this.nodesWidget.onNodeClicked.subscribe((eventNode: NodeEvent) => {
|
||||||
|
this.nodeSelectInterfaceMenu.open(
|
||||||
|
eventNode.node,
|
||||||
|
eventNode.event.clientY,
|
||||||
|
eventNode.event.clientX
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if(this.drawingLineTool.isDrawing()) {
|
||||||
|
this.drawingLineTool.stop();
|
||||||
|
}
|
||||||
|
this.onNodeClicked.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onChooseInterface(event) {
|
||||||
|
const node: Node = event.node;
|
||||||
|
const port: Port = event.port;
|
||||||
|
if (this.drawingLineTool.isDrawing()) {
|
||||||
|
const data = this.drawingLineTool.stop();
|
||||||
|
this.linkCreated.emit(new LinkCreated(data['node'], data['port'], node, port));
|
||||||
|
} else {
|
||||||
|
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
|
||||||
|
'node': node,
|
||||||
|
'port': port
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,6 @@
|
|||||||
<svg class="map" preserveAspectRatio="none">
|
<svg
|
||||||
</svg>
|
class="map"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
></svg>
|
||||||
|
|
||||||
|
<app-draw-link-tool *ngIf="drawLinkTool" (linkCreated)="linkCreated($event)"></app-draw-link-tool>
|
Before Width: | Height: | Size: 52 B After Width: | Height: | Size: 159 B |
@ -1,16 +1,24 @@
|
|||||||
import {
|
import {
|
||||||
Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChange
|
Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChange, EventEmitter, Output
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { D3, D3Service } from 'd3-ng2-service';
|
import { D3, D3Service } from 'd3-ng2-service';
|
||||||
import { select, Selection } from 'd3-selection';
|
import { Selection } from 'd3-selection';
|
||||||
|
|
||||||
import { Node } from "../../models/node";
|
import { Node } from "../../models/node";
|
||||||
import { Link } from "../../models/link";
|
import { Link } from "../../../models/link";
|
||||||
import { GraphLayout } from "../../widgets/graph-layout";
|
import { GraphLayout } from "../../widgets/graph-layout";
|
||||||
import { Context } from "../../models/context";
|
import { Context } from "../../models/context";
|
||||||
import { Size } from "../../models/size";
|
import { Size } from "../../models/size";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { Symbol } from "../../models/symbol";
|
import { Symbol } from '../../../models/symbol';
|
||||||
|
import { NodeEvent, NodesWidget } from '../../widgets/nodes';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { InterfaceLabelWidget } from '../../widgets/interface-label';
|
||||||
|
import { SelectionTool } from '../../tools/selection-tool';
|
||||||
|
import { MovingTool } from '../../tools/moving-tool';
|
||||||
|
import { LinksWidget } from '../../widgets/links';
|
||||||
|
import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
|
||||||
|
import { LinkCreated } from '../draw-link-tool/draw-link-tool.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -27,20 +35,60 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() width = 1500;
|
@Input() width = 1500;
|
||||||
@Input() height = 600;
|
@Input() height = 600;
|
||||||
|
|
||||||
|
@Output() onNodeDragged: EventEmitter<NodeEvent>;
|
||||||
|
@Output() onLinkCreated = new EventEmitter<LinkCreated>();
|
||||||
|
|
||||||
private d3: D3;
|
private d3: D3;
|
||||||
private parentNativeElement: any;
|
private parentNativeElement: any;
|
||||||
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
||||||
private graphContext: Context;
|
|
||||||
|
|
||||||
public graphLayout: GraphLayout;
|
private isReady = false;
|
||||||
|
|
||||||
constructor(protected element: ElementRef,
|
private onNodeDraggingSubscription: Subscription;
|
||||||
protected d3Service: D3Service
|
private onChangesDetected: Subscription;
|
||||||
|
|
||||||
|
protected settings = {
|
||||||
|
'show_interface_labels': true
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private context: Context,
|
||||||
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
|
protected element: ElementRef,
|
||||||
|
protected d3Service: D3Service,
|
||||||
|
protected nodesWidget: NodesWidget,
|
||||||
|
protected linksWidget: LinksWidget,
|
||||||
|
protected interfaceLabelWidget: InterfaceLabelWidget,
|
||||||
|
protected selectionToolWidget: SelectionTool,
|
||||||
|
protected movingToolWidget: MovingTool,
|
||||||
|
public graphLayout: GraphLayout
|
||||||
) {
|
) {
|
||||||
this.d3 = d3Service.getD3();
|
this.d3 = d3Service.getD3();
|
||||||
this.parentNativeElement = element.nativeElement;
|
this.parentNativeElement = element.nativeElement;
|
||||||
|
this.onNodeDragged = nodesWidget.onNodeDragged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input('show-interface-labels')
|
||||||
|
set showInterfaceLabels(value) {
|
||||||
|
this.settings.show_interface_labels = value;
|
||||||
|
this.interfaceLabelWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input('moving-tool')
|
||||||
|
set movingTool(value) {
|
||||||
|
this.movingToolWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input('selection-tool')
|
||||||
|
set selectionTool(value) {
|
||||||
|
this.selectionToolWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input('draw-link-tool') drawLinkTool: boolean;
|
||||||
|
|
||||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||||
if (
|
if (
|
||||||
(changes['width'] && !changes['width'].isFirstChange()) ||
|
(changes['width'] && !changes['width'].isFirstChange()) ||
|
||||||
@ -67,37 +115,37 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.graphLayout.disconnect(this.svg);
|
this.graphLayout.disconnect(this.svg);
|
||||||
|
this.onNodeDraggingSubscription.unsubscribe();
|
||||||
|
this.onChangesDetected.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const d3 = this.d3;
|
|
||||||
|
|
||||||
let rootElement: Selection<HTMLElement, any, null, undefined>;
|
|
||||||
|
|
||||||
if (this.parentNativeElement !== null) {
|
if (this.parentNativeElement !== null) {
|
||||||
rootElement = d3.select(this.parentNativeElement);
|
this.createGraph(this.parentNativeElement);
|
||||||
|
}
|
||||||
|
this.context.size = this.getSize();
|
||||||
|
|
||||||
|
this.onNodeDraggingSubscription = this.nodesWidget.onNodeDragging.subscribe((eventNode: NodeEvent) => {
|
||||||
|
const links = this.links.filter((link) => link.target.node_id === eventNode.node.node_id || link.source.node_id === eventNode.node.node_id)
|
||||||
|
|
||||||
|
links.forEach((link) => {
|
||||||
|
this.linksWidget.redrawLink(this.svg, link);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
|
||||||
|
if (this.isReady) {
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public createGraph(domElement: HTMLElement) {
|
||||||
|
const rootElement = this.d3.select(domElement);
|
||||||
this.svg = rootElement.select<SVGSVGElement>('svg');
|
this.svg = rootElement.select<SVGSVGElement>('svg');
|
||||||
|
this.graphLayout.connect(this.svg, this.context);
|
||||||
this.graphContext = new Context(true);
|
this.graphLayout.draw(this.svg, this.context);
|
||||||
|
this.isReady = true;
|
||||||
this.graphContext.size = this.getSize();
|
|
||||||
|
|
||||||
this.graphLayout = new GraphLayout();
|
|
||||||
this.graphLayout.connect(this.svg, this.graphContext);
|
|
||||||
|
|
||||||
this.graphLayout.getNodesWidget().addOnNodeDraggingCallback((event: any, n: Node) => {
|
|
||||||
const linksWidget = this.graphLayout.getLinksWidget();
|
|
||||||
linksWidget.select(this.svg).each(function(this: SVGGElement, link: Link) {
|
|
||||||
if (link.target.node_id === n.node_id || link.source.node_id === n.node_id) {
|
|
||||||
const selection = select<SVGElement, Link>(this);
|
|
||||||
linksWidget.revise(selection);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.graphLayout.draw(this.svg, this.graphContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSize(): Size {
|
public getSize(): Size {
|
||||||
@ -112,9 +160,13 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return new Size(width, height);
|
return new Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected linkCreated(evt) {
|
||||||
|
this.onLinkCreated.emit(evt);
|
||||||
|
}
|
||||||
|
|
||||||
private changeLayout() {
|
private changeLayout() {
|
||||||
if (this.parentNativeElement != null) {
|
if (this.parentNativeElement != null) {
|
||||||
this.graphContext.size = this.getSize();
|
this.context.size = this.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.graphLayout.setNodes(this.nodes);
|
this.graphLayout.setNodes(this.nodes);
|
||||||
@ -156,7 +208,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public redraw() {
|
public redraw() {
|
||||||
this.graphLayout.draw(this.svg, this.graphContext);
|
this.graphLayout.draw(this.svg, this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public reload() {
|
public reload() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="context-menu" [style.left]="leftPosition" [style.top]="topPosition" *ngIf="node">
|
<div class="context-menu" [style.left]="leftPosition" [style.top]="topPosition" *ngIf="node">
|
||||||
<span [matMenuTriggerFor]="selectInterfaceMenu"></span>
|
<span [matMenuTriggerFor]="selectInterfaceMenu"></span>
|
||||||
<mat-menu #selectInterfaceMenu="matMenu">
|
<mat-menu #selectInterfaceMenu="matMenu" class="context-menu-items">
|
||||||
<button mat-menu-item *ngFor="let port of node.ports" (click)="chooseInterface(port)">
|
<button mat-menu-item *ngFor="let port of node.ports" (click)="chooseInterface(port)">
|
||||||
<mat-icon>add_circle_outline</mat-icon>
|
<mat-icon>add_circle_outline</mat-icon>
|
||||||
<span>{{ port.name }}</span>
|
<span>{{ port.name }}</span>
|
@ -1,4 +1,4 @@
|
|||||||
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, OnDestroy} from '@angular/core';
|
||||||
import {MatMenuTrigger} from "@angular/material";
|
import {MatMenuTrigger} from "@angular/material";
|
||||||
import {DomSanitizer} from "@angular/platform-browser";
|
import {DomSanitizer} from "@angular/platform-browser";
|
||||||
import {Node} from "../../../cartography/models/node";
|
import {Node} from "../../../cartography/models/node";
|
||||||
@ -15,13 +15,15 @@ export class NodeSelectInterfaceComponent implements OnInit {
|
|||||||
|
|
||||||
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
|
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
|
||||||
|
|
||||||
private topPosition;
|
protected topPosition;
|
||||||
private leftPosition;
|
protected leftPosition;
|
||||||
|
|
||||||
public node: Node;
|
public node: Node;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private changeDetector: ChangeDetectorRef) {}
|
private changeDetector: ChangeDetectorRef,
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.setPosition(0, 0);
|
this.setPosition(0, 0);
|
@ -9,7 +9,7 @@ class TestDataSource extends DataSource<Item> {
|
|||||||
protected findIndex(item: Item) {
|
protected findIndex(item: Item) {
|
||||||
return this.data.findIndex((i: Item) => i.id === item.id);
|
return this.data.findIndex((i: Item) => i.id === item.id);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('TestDataSource', () => {
|
describe('TestDataSource', () => {
|
||||||
@ -18,7 +18,7 @@ describe('TestDataSource', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dataSource = new TestDataSource();
|
dataSource = new TestDataSource();
|
||||||
dataSource.connect().subscribe((updated: Item[]) => {
|
dataSource.changes.subscribe((updated: Item[]) => {
|
||||||
data = updated;
|
data = updated;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject, Subject } from "rxjs";
|
||||||
|
|
||||||
export abstract class DataSource<T> {
|
export abstract class DataSource<T> {
|
||||||
protected data: T[] = [];
|
protected data: T[] = [];
|
||||||
protected dataChange: BehaviorSubject<T[]> = new BehaviorSubject<T[]>([]);
|
protected dataChange: BehaviorSubject<T[]> = new BehaviorSubject<T[]>([]);
|
||||||
|
protected itemUpdated: Subject<T> = new Subject<T>();
|
||||||
|
|
||||||
public getItems(): T[] {
|
public getItems(): T[] {
|
||||||
return this.data;
|
return this.data;
|
||||||
@ -26,8 +27,10 @@ export abstract class DataSource<T> {
|
|||||||
public update(item: T) {
|
public update(item: T) {
|
||||||
const index = this.findIndex(item);
|
const index = this.findIndex(item);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
this.data[index] = Object.assign(this.data[index], item);
|
const updated = Object.assign(this.data[index], item);
|
||||||
|
this.data[index] = updated;
|
||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
|
this.itemUpdated.next(updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,10 +42,14 @@ export abstract class DataSource<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect() {
|
public get changes() {
|
||||||
return this.dataChange;
|
return this.dataChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get itemChanged() {
|
||||||
|
return this.itemUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
public clear() {
|
public clear() {
|
||||||
this.data = [];
|
this.data = [];
|
||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
|
@ -8,7 +8,7 @@ describe('DrawingsDataSource', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dataSource = new DrawingsDataSource();
|
dataSource = new DrawingsDataSource();
|
||||||
dataSource.connect().subscribe((drawings: Drawing[]) => {
|
dataSource.changes.subscribe((drawings: Drawing[]) => {
|
||||||
data = drawings;
|
data = drawings;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { LinksDataSource } from "./links-datasource";
|
import { LinksDataSource } from "./links-datasource";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
|
|
||||||
|
|
||||||
describe('LinksDataSource', () => {
|
describe('LinksDataSource', () => {
|
||||||
@ -8,7 +8,7 @@ describe('LinksDataSource', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dataSource = new LinksDataSource();
|
dataSource = new LinksDataSource();
|
||||||
dataSource.connect().subscribe((links: Link[]) => {
|
dataSource.changes.subscribe((links: Link[]) => {
|
||||||
data = links;
|
data = links;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { DataSource } from "./datasource";
|
import { DataSource } from "./datasource";
|
||||||
import { Link} from "../models/link";
|
import { Link} from "../../models/link";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -8,7 +8,7 @@ describe('NodesDataSource', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dataSource = new NodesDataSource();
|
dataSource = new NodesDataSource();
|
||||||
dataSource.connect().subscribe((nodes: Node[]) => {
|
dataSource.changes.subscribe((nodes: Node[]) => {
|
||||||
data = nodes;
|
data = nodes;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { SymbolsDataSource } from "./symbols-datasource";
|
import { SymbolsDataSource } from "./symbols-datasource";
|
||||||
import { Symbol } from "../models/symbol";
|
import { Symbol } from "../../models/symbol";
|
||||||
|
|
||||||
|
|
||||||
describe('SymbolsDataSource', () => {
|
describe('SymbolsDataSource', () => {
|
||||||
@ -8,7 +8,7 @@ describe('SymbolsDataSource', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dataSource = new SymbolsDataSource();
|
dataSource = new SymbolsDataSource();
|
||||||
dataSource.connect().subscribe((symbols: Symbol[]) => {
|
dataSource.changes.subscribe((symbols: Symbol[]) => {
|
||||||
data = symbols;
|
data = symbols;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { DataSource } from "./datasource";
|
import { DataSource } from "./datasource";
|
||||||
import { Symbol } from "../models/symbol";
|
import { Symbol } from "../../models/symbol";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { InRectangleHelper } from "./in-rectangle-helper";
|
import { InRectangleHelper } from "./in-rectangle-helper";
|
||||||
import { Rectangle } from "../../../models/rectangle";
|
import { Rectangle } from "../models/rectangle";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { Selectable } from "../../../managers/selection-manager";
|
import { Rectangle } from "../models/rectangle";
|
||||||
import { Rectangle } from "../../../models/rectangle";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
@ -1,5 +1,7 @@
|
|||||||
import {Link} from "../../../models/link";
|
import {Link} from "../../models/link";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class MultiLinkCalculatorHelper {
|
export class MultiLinkCalculatorHelper {
|
||||||
LINK_WIDTH = 2;
|
LINK_WIDTH = 2;
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import { LayersManager } from "./layers-manager";
|
import { LayersManager } from "./layers-manager";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Drawing } from "../models/drawing";
|
import { Drawing } from "../models/drawing";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
|
|
||||||
|
|
||||||
describe('LayersManager', () => {
|
describe('LayersManager', () => {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Drawing } from "../models/drawing";
|
import { Drawing } from "../models/drawing";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { Dictionary } from "../models/types";
|
import { Dictionary } from "../models/types";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class LayersManager {
|
export class LayersManager {
|
||||||
private layers: Dictionary<Layer>;
|
private layers: Dictionary<Layer>;
|
||||||
|
|
||||||
@ -57,4 +60,5 @@ export class LayersManager {
|
|||||||
}
|
}
|
||||||
return this.layers[key];
|
return this.layers[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Subject} from "rxjs";
|
import { Subject} from "rxjs";
|
||||||
|
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { Drawing } from "../models/drawing";
|
import { Drawing } from "../models/drawing";
|
||||||
import { Rectangle } from "../models/rectangle";
|
import { Rectangle } from "../models/rectangle";
|
||||||
import { SelectionManager } from "./selection-manager";
|
import { SelectionManager } from "./selection-manager";
|
||||||
import { NodesDataSource } from "../datasources/nodes-datasource";
|
import { NodesDataSource } from "../datasources/nodes-datasource";
|
||||||
import { LinksDataSource } from "../datasources/links-datasource";
|
import { LinksDataSource } from "../datasources/links-datasource";
|
||||||
import { InRectangleHelper } from "../components/map/helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
|
||||||
import { DrawingsDataSource } from "../datasources/drawings-datasource";
|
import { DrawingsDataSource } from "../datasources/drawings-datasource";
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ import { Subscription } from "rxjs";
|
|||||||
import { NodesDataSource } from "../datasources/nodes-datasource";
|
import { NodesDataSource } from "../datasources/nodes-datasource";
|
||||||
import { LinksDataSource } from "../datasources/links-datasource";
|
import { LinksDataSource } from "../datasources/links-datasource";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { InRectangleHelper } from "../components/map/helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
|
||||||
import { Rectangle } from "../models/rectangle";
|
import { Rectangle } from "../models/rectangle";
|
||||||
import { Link} from "../models/link";
|
import { Link} from "../../models/link";
|
||||||
import { DataSource } from "../datasources/datasource";
|
import { DataSource } from "../datasources/datasource";
|
||||||
import { Drawing } from "../models/drawing";
|
import { Drawing } from "../models/drawing";
|
||||||
import { InterfaceLabel } from "../models/interface-label";
|
import { InterfaceLabel } from "../models/interface-label";
|
||||||
@ -38,13 +38,17 @@ export class SelectionManager {
|
|||||||
|
|
||||||
public subscribe(subject: Subject<Rectangle>) {
|
public subscribe(subject: Subject<Rectangle>) {
|
||||||
this.subscription = subject.subscribe((rectangle: Rectangle) => {
|
this.subscription = subject.subscribe((rectangle: Rectangle) => {
|
||||||
|
this.onSelection(rectangle);
|
||||||
|
});
|
||||||
|
return this.subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSelection(rectangle: Rectangle) {
|
||||||
this.selectedNodes = this.getSelectedItemsInRectangle<Node>(this.nodesDataSource, rectangle);
|
this.selectedNodes = this.getSelectedItemsInRectangle<Node>(this.nodesDataSource, rectangle);
|
||||||
this.selectedLinks = this.getSelectedItemsInRectangle<Link>(this.linksDataSource, rectangle);
|
this.selectedLinks = this.getSelectedItemsInRectangle<Link>(this.linksDataSource, rectangle);
|
||||||
this.selectedDrawings = this.getSelectedItemsInRectangle<Drawing>(this.drawingsDataSource, rectangle);
|
this.selectedDrawings = this.getSelectedItemsInRectangle<Drawing>(this.drawingsDataSource, rectangle);
|
||||||
// don't select interfaces for now
|
// don't select interfaces for now
|
||||||
// this.selectedInterfaceLabels = this.getSelectedInterfaceLabelsInRectangle(rectangle);
|
// this.selectedInterfaceLabels = this.getSelectedInterfaceLabelsInRectangle(rectangle);
|
||||||
});
|
|
||||||
return this.subscription;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelectedNodes() {
|
public getSelectedNodes() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Size } from "./size";
|
import { Size } from "./size";
|
||||||
import { Point } from "./point";
|
import { Point } from "./point";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
export class Transformation {
|
export class Transformation {
|
||||||
constructor(
|
constructor(
|
||||||
@ -9,12 +10,13 @@ export class Transformation {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class Context {
|
export class Context {
|
||||||
public transformation: Transformation;
|
public transformation: Transformation;
|
||||||
public size: Size;
|
public size: Size;
|
||||||
|
public centerZeroZeroPoint = true;
|
||||||
|
|
||||||
constructor(public centerZeroZeroPoint = true) {
|
constructor() {
|
||||||
this.size = new Size(0, 0);
|
this.size = new Size(0, 0);
|
||||||
this.transformation = new Transformation(0, 0, 1);
|
this.transformation = new Transformation(0, 0, 1);
|
||||||
}
|
}
|
||||||
|
8
src/app/cartography/models/graph-link.ts
Normal file
8
src/app/cartography/models/graph-link.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export class GraphLink {
|
||||||
|
distance: number; // this is not from server
|
||||||
|
length: number; // this is not from server
|
||||||
|
source: Node; // this is not from server
|
||||||
|
target: Node; // this is not from server
|
||||||
|
x: number; // this is not from server
|
||||||
|
y: number; // this is not from server
|
||||||
|
}
|
@ -1,16 +1,13 @@
|
|||||||
import {Drawing} from "./drawing";
|
import {Drawing} from "./drawing";
|
||||||
import {Link} from "./link";
|
import {Link} from "../../models/link";
|
||||||
import {Node} from "./node";
|
import {Node} from "./node";
|
||||||
|
|
||||||
export class Layer {
|
export class Layer {
|
||||||
index: number;
|
constructor(
|
||||||
nodes: Node[];
|
public index?: number,
|
||||||
drawings: Drawing[];
|
public nodes: Node[] = [],
|
||||||
links: Link[];
|
public drawings: Drawing[] = [],
|
||||||
|
public links: Link[] = []
|
||||||
constructor() {
|
) {
|
||||||
this.nodes = [];
|
|
||||||
this.drawings = [];
|
|
||||||
this.links = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
export class Point {
|
export class Point {
|
||||||
constructor(public x: number, public y: number) {}
|
constructor(
|
||||||
|
public x?: number,
|
||||||
|
public y?: number
|
||||||
|
) {}
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export class Size {
|
export class Size {
|
||||||
constructor(public width: number, public height: number) {}
|
constructor(
|
||||||
|
public width: number,
|
||||||
|
public height: number) {}
|
||||||
}
|
}
|
||||||
|
15
src/app/cartography/services/map-change-detector-ref.spec.ts
Normal file
15
src/app/cartography/services/map-change-detector-ref.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { MapChangeDetectorRef } from "./map-change-detector-ref";
|
||||||
|
|
||||||
|
describe('MapChangeDetectorRef', () => {
|
||||||
|
let detector: MapChangeDetectorRef;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
detector = new MapChangeDetectorRef();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit event", () => {
|
||||||
|
spyOn(detector.changesDetected, 'emit');
|
||||||
|
detector.detectChanges();
|
||||||
|
expect(detector.changesDetected.emit).toHaveBeenCalledWith(true);
|
||||||
|
})
|
||||||
|
});
|
11
src/app/cartography/services/map-change-detector-ref.ts
Normal file
11
src/app/cartography/services/map-change-detector-ref.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapChangeDetectorRef {
|
||||||
|
public changesDetected = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
public detectChanges() {
|
||||||
|
this.changesDetected.emit(true);
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,9 @@ describe('MovingTool', () => {
|
|||||||
let node: SVGSelection;
|
let node: SVGSelection;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tool = new MovingTool();
|
context = new Context();
|
||||||
|
|
||||||
|
tool = new MovingTool(context);
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
|
|
||||||
node = svg.canvas
|
node = svg.canvas
|
||||||
@ -21,11 +23,8 @@ describe('MovingTool', () => {
|
|||||||
.attr('x', 10)
|
.attr('x', 10)
|
||||||
.attr('y', 20);
|
.attr('y', 20);
|
||||||
|
|
||||||
context = new Context();
|
tool.setEnabled(true);
|
||||||
|
|
||||||
tool.connect(svg.svg, context);
|
|
||||||
tool.draw(svg.svg, context);
|
tool.draw(svg.svg, context);
|
||||||
tool.activate();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -86,7 +85,8 @@ describe('MovingTool', () => {
|
|||||||
|
|
||||||
describe('MovingTool can be deactivated', () => {
|
describe('MovingTool can be deactivated', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tool.deactivate();
|
tool.setEnabled(false);
|
||||||
|
tool.draw(svg.svg, context);
|
||||||
|
|
||||||
svg.svg.node().dispatchEvent(
|
svg.svg.node().dispatchEvent(
|
||||||
new MouseEvent('mousedown', {
|
new MouseEvent('mousedown', {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { D3ZoomEvent, zoom, ZoomBehavior} from "d3-zoom";
|
import { D3ZoomEvent, zoom, ZoomBehavior} from "d3-zoom";
|
||||||
import { event } from "d3-selection";
|
import { event } from "d3-selection";
|
||||||
|
|
||||||
@ -5,33 +7,49 @@ import { SVGSelection} from "../models/types";
|
|||||||
import { Context} from "../models/context";
|
import { Context} from "../models/context";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class MovingTool {
|
export class MovingTool {
|
||||||
private selection: SVGSelection;
|
|
||||||
private context: Context;
|
|
||||||
private zoom: ZoomBehavior<SVGSVGElement, any>;
|
private zoom: ZoomBehavior<SVGSVGElement, any>;
|
||||||
|
private enabled = false;
|
||||||
|
private needsDeactivate = false;
|
||||||
|
private needsActivate = false;
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private context: Context
|
||||||
|
) {
|
||||||
this.zoom = zoom<SVGSVGElement, any>()
|
this.zoom = zoom<SVGSVGElement, any>()
|
||||||
.scaleExtent([1 / 2, 8]);
|
.scaleExtent([1 / 2, 8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect(selection: SVGSelection, context: Context) {
|
public setEnabled(enabled) {
|
||||||
this.selection = selection;
|
if (this.enabled != enabled) {
|
||||||
this.context = context;
|
if (enabled) {
|
||||||
|
this.needsActivate = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.needsDeactivate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(selection: SVGSelection, context: Context) {
|
public draw(selection: SVGSelection, context: Context) {
|
||||||
this.selection = selection;
|
if(this.needsActivate) {
|
||||||
this.context = context;
|
this.activate(selection);
|
||||||
|
this.needsActivate = false;
|
||||||
|
}
|
||||||
|
if(this.needsDeactivate) {
|
||||||
|
this.deactivate(selection);
|
||||||
|
this.needsDeactivate = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate() {
|
private activate(selection: SVGSelection) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
const onZoom = function(this: SVGSVGElement) {
|
const onZoom = function(this: SVGSVGElement) {
|
||||||
|
|
||||||
const canvas = self.selection.select<SVGGElement>("g.canvas");
|
const canvas = selection.select<SVGGElement>("g.canvas");
|
||||||
const e: D3ZoomEvent<SVGSVGElement, any> = event;
|
const e: D3ZoomEvent<SVGSVGElement, any> = event;
|
||||||
canvas.attr(
|
canvas.attr(
|
||||||
'transform',
|
'transform',
|
||||||
@ -48,12 +66,12 @@ export class MovingTool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.zoom.on('zoom', onZoom);
|
this.zoom.on('zoom', onZoom);
|
||||||
this.selection.call(this.zoom);
|
selection.call(this.zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deactivate() {
|
private deactivate(selection: SVGSelection) {
|
||||||
// d3.js preserves event `mousedown.zoom` and blocks selection
|
// d3.js preserves event `mousedown.zoom` and blocks selection
|
||||||
this.selection.on('mousedown.zoom', null);
|
selection.on('mousedown.zoom', null);
|
||||||
this.zoom.on('zoom', null);
|
this.zoom.on('zoom', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,16 @@ describe('SelectionTool', () => {
|
|||||||
let selected_rectangle: Rectangle;
|
let selected_rectangle: Rectangle;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tool = new SelectionTool();
|
context = new Context();
|
||||||
|
tool = new SelectionTool(context);
|
||||||
tool.rectangleSelected.subscribe((rectangle: Rectangle) => {
|
tool.rectangleSelected.subscribe((rectangle: Rectangle) => {
|
||||||
selected_rectangle = rectangle;
|
selected_rectangle = rectangle;
|
||||||
});
|
});
|
||||||
|
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
context = new Context();
|
|
||||||
|
|
||||||
tool.connect(svg.svg, context);
|
tool.setEnabled(true);
|
||||||
tool.draw(svg.svg, context);
|
tool.draw(svg.svg, context);
|
||||||
tool.activate();
|
|
||||||
|
|
||||||
selection_line_tool = svg.svg.select('g.selection-line-tool');
|
selection_line_tool = svg.svg.select('g.selection-line-tool');
|
||||||
path_selection = selection_line_tool.select('path.selection');
|
path_selection = selection_line_tool.select('path.selection');
|
||||||
@ -105,7 +104,9 @@ describe('SelectionTool', () => {
|
|||||||
|
|
||||||
describe('SelectionTool can be deactivated', () => {
|
describe('SelectionTool can be deactivated', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tool.deactivate();
|
tool.setEnabled(false);
|
||||||
|
tool.draw(svg.svg, context);
|
||||||
|
|
||||||
svg.svg.node().dispatchEvent(new MouseEvent('mousedown', {clientX: 100, clientY: 100}));
|
svg.svg.node().dispatchEvent(new MouseEvent('mousedown', {clientX: 100, clientY: 100}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,26 +11,33 @@ import { Rectangle } from "../models/rectangle";
|
|||||||
export class SelectionTool {
|
export class SelectionTool {
|
||||||
static readonly SELECTABLE_CLASS = '.selectable';
|
static readonly SELECTABLE_CLASS = '.selectable';
|
||||||
|
|
||||||
public rectangleSelected: Subject<Rectangle>;
|
public rectangleSelected = new Subject<Rectangle>();
|
||||||
|
|
||||||
private selection: SVGSelection;
|
|
||||||
private path;
|
private path;
|
||||||
private context: Context;
|
private enabled = false;
|
||||||
|
private needsDeactivate = false;
|
||||||
|
private needsActivate = false;
|
||||||
|
|
||||||
public constructor() {
|
public constructor(
|
||||||
this.rectangleSelected = new Subject<Rectangle>();
|
private context: Context
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public setEnabled(enabled) {
|
||||||
|
if (this.enabled != enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
this.needsActivate = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.needsDeactivate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public connect(selection: SVGSelection, context: Context) {
|
private activate(selection) {
|
||||||
this.selection = selection;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public activate() {
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
selection.on("mousedown", function() {
|
||||||
this.selection.on("mousedown", function() {
|
|
||||||
const subject = select(window);
|
const subject = select(window);
|
||||||
const parent = this.parentElement;
|
const parent = this.parentElement;
|
||||||
|
|
||||||
@ -38,7 +45,7 @@ export class SelectionTool {
|
|||||||
self.startSelection(start);
|
self.startSelection(start);
|
||||||
|
|
||||||
// clear selection
|
// clear selection
|
||||||
self.selection
|
selection
|
||||||
.selectAll(SelectionTool.SELECTABLE_CLASS)
|
.selectAll(SelectionTool.SELECTABLE_CLASS)
|
||||||
.classed("selected", false);
|
.classed("selected", false);
|
||||||
|
|
||||||
@ -56,8 +63,8 @@ export class SelectionTool {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deactivate() {
|
private deactivate(selection) {
|
||||||
this.selection.on('mousedown', null);
|
selection.on('mousedown', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(selection: SVGSelection, context: Context) {
|
public draw(selection: SVGSelection, context: Context) {
|
||||||
@ -72,7 +79,15 @@ export class SelectionTool {
|
|||||||
.attr("class", "selection")
|
.attr("class", "selection")
|
||||||
.attr("visibility", "hidden");
|
.attr("visibility", "hidden");
|
||||||
}
|
}
|
||||||
this.selection = selection;
|
|
||||||
|
if(this.needsActivate) {
|
||||||
|
this.activate(selection);
|
||||||
|
this.needsActivate = false;
|
||||||
|
}
|
||||||
|
if(this.needsDeactivate) {
|
||||||
|
this.deactivate(selection);
|
||||||
|
this.needsDeactivate = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private startSelection(start) {
|
private startSelection(start) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { line } from "d3-shape";
|
import { line } from "d3-shape";
|
||||||
import { mouse } from "d3-selection";
|
import { mouse } from "d3-selection";
|
||||||
|
|
||||||
@ -7,6 +9,7 @@ import { Point } from "../models/point";
|
|||||||
import { Context } from "../models/context";
|
import { Context } from "../models/context";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class DrawingLineWidget {
|
export class DrawingLineWidget {
|
||||||
private drawingLine: DrawingLine = new DrawingLine();
|
private drawingLine: DrawingLine = new DrawingLine();
|
||||||
private selection: SVGSelection;
|
private selection: SVGSelection;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { Drawing } from "../models/drawing";
|
import { Drawing } from "../models/drawing";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
@ -11,15 +13,27 @@ import { EllipseDrawingWidget } from "./drawings/ellipse-drawing";
|
|||||||
import { DrawingWidget } from "./drawings/drawing-widget";
|
import { DrawingWidget } from "./drawings/drawing-widget";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class DrawingsWidget implements Widget {
|
export class DrawingsWidget implements Widget {
|
||||||
private svgToDrawingConverter: SvgToDrawingConverter;
|
private drawingWidgets: DrawingWidget[] = [];
|
||||||
private drawingWidgets: DrawingWidget[] = [
|
|
||||||
new TextDrawingWidget(), new ImageDrawingWidget(), new RectDrawingWidget(),
|
|
||||||
new LineDrawingWidget(), new EllipseDrawingWidget()
|
|
||||||
];
|
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private svgToDrawingConverter: SvgToDrawingConverter,
|
||||||
|
private textDrawingWidget: TextDrawingWidget,
|
||||||
|
private imageDrawingWidget: ImageDrawingWidget,
|
||||||
|
private rectDrawingWidget: RectDrawingWidget,
|
||||||
|
private lineDrawingWidget: LineDrawingWidget,
|
||||||
|
private ellipseDrawingWidget: EllipseDrawingWidget
|
||||||
|
) {
|
||||||
this.svgToDrawingConverter = new SvgToDrawingConverter();
|
this.svgToDrawingConverter = new SvgToDrawingConverter();
|
||||||
|
|
||||||
|
this.drawingWidgets = [
|
||||||
|
this.textDrawingWidget,
|
||||||
|
this.imageDrawingWidget,
|
||||||
|
this.rectDrawingWidget,
|
||||||
|
this.lineDrawingWidget,
|
||||||
|
this.ellipseDrawingWidget
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(view: SVGSelection, drawings?: Drawing[]) {
|
public draw(view: SVGSelection, drawings?: Drawing[]) {
|
||||||
|
@ -2,6 +2,7 @@ import { TestSVGCanvas } from "../../testing";
|
|||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { EllipseDrawingWidget } from "./ellipse-drawing";
|
import { EllipseDrawingWidget } from "./ellipse-drawing";
|
||||||
import { EllipseElement } from "../../models/drawings/ellipse-element";
|
import { EllipseElement } from "../../models/drawings/ellipse-element";
|
||||||
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
describe('EllipseDrawingWidget', () => {
|
describe('EllipseDrawingWidget', () => {
|
||||||
@ -13,7 +14,7 @@ describe('EllipseDrawingWidget', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
drawing = new Drawing();
|
drawing = new Drawing();
|
||||||
widget = new EllipseDrawingWidget();
|
widget = new EllipseDrawingWidget(new QtDasharrayFixer());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../../models/types";
|
import { SVGSelection } from "../../models/types";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { EllipseElement } from "../../models/drawings/ellipse-element";
|
import { EllipseElement } from "../../models/drawings/ellipse-element";
|
||||||
@ -5,12 +7,12 @@ import { DrawingWidget } from "./drawing-widget";
|
|||||||
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class EllipseDrawingWidget implements DrawingWidget {
|
export class EllipseDrawingWidget implements DrawingWidget {
|
||||||
private qtDasharrayFixer: QtDasharrayFixer;
|
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
this.qtDasharrayFixer = new QtDasharrayFixer();
|
private qtDasharrayFixer: QtDasharrayFixer
|
||||||
}
|
) {}
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const drawing = view
|
const drawing = view
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../../models/types";
|
import { SVGSelection } from "../../models/types";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { ImageElement } from "../../models/drawings/image-element";
|
import { ImageElement } from "../../models/drawings/image-element";
|
||||||
import { DrawingWidget } from "./drawing-widget";
|
import { DrawingWidget } from "./drawing-widget";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class ImageDrawingWidget implements DrawingWidget {
|
export class ImageDrawingWidget implements DrawingWidget {
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const drawing = view
|
const drawing = view
|
||||||
|
@ -2,6 +2,7 @@ import { TestSVGCanvas } from "../../testing";
|
|||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { LineDrawingWidget } from "./line-drawing";
|
import { LineDrawingWidget } from "./line-drawing";
|
||||||
import { LineElement } from "../../models/drawings/line-element";
|
import { LineElement } from "../../models/drawings/line-element";
|
||||||
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
describe('LineDrawingWidget', () => {
|
describe('LineDrawingWidget', () => {
|
||||||
@ -13,7 +14,7 @@ describe('LineDrawingWidget', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
drawing = new Drawing();
|
drawing = new Drawing();
|
||||||
widget = new LineDrawingWidget();
|
widget = new LineDrawingWidget(new QtDasharrayFixer());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../../models/types";
|
import { SVGSelection } from "../../models/types";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { LineElement } from "../../models/drawings/line-element";
|
import { LineElement } from "../../models/drawings/line-element";
|
||||||
@ -5,12 +7,12 @@ import { DrawingWidget } from "./drawing-widget";
|
|||||||
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class LineDrawingWidget implements DrawingWidget {
|
export class LineDrawingWidget implements DrawingWidget {
|
||||||
private qtDasharrayFixer: QtDasharrayFixer;
|
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
this.qtDasharrayFixer = new QtDasharrayFixer();
|
private qtDasharrayFixer: QtDasharrayFixer
|
||||||
}
|
) {}
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const drawing = view
|
const drawing = view
|
||||||
|
@ -2,6 +2,7 @@ import { TestSVGCanvas } from "../../testing";
|
|||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { RectDrawingWidget } from "./rect-drawing";
|
import { RectDrawingWidget } from "./rect-drawing";
|
||||||
import { RectElement } from "../../models/drawings/rect-element";
|
import { RectElement } from "../../models/drawings/rect-element";
|
||||||
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
describe('RectDrawingWidget', () => {
|
describe('RectDrawingWidget', () => {
|
||||||
@ -13,7 +14,7 @@ describe('RectDrawingWidget', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
drawing = new Drawing();
|
drawing = new Drawing();
|
||||||
widget = new RectDrawingWidget();
|
widget = new RectDrawingWidget(new QtDasharrayFixer());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../../models/types";
|
import { SVGSelection } from "../../models/types";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { RectElement } from "../../models/drawings/rect-element";
|
import { RectElement } from "../../models/drawings/rect-element";
|
||||||
@ -5,12 +7,11 @@ import { DrawingWidget } from "./drawing-widget";
|
|||||||
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class RectDrawingWidget implements DrawingWidget {
|
export class RectDrawingWidget implements DrawingWidget {
|
||||||
private qtDasharrayFixer: QtDasharrayFixer;
|
constructor(
|
||||||
|
private qtDasharrayFixer: QtDasharrayFixer
|
||||||
constructor() {
|
) {}
|
||||||
this.qtDasharrayFixer = new QtDasharrayFixer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const drawing = view
|
const drawing = view
|
||||||
|
@ -2,6 +2,7 @@ import { TestSVGCanvas } from "../../testing";
|
|||||||
import { TextDrawingWidget } from "./text-drawing";
|
import { TextDrawingWidget } from "./text-drawing";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
import { TextElement } from "../../models/drawings/text-element";
|
import { TextElement } from "../../models/drawings/text-element";
|
||||||
|
import { FontFixer } from "../../helpers/font-fixer";
|
||||||
|
|
||||||
describe('TextDrawingWidget', () => {
|
describe('TextDrawingWidget', () => {
|
||||||
let svg: TestSVGCanvas;
|
let svg: TestSVGCanvas;
|
||||||
@ -12,7 +13,7 @@ describe('TextDrawingWidget', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
drawing = new Drawing();
|
drawing = new Drawing();
|
||||||
widget = new TextDrawingWidget();
|
widget = new TextDrawingWidget(new FontFixer());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../../models/types";
|
import { SVGSelection } from "../../models/types";
|
||||||
import { TextElement } from "../../models/drawings/text-element";
|
import { TextElement } from "../../models/drawings/text-element";
|
||||||
import { Drawing } from "../../models/drawing";
|
import { Drawing } from "../../models/drawing";
|
||||||
@ -6,14 +8,13 @@ import { FontFixer } from "../../helpers/font-fixer";
|
|||||||
import { select } from "d3-selection";
|
import { select } from "d3-selection";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class TextDrawingWidget implements DrawingWidget {
|
export class TextDrawingWidget implements DrawingWidget {
|
||||||
static MARGIN = 4;
|
static MARGIN = 4;
|
||||||
|
|
||||||
private fontFixer: FontFixer;
|
constructor(
|
||||||
|
private fontFixer: FontFixer
|
||||||
constructor() {
|
) {}
|
||||||
this.fontFixer = new FontFixer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import { line } from "d3-shape";
|
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
|
||||||
import { SVGSelection } from "../models/types";
|
|
||||||
import { Link } from "../models/link";
|
|
||||||
|
|
||||||
|
|
||||||
export class EthernetLinkWidget implements Widget {
|
|
||||||
|
|
||||||
public draw(view: SVGSelection, link: Link) {
|
|
||||||
|
|
||||||
const link_data = [[
|
|
||||||
[link.source.x + link.source.width / 2., link.source.y + link.source.height / 2.],
|
|
||||||
[link.target.x + link.target.width / 2., link.target.y + link.target.height / 2.]
|
|
||||||
]];
|
|
||||||
|
|
||||||
const value_line = line();
|
|
||||||
|
|
||||||
let link_path = view.select<SVGPathElement>('path');
|
|
||||||
link_path.classed('selected', (l: Link) => l.is_selected);
|
|
||||||
|
|
||||||
if (!link_path.node()) {
|
|
||||||
link_path = view.append<SVGPathElement>('path');
|
|
||||||
}
|
|
||||||
|
|
||||||
const link_path_data = link_path.data(link_data);
|
|
||||||
|
|
||||||
link_path_data
|
|
||||||
.attr('d', value_line)
|
|
||||||
.attr('stroke', '#000')
|
|
||||||
.attr('stroke-width', '2');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import { Context } from "../models/context";
|
import { Context } from "../models/context";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { NodesWidget } from "./nodes";
|
import { NodesWidget } from "./nodes";
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
@ -12,29 +12,24 @@ import { SelectionTool } from "../tools/selection-tool";
|
|||||||
import { MovingTool } from "../tools/moving-tool";
|
import { MovingTool } from "../tools/moving-tool";
|
||||||
import { LayersWidget } from "./layers";
|
import { LayersWidget } from "./layers";
|
||||||
import { LayersManager } from "../managers/layers-manager";
|
import { LayersManager } from "../managers/layers-manager";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class GraphLayout implements Widget {
|
export class GraphLayout implements Widget {
|
||||||
private nodes: Node[] = [];
|
private nodes: Node[] = [];
|
||||||
private links: Link[] = [];
|
private links: Link[] = [];
|
||||||
private drawings: Drawing[] = [];
|
private drawings: Drawing[] = [];
|
||||||
|
|
||||||
private linksWidget: LinksWidget;
|
constructor(
|
||||||
private nodesWidget: NodesWidget;
|
private linksWidget: LinksWidget,
|
||||||
private drawingsWidget: DrawingsWidget;
|
private nodesWidget: NodesWidget,
|
||||||
private drawingLineTool: DrawingLineWidget;
|
private drawingsWidget: DrawingsWidget,
|
||||||
private selectionTool: SelectionTool;
|
private drawingLineTool: DrawingLineWidget,
|
||||||
private movingTool: MovingTool;
|
private selectionTool: SelectionTool,
|
||||||
private layersWidget: LayersWidget;
|
private movingTool: MovingTool,
|
||||||
|
private layersWidget: LayersWidget
|
||||||
constructor() {
|
) {
|
||||||
this.linksWidget = new LinksWidget();
|
|
||||||
this.nodesWidget = new NodesWidget();
|
|
||||||
this.drawingsWidget = new DrawingsWidget();
|
|
||||||
this.drawingLineTool = new DrawingLineWidget();
|
|
||||||
this.selectionTool = new SelectionTool();
|
|
||||||
this.movingTool = new MovingTool();
|
|
||||||
this.layersWidget = new LayersWidget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setNodes(nodes: Node[]) {
|
public setNodes(nodes: Node[]) {
|
||||||
@ -53,32 +48,16 @@ export class GraphLayout implements Widget {
|
|||||||
return this.nodesWidget;
|
return this.nodesWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLinksWidget() {
|
|
||||||
return this.linksWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDrawingsWidget() {
|
|
||||||
return this.drawingsWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDrawingLineTool() {
|
public getDrawingLineTool() {
|
||||||
return this.drawingLineTool;
|
return this.drawingLineTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMovingTool() {
|
|
||||||
return this.movingTool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSelectionTool() {
|
public getSelectionTool() {
|
||||||
return this.selectionTool;
|
return this.selectionTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(view: SVGSelection, context: Context) {
|
connect(view: SVGSelection, context: Context) {
|
||||||
this.drawingLineTool.connect(view, context);
|
this.drawingLineTool.connect(view, context);
|
||||||
this.selectionTool.connect(view, context);
|
|
||||||
this.movingTool.connect(view, context);
|
|
||||||
|
|
||||||
this.selectionTool.activate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(view: SVGSelection, context: Context) {
|
draw(view: SVGSelection, context: Context) {
|
||||||
@ -105,12 +84,12 @@ export class GraphLayout implements Widget {
|
|||||||
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
|
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @fix me
|
||||||
const layersManager = new LayersManager();
|
const layersManager = new LayersManager();
|
||||||
layersManager.setNodes(this.nodes);
|
layersManager.setNodes(this.nodes);
|
||||||
layersManager.setDrawings(this.drawings);
|
layersManager.setDrawings(this.drawings);
|
||||||
layersManager.setLinks(this.links);
|
layersManager.setLinks(this.links);
|
||||||
|
|
||||||
this.layersWidget.graphLayout = this;
|
|
||||||
this.layersWidget.draw(canvas, layersManager.getLayersList());
|
this.layersWidget.draw(canvas, layersManager.getLayersList());
|
||||||
|
|
||||||
this.drawingLineTool.draw(view, context);
|
this.drawingLineTool.draw(view, context);
|
||||||
|
@ -2,11 +2,12 @@ import { Selection } from "d3-selection";
|
|||||||
|
|
||||||
import { TestSVGCanvas } from "../testing";
|
import { TestSVGCanvas } from "../testing";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { LinkNode } from "../models/link-node";
|
import { LinkNode } from "../../models/link-node";
|
||||||
import { Label } from "../models/label";
|
import { Label } from "../models/label";
|
||||||
import { InterfaceLabel } from "../models/interface-label";
|
import { InterfaceLabel } from "../models/interface-label";
|
||||||
import { InterfaceLabelWidget } from "./interface-label";
|
import { InterfaceLabelWidget } from "./interface-label";
|
||||||
|
import { CssFixer } from "../helpers/css-fixer";
|
||||||
|
|
||||||
|
|
||||||
describe('InterfaceLabelsWidget', () => {
|
describe('InterfaceLabelsWidget', () => {
|
||||||
@ -66,7 +67,7 @@ describe('InterfaceLabelsWidget', () => {
|
|||||||
.exit()
|
.exit()
|
||||||
.remove();
|
.remove();
|
||||||
|
|
||||||
widget = new InterfaceLabelWidget();
|
widget = new InterfaceLabelWidget(new CssFixer());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { InterfaceLabel } from "../models/interface-label";
|
import { InterfaceLabel } from "../models/interface-label";
|
||||||
import { CssFixer } from "../helpers/css-fixer";
|
import { CssFixer } from "../helpers/css-fixer";
|
||||||
import { select } from "d3-selection";
|
import { select } from "d3-selection";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class InterfaceLabelWidget {
|
export class InterfaceLabelWidget {
|
||||||
static SURROUNDING_TEXT_BORDER = 5;
|
static SURROUNDING_TEXT_BORDER = 5;
|
||||||
|
|
||||||
private cssFixer: CssFixer;
|
|
||||||
private enabled = true;
|
private enabled = true;
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
this.cssFixer = new CssFixer();
|
private cssFixer: CssFixer
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public setEnabled(enabled: boolean) {
|
public setEnabled(enabled: boolean) {
|
||||||
|
76
src/app/cartography/widgets/interface-status.ts
Normal file
76
src/app/cartography/widgets/interface-status.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { select } from "d3-selection";
|
||||||
|
|
||||||
|
import { Widget } from "./widget";
|
||||||
|
import { SVGSelection } from "../models/types";
|
||||||
|
import { Link } from "../../models/link";
|
||||||
|
import { LinkStatus } from "../models/link-status";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InterfaceStatusWidget implements Widget {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
public draw(view: SVGSelection) {
|
||||||
|
view.each(function (this: SVGGElement, l: Link) {
|
||||||
|
const link_group = select<SVGGElement, Link>(this);
|
||||||
|
const link_path = link_group.select<SVGPathElement>('path');
|
||||||
|
|
||||||
|
const start_point: SVGPoint = link_path.node().getPointAtLength(45);
|
||||||
|
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45);
|
||||||
|
|
||||||
|
let statuses = [];
|
||||||
|
|
||||||
|
if (link_path.node().getTotalLength() > 2 * 45 + 10) {
|
||||||
|
statuses = [
|
||||||
|
new LinkStatus(start_point.x, start_point.y, l.source.status),
|
||||||
|
new LinkStatus(end_point.x, end_point.y, l.target.status)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const status_started = link_group
|
||||||
|
.selectAll<SVGCircleElement, LinkStatus>('circle.status_started')
|
||||||
|
.data(statuses.filter((link_status: LinkStatus) => link_status.status === 'started'));
|
||||||
|
|
||||||
|
const status_started_enter = status_started
|
||||||
|
.enter()
|
||||||
|
.append<SVGCircleElement>('circle');
|
||||||
|
|
||||||
|
status_started
|
||||||
|
.merge(status_started_enter)
|
||||||
|
.attr('class', 'status_started')
|
||||||
|
.attr('cx', (ls: LinkStatus) => ls.x)
|
||||||
|
.attr('cy', (ls: LinkStatus) => ls.y)
|
||||||
|
.attr('r', 6)
|
||||||
|
.attr('fill', '#2ecc71');
|
||||||
|
|
||||||
|
status_started
|
||||||
|
.exit()
|
||||||
|
.remove();
|
||||||
|
|
||||||
|
const status_stopped = link_group
|
||||||
|
.selectAll<SVGRectElement, LinkStatus>('rect.status_stopped')
|
||||||
|
.data(statuses.filter((link_status: LinkStatus) => link_status.status === 'stopped'));
|
||||||
|
|
||||||
|
const status_stopped_enter = status_stopped
|
||||||
|
.enter()
|
||||||
|
.append<SVGRectElement>('rect');
|
||||||
|
|
||||||
|
const STOPPED_STATUS_RECT_WIDTH = 10;
|
||||||
|
|
||||||
|
status_stopped
|
||||||
|
.merge(status_stopped_enter)
|
||||||
|
.attr('class', 'status_stopped')
|
||||||
|
.attr('x', (ls: LinkStatus) => ls.x - STOPPED_STATUS_RECT_WIDTH / 2.)
|
||||||
|
.attr('y', (ls: LinkStatus) => ls.y - STOPPED_STATUS_RECT_WIDTH / 2.)
|
||||||
|
.attr('width', STOPPED_STATUS_RECT_WIDTH)
|
||||||
|
.attr('height', STOPPED_STATUS_RECT_WIDTH)
|
||||||
|
.attr('fill', 'red');
|
||||||
|
|
||||||
|
status_stopped
|
||||||
|
.exit()
|
||||||
|
.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { instance, mock, when } from "ts-mockito";
|
import { instance, mock, when, verify } from "ts-mockito";
|
||||||
|
|
||||||
import { TestSVGCanvas } from "../testing";
|
import { TestSVGCanvas } from "../testing";
|
||||||
import { LayersWidget } from "./layers";
|
import { LayersWidget } from "./layers";
|
||||||
@ -6,13 +6,11 @@ import { Layer } from "../models/layer";
|
|||||||
import { LinksWidget } from "./links";
|
import { LinksWidget } from "./links";
|
||||||
import { NodesWidget } from "./nodes";
|
import { NodesWidget } from "./nodes";
|
||||||
import { DrawingsWidget } from "./drawings";
|
import { DrawingsWidget } from "./drawings";
|
||||||
import { GraphLayout } from "./graph-layout";
|
|
||||||
|
|
||||||
|
|
||||||
describe('LayersWidget', () => {
|
describe('LayersWidget', () => {
|
||||||
let svg: TestSVGCanvas;
|
let svg: TestSVGCanvas;
|
||||||
let widget: LayersWidget;
|
let widget: LayersWidget;
|
||||||
let mockedGraphLayout: GraphLayout;
|
|
||||||
let mockedLinksWidget: LinksWidget;
|
let mockedLinksWidget: LinksWidget;
|
||||||
let mockedNodesWidget: NodesWidget;
|
let mockedNodesWidget: NodesWidget;
|
||||||
let mockedDrawingsWidget: DrawingsWidget;
|
let mockedDrawingsWidget: DrawingsWidget;
|
||||||
@ -20,16 +18,11 @@ describe('LayersWidget', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
widget = new LayersWidget();
|
mockedLinksWidget = instance(mock(LinksWidget));
|
||||||
mockedGraphLayout = mock(GraphLayout);
|
mockedNodesWidget = instance(mock(NodesWidget));
|
||||||
mockedLinksWidget = mock(LinksWidget);
|
mockedDrawingsWidget = instance(mock(DrawingsWidget));
|
||||||
mockedNodesWidget = mock(NodesWidget);
|
|
||||||
mockedDrawingsWidget = mock(DrawingsWidget);
|
|
||||||
when(mockedGraphLayout.getLinksWidget()).thenReturn(instance(mockedLinksWidget));
|
|
||||||
when(mockedGraphLayout.getNodesWidget()).thenReturn(instance(mockedNodesWidget));
|
|
||||||
when(mockedGraphLayout.getDrawingsWidget()).thenReturn(instance(mockedDrawingsWidget));
|
|
||||||
|
|
||||||
widget.graphLayout = instance(mockedGraphLayout);
|
widget = new LayersWidget(mockedLinksWidget, mockedNodesWidget, mockedDrawingsWidget);
|
||||||
|
|
||||||
const layer_1 = new Layer();
|
const layer_1 = new Layer();
|
||||||
layer_1.index = 1;
|
layer_1.index = 1;
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { GraphLayout } from "./graph-layout";
|
import { GraphLayout } from "./graph-layout";
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
|
import { LinksWidget } from "./links";
|
||||||
|
import { NodesWidget } from "./nodes";
|
||||||
|
import { DrawingsWidget } from "./drawings";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class LayersWidget implements Widget {
|
export class LayersWidget implements Widget {
|
||||||
public graphLayout: GraphLayout;
|
constructor(
|
||||||
|
private linksWidget: LinksWidget,
|
||||||
|
private nodesWidget: NodesWidget,
|
||||||
|
private drawingsWidget: DrawingsWidget
|
||||||
|
) {}
|
||||||
|
|
||||||
public draw(view: SVGSelection, layers: Layer[]) {
|
public draw(view: SVGSelection, layers: Layer[]) {
|
||||||
|
|
||||||
@ -53,17 +63,9 @@ export class LayersWidget implements Widget {
|
|||||||
.exit()
|
.exit()
|
||||||
.remove();
|
.remove();
|
||||||
|
|
||||||
this.graphLayout
|
this.linksWidget.draw(links_container);
|
||||||
.getLinksWidget()
|
this.nodesWidget.draw(nodes_container);
|
||||||
.draw(links_container);
|
this.drawingsWidget.draw(drawings_container);
|
||||||
|
|
||||||
this.graphLayout
|
|
||||||
.getNodesWidget()
|
|
||||||
.draw(nodes_container);
|
|
||||||
|
|
||||||
this.graphLayout
|
|
||||||
.getDrawingsWidget()
|
|
||||||
.draw(drawings_container);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
58
src/app/cartography/widgets/link.ts
Normal file
58
src/app/cartography/widgets/link.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { Widget } from "./widget";
|
||||||
|
import { SVGSelection } from "../models/types";
|
||||||
|
import { Link } from "../../models/link";
|
||||||
|
import { SerialLinkWidget } from "./links/serial-link";
|
||||||
|
import { EthernetLinkWidget } from "./links/ethernet-link";
|
||||||
|
import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
|
||||||
|
import { InterfaceLabelWidget } from "./interface-label";
|
||||||
|
import { InterfaceStatusWidget } from "./interface-status";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LinkWidget implements Widget {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
||||||
|
private interfaceLabelWidget: InterfaceLabelWidget,
|
||||||
|
private interfaceStatusWidget: InterfaceStatusWidget
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public getInterfaceLabelWidget() {
|
||||||
|
return this.interfaceLabelWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInterfaceStatusWidget() {
|
||||||
|
return this.interfaceStatusWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(view: SVGSelection) {
|
||||||
|
const link_body = view.selectAll<SVGGElement, Link>("g.link_body")
|
||||||
|
.data((l) => [l]);
|
||||||
|
|
||||||
|
const link_body_enter = link_body.enter()
|
||||||
|
.append<SVGGElement>('g')
|
||||||
|
.attr("class", "link_body");
|
||||||
|
|
||||||
|
const link_body_merge = link_body.merge(link_body_enter)
|
||||||
|
.attr('transform', (link) => {
|
||||||
|
const translation = this.multiLinkCalculatorHelper.linkTranslation(link.distance, link.source, link.target);
|
||||||
|
return `translate (${translation.dx}, ${translation.dy})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const serial_link_widget = new SerialLinkWidget();
|
||||||
|
serial_link_widget.draw(link_body_merge);
|
||||||
|
|
||||||
|
const ethernet_link_widget = new EthernetLinkWidget();
|
||||||
|
ethernet_link_widget.draw(link_body_merge);
|
||||||
|
|
||||||
|
link_body_merge
|
||||||
|
.select<SVGPathElement>('path')
|
||||||
|
.classed('selected', (l: Link) => l.is_selected);
|
||||||
|
|
||||||
|
this.getInterfaceLabelWidget().draw(link_body_merge);
|
||||||
|
this.getInterfaceStatusWidget().draw(link_body_merge);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { anything, instance, mock, verify } from "ts-mockito";
|
import { instance, mock } from "ts-mockito";
|
||||||
import { Selection } from "d3-selection";
|
import { Selection } from "d3-selection";
|
||||||
|
|
||||||
|
|
||||||
@ -6,8 +6,9 @@ import { TestSVGCanvas } from "../testing";
|
|||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { LinksWidget } from "./links";
|
import { LinksWidget } from "./links";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { InterfaceLabelWidget } from "./interface-label";
|
import { LinkWidget } from "./link";
|
||||||
|
import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
|
||||||
|
|
||||||
|
|
||||||
describe('LinksWidget', () => {
|
describe('LinksWidget', () => {
|
||||||
@ -15,10 +16,12 @@ describe('LinksWidget', () => {
|
|||||||
let widget: LinksWidget;
|
let widget: LinksWidget;
|
||||||
let layersEnter: Selection<SVGGElement, Layer, SVGGElement, any>;
|
let layersEnter: Selection<SVGGElement, Layer, SVGGElement, any>;
|
||||||
let layer: Layer;
|
let layer: Layer;
|
||||||
|
let mockedLinkWidget: LinkWidget;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
widget = new LinksWidget();
|
mockedLinkWidget = instance(mock(LinkWidget));
|
||||||
|
widget = new LinksWidget(new MultiLinkCalculatorHelper(), mockedLinkWidget);
|
||||||
|
|
||||||
const node_1 = new Node();
|
const node_1 = new Node();
|
||||||
node_1.node_id = "1";
|
node_1.node_id = "1";
|
||||||
@ -62,9 +65,9 @@ describe('LinksWidget', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should draw links', () => {
|
it('should draw links', () => {
|
||||||
const interfaceLabelWidgetMock = mock(InterfaceLabelWidget);
|
const linkWidgetMock = mock(LinkWidget);
|
||||||
const interfaceLabelWidget = instance(interfaceLabelWidgetMock);
|
const linkWidget = instance(linkWidgetMock);
|
||||||
spyOn(widget, 'getInterfaceLabelWidget').and.returnValue(interfaceLabelWidget);
|
spyOn(widget, 'getLinkWidget').and.returnValue(linkWidget);
|
||||||
|
|
||||||
widget.draw(layersEnter);
|
widget.draw(layersEnter);
|
||||||
|
|
||||||
@ -73,9 +76,6 @@ describe('LinksWidget', () => {
|
|||||||
expect(linkNode.getAttribute('link_id')).toEqual('link1');
|
expect(linkNode.getAttribute('link_id')).toEqual('link1');
|
||||||
expect(linkNode.getAttribute('map-source')).toEqual('1');
|
expect(linkNode.getAttribute('map-source')).toEqual('1');
|
||||||
expect(linkNode.getAttribute('map-target')).toEqual('2');
|
expect(linkNode.getAttribute('map-target')).toEqual('2');
|
||||||
expect(linkNode.getAttribute('transform')).toEqual('translate (0, 0)');
|
|
||||||
|
|
||||||
verify(interfaceLabelWidgetMock.draw(anything())).called();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,124 +1,29 @@
|
|||||||
import { select } from "d3-selection";
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { Link } from "../models/link";
|
import { Link } from "../../models/link";
|
||||||
import { LinkStatus } from "../models/link-status";
|
import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
|
||||||
import { MultiLinkCalculatorHelper } from "../components/map/helpers/multi-link-calculator-helper";
|
|
||||||
import { SerialLinkWidget } from "./serial-link";
|
|
||||||
import { EthernetLinkWidget } from "./ethernet-link";
|
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { InterfaceLabelWidget } from "./interface-label";
|
import { LinkWidget } from "./link";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class LinksWidget implements Widget {
|
export class LinksWidget implements Widget {
|
||||||
private multiLinkCalculatorHelper = new MultiLinkCalculatorHelper();
|
constructor(
|
||||||
|
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
||||||
private interfaceLabelWidget: InterfaceLabelWidget;
|
private linkWidget: LinkWidget
|
||||||
|
) {
|
||||||
constructor() {
|
|
||||||
this.interfaceLabelWidget = new InterfaceLabelWidget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getInterfaceLabelWidget() {
|
public getLinkWidget() {
|
||||||
return this.interfaceLabelWidget;
|
return this.linkWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setInterfaceLabelWidget(interfaceLabelWidget: InterfaceLabelWidget) {
|
public redrawLink(view: SVGSelection, link: Link) {
|
||||||
this.interfaceLabelWidget = interfaceLabelWidget;
|
this.getLinkWidget().draw(this.selectLink(view, link));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLinkWidget(link: Link) {
|
public draw(view: SVGSelection) {
|
||||||
if (link.link_type === 'serial') {
|
|
||||||
return new SerialLinkWidget();
|
|
||||||
}
|
|
||||||
return new EthernetLinkWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public select(view: SVGSelection) {
|
|
||||||
return view.selectAll<SVGGElement, Link>("g.link");
|
|
||||||
}
|
|
||||||
|
|
||||||
public revise(selection: SVGSelection) {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
selection
|
|
||||||
.each(function (this: SVGGElement, l: Link) {
|
|
||||||
const link_group = select<SVGGElement, Link>(this);
|
|
||||||
const link_widget = self.getLinkWidget(l);
|
|
||||||
|
|
||||||
link_widget.draw(link_group, l);
|
|
||||||
|
|
||||||
const link_path = link_group.select<SVGPathElement>('path');
|
|
||||||
|
|
||||||
const start_point: SVGPoint = link_path.node().getPointAtLength(45);
|
|
||||||
const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45);
|
|
||||||
|
|
||||||
let statuses = [];
|
|
||||||
|
|
||||||
if (link_path.node().getTotalLength() > 2 * 45 + 10) {
|
|
||||||
statuses = [
|
|
||||||
new LinkStatus(start_point.x, start_point.y, l.source.status),
|
|
||||||
new LinkStatus(end_point.x, end_point.y, l.target.status)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const status_started = link_group
|
|
||||||
.selectAll<SVGCircleElement, LinkStatus>('circle.status_started')
|
|
||||||
.data(statuses.filter((link_status: LinkStatus) => link_status.status === 'started'));
|
|
||||||
|
|
||||||
const status_started_enter = status_started
|
|
||||||
.enter()
|
|
||||||
.append<SVGCircleElement>('circle');
|
|
||||||
|
|
||||||
status_started
|
|
||||||
.merge(status_started_enter)
|
|
||||||
.attr('class', 'status_started')
|
|
||||||
.attr('cx', (ls: LinkStatus) => ls.x)
|
|
||||||
.attr('cy', (ls: LinkStatus) => ls.y)
|
|
||||||
.attr('r', 6)
|
|
||||||
.attr('fill', '#2ecc71');
|
|
||||||
|
|
||||||
status_started
|
|
||||||
.exit()
|
|
||||||
.remove();
|
|
||||||
|
|
||||||
const status_stopped = link_group
|
|
||||||
.selectAll<SVGRectElement, LinkStatus>('rect.status_stopped')
|
|
||||||
.data(statuses.filter((link_status: LinkStatus) => link_status.status === 'stopped'));
|
|
||||||
|
|
||||||
const status_stopped_enter = status_stopped
|
|
||||||
.enter()
|
|
||||||
.append<SVGRectElement>('rect');
|
|
||||||
|
|
||||||
const STOPPED_STATUS_RECT_WIDTH = 10;
|
|
||||||
|
|
||||||
status_stopped
|
|
||||||
.merge(status_stopped_enter)
|
|
||||||
.attr('class', 'status_stopped')
|
|
||||||
.attr('x', (ls: LinkStatus) => ls.x - STOPPED_STATUS_RECT_WIDTH / 2.)
|
|
||||||
.attr('y', (ls: LinkStatus) => ls.y - STOPPED_STATUS_RECT_WIDTH / 2.)
|
|
||||||
.attr('width', STOPPED_STATUS_RECT_WIDTH)
|
|
||||||
.attr('height', STOPPED_STATUS_RECT_WIDTH)
|
|
||||||
.attr('fill', 'red');
|
|
||||||
|
|
||||||
status_stopped
|
|
||||||
.exit()
|
|
||||||
.remove();
|
|
||||||
|
|
||||||
})
|
|
||||||
.attr('transform', function(l) {
|
|
||||||
if (l.source && l.target) {
|
|
||||||
const translation = self.multiLinkCalculatorHelper.linkTranslation(l.distance, l.source, l.target);
|
|
||||||
return `translate (${translation.dx}, ${translation.dy})`;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.getInterfaceLabelWidget().draw(selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(view: SVGSelection, links?: Link[]) {
|
|
||||||
const link = view
|
const link = view
|
||||||
.selectAll<SVGGElement, Link>("g.link")
|
.selectAll<SVGGElement, Link>("g.link")
|
||||||
.data((layer: Layer) => {
|
.data((layer: Layer) => {
|
||||||
@ -143,12 +48,14 @@ export class LinksWidget implements Widget {
|
|||||||
|
|
||||||
const merge = link.merge(link_enter);
|
const merge = link.merge(link_enter);
|
||||||
|
|
||||||
this.revise(merge);
|
this.getLinkWidget().draw(merge);
|
||||||
|
|
||||||
|
|
||||||
link
|
link
|
||||||
.exit()
|
.exit()
|
||||||
.remove();
|
.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private selectLink(view: SVGSelection, link: Link) {
|
||||||
|
return view.selectAll<SVGGElement, Link>(`g.link[link_id="${link.link_id}"]`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
src/app/cartography/widgets/links/ethernet-link.ts
Normal file
52
src/app/cartography/widgets/links/ethernet-link.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { path } from "d3-path";
|
||||||
|
|
||||||
|
import { Widget } from "../widget";
|
||||||
|
import { SVGSelection } from "../../models/types";
|
||||||
|
import { Link } from "../../../models/link";
|
||||||
|
|
||||||
|
class EthernetLinkPath {
|
||||||
|
constructor(
|
||||||
|
public source: [number, number],
|
||||||
|
public target: [number, number]
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EthernetLinkWidget implements Widget {
|
||||||
|
private linktoEthernetLink(link: Link) {
|
||||||
|
return new EthernetLinkPath(
|
||||||
|
[link.source.x + link.source.width / 2., link.source.y + link.source.height / 2.],
|
||||||
|
[link.target.x + link.target.width / 2., link.target.y + link.target.height / 2.]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(view: SVGSelection) {
|
||||||
|
|
||||||
|
const link = view
|
||||||
|
.selectAll<SVGPathElement, EthernetLinkPath>('path.ethernet_link')
|
||||||
|
.data((link) => {
|
||||||
|
if(link.link_type === 'ethernet') {
|
||||||
|
return [this.linktoEthernetLink(link)];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const link_enter = link.enter()
|
||||||
|
.append<SVGPathElement>('path')
|
||||||
|
.attr('class', 'ethernet_link');
|
||||||
|
|
||||||
|
link_enter
|
||||||
|
.attr('stroke', '#000')
|
||||||
|
.attr('stroke-width', '2');
|
||||||
|
|
||||||
|
const link_merge = link.merge(link_enter);
|
||||||
|
|
||||||
|
link_merge
|
||||||
|
.attr('d', (ethernet) => {
|
||||||
|
const line_generator = path();
|
||||||
|
line_generator.moveTo(ethernet.source[0], ethernet.source[1]);
|
||||||
|
line_generator.lineTo(ethernet.target[0], ethernet.target[1]);
|
||||||
|
return line_generator.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
92
src/app/cartography/widgets/links/serial-link.ts
Normal file
92
src/app/cartography/widgets/links/serial-link.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { path } from "d3-path";
|
||||||
|
|
||||||
|
import { Widget } from "../widget";
|
||||||
|
import { SVGSelection } from "../../models/types";
|
||||||
|
import { Link } from "../../../models/link";
|
||||||
|
|
||||||
|
|
||||||
|
class SerialLinkPath {
|
||||||
|
constructor(
|
||||||
|
public source: [number, number],
|
||||||
|
public source_angle: [number, number],
|
||||||
|
public target_angle: [number, number],
|
||||||
|
public target: [number, number]
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class SerialLinkWidget implements Widget {
|
||||||
|
|
||||||
|
private linkToSerialLink(link: Link) {
|
||||||
|
const source = {
|
||||||
|
'x': link.source.x + link.source.width / 2,
|
||||||
|
'y': link.source.y + link.source.height / 2
|
||||||
|
};
|
||||||
|
const target = {
|
||||||
|
'x': link.target.x + link.target.width / 2,
|
||||||
|
'y': link.target.y + link.target.height / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const dx = target.x - source.x;
|
||||||
|
const dy = target.y - source.y;
|
||||||
|
|
||||||
|
const vector_angle = Math.atan2(dy, dx);
|
||||||
|
const rot_angle = -Math.PI / 4.0;
|
||||||
|
const vect_rot = [
|
||||||
|
Math.cos(vector_angle + rot_angle),
|
||||||
|
Math.sin(vector_angle + rot_angle)
|
||||||
|
];
|
||||||
|
|
||||||
|
const angle_source: [number, number] = [
|
||||||
|
source.x + dx / 2.0 + 15 * vect_rot[0],
|
||||||
|
source.y + dy / 2.0 + 15 * vect_rot[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
const angle_target: [number, number] = [
|
||||||
|
target.x - dx / 2.0 - 15 * vect_rot[0],
|
||||||
|
target.y - dy / 2.0 - 15 * vect_rot[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
return new SerialLinkPath(
|
||||||
|
[source.x, source.y],
|
||||||
|
angle_source,
|
||||||
|
angle_target,
|
||||||
|
[target.x, target.y]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(view: SVGSelection) {
|
||||||
|
|
||||||
|
const link = view
|
||||||
|
.selectAll<SVGPathElement, SerialLinkPath>('path.serial_link')
|
||||||
|
.data((link) => {
|
||||||
|
if(link.link_type === 'serial') {
|
||||||
|
return [this.linkToSerialLink(link)];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const link_enter = link.enter()
|
||||||
|
.append<SVGPathElement>('path')
|
||||||
|
.attr('class', 'serial_link');
|
||||||
|
|
||||||
|
link_enter
|
||||||
|
.attr('stroke', '#B22222')
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('stroke-width', '2');
|
||||||
|
|
||||||
|
const link_merge = link.merge(link_enter);
|
||||||
|
|
||||||
|
link_merge
|
||||||
|
.attr('d', (serial) => {
|
||||||
|
const line_generator = path();
|
||||||
|
line_generator.moveTo(serial.source[0], serial.source[1]);
|
||||||
|
line_generator.lineTo(serial.source_angle[0], serial.source_angle[1]);
|
||||||
|
line_generator.lineTo(serial.target_angle[0], serial.target_angle[1]);
|
||||||
|
line_generator.lineTo(serial.target[0], serial.target[1]);
|
||||||
|
return line_generator.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,8 @@ import { TestSVGCanvas } from "../testing";
|
|||||||
import { NodesWidget } from "./nodes";
|
import { NodesWidget } from "./nodes";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { Label } from "../models/label";
|
import { Label } from "../models/label";
|
||||||
|
import { CssFixer } from "../helpers/css-fixer";
|
||||||
|
import { FontFixer } from "../helpers/font-fixer";
|
||||||
|
|
||||||
|
|
||||||
describe('NodesWidget', () => {
|
describe('NodesWidget', () => {
|
||||||
@ -11,7 +13,10 @@ describe('NodesWidget', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
svg = new TestSVGCanvas();
|
svg = new TestSVGCanvas();
|
||||||
widget = new NodesWidget();
|
widget = new NodesWidget(
|
||||||
|
new CssFixer(),
|
||||||
|
new FontFixer()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -1,50 +1,44 @@
|
|||||||
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
import { event, select, Selection } from "d3-selection";
|
import { event, select, Selection } from "d3-selection";
|
||||||
import { D3DragEvent, drag } from "d3-drag";
|
import { D3DragEvent, drag } from "d3-drag";
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
import { Widget } from "./widget";
|
||||||
import { Node } from "../models/node";
|
import { Node } from "../models/node";
|
||||||
import { SVGSelection } from "../models/types";
|
import { SVGSelection } from "../models/types";
|
||||||
import { Symbol } from "../models/symbol";
|
import { Symbol } from "../../models/symbol";
|
||||||
import { Layer } from "../models/layer";
|
import { Layer } from "../models/layer";
|
||||||
import { CssFixer } from "../helpers/css-fixer";
|
import { CssFixer } from "../helpers/css-fixer";
|
||||||
import { FontFixer } from "../helpers/font-fixer";
|
import { FontFixer } from "../helpers/font-fixer";
|
||||||
|
|
||||||
|
|
||||||
|
export class NodeEvent {
|
||||||
|
constructor(
|
||||||
|
public event: any,
|
||||||
|
public node: Node
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class NodesWidget implements Widget {
|
export class NodesWidget implements Widget {
|
||||||
static NODE_LABEL_MARGIN = 3;
|
static NODE_LABEL_MARGIN = 3;
|
||||||
|
|
||||||
private debug = false;
|
private debug = false;
|
||||||
private draggingEnabled = false;
|
private draggingEnabled = false;
|
||||||
|
|
||||||
private onContextMenuCallback: (event: any, node: Node) => void;
|
private symbols: Symbol[] = [];
|
||||||
private onNodeClickedCallback: (event: any, node: Node) => void;
|
|
||||||
private onNodeDraggedCallback: (event: any, node: Node) => void;
|
|
||||||
private onNodeDraggingCallbacks: ((event: any, node: Node) => void)[] = [];
|
|
||||||
|
|
||||||
private symbols: Symbol[];
|
public onContextMenu = new EventEmitter<NodeEvent>();
|
||||||
private cssFixer: CssFixer;
|
public onNodeClicked = new EventEmitter<NodeEvent>();
|
||||||
private fontFixer: FontFixer;
|
public onNodeDragged = new EventEmitter<NodeEvent>();
|
||||||
|
public onNodeDragging = new EventEmitter<NodeEvent>();
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private cssFixer: CssFixer,
|
||||||
|
private fontFixer: FontFixer
|
||||||
|
) {
|
||||||
this.symbols = [];
|
this.symbols = [];
|
||||||
this.cssFixer = new CssFixer();
|
|
||||||
this.fontFixer = new FontFixer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public setOnContextMenuCallback(onContextMenuCallback: (event: any, node: Node) => void) {
|
|
||||||
this.onContextMenuCallback = onContextMenuCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setOnNodeClickedCallback(onNodeClickedCallback: (event: any, node: Node) => void) {
|
|
||||||
this.onNodeClickedCallback = onNodeClickedCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setOnNodeDraggedCallback(onNodeDraggedCallback: (event: any, node: Node) => void) {
|
|
||||||
this.onNodeDraggedCallback = onNodeDraggedCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addOnNodeDraggingCallback(onNodeDraggingCallback: (event: any, n: Node) => void) {
|
|
||||||
this.onNodeDraggingCallbacks.push(onNodeDraggingCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSymbols(symbols: Symbol[]) {
|
public setSymbols(symbols: Symbol[]) {
|
||||||
@ -55,12 +49,6 @@ export class NodesWidget implements Widget {
|
|||||||
this.draggingEnabled = enabled;
|
this.draggingEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private executeOnNodeDraggingCallback(callback_event: any, node: Node) {
|
|
||||||
this.onNodeDraggingCallbacks.forEach((callback: (e: any, n: Node) => void) => {
|
|
||||||
callback(callback_event, node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public revise(selection: SVGSelection) {
|
public revise(selection: SVGSelection) {
|
||||||
selection
|
selection
|
||||||
.attr('transform', (n: Node) => {
|
.attr('transform', (n: Node) => {
|
||||||
@ -149,14 +137,10 @@ export class NodesWidget implements Widget {
|
|||||||
.classed('selected', (n: Node) => n.is_selected)
|
.classed('selected', (n: Node) => n.is_selected)
|
||||||
.on("contextmenu", function (n: Node, i: number) {
|
.on("contextmenu", function (n: Node, i: number) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (self.onContextMenuCallback !== null) {
|
self.onContextMenu.emit(new NodeEvent(event, n));
|
||||||
self.onContextMenuCallback(event, n);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.on('click', (n: Node) => {
|
.on('click', (n: Node) => {
|
||||||
if (self.onNodeClickedCallback) {
|
this.onNodeClicked.emit(new NodeEvent(event, n));
|
||||||
self.onNodeClickedCallback(event, n);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// update image of node
|
// update image of node
|
||||||
@ -190,17 +174,15 @@ export class NodesWidget implements Widget {
|
|||||||
n.y = e.y;
|
n.y = e.y;
|
||||||
|
|
||||||
self.revise(select(this));
|
self.revise(select(this));
|
||||||
self.executeOnNodeDraggingCallback(event, n);
|
self.onNodeDragging.emit(new NodeEvent(event, n));
|
||||||
};
|
};
|
||||||
|
|
||||||
const dragging = () => {
|
const dragging = () => {
|
||||||
return drag<SVGGElement, Node>()
|
return drag<SVGGElement, Node>()
|
||||||
.on('drag', callback)
|
.on('drag', callback)
|
||||||
.on('end', (n: Node) => {
|
.on('end', (n: Node) => {
|
||||||
if (self.onNodeDraggedCallback) {
|
|
||||||
const e: D3DragEvent<SVGGElement, Node, Node> = event;
|
const e: D3DragEvent<SVGGElement, Node, Node> = event;
|
||||||
self.onNodeDraggedCallback(e, n);
|
self.onNodeDragged.emit(new NodeEvent(e, n));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
import { path } from "d3-path";
|
|
||||||
|
|
||||||
import { Widget } from "./widget";
|
|
||||||
import { SVGSelection } from "../models/types";
|
|
||||||
import { Link } from "../models/link";
|
|
||||||
|
|
||||||
|
|
||||||
export class SerialLinkWidget implements Widget {
|
|
||||||
|
|
||||||
public draw(view: SVGSelection, link: Link) {
|
|
||||||
const source = {
|
|
||||||
'x': link.source.x + link.source.width / 2,
|
|
||||||
'y': link.source.y + link.source.height / 2
|
|
||||||
};
|
|
||||||
const target = {
|
|
||||||
'x': link.target.x + link.target.width / 2,
|
|
||||||
'y': link.target.y + link.target.height / 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const dx = target.x - source.x;
|
|
||||||
const dy = target.y - source.y;
|
|
||||||
|
|
||||||
const vector_angle = Math.atan2(dy, dx);
|
|
||||||
const rot_angle = -Math.PI / 4.0;
|
|
||||||
const vect_rot = [
|
|
||||||
Math.cos(vector_angle + rot_angle),
|
|
||||||
Math.sin(vector_angle + rot_angle)
|
|
||||||
];
|
|
||||||
|
|
||||||
const angle_source = [
|
|
||||||
source.x + dx / 2.0 + 15 * vect_rot[0],
|
|
||||||
source.y + dy / 2.0 + 15 * vect_rot[1]
|
|
||||||
];
|
|
||||||
|
|
||||||
const angle_target = [
|
|
||||||
target.x - dx / 2.0 - 15 * vect_rot[0],
|
|
||||||
target.y - dy / 2.0 - 15 * vect_rot[1]
|
|
||||||
];
|
|
||||||
|
|
||||||
const line_data = [
|
|
||||||
[source.x, source.y],
|
|
||||||
angle_source,
|
|
||||||
angle_target,
|
|
||||||
[target.x, target.y]
|
|
||||||
];
|
|
||||||
|
|
||||||
let link_path = view.select<SVGPathElement>('path');
|
|
||||||
|
|
||||||
if (!link_path.node()) {
|
|
||||||
link_path = view.append<SVGPathElement>('path');
|
|
||||||
}
|
|
||||||
|
|
||||||
const line_generator = path();
|
|
||||||
line_generator.moveTo(line_data[0][0], line_data[0][1]);
|
|
||||||
line_generator.lineTo(line_data[1][0], line_data[1][1]);
|
|
||||||
line_generator.lineTo(line_data[2][0], line_data[2][1]);
|
|
||||||
line_generator.lineTo(line_data[3][0], line_data[3][1]);
|
|
||||||
|
|
||||||
link_path
|
|
||||||
.attr('d', line_generator.toString())
|
|
||||||
.attr('stroke', '#B22222')
|
|
||||||
.attr('fill', 'none')
|
|
||||||
.attr('stroke-width', '2');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,3 +3,14 @@ export interface Widget {
|
|||||||
draw(view: any, datum: any): void;
|
draw(view: any, datum: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OnDraw {
|
||||||
|
draw(view: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OnConnect {
|
||||||
|
onConnect(view: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OnDisconnect {
|
||||||
|
onDisconnect(view: any): void;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="context-menu" [style.left]="leftPosition" [style.top]="topPosition" *ngIf="node">
|
<div class="context-menu" [style.left]="leftPosition" [style.top]="topPosition" *ngIf="node">
|
||||||
<span [matMenuTriggerFor]="contextMenu"></span>
|
<span [matMenuTriggerFor]="contextMenu"></span>
|
||||||
<mat-menu #contextMenu="matMenu">
|
<mat-menu #contextMenu="matMenu" class="context-menu-items">
|
||||||
<app-start-node-action [server]="server" [node]="node"></app-start-node-action>
|
<app-start-node-action [server]="server" [node]="node"></app-start-node-action>
|
||||||
<app-stop-node-action [server]="server" [node]="node"></app-stop-node-action>
|
<app-stop-node-action [server]="server" [node]="node"></app-stop-node-action>
|
||||||
<app-move-layer-up-action *ngIf="!projectService.isReadOnly(project)" [server]="server" [node]="node"></app-move-layer-up-action>
|
<app-move-layer-up-action *ngIf="!projectService.isReadOnly(project)" [server]="server" [node]="node"></app-move-layer-up-action>
|
||||||
|
@ -18,8 +18,8 @@ export class NodeContextMenuComponent implements OnInit {
|
|||||||
|
|
||||||
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
|
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
|
||||||
|
|
||||||
private topPosition;
|
protected topPosition;
|
||||||
private leftPosition;
|
protected leftPosition;
|
||||||
public node: Node;
|
public node: Node;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -4,7 +4,7 @@ import { inject } from "@angular/core/testing";
|
|||||||
|
|
||||||
import { mock, instance, capture, when } from "ts-mockito";
|
import { mock, instance, capture, when } from "ts-mockito";
|
||||||
import { HotkeyModule, HotkeysService, Hotkey } from "angular2-hotkeys";
|
import { HotkeyModule, HotkeysService, Hotkey } from "angular2-hotkeys";
|
||||||
import { Observable, of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import { ProjectMapShortcutsComponent } from './project-map-shortcuts.component';
|
import { ProjectMapShortcutsComponent } from './project-map-shortcuts.component';
|
||||||
import { ToasterService, } from "../../../services/toaster.service";
|
import { ToasterService, } from "../../../services/toaster.service";
|
||||||
@ -64,7 +64,7 @@ describe('ProjectMapShortcutsComponent', () => {
|
|||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
const [hotkey] = capture(hotkeyServiceMock.add).last();
|
const [hotkey] = capture(hotkeyServiceMock.add).last();
|
||||||
expect((hotkey as Hotkey).combo).toEqual([ 'del' ]);
|
expect((hotkey as Hotkey).combo).toEqual([ 'del' ]);
|
||||||
expect((hotkey as Hotkey).callback).toEqual(component.onDeleteHandler);
|
expect((hotkey as Hotkey).callback).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove binding', () => {
|
it('should remove binding', () => {
|
||||||
|
@ -28,7 +28,10 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.deleteHotkey = new Hotkey('del', this.onDeleteHandler);
|
const self = this;
|
||||||
|
this.deleteHotkey = new Hotkey('del', (event: KeyboardEvent) => {
|
||||||
|
return self.onDeleteHandler(event);
|
||||||
|
});
|
||||||
this.hotkeysService.add(this.deleteHotkey);
|
this.hotkeysService.add(this.deleteHotkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ g.node:hover {
|
|||||||
|
|
||||||
|
|
||||||
svg.map image:hover, svg.map image.chosen, g.selected {
|
svg.map image:hover, svg.map image.chosen, g.selected {
|
||||||
|
-webkit-filter: grayscale(100%);
|
||||||
|
-moz-filter: grayscale(100%);
|
||||||
|
-ms-filter: grayscale(100%);
|
||||||
|
-o-filter: grayscale(100%);
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,3 +85,14 @@ g.node text,
|
|||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.context-menu-items .mat-menu-item {
|
||||||
|
line-height: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-items .mat-menu-item .mat-icon {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
<div *ngIf="project" class="project-map">
|
<div *ngIf="project" class="project-map">
|
||||||
<app-map [symbols]="symbols" [nodes]="nodes" [links]="links" [drawings]="drawings" [width]="project.scene_width" [height]="project.scene_height"></app-map>
|
<app-map
|
||||||
|
[symbols]="symbols"
|
||||||
|
[nodes]="nodes"
|
||||||
|
[links]="links"
|
||||||
|
[drawings]="drawings"
|
||||||
|
[width]="project.scene_width"
|
||||||
|
[height]="project.scene_height"
|
||||||
|
[show-interface-labels]="project.show_interface_labels"
|
||||||
|
[selection-tool]="tools.selection"
|
||||||
|
[moving-tool]="tools.moving"
|
||||||
|
[draw-link-tool]="tools.draw_link"
|
||||||
|
(onNodeDragged)="onNodeDragged($event)"
|
||||||
|
(onLinkCreated)="onLinkCreated($event)"
|
||||||
|
></app-map>
|
||||||
<div class="project-toolbar">
|
<div class="project-toolbar">
|
||||||
<mat-toolbar color="primary" class="project-toolbar">
|
<mat-toolbar color="primary" class="project-toolbar">
|
||||||
|
|
||||||
@ -29,39 +41,51 @@
|
|||||||
|
|
||||||
<mat-menu #viewMenu="matMenu" [overlapTrigger]="false">
|
<mat-menu #viewMenu="matMenu" [overlapTrigger]="false">
|
||||||
<div class="options-item">
|
<div class="options-item">
|
||||||
<mat-checkbox [(ngModel)]="showIntefaceLabels" (change)="toggleShowInterfaceLabels($event.checked)">Show interface labels</mat-checkbox>
|
<mat-checkbox
|
||||||
|
[ngModel]="project.show_interface_labels"
|
||||||
|
(change)="toggleShowInterfaceLabels($event.checked)">
|
||||||
|
Show interface labels
|
||||||
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
<mat-toolbar-row *ngIf="!readonly">
|
<mat-toolbar-row *ngIf="!readonly">
|
||||||
<button mat-icon-button [color]="drawLineMode ? 'primary': 'basic'" (click)="toggleDrawLineMode()">
|
<button mat-icon-button [color]="tools.draw_link ? 'primary': 'basic'" (click)="toggleDrawLineMode()">
|
||||||
<mat-icon>timeline</mat-icon>
|
<mat-icon>timeline</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
|
|
||||||
<mat-toolbar-row>
|
<mat-toolbar-row>
|
||||||
<button mat-icon-button [color]="movingMode ? 'primary': 'basic'" (click)="toggleMovingMode()">
|
<button mat-icon-button [color]="tools.moving ? 'primary': 'basic'" (click)="toggleMovingMode()">
|
||||||
<mat-icon>zoom_out_map</mat-icon>
|
<mat-icon>zoom_out_map</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
|
|
||||||
<mat-toolbar-row *ngIf="!readonly" >
|
<mat-toolbar-row *ngIf="!readonly" >
|
||||||
<button mat-icon-button (click)="createSnapshotModal()">
|
<app-snapshot-menu-item
|
||||||
<mat-icon>snooze</mat-icon>
|
[server]="server"
|
||||||
</button>
|
[project]="project">
|
||||||
|
</app-snapshot-menu-item>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
|
|
||||||
<mat-toolbar-row *ngIf="!readonly" >
|
<mat-toolbar-row *ngIf="!readonly" >
|
||||||
<app-appliance [server]="server" (onNodeCreation)="onNodeCreation($event)"></app-appliance>
|
<app-appliance
|
||||||
|
[server]="server"
|
||||||
|
(onNodeCreation)="onNodeCreation($event)"
|
||||||
|
></app-appliance>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
|
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
||||||
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-progress></app-progress>
|
<app-progress></app-progress>
|
||||||
|
|
||||||
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server" [selectionManager]="selectionManager"></app-project-map-shortcuts>
|
<app-project-map-shortcuts
|
||||||
|
*ngIf="project"
|
||||||
|
[project]="project"
|
||||||
|
[server]="server"
|
||||||
|
[selectionManager]="selectionManager">
|
||||||
|
</app-project-map-shortcuts>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
|
||||||
import { Observable, Subject, Subscription, from } from 'rxjs';
|
import { Observable, Subject, Subscription, from } from 'rxjs';
|
||||||
@ -8,33 +8,27 @@ import { map, mergeMap } from "rxjs/operators";
|
|||||||
import { Project } from '../../models/project';
|
import { Project } from '../../models/project';
|
||||||
import { Node } from '../../cartography/models/node';
|
import { Node } from '../../cartography/models/node';
|
||||||
import { SymbolService } from '../../services/symbol.service';
|
import { SymbolService } from '../../services/symbol.service';
|
||||||
import { Link } from "../../cartography/models/link";
|
import { Link } from "../../models/link";
|
||||||
import { MapComponent } from "../../cartography/components/map/map.component";
|
import { MapComponent } from "../../cartography/components/map/map.component";
|
||||||
import { ServerService } from "../../services/server.service";
|
import { ServerService } from "../../services/server.service";
|
||||||
import { ProjectService } from '../../services/project.service';
|
import { ProjectService } from '../../services/project.service';
|
||||||
import { Server } from "../../models/server";
|
import { Server } from "../../models/server";
|
||||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material";
|
|
||||||
import { SnapshotService } from "../../services/snapshot.service";
|
|
||||||
import { Snapshot } from "../../models/snapshot";
|
|
||||||
import { ProgressDialogService } from "../../common/progress-dialog/progress-dialog.service";
|
|
||||||
import { ProgressDialogComponent } from "../../common/progress-dialog/progress-dialog.component";
|
|
||||||
import { Drawing } from "../../cartography/models/drawing";
|
import { Drawing } from "../../cartography/models/drawing";
|
||||||
import { NodeContextMenuComponent } from "./node-context-menu/node-context-menu.component";
|
import { NodeContextMenuComponent } from "./node-context-menu/node-context-menu.component";
|
||||||
import { Appliance } from "../../models/appliance";
|
import { Appliance } from "../../models/appliance";
|
||||||
import { NodeService } from "../../services/node.service";
|
import { NodeService } from "../../services/node.service";
|
||||||
import { Symbol } from "../../cartography/models/symbol";
|
import { Symbol } from "../../models/symbol";
|
||||||
import { NodeSelectInterfaceComponent } from "./node-select-interface/node-select-interface.component";
|
|
||||||
import { Port } from "../../models/port";
|
|
||||||
import { LinkService } from "../../services/link.service";
|
import { LinkService } from "../../services/link.service";
|
||||||
import { ToasterService } from '../../services/toaster.service';
|
|
||||||
import { NodesDataSource } from "../../cartography/datasources/nodes-datasource";
|
import { NodesDataSource } from "../../cartography/datasources/nodes-datasource";
|
||||||
import { LinksDataSource } from "../../cartography/datasources/links-datasource";
|
import { LinksDataSource } from "../../cartography/datasources/links-datasource";
|
||||||
import { ProjectWebServiceHandler } from "../../handlers/project-web-service-handler";
|
import { ProjectWebServiceHandler } from "../../handlers/project-web-service-handler";
|
||||||
import { SelectionManager } from "../../cartography/managers/selection-manager";
|
import { SelectionManager } from "../../cartography/managers/selection-manager";
|
||||||
import { InRectangleHelper } from "../../cartography/components/map/helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "../../cartography/helpers/in-rectangle-helper";
|
||||||
import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
|
||||||
import { SettingsService } from "../../services/settings.service";
|
|
||||||
import { ProgressService } from "../../common/progress/progress.service";
|
import { ProgressService } from "../../common/progress/progress.service";
|
||||||
|
import { NodeEvent } from '../../cartography/widgets/nodes';
|
||||||
|
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
||||||
|
import { LinkCreated } from '../../cartography/components/draw-link-tool/draw-link-tool.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -53,16 +47,20 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
public server: Server;
|
public server: Server;
|
||||||
|
|
||||||
private ws: Subject<any>;
|
private ws: Subject<any>;
|
||||||
private drawLineMode = false;
|
|
||||||
private movingMode = false;
|
protected tools = {
|
||||||
private readonly = false;
|
'selection': true,
|
||||||
|
'moving': false,
|
||||||
|
'draw_link': false
|
||||||
|
};
|
||||||
|
|
||||||
|
private inReadOnlyMode = false;
|
||||||
|
|
||||||
protected selectionManager: SelectionManager;
|
protected selectionManager: SelectionManager;
|
||||||
|
|
||||||
@ViewChild(MapComponent) mapChild: MapComponent;
|
@ViewChild(MapComponent) mapChild: MapComponent;
|
||||||
|
|
||||||
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
||||||
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
|
|
||||||
|
|
||||||
private subscriptions: Subscription[];
|
private subscriptions: Subscription[];
|
||||||
|
|
||||||
@ -71,15 +69,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private symbolService: SymbolService,
|
private symbolService: SymbolService,
|
||||||
private snapshotService: SnapshotService,
|
|
||||||
private nodeService: NodeService,
|
private nodeService: NodeService,
|
||||||
private linkService: LinkService,
|
private linkService: LinkService,
|
||||||
private dialog: MatDialog,
|
|
||||||
private progressDialogService: ProgressDialogService,
|
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
private toaster: ToasterService,
|
|
||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private settingsService: SettingsService,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
protected nodesDataSource: NodesDataSource,
|
protected nodesDataSource: NodesDataSource,
|
||||||
protected linksDataSource: LinksDataSource,
|
protected linksDataSource: LinksDataSource,
|
||||||
protected drawingsDataSource: DrawingsDataSource,
|
protected drawingsDataSource: DrawingsDataSource,
|
||||||
@ -134,29 +128,23 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.drawingsDataSource.connect().subscribe((drawings: Drawing[]) => {
|
this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => {
|
||||||
this.drawings = drawings;
|
this.drawings = drawings;
|
||||||
if (this.mapChild) {
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
this.mapChild.reload();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.nodesDataSource.connect().subscribe((nodes: Node[]) => {
|
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
if (this.mapChild) {
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
this.mapChild.reload();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.linksDataSource.connect().subscribe((links: Link[]) => {
|
this.linksDataSource.changes.subscribe((links: Link[]) => {
|
||||||
this.links = links;
|
this.links = links;
|
||||||
if (this.mapChild) {
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
this.mapChild.reload();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -200,39 +188,24 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUpMapCallbacks(project: Project) {
|
setUpMapCallbacks(project: Project) {
|
||||||
if (this.readonly) {
|
|
||||||
this.mapChild.graphLayout.getSelectionTool().deactivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mapChild.graphLayout.getNodesWidget().setDraggingEnabled(!this.readonly);
|
this.mapChild.graphLayout.getNodesWidget().setDraggingEnabled(!this.readonly);
|
||||||
|
|
||||||
this.mapChild.graphLayout.getNodesWidget().setOnContextMenuCallback((event: any, node: Node) => {
|
const onContextMenu = this.mapChild.graphLayout.getNodesWidget().onContextMenu.subscribe((eventNode: NodeEvent) => {
|
||||||
this.nodeContextMenu.open(node, event.clientY, event.clientX);
|
this.nodeContextMenu.open(
|
||||||
|
eventNode.node,
|
||||||
|
eventNode.event.clientY,
|
||||||
|
eventNode.event.clientX
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mapChild.graphLayout.getNodesWidget().setOnNodeClickedCallback((event: any, node: Node) => {
|
this.subscriptions.push(onContextMenu);
|
||||||
this.selectionManager.clearSelection();
|
|
||||||
this.selectionManager.setSelectedNodes([node]);
|
|
||||||
if (this.drawLineMode) {
|
|
||||||
this.nodeSelectInterfaceMenu.open(node, event.clientY, event.clientX);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.mapChild.graphLayout.getNodesWidget().setOnNodeDraggedCallback((event: any, node: Node) => {
|
|
||||||
this.nodesDataSource.update(node);
|
|
||||||
this.nodeService
|
|
||||||
.updatePosition(this.server, node, node.x, node.y)
|
|
||||||
.subscribe((n: Node) => {
|
|
||||||
this.nodesDataSource.update(n);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.selectionManager.subscribe(this.mapChild.graphLayout.getSelectionTool().rectangleSelected)
|
this.selectionManager.subscribe(
|
||||||
|
this.mapChild.graphLayout.getSelectionTool().rectangleSelected)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.mapChild.graphLayout.getLinksWidget().getInterfaceLabelWidget().setEnabled(this.project.show_interface_labels);
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
this.mapChild.reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeCreation(appliance: Appliance) {
|
onNodeCreation(appliance: Appliance) {
|
||||||
@ -247,76 +220,43 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public createSnapshotModal() {
|
onNodeDragged(nodeEvent: NodeEvent) {
|
||||||
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
|
this.nodesDataSource.update(nodeEvent.node);
|
||||||
width: '250px',
|
this.nodeService
|
||||||
});
|
.updatePosition(this.server, nodeEvent.node, nodeEvent.node.x, nodeEvent.node.y)
|
||||||
|
.subscribe((n: Node) => {
|
||||||
dialogRef.afterClosed().subscribe(snapshot => {
|
this.nodesDataSource.update(n);
|
||||||
if (snapshot) {
|
|
||||||
const creation = this.snapshotService.create(this.server, this.project.project_id, snapshot);
|
|
||||||
|
|
||||||
const progress = this.progressDialogService.open();
|
|
||||||
|
|
||||||
const subscription = creation.subscribe((created_snapshot: Snapshot) => {
|
|
||||||
this.toaster.success(`Snapshot '${snapshot.name}' has been created.`);
|
|
||||||
progress.close();
|
|
||||||
}, (response) => {
|
|
||||||
const error = response.json();
|
|
||||||
this.toaster.error(`Cannot create snapshot: ${error.message}`);
|
|
||||||
progress.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
progress.afterClosed().subscribe((result) => {
|
|
||||||
if (result === ProgressDialogComponent.CANCELLED) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleDrawLineMode() {
|
public set readonly(value) {
|
||||||
this.drawLineMode = !this.drawLineMode;
|
this.inReadOnlyMode = value;
|
||||||
if (!this.drawLineMode) {
|
if (value) {
|
||||||
this.mapChild.graphLayout.getDrawingLineTool().stop();
|
this.tools.selection = false;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.tools.selection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get readonly() {
|
||||||
|
return this.inReadOnlyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleMovingMode() {
|
public toggleMovingMode() {
|
||||||
this.movingMode = !this.movingMode;
|
this.tools.moving = !this.tools.moving;
|
||||||
if (this.movingMode) {
|
|
||||||
if (!this.readonly) {
|
if (!this.readonly) {
|
||||||
this.mapChild.graphLayout.getSelectionTool().deactivate();
|
this.tools.selection = !this.tools.moving;
|
||||||
}
|
|
||||||
this.mapChild.graphLayout.getMovingTool().activate();
|
|
||||||
} else {
|
|
||||||
this.mapChild.graphLayout.getMovingTool().deactivate();
|
|
||||||
if (!this.readonly) {
|
|
||||||
this.mapChild.graphLayout.getSelectionTool().activate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public onChooseInterface(event) {
|
|
||||||
const node: Node = event.node;
|
|
||||||
const port: Port = event.port;
|
|
||||||
const drawingLineTool = this.mapChild.graphLayout.getDrawingLineTool();
|
|
||||||
if (drawingLineTool.isDrawing()) {
|
|
||||||
const data = drawingLineTool.stop();
|
|
||||||
this.onLineCreation(data['node'], data['port'], node, port);
|
|
||||||
} else {
|
|
||||||
drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
|
|
||||||
'node': node,
|
|
||||||
'port': port
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onLineCreation(source_node: Node, source_port: Port, target_node: Node, target_port: Port) {
|
public toggleDrawLineMode() {
|
||||||
|
this.tools.draw_link = !this.tools.draw_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLinkCreated(linkCreated: LinkCreated) {
|
||||||
this.linkService
|
this.linkService
|
||||||
.createLink(this.server, source_node, source_port, target_node, target_port)
|
.createLink(this.server, linkCreated.sourceNode, linkCreated.sourcePort, linkCreated.targetNode, linkCreated.targetPort)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.projectService.links(this.server, this.project.project_id).subscribe((links: Link[]) => {
|
this.projectService.links(this.server, this.project.project_id).subscribe((links: Link[]) => {
|
||||||
this.linksDataSource.set(links);
|
this.linksDataSource.set(links);
|
||||||
@ -326,10 +266,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public toggleShowInterfaceLabels(enabled: boolean) {
|
public toggleShowInterfaceLabels(enabled: boolean) {
|
||||||
this.project.show_interface_labels = enabled;
|
this.project.show_interface_labels = enabled;
|
||||||
|
|
||||||
this.mapChild.graphLayout.getLinksWidget().getInterfaceLabelWidget()
|
|
||||||
.setEnabled(this.project.show_interface_labels);
|
|
||||||
this.mapChild.reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
@ -344,27 +280,3 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-create-snapshot-dialog',
|
|
||||||
templateUrl: 'create-snapshot-dialog.html',
|
|
||||||
})
|
|
||||||
export class CreateSnapshotDialogComponent {
|
|
||||||
snapshot: Snapshot = new Snapshot();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
|
||||||
}
|
|
||||||
|
|
||||||
onAddClick(): void {
|
|
||||||
this.dialogRef.close(this.snapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
onNoClick(): void {
|
|
||||||
this.dialogRef.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CreateSnapshotDialogComponent } from './create-snapshot-dialog.component';
|
||||||
|
|
||||||
|
describe('CreateSnapshotDialogComponent', () => {
|
||||||
|
let component: CreateSnapshotDialogComponent;
|
||||||
|
let fixture: ComponentFixture<CreateSnapshotDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CreateSnapshotDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CreateSnapshotDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('should create', () => {
|
||||||
|
// expect(component).toBeTruthy();
|
||||||
|
// });
|
||||||
|
});
|
@ -0,0 +1,26 @@
|
|||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { Snapshot } from '../../../models/snapshot';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create-snapshot-dialog',
|
||||||
|
templateUrl: './create-snapshot-dialog.component.html',
|
||||||
|
styleUrls: ['./create-snapshot-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class CreateSnapshotDialogComponent {
|
||||||
|
|
||||||
|
snapshot: Snapshot = new Snapshot();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddClick(): void {
|
||||||
|
this.dialogRef.close(this.snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
onNoClick(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
<button mat-icon-button (click)="createSnapshotModal()">
|
||||||
|
<mat-icon>snooze</mat-icon>
|
||||||
|
</button>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SnapshotMenuItemComponent } from './snapshot-menu-item.component';
|
||||||
|
|
||||||
|
describe('SnapshotMenuItemComponent', () => {
|
||||||
|
let component: SnapshotMenuItemComponent;
|
||||||
|
let fixture: ComponentFixture<SnapshotMenuItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SnapshotMenuItemComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SnapshotMenuItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('should create', () => {
|
||||||
|
// expect(component).toBeTruthy();
|
||||||
|
// });
|
||||||
|
});
|
@ -0,0 +1,60 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material';
|
||||||
|
|
||||||
|
import { CreateSnapshotDialogComponent } from '../create-snapshot-dialog/create-snapshot-dialog.component';
|
||||||
|
import { SnapshotService } from '../../../services/snapshot.service';
|
||||||
|
import { ProgressDialogService } from '../../../common/progress-dialog/progress-dialog.service';
|
||||||
|
import { ToasterService } from '../../../services/toaster.service';
|
||||||
|
import { ProgressDialogComponent } from '../../../common/progress-dialog/progress-dialog.component';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Snapshot } from '../../../models/snapshot';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-snapshot-menu-item',
|
||||||
|
templateUrl: './snapshot-menu-item.component.html',
|
||||||
|
styleUrls: ['./snapshot-menu-item.component.scss']
|
||||||
|
})
|
||||||
|
export class SnapshotMenuItemComponent implements OnInit {
|
||||||
|
@Input('project') project: Project;
|
||||||
|
@Input('server') server: Server;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private snapshotService: SnapshotService,
|
||||||
|
private progressDialogService: ProgressDialogService,
|
||||||
|
private toaster: ToasterService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
public createSnapshotModal() {
|
||||||
|
const dialogRef = this.dialog.open(CreateSnapshotDialogComponent, {
|
||||||
|
width: '250px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(snapshot => {
|
||||||
|
if (snapshot) {
|
||||||
|
const creation = this.snapshotService.create(this.server, this.project.project_id, snapshot);
|
||||||
|
|
||||||
|
const progress = this.progressDialogService.open();
|
||||||
|
|
||||||
|
const subscription = creation.subscribe((created_snapshot: Snapshot) => {
|
||||||
|
this.toaster.success(`Snapshot '${snapshot.name}' has been created.`);
|
||||||
|
progress.close();
|
||||||
|
}, (response) => {
|
||||||
|
const error = response.json();
|
||||||
|
this.toaster.error(`Cannot create snapshot: ${error.message}`);
|
||||||
|
progress.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
progress.afterClosed().subscribe((result) => {
|
||||||
|
if (result === ProgressDialogComponent.CANCELLED) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
src/app/components/snapshots/snapshots.component.html
Normal file
3
src/app/components/snapshots/snapshots.component.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<p>
|
||||||
|
snapshots works!
|
||||||
|
</p>
|
25
src/app/components/snapshots/snapshots.component.spec.ts
Normal file
25
src/app/components/snapshots/snapshots.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SnapshotsComponent } from './snapshots.component';
|
||||||
|
|
||||||
|
describe('SnapshotsComponent', () => {
|
||||||
|
let component: SnapshotsComponent;
|
||||||
|
let fixture: ComponentFixture<SnapshotsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SnapshotsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SnapshotsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
15
src/app/components/snapshots/snapshots.component.ts
Normal file
15
src/app/components/snapshots/snapshots.component.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-snapshots',
|
||||||
|
templateUrl: './snapshots.component.html',
|
||||||
|
styleUrls: ['./snapshots.component.scss']
|
||||||
|
})
|
||||||
|
export class SnapshotsComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
src/app/converters/converter.ts
Normal file
3
src/app/converters/converter.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface Converter<TSource, TDestination> {
|
||||||
|
convert(from: TSource): TDestination;
|
||||||
|
}
|
@ -7,7 +7,7 @@ import { NodesDataSource } from "../cartography/datasources/nodes-datasource";
|
|||||||
import { LinksDataSource } from "../cartography/datasources/links-datasource";
|
import { LinksDataSource } from "../cartography/datasources/links-datasource";
|
||||||
import { DrawingsDataSource } from "../cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "../cartography/datasources/drawings-datasource";
|
||||||
import { Node } from "../cartography/models/node";
|
import { Node } from "../cartography/models/node";
|
||||||
import { Link } from "../cartography/models/link";
|
import { Link } from "../models/link";
|
||||||
import { Drawing } from "../cartography/models/drawing";
|
import { Drawing } from "../cartography/models/drawing";
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { Subject } from "rxjs";
|
|||||||
import { NodesDataSource } from "../cartography/datasources/nodes-datasource";
|
import { NodesDataSource } from "../cartography/datasources/nodes-datasource";
|
||||||
import { LinksDataSource } from "../cartography/datasources/links-datasource";
|
import { LinksDataSource } from "../cartography/datasources/links-datasource";
|
||||||
import { DrawingsDataSource } from "../cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "../cartography/datasources/drawings-datasource";
|
||||||
import { Link } from "../cartography/models/link";
|
import { Link } from "../models/link";
|
||||||
import { Node } from "../cartography/models/node";
|
import { Node } from "../cartography/models/node";
|
||||||
import { Drawing } from "../cartography/models/drawing";
|
import { Drawing } from "../cartography/models/drawing";
|
||||||
|
|
||||||
|
41
src/app/material.imports.ts
Normal file
41
src/app/material.imports.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatSnackBarModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatListModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatTooltipModule
|
||||||
|
} from '@angular/material';
|
||||||
|
|
||||||
|
export const MATERIAL_IMPORTS = [
|
||||||
|
MatButtonModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatSnackBarModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatListModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
];
|
@ -1,4 +1,4 @@
|
|||||||
import { Label } from "./label";
|
import { Label } from "../cartography/models/label";
|
||||||
|
|
||||||
export class LinkNode {
|
export class LinkNode {
|
||||||
node_id: string;
|
node_id: string;
|
@ -1,6 +1,6 @@
|
|||||||
import { Node } from "./node";
|
import { Node } from "../cartography/models/node";
|
||||||
import { LinkNode } from "./link-node";
|
import { LinkNode } from "./link-node";
|
||||||
import { Selectable } from "../managers/selection-manager";
|
import { Selectable } from "../cartography/managers/selection-manager";
|
||||||
|
|
||||||
|
|
||||||
export class Link implements Selectable {
|
export class Link implements Selectable {
|
||||||
@ -11,12 +11,14 @@ export class Link implements Selectable {
|
|||||||
link_type: string;
|
link_type: string;
|
||||||
nodes: LinkNode[];
|
nodes: LinkNode[];
|
||||||
project_id: string;
|
project_id: string;
|
||||||
|
|
||||||
|
|
||||||
distance: number; // this is not from server
|
distance: number; // this is not from server
|
||||||
length: number; // this is not from server
|
length: number; // this is not from server
|
||||||
source: Node; // this is not from server
|
source: Node; // this is not from server
|
||||||
target: Node; // this is not from server
|
target: Node; // this is not from server
|
||||||
|
|
||||||
is_selected = false;
|
is_selected = false; // this is not from server
|
||||||
x: number;
|
x: number; // this is not from server
|
||||||
y: number;
|
y: number; // this is not from server
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ import { Project } from '../models/project';
|
|||||||
import { Node } from '../cartography/models/node';
|
import { Node } from '../cartography/models/node';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { Link } from "../cartography/models/link";
|
import { Link } from "../models/link";
|
||||||
import { Server } from "../models/server";
|
import { Server } from "../models/server";
|
||||||
import { HttpServer } from "./http-server.service";
|
import { HttpServer } from "./http-server.service";
|
||||||
import { Drawing } from "../cartography/models/drawing";
|
import { Drawing } from "../cartography/models/drawing";
|
||||||
|
@ -5,8 +5,6 @@ import { Server } from "../models/server";
|
|||||||
import { IndexedDbService } from "./indexed-db.service";
|
import { IndexedDbService } from "./indexed-db.service";
|
||||||
import { AngularIndexedDB } from "angular2-indexeddb";
|
import { AngularIndexedDB } from "angular2-indexeddb";
|
||||||
import Spy = jasmine.Spy;
|
import Spy = jasmine.Spy;
|
||||||
import { resolve } from 'path';
|
|
||||||
import { reject } from 'q';
|
|
||||||
|
|
||||||
|
|
||||||
export class MockedServerService {
|
export class MockedServerService {
|
||||||
|
@ -2,7 +2,6 @@ import { TestBed, inject } from '@angular/core/testing';
|
|||||||
import { PersistenceService, StorageType } from "angular-persistence";
|
import { PersistenceService, StorageType } from "angular-persistence";
|
||||||
|
|
||||||
import { Settings, SettingsService } from './settings.service';
|
import { Settings, SettingsService } from './settings.service';
|
||||||
import createSpyObj = jasmine.createSpyObj;
|
|
||||||
|
|
||||||
|
|
||||||
export class MockedSettingsService {
|
export class MockedSettingsService {
|
||||||
|
@ -6,7 +6,7 @@ import { HttpServer } from './http-server.service';
|
|||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
import { SymbolService } from './symbol.service';
|
import { SymbolService } from './symbol.service';
|
||||||
import { Symbol } from '../cartography/models/symbol';
|
import { Symbol } from '../models/symbol';
|
||||||
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BehaviorSubject, forkJoin, Observable } from 'rxjs';
|
import { BehaviorSubject, forkJoin, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { Symbol } from '../cartography/models/symbol';
|
import { Symbol } from '../models/symbol';
|
||||||
import { Server } from "../models/server";
|
import { Server } from "../models/server";
|
||||||
import { HttpServer } from "./http-server.service";
|
import { HttpServer } from "./http-server.service";
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user