Merge pull request #198 from GNS3/data-model-change

Data model change
This commit is contained in:
ziajka 2018-11-15 11:43:28 +01:00 committed by GitHub
commit 5a8206750e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 1404 additions and 807 deletions

View File

@ -17,10 +17,27 @@ import { Context } from './models/context';
import { D3_MAP_IMPORTS } from './d3-map.imports'; import { D3_MAP_IMPORTS } from './d3-map.imports';
import { CanvasSizeDetector } from './helpers/canvas-size-detector'; import { CanvasSizeDetector } from './helpers/canvas-size-detector';
import { MapListeners } from './listeners/map-listeners'; import { MapListeners } from './listeners/map-listeners';
import { DrawingsDraggableListener } from './listeners/drawings-draggable-listener'; import { DraggableListener } from './listeners/draggable-listener';
import { NodesDraggableListener } from './listeners/nodes-draggable-listener';
import { DrawingsEventSource } from './events/drawings-event-source'; import { DrawingsEventSource } from './events/drawings-event-source';
import { NodesEventSource } from './events/nodes-event-source'; import { NodesEventSource } from './events/nodes-event-source';
import { DrawingToMapDrawingConverter } from './converters/map/drawing-to-map-drawing-converter';
import { LabelToMapLabelConverter } from './converters/map/label-to-map-label-converter';
import { LinkToMapLinkConverter } from './converters/map/link-to-map-link-converter';
import { MapDrawingToDrawingConverter } from './converters/map/map-drawing-to-drawing-converter';
import { MapLabelToLabelConverter } from './converters/map/map-label-to-label-converter';
import { MapLinkNodeToLinkNodeConverter } from './converters/map/map-link-node-to-link-node-converter';
import { MapLinkToLinkConverter } from './converters/map/map-link-to-link-converter';
import { MapNodeToNodeConverter } from './converters/map/map-node-to-node-converter';
import { MapPortToPortConverter } from './converters/map/map-port-to-port-converter';
import { MapSymbolToSymbolConverter } from './converters/map/map-symbol-to-symbol-converter';
import { NodeToMapNodeConverter } from './converters/map/node-to-map-node-converter';
import { PortToMapPortConverter } from './converters/map/port-to-map-port-converter';
import { SymbolToMapSymbolConverter } from './converters/map/symbol-to-map-symbol-converter';
import { LinkNodeToMapLinkNodeConverter } from './converters/map/link-node-to-map-link-node-converter';
import { GraphDataManager } from './managers/graph-data-manager';
import { SelectionUpdateListener } from './listeners/selection-update-listener';
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource } from './datasources/map-datasource';
import { SelectionListener } from './listeners/selection-listener';
@NgModule({ @NgModule({
@ -44,11 +61,31 @@ import { NodesEventSource } from './events/nodes-event-source';
MapChangeDetectorRef, MapChangeDetectorRef,
CanvasSizeDetector, CanvasSizeDetector,
Context, Context,
SelectionUpdateListener,
MapListeners, MapListeners,
DrawingsDraggableListener, DraggableListener,
NodesDraggableListener, SelectionListener,
DrawingsEventSource, DrawingsEventSource,
NodesEventSource, NodesEventSource,
DrawingToMapDrawingConverter,
LabelToMapLabelConverter,
LinkToMapLinkConverter,
LinkNodeToMapLinkNodeConverter,
MapDrawingToDrawingConverter,
MapLabelToLabelConverter,
MapLinkNodeToLinkNodeConverter,
MapLinkToLinkConverter,
MapNodeToNodeConverter,
MapPortToPortConverter,
MapSymbolToSymbolConverter,
NodeToMapNodeConverter,
PortToMapPortConverter,
SymbolToMapSymbolConverter,
GraphDataManager,
MapNodesDataSource,
MapLinksDataSource,
MapDrawingsDataSource,
MapSymbolsDataSource,
...D3_MAP_IMPORTS ...D3_MAP_IMPORTS
], ],
exports: [ MapComponent ] exports: [ MapComponent ]

View File

@ -1,12 +1,12 @@
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild } from '@angular/core'; import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild } from '@angular/core';
import { Port } from '../../../models/port';
import { DrawingLineWidget } from '../../widgets/drawing-line'; import { DrawingLineWidget } from '../../widgets/drawing-line';
import { Node } from '../../models/node';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component'; import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component';
import { LinkCreated } from '../../events/links'; import { MapLinkCreated } from '../../events/links';
import { NodeClicked } from '../../events/nodes'; import { NodeClicked } from '../../events/nodes';
import { NodeWidget } from '../../widgets/node'; import { NodeWidget } from '../../widgets/node';
import { MapNode } from '../../models/map/map-node';
import { MapPort } from '../../models/map/map-port';
@Component({ @Component({
@ -17,7 +17,7 @@ import { NodeWidget } from '../../widgets/node';
export class DrawLinkToolComponent implements OnInit, OnDestroy { export class DrawLinkToolComponent implements OnInit, OnDestroy {
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent; @ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
@Output('linkCreated') linkCreated = new EventEmitter<LinkCreated>(); @Output('linkCreated') linkCreated = new EventEmitter<MapLinkCreated>();
private onNodeClicked: Subscription; private onNodeClicked: Subscription;
@ -37,18 +37,18 @@ export class DrawLinkToolComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
if(this.drawingLineTool.isDrawing()) { if (this.drawingLineTool.isDrawing()) {
this.drawingLineTool.stop(); this.drawingLineTool.stop();
} }
this.onNodeClicked.unsubscribe(); this.onNodeClicked.unsubscribe();
} }
public onChooseInterface(event) { public onChooseInterface(event) {
const node: Node = event.node; const node: MapNode = event.node;
const port: Port = event.port; const port: MapPort = event.port;
if (this.drawingLineTool.isDrawing()) { if (this.drawingLineTool.isDrawing()) {
const data = this.drawingLineTool.stop(); const data = this.drawingLineTool.stop();
this.linkCreated.emit(new LinkCreated(data['node'], data['port'], node, port)); this.linkCreated.emit(new MapLinkCreated(data['node'], data['port'], node, port));
} else { } else {
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., { this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
'node': node, 'node': node,

View File

@ -3,29 +3,30 @@ import {
} from '@angular/core'; } from '@angular/core';
import { Selection, select } from 'd3-selection'; import { Selection, select } from 'd3-selection';
import { Node } from "../../models/node";
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 { Symbol } from '../../../models/symbol';
import { NodesWidget } from '../../widgets/nodes'; import { NodesWidget } from '../../widgets/nodes';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { InterfaceLabelWidget } from '../../widgets/interface-label'; import { InterfaceLabelWidget } from '../../widgets/interface-label';
import { SelectionTool } from '../../tools/selection-tool'; import { SelectionTool } from '../../tools/selection-tool';
import { MovingTool } from '../../tools/moving-tool'; import { MovingTool } from '../../tools/moving-tool';
import { LinksWidget } from '../../widgets/links';
import { MapChangeDetectorRef } from '../../services/map-change-detector-ref'; import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
import { NodeDragging, NodeDragged, NodeClicked } from '../../events/nodes';
import { LinkCreated } from '../../events/links'; import { LinkCreated } from '../../events/links';
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector'; import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
import { NodeWidget } from '../../widgets/node';
import { MapListeners } from '../../listeners/map-listeners'; import { MapListeners } from '../../listeners/map-listeners';
import { DraggedDataEvent } from '../../events/event-source'; import { DraggedDataEvent } from '../../events/event-source';
import { NodesEventSource } from '../../events/nodes-event-source'; import { NodesEventSource } from '../../events/nodes-event-source';
import { DrawingsEventSource } from '../../events/drawings-event-source'; import { DrawingsEventSource } from '../../events/drawings-event-source';
import { DrawingsWidget } from '../../widgets/drawings'; import { DrawingsWidget } from '../../widgets/drawings';
import { Node } from '../../models/node';
import { Link } from '../../../models/link';
import { Drawing } from '../../models/drawing';
import { Symbol } from '../../../models/symbol';
import { MapNodeToNodeConverter } from '../../converters/map/map-node-to-node-converter';
import { MapPortToPortConverter } from '../../converters/map/map-port-to-port-converter';
import { GraphDataManager } from '../../managers/graph-data-manager';
import { MapDrawingToDrawingConverter } from '../../converters/map/map-drawing-to-drawing-converter';
@Component({ @Component({
@ -42,39 +43,41 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
@Input() width = 1500; @Input() width = 1500;
@Input() height = 600; @Input() height = 600;
@Output() nodeDragged: EventEmitter<DraggedDataEvent<Node>>; @Output() nodeDragged = new EventEmitter<DraggedDataEvent<Node>>();
@Output() drawingDragged: EventEmitter<DraggedDataEvent<Drawing>>; @Output() drawingDragged = new EventEmitter<DraggedDataEvent<Drawing>>();
@Output() onLinkCreated = new EventEmitter<LinkCreated>(); @Output() onLinkCreated = new EventEmitter<LinkCreated>();
private parentNativeElement: any; private parentNativeElement: any;
private svg: Selection<SVGSVGElement, any, null, undefined>; private svg: Selection<SVGSVGElement, any, null, undefined>;
private onChangesDetected: Subscription; private onChangesDetected: Subscription;
private nodeDraggedSub: Subscription;
private drawingDraggedSub: Subscription;
protected settings = { protected settings = {
'show_interface_labels': true 'show_interface_labels': true
}; };
constructor( constructor(
private graphDataManager: GraphDataManager,
private context: Context, private context: Context,
private mapChangeDetectorRef: MapChangeDetectorRef, private mapChangeDetectorRef: MapChangeDetectorRef,
private canvasSizeDetector: CanvasSizeDetector, private canvasSizeDetector: CanvasSizeDetector,
private mapListeners: MapListeners, private mapListeners: MapListeners,
private mapNodeToNode: MapNodeToNodeConverter,
private mapPortToPort: MapPortToPortConverter,
private mapDrawingToDrawing: MapDrawingToDrawingConverter,
protected element: ElementRef, protected element: ElementRef,
protected nodesWidget: NodesWidget, protected nodesWidget: NodesWidget,
protected nodeWidget: NodeWidget,
protected linksWidget: LinksWidget,
protected drawingsWidget: DrawingsWidget, protected drawingsWidget: DrawingsWidget,
protected interfaceLabelWidget: InterfaceLabelWidget, protected interfaceLabelWidget: InterfaceLabelWidget,
protected selectionToolWidget: SelectionTool, protected selectionToolWidget: SelectionTool,
protected movingToolWidget: MovingTool, protected movingToolWidget: MovingTool,
public graphLayout: GraphLayout, public graphLayout: GraphLayout,
nodesEventSource: NodesEventSource, private nodesEventSource: NodesEventSource,
drawingsEventSource: DrawingsEventSource, private drawingsEventSource: DrawingsEventSource,
) { ) {
this.parentNativeElement = element.nativeElement; this.parentNativeElement = element.nativeElement;
this.nodeDragged = nodesEventSource.dragged;
this.drawingDragged = drawingsEventSource.dragged;
} }
@Input('show-interface-labels') @Input('show-interface-labels')
@ -100,7 +103,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
@Input('readonly') set readonly(value) { @Input('readonly') set readonly(value) {
this.nodesWidget.draggingEnabled = !value; this.nodesWidget.draggingEnabled = !value;
this.drawingsWidget.draggingEnabled == !value; this.drawingsWidget.draggingEnabled = !value;
} }
ngOnChanges(changes: { [propKey: string]: SimpleChange }) { ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
@ -113,12 +116,6 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
(changes['symbols'] && !changes['symbols'].isFirstChange()) (changes['symbols'] && !changes['symbols'].isFirstChange())
) { ) {
if (this.svg.empty && !this.svg.empty()) { if (this.svg.empty && !this.svg.empty()) {
if (changes['nodes']) {
this.onNodesChange(changes['nodes']);
}
if (changes['links']) {
this.onLinksChange(changes['links']);
}
if (changes['symbols']) { if (changes['symbols']) {
this.onSymbolsChange(changes['symbols']); this.onSymbolsChange(changes['symbols']);
} }
@ -135,10 +132,18 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => { this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
if (this.mapChangeDetectorRef.hasBeenDrawn) { if (this.mapChangeDetectorRef.hasBeenDrawn) {
this.reload(); this.redraw();
} }
}); });
this.nodeDraggedSub = this.nodesEventSource.dragged.subscribe((evt) => {
this.nodeDragged.emit(new DraggedDataEvent<Node>(this.mapNodeToNode.convert(evt.datum), evt.dx, evt.dy));
});
this.drawingDraggedSub = this.drawingsEventSource.dragged.subscribe((evt) => {
this.drawingDragged.emit(new DraggedDataEvent<Drawing>(this.mapDrawingToDrawing.convert(evt.datum), evt.dx, evt.dy));
});
this.mapListeners.onInit(this.svg); this.mapListeners.onInit(this.svg);
} }
@ -146,6 +151,8 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
this.graphLayout.disconnect(this.svg); this.graphLayout.disconnect(this.svg);
this.onChangesDetected.unsubscribe(); this.onChangesDetected.unsubscribe();
this.mapListeners.onDestroy(); this.mapListeners.onDestroy();
this.nodeDraggedSub.unsubscribe();
this.drawingDraggedSub.unsubscribe();
} }
public createGraph(domElement: HTMLElement) { public createGraph(domElement: HTMLElement) {
@ -161,7 +168,14 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
} }
protected linkCreated(evt) { protected linkCreated(evt) {
this.onLinkCreated.emit(evt); const linkCreated = new LinkCreated(
this.mapNodeToNode.convert(evt.sourceNode),
this.mapPortToPort.convert(evt.sourcePort),
this.mapNodeToNode.convert(evt.targetNode),
this.mapPortToPort.convert(evt.targetPort)
);
this.onLinkCreated.emit(linkCreated);
} }
private changeLayout() { private changeLayout() {
@ -169,53 +183,22 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
this.context.size = this.getSize(); this.context.size = this.getSize();
} }
this.graphLayout.setNodes(this.nodes);
this.graphLayout.setLinks(this.links);
this.graphLayout.setDrawings(this.drawings);
this.redraw(); this.redraw();
} }
private onLinksChange(change: SimpleChange) {
const nodes_by_id = {};
this.nodes.forEach((n: Node) => {
nodes_by_id[n.node_id] = n;
});
this.links.forEach((link: Link) => {
const source_id = link.nodes[0].node_id;
const target_id = link.nodes[1].node_id;
if (source_id in nodes_by_id) {
link.source = nodes_by_id[source_id];
}
if (target_id in nodes_by_id) {
link.target = nodes_by_id[target_id];
}
if (link.source && link.target) {
link.x = link.source.x + (link.target.x - link.source.x) * 0.5;
link.y = link.source.y + (link.target.y - link.source.y) * 0.5;
}
});
}
private onNodesChange(change: SimpleChange) {
this.onLinksChange(null);
}
private onSymbolsChange(change: SimpleChange) { private onSymbolsChange(change: SimpleChange) {
this.nodeWidget.setSymbols(this.symbols); this.graphDataManager.setSymbols(this.symbols);
} }
public redraw() { private redraw() {
this.graphDataManager.setNodes(this.nodes);
this.graphDataManager.setLinks(this.links);
this.graphDataManager.setDrawings(this.drawings);
this.graphLayout.draw(this.svg, this.context); this.graphLayout.draw(this.svg, this.context);
} }
public reload() {
this.onLinksChange(null);
this.redraw();
}
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
onResize(event) { onResize(event) {
this.changeLayout(); this.changeLayout();

View File

@ -1,8 +1,8 @@
import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output, ViewChild } 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 { MapNode } from '../../models/map/map-node';
import { Port } from "../../../models/port"; import { MapPort } from '../../models/map/map-port';
@Component({ @Component({
@ -18,7 +18,7 @@ export class NodeSelectInterfaceComponent implements OnInit {
protected topPosition; protected topPosition;
protected leftPosition; protected leftPosition;
public node: Node; public node: MapNode;
constructor( constructor(
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
@ -35,13 +35,13 @@ export class NodeSelectInterfaceComponent implements OnInit {
this.changeDetector.detectChanges(); this.changeDetector.detectChanges();
} }
public open(node: Node, top: number, left: number) { public open(node: MapNode, top: number, left: number) {
this.node = node; this.node = node;
this.setPosition(top, left); this.setPosition(top, left);
this.contextMenu.openMenu(); this.contextMenu.openMenu();
} }
public chooseInterface(port: Port) { public chooseInterface(port: MapPort) {
this.onChooseInterface.emit({ this.onChooseInterface.emit({
'node': this.node, 'node': this.node,
'port': port 'port': port

View File

@ -0,0 +1,3 @@
export interface Converter<F, T> {
convert(obj: F): T;
}

View File

@ -0,0 +1,24 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Drawing } from "../../models/drawing";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable()
export class DrawingToMapDrawingConverter implements Converter<Drawing, MapDrawing> {
constructor(
) {}
convert(drawing: Drawing) {
const mapDrawing = new MapDrawing();
mapDrawing.id = drawing.drawing_id;
mapDrawing.projectId = drawing.project_id;
mapDrawing.rotation = drawing.rotation;
mapDrawing.svg = drawing.svg;
mapDrawing.x = drawing.x;
mapDrawing.y = drawing.y;
mapDrawing.z = drawing.z;
return mapDrawing;
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Label } from "../../models/label";
import { MapLabel } from "../../models/map/map-label";
@Injectable()
export class LabelToMapLabelConverter implements Converter<Label, MapLabel> {
convert(label: Label) {
const mapLabel = new MapLabel();
mapLabel.rotation = label.rotation;
mapLabel.style = label.style;
mapLabel.text = label.text;
mapLabel.x = label.x;
mapLabel.y = label.y;
return mapLabel;
}
}

View File

@ -0,0 +1,23 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { LabelToMapLabelConverter } from "./label-to-map-label-converter";
import { LinkNode } from "../../../models/link-node";
import { MapLinkNode } from "../../models/map/map-link-node";
@Injectable()
export class LinkNodeToMapLinkNodeConverter implements Converter<LinkNode, MapLinkNode> {
constructor(
private labelToMapLabel: LabelToMapLabelConverter
) {}
convert(linkNode: LinkNode) {
const mapLinkNode = new MapLinkNode();
mapLinkNode.nodeId = linkNode.node_id;
mapLinkNode.adapterNumber = linkNode.adapter_number;
mapLinkNode.portNumber = linkNode.port_number;
mapLinkNode.label = this.labelToMapLabel.convert(linkNode.label);
return mapLinkNode;
}
}

View File

@ -0,0 +1,26 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { LinkNodeToMapLinkNodeConverter } from "./link-node-to-map-link-node-converter";
import { Link } from "../../../models/link";
import { MapLink } from "../../models/map/map-link";
@Injectable()
export class LinkToMapLinkConverter implements Converter<Link, MapLink> {
constructor(
private linkNodeToMapLinkNode: LinkNodeToMapLinkNodeConverter
) {}
convert(link: Link) {
const mapLink = new MapLink();
mapLink.id = link.link_id;
mapLink.captureFileName = link.capture_file_name;
mapLink.captureFilePath = link.capture_file_path;
mapLink.capturing = link.capturing;
mapLink.linkType = link.link_type;
mapLink.nodes = link.nodes.map((linkNode) => this.linkNodeToMapLinkNode.convert(linkNode));
mapLink.projectId = link.project_id;
return mapLink;
}
}

View File

@ -0,0 +1,24 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Drawing } from "../../models/drawing";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable()
export class MapDrawingToDrawingConverter implements Converter<MapDrawing, Drawing> {
constructor(
) {}
convert(mapDrawing: MapDrawing) {
const drawing = new Drawing();
drawing.drawing_id = mapDrawing.id;
drawing.project_id = mapDrawing.projectId;
drawing.rotation = mapDrawing.rotation;
drawing.svg = mapDrawing.svg;
drawing.x = mapDrawing.x;
drawing.y = mapDrawing.y;
drawing.z = mapDrawing.z;
return drawing;
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Label } from "../../models/label";
import { MapLabel } from "../../models/map/map-label";
@Injectable()
export class MapLabelToLabelConverter implements Converter<MapLabel, Label> {
convert(mapLabel: MapLabel) {
const label = new Label();
label.rotation = mapLabel.rotation;
label.style = mapLabel.style;
label.text = mapLabel.text;
label.x = mapLabel.x;
label.y = mapLabel.y;
return label;
}
}

View File

@ -0,0 +1,23 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { MapLinkNode } from "../../models/map/map-link-node";
import { MapLabelToLabelConverter } from "./map-label-to-label-converter";
import { LinkNode } from "../../../models/link-node";
@Injectable()
export class MapLinkNodeToLinkNodeConverter implements Converter<MapLinkNode, LinkNode> {
constructor(
private mapLabelToLabel: MapLabelToLabelConverter
) {}
convert(mapLinkNode: MapLinkNode) {
const linkNode = new LinkNode();
linkNode.node_id = mapLinkNode.nodeId;
linkNode.adapter_number = mapLinkNode.adapterNumber;
linkNode.port_number = mapLinkNode.portNumber;
linkNode.label = this.mapLabelToLabel.convert(mapLinkNode.label);
return linkNode;
}
}

View File

@ -0,0 +1,26 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { MapLinkNodeToLinkNodeConverter } from "./map-link-node-to-link-node-converter";
import { Link } from "../../../models/link";
import { MapLink } from "../../models/map/map-link";
@Injectable()
export class MapLinkToLinkConverter implements Converter<MapLink, Link> {
constructor(
private mapLinkNodeToMapLinkNode: MapLinkNodeToLinkNodeConverter
) {}
convert(mapLink: MapLink) {
const link = new Link();
link.link_id = mapLink.id;
link.capture_file_name = mapLink.captureFileName;
link.capture_file_path = mapLink.captureFilePath;
link.capturing = mapLink.capturing;
link.link_type = mapLink.linkType;
link.nodes = mapLink.nodes.map((mapLinkNode) => this.mapLinkNodeToMapLinkNode.convert(mapLinkNode));
link.project_id = mapLink.projectId;
return link;
}
}

View File

@ -0,0 +1,42 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { MapNode } from "../../models/map/map-node";
import { MapLabelToLabelConverter } from "./map-label-to-label-converter";
import { MapPortToPortConverter } from "./map-port-to-port-converter";
import { Node } from "../../models/node";
@Injectable()
export class MapNodeToNodeConverter implements Converter<MapNode, Node> {
constructor(
private mapLabelToLabel: MapLabelToLabelConverter,
private mapPortToPort: MapPortToPortConverter
) {}
convert(mapNode: MapNode) {
const node = new Node();
node.node_id = mapNode.id;
node.command_line = mapNode.commandLine;
node.compute_id = mapNode.computeId;
node.console = mapNode.console;
node.console_host = mapNode.consoleHost;
node.first_port_name = mapNode.firstPortName;
node.height = mapNode.height;
node.label = mapNode.label ? this.mapLabelToLabel.convert(mapNode.label) : undefined;
node.name = mapNode.name;
node.node_directory = mapNode.nodeDirectory;
node.node_type = mapNode.nodeType;
node.port_name_format = mapNode.portNameFormat;
node.port_segment_size = mapNode.portSegmentSize;
node.ports = mapNode.ports ? mapNode.ports.map((mapPort) => this.mapPortToPort.convert(mapPort)) : [];
node.project_id = mapNode.projectId;
node.status = mapNode.status;
node.symbol = mapNode.symbol;
node.width = mapNode.width;
node.x = mapNode.x;
node.y = mapNode.y;
node.z = mapNode.z;
return node;
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Port } from "../../../models/port";
import { MapPort } from "../../models/map/map-port";
@Injectable()
export class MapPortToPortConverter implements Converter<MapPort, Port> {
convert(mapPort: MapPort) {
const port = new Port();
port.adapter_number = mapPort.adapterNumber;
port.link_type = mapPort.linkType;
port.name = mapPort.name;
port.port_number = mapPort.portNumber;
port.short_name = mapPort.shortName;
return port;
}
}

View File

@ -0,0 +1,18 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { MapSymbol } from "../../models/map/map-symbol";
import { Symbol } from "../../../models/symbol";
@Injectable()
export class MapSymbolToSymbolConverter implements Converter<MapSymbol, Symbol> {
convert(mapSymbol: MapSymbol) {
const symbol = new Symbol();
symbol.symbol_id = mapSymbol.id;
symbol.builtin = mapSymbol.builtin;
symbol.filename = mapSymbol.filename;
symbol.raw = mapSymbol.raw;
return symbol;
}
}

View File

@ -0,0 +1,42 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { MapNode } from "../../models/map/map-node";
import { Node } from "../../models/node";
import { LabelToMapLabelConverter } from "./label-to-map-label-converter";
import { PortToMapPortConverter } from "./port-to-map-port-converter";
@Injectable()
export class NodeToMapNodeConverter implements Converter<Node, MapNode> {
constructor(
private labelToMapLabel: LabelToMapLabelConverter,
private portToMapPort: PortToMapPortConverter
) {}
convert(node: Node) {
const mapNode = new MapNode();
mapNode.id = node.node_id;
mapNode.commandLine = node.command_line;
mapNode.computeId = node.compute_id;
mapNode.console = node.console;
mapNode.consoleHost = node.console_host;
mapNode.firstPortName = node.first_port_name;
mapNode.height = node.height;
mapNode.label = this.labelToMapLabel.convert(node.label);
mapNode.name = node.name;
mapNode.nodeDirectory = node.node_directory;
mapNode.nodeType = node.node_type;
mapNode.portNameFormat = node.port_name_format;
mapNode.portSegmentSize = node.port_segment_size;
mapNode.ports = node.ports.map((port) => this.portToMapPort.convert(port));
mapNode.projectId = node.project_id;
mapNode.status = node.status;
mapNode.symbol = node.symbol;
mapNode.width = node.width;
mapNode.x = node.x;
mapNode.y = node.y;
mapNode.z = node.z;
return mapNode;
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Port } from "../../../models/port";
import { MapPort } from "../../models/map/map-port";
@Injectable()
export class PortToMapPortConverter implements Converter<Port, MapPort> {
convert(port: Port) {
const mapPort = new MapPort();
mapPort.adapterNumber = port.adapter_number;
mapPort.linkType = port.link_type;
mapPort.name = port.name;
mapPort.portNumber = port.port_number;
mapPort.shortName = port.short_name;
return mapPort;
}
}

View File

@ -0,0 +1,18 @@
import { Injectable } from "@angular/core";
import { Converter } from "../converter";
import { Symbol } from "../../../models/symbol";
import { MapSymbol } from "../../models/map/map-symbol";
@Injectable()
export class SymbolToMapSymbolConverter implements Converter<Symbol, MapSymbol> {
convert(symbol: Symbol) {
const mapSymbol = new MapSymbol();
mapSymbol.id = symbol.symbol_id;
mapSymbol.builtin = symbol.builtin;
mapSymbol.filename = symbol.filename;
mapSymbol.raw = symbol.raw;
return mapSymbol;
}
}

View File

@ -6,9 +6,9 @@ class Item {
class TestDataSource extends DataSource<Item> { class TestDataSource extends DataSource<Item> {
protected findIndex(item: Item) { protected getItemKey(item: Item) {
return this.data.findIndex((i: Item) => i.id === item.id); return item.id;
} };
} }

View File

@ -20,10 +20,30 @@ export abstract class DataSource<T> {
} }
public set(data: T[]) { public set(data: T[]) {
this.data = data; data.forEach((item) => {
const index = this.findIndex(item);
if (index >= 0) {
const updated = Object.assign(this.data[index], item);
this.data[index] = updated;
}
else {
this.data.push(item);
}
});
const toRemove = this.data.filter((item) => data.filter((i) => this.getItemKey(i) === this.getItemKey(item)).length === 0);
toRemove.forEach((item) => this.remove(item));
this.dataChange.next(this.data); this.dataChange.next(this.data);
} }
public get(key: string | number) {
const index = this.data.findIndex((i: T) => this.getItemKey(i) === key);
if (index >= 0) {
return this.data[index];
}
}
public update(item: T) { public update(item: T) {
const index = this.findIndex(item); const index = this.findIndex(item);
if (index >= 0) { if (index >= 0) {
@ -55,5 +75,9 @@ export abstract class DataSource<T> {
this.dataChange.next(this.data); this.dataChange.next(this.data);
} }
protected abstract findIndex(item: T): number; private findIndex(item: T) {
return this.data.findIndex((i: T) => this.getItemKey(i) === this.getItemKey(item));
}
protected abstract getItemKey(item: T): any;
} }

View File

@ -1,12 +1,12 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Drawing } from "../models/drawing";
import { DataSource } from "./datasource"; import { DataSource } from "./datasource";
import { Drawing } from "../models/drawing";
@Injectable() @Injectable()
export class DrawingsDataSource extends DataSource<Drawing> { export class DrawingsDataSource extends DataSource<Drawing> {
protected findIndex(drawing: Drawing) { protected getItemKey(drawing: Drawing) {
return this.data.findIndex((d: Drawing) => d.drawing_id === drawing.drawing_id); return drawing.drawing_id;
} }
} }

View File

@ -1,12 +1,12 @@
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()
export class LinksDataSource extends DataSource<Link> { export class LinksDataSource extends DataSource<Link> {
protected findIndex(link: Link) { protected getItemKey(link: Link) {
return this.data.findIndex((l: Link) => l.link_id === link.link_id); return link.link_id;
} }
} }

View File

@ -0,0 +1,28 @@
import { DataSource } from "./datasource";
import { MapNode } from "../models/map/map-node";
import { MapLink } from "../models/map/map-link";
import { MapDrawing } from "../models/map/map-drawing";
import { MapSymbol } from "../models/map/map-symbol";
import { Injectable } from "@angular/core";
export interface Indexed {
id: number | string;
}
export class MapDataSource<T extends Indexed> extends DataSource<T> {
protected getItemKey(item: Indexed) {
return item.id;
}
}
@Injectable()
export class MapNodesDataSource extends MapDataSource<MapNode> {}
@Injectable()
export class MapLinksDataSource extends MapDataSource<MapLink> {}
@Injectable()
export class MapDrawingsDataSource extends MapDataSource<MapDrawing> {}
@Injectable()
export class MapSymbolsDataSource extends MapDataSource<MapSymbol> {}

View File

@ -6,7 +6,7 @@ import { DataSource } from "./datasource";
@Injectable() @Injectable()
export class NodesDataSource extends DataSource<Node> { export class NodesDataSource extends DataSource<Node> {
protected findIndex(node: Node) { protected getItemKey(node: Node) {
return this.data.findIndex((n: Node) => n.node_id === node.node_id); return node.node_id;
} }
} }

View File

@ -6,7 +6,7 @@ import { Symbol } from "../../models/symbol";
@Injectable() @Injectable()
export class SymbolsDataSource extends DataSource<Symbol> { export class SymbolsDataSource extends DataSource<Symbol> {
protected findIndex(symbol: Symbol) { protected getItemKey(symbol: Symbol) {
return this.data.findIndex((s: Symbol) => s.symbol_id === symbol.symbol_id); return symbol.symbol_id;
} }
} }

View File

@ -43,14 +43,16 @@ export class Draggable<GElement extends DraggedElementBaseType, Datum> {
} }
private behaviour() { private behaviour() {
let startEvt;
return drag<GElement, Datum>() return drag<GElement, Datum>()
.on('start', (datum: Datum) => { .on('start', (datum: Datum) => {
const evt = new DraggableStart<Datum>(datum); startEvt = new DraggableStart<Datum>(datum);
evt.dx = event.dx; startEvt.dx = event.dx;
evt.dy = event.dy; startEvt.dy = event.dy;
evt.x = event.x; startEvt.x = event.x;
evt.y = event.y; startEvt.y = event.y;
this.start.emit(evt); this.start.emit(startEvt);
}) })
.on('drag', (datum: Datum) => { .on('drag', (datum: Datum) => {
const evt = new DraggableDrag<Datum>(datum); const evt = new DraggableDrag<Datum>(datum);
@ -62,8 +64,8 @@ export class Draggable<GElement extends DraggedElementBaseType, Datum> {
}) })
.on('end', (datum: Datum) => { .on('end', (datum: Datum) => {
const evt = new DraggableEnd<Datum>(datum); const evt = new DraggableEnd<Datum>(datum);
evt.dx = event.dx; evt.dx = event.x - startEvt.x;
evt.dy = event.dy; evt.dy = event.y - startEvt.y;
evt.x = event.x; evt.x = event.x;
evt.y = event.y; evt.y = event.y;
this.end.emit(evt); this.end.emit(evt);

View File

@ -1,9 +1,9 @@
import { Injectable, EventEmitter } from "@angular/core"; import { Injectable, EventEmitter } from "@angular/core";
import { Drawing } from "../models/drawing";
import { DraggedDataEvent } from "./event-source"; import { DraggedDataEvent } from "./event-source";
import { MapDrawing } from "../models/map/map-drawing";
@Injectable() @Injectable()
export class DrawingsEventSource { export class DrawingsEventSource {
public dragged = new EventEmitter<DraggedDataEvent<Drawing>>(); public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
} }

View File

@ -1,9 +1,9 @@
export class DataEventSource<T> { export class DataEventSource<T> {
constructor( constructor(
public datum: T public datum: T,
public dx: number,
public dy: number
) {} ) {}
} }
// class CreatedDataEvent<T> extends DataEventSource<T> {}
export class DraggedDataEvent<T> extends DataEventSource<T> {} export class DraggedDataEvent<T> extends DataEventSource<T> {}

View File

@ -1,12 +1,23 @@
import { Node } from "../models/node";
import { Port } from "../../models/port"; import { Port } from "../../models/port";
import { Node } from "../models/node";
import { MapNode } from "../models/map/map-node";
import { MapPort } from "../models/map/map-port";
export class LinkCreated { export class LinkCreated {
constructor( constructor(
public sourceNode: Node, public sourceNode: Node,
public sourcePort: Port, public sourcePort: Port,
public targetNode: Node, public targetNode: Node,
public targetPort: Port public targetPort: Port
){} ) {}
}
export class MapLinkCreated {
constructor(
public sourceNode: MapNode,
public sourcePort: MapPort,
public targetNode: MapNode,
public targetPort: MapPort
) {}
} }

View File

@ -1,9 +1,9 @@
import { Injectable, EventEmitter } from "@angular/core"; import { Injectable, EventEmitter } from "@angular/core";
import { Node } from "../models/node";
import { DraggedDataEvent } from "./event-source"; import { DraggedDataEvent } from "./event-source";
import { MapNode } from "../models/map/map-node";
@Injectable() @Injectable()
export class NodesEventSource { export class NodesEventSource {
public dragged = new EventEmitter<DraggedDataEvent<Node>>(); public dragged = new EventEmitter<DraggedDataEvent<MapNode>>();
} }

View File

@ -1,9 +1,9 @@
import { Node } from "../models/node"; import { MapNode } from "../models/map/map-node";
class NodeEvent { class NodeEvent {
constructor( constructor(
public event: any, public event: any,
public node: Node public node: MapNode
) {} ) {}
} }
@ -11,4 +11,4 @@ export class NodeDragging extends NodeEvent {}
export class NodeDragged extends NodeEvent {} export class NodeDragged extends NodeEvent {}
export class NodeClicked extends NodeEvent {} export class NodeClicked extends NodeEvent {}
export class NodeContextMenu extends NodeEvent {} export class NodeContextMenu extends NodeEvent {}

View File

@ -1,5 +1,5 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Link } from "../../models/link"; import { MapLink } from "../models/map/map-link";
@Injectable() @Injectable()
@ -30,11 +30,11 @@ export class MultiLinkCalculatorHelper {
}; };
} }
public assignDataToLinks(links: Link[]) { public assignDataToLinks(links: MapLink[]) {
const links_from_nodes = {}; const links_from_nodes = {};
links.forEach((l: Link, i: number) => { links.forEach((l: MapLink, i: number) => {
const sid = l.source.node_id; const sid = l.source.id;
const tid = l.target.node_id; const tid = l.target.id;
const key = (sid < tid ? sid + "," + tid : tid + "," + sid); const key = (sid < tid ? sid + "," + tid : tid + "," + sid);
let idx = 1; let idx = 1;
if (!(key in links_from_nodes)) { if (!(key in links_from_nodes)) {

View File

@ -0,0 +1,105 @@
import { Injectable } from "@angular/core";
import { NodesWidget } from "../widgets/nodes";
import { DraggableStart, DraggableDrag, DraggableEnd } from "../events/draggable";
import { Subscription } from "rxjs";
import { SelectionManager } from "../managers/selection-manager";
import { LinksWidget } from "../widgets/links";
import { NodesEventSource } from "../events/nodes-event-source";
import { DraggedDataEvent } from "../events/event-source";
import { MapNode } from "../models/map/map-node";
import { GraphDataManager } from "../managers/graph-data-manager";
import { DrawingsWidget } from "../widgets/drawings";
import { merge } from "rxjs";
import { MapDrawing } from "../models/map/map-drawing";
import { DrawingsEventSource } from "../events/drawings-event-source";
@Injectable()
export class DraggableListener {
private start: Subscription;
private drag: Subscription;
private end: Subscription;
constructor(
private nodesWidget: NodesWidget,
private drawingsWidget: DrawingsWidget,
private linksWidget: LinksWidget,
private selectionManager: SelectionManager,
private nodesEventSource: NodesEventSource,
private drawingsEventSource: DrawingsEventSource,
private graphDataManager: GraphDataManager
) {
}
public onInit(svg: any) {
this.start = merge(
this.nodesWidget.draggable.start,
this.drawingsWidget.draggable.start
).subscribe((evt: DraggableStart<any>) => {
const selected = this.selectionManager.getSelected();
if (evt.datum instanceof MapNode) {
if (selected.filter((item) => item instanceof MapNode && item.id === evt.datum.id).length === 0) {
this.selectionManager.setSelected([evt.datum]);
}
}
if (evt.datum instanceof MapDrawing) {
if (selected.filter((item) => item instanceof MapDrawing && item.id === evt.datum.id).length === 0) {
this.selectionManager.setSelected([evt.datum]);
}
}
});
this.drag = merge(
this.nodesWidget.draggable.drag,
this.drawingsWidget.draggable.drag
).subscribe((evt: DraggableDrag<any>) => {
const selected = this.selectionManager.getSelected();
// update nodes
selected.filter((item) => item instanceof MapNode).forEach((node: MapNode) => {
node.x += evt.dx;
node.y += evt.dy;
this.nodesWidget.redrawNode(svg, node);
const links = this.graphDataManager.getLinks().filter(
(link) => link.target.id === node.id || link.source.id === node.id);
links.forEach((link) => {
this.linksWidget.redrawLink(svg, link);
});
});
// update drawings
selected.filter((item) => item instanceof MapDrawing).forEach((drawing: MapDrawing) => {
drawing.x += evt.dx;
drawing.y += evt.dy;
this.drawingsWidget.redrawDrawing(svg, drawing);
});
});
this.end = merge(
this.nodesWidget.draggable.end,
this.drawingsWidget.draggable.end
).subscribe((evt: DraggableEnd<any>) => {
const selected = this.selectionManager.getSelected();
selected.filter((item) => item instanceof MapNode).forEach((item: MapNode) => {
this.nodesEventSource.dragged.emit(new DraggedDataEvent<MapNode>(item, evt.dx, evt.dy));
})
selected.filter((item) => item instanceof MapDrawing).forEach((item: MapDrawing) => {
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing>(item, evt.dx, evt.dy));
});
});
}
public onDestroy() {
this.start.unsubscribe();
this.drag.unsubscribe();
this.end.unsubscribe();
}
}

View File

@ -1,55 +0,0 @@
import { Injectable } from "@angular/core";
import { DrawingsWidget } from "../widgets/drawings";
import { DraggableStart } from "../events/draggable";
import { Drawing } from "../models/drawing";
import { Subscription } from "rxjs";
import { SelectionManager } from "../managers/selection-manager";
import { DrawingsEventSource } from "../events/drawings-event-source";
import { DraggedDataEvent } from "../events/event-source";
@Injectable()
export class DrawingsDraggableListener {
private start: Subscription;
private drag: Subscription;
private end: Subscription;
constructor(
private drawingsWidget: DrawingsWidget,
private selectionManager: SelectionManager,
private drawingsEventSource: DrawingsEventSource
) {
}
public onInit(svg: any) {
this.start = this.drawingsWidget.draggable.start.subscribe((evt: DraggableStart<Drawing>) => {
let drawings = this.selectionManager.getSelectedDrawings();
if (drawings.filter((n: Drawing) => n.drawing_id === evt.datum.drawing_id).length === 0) {
this.selectionManager.setSelectedDrawings([evt.datum]);
drawings = this.selectionManager.getSelectedDrawings();
}
});
this.drag = this.drawingsWidget.draggable.drag.subscribe((evt: DraggableStart<Drawing>) => {
let drawings = this.selectionManager.getSelectedDrawings();
drawings.forEach((drawing: Drawing) => {
drawing.x += evt.dx;
drawing.y += evt.dy;
this.drawingsWidget.redrawDrawing(svg, drawing);
});
});
this.end = this.drawingsWidget.draggable.end.subscribe((evt: DraggableStart<Drawing>) => {
let drawings = this.selectionManager.getSelectedDrawings();
drawings.forEach((drawing: Drawing) => {
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<Drawing>(drawing));
});
});
}
public onDestroy() {
this.start.unsubscribe();
this.drag.unsubscribe();
this.end.unsubscribe();
}
}

View File

@ -1,18 +1,21 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { MapListener } from "./map-listener"; import { MapListener } from "./map-listener";
import { DrawingsDraggableListener } from "./drawings-draggable-listener"; import { DraggableListener } from "./draggable-listener";
import { NodesDraggableListener } from "./nodes-draggable-listener"; import { SelectionUpdateListener } from "./selection-update-listener";
import { SelectionListener } from "./selection-listener";
@Injectable() @Injectable()
export class MapListeners { export class MapListeners {
private listeners: MapListener[] = []; private listeners: MapListener[] = [];
constructor( constructor(
private drawingsDraggableListener: DrawingsDraggableListener, private nodesDraggableListener: DraggableListener,
private nodesDraggableListener: NodesDraggableListener private selectionUpdateListener: SelectionUpdateListener,
private selectionListener: SelectionListener
) { ) {
this.listeners.push(this.drawingsDraggableListener);
this.listeners.push(this.nodesDraggableListener); this.listeners.push(this.nodesDraggableListener);
this.listeners.push(this.selectionUpdateListener);
this.listeners.push(this.selectionListener);
} }
public onInit(svg: any) { public onInit(svg: any) {

View File

@ -1,64 +0,0 @@
import { Injectable } from "@angular/core";
import { NodesWidget } from "../widgets/nodes";
import { DraggableStart } from "../events/draggable";
import { Node } from "../models/node";
import { Subscription } from "rxjs";
import { SelectionManager } from "../managers/selection-manager";
import { LinksWidget } from "../widgets/links";
import { GraphLayout } from "../widgets/graph-layout";
import { NodesEventSource } from "../events/nodes-event-source";
import { DraggedDataEvent } from "../events/event-source";
@Injectable()
export class NodesDraggableListener {
private start: Subscription;
private drag: Subscription;
private end: Subscription;
constructor(
private nodesWidget: NodesWidget,
private linksWidget: LinksWidget,
private selectionManager: SelectionManager,
private graphLayout: GraphLayout,
private nodesEventSource: NodesEventSource
) {
}
public onInit(svg: any) {
this.start = this.nodesWidget.draggable.start.subscribe((evt: DraggableStart<Node>) => {
let nodes = this.selectionManager.getSelectedNodes();
if (nodes.filter((n: Node) => n.node_id === evt.datum.node_id).length === 0) {
this.selectionManager.setSelectedNodes([evt.datum]);
nodes = this.selectionManager.getSelectedNodes();
}
});
this.drag = this.nodesWidget.draggable.drag.subscribe((evt: DraggableStart<Node>) => {
let nodes = this.selectionManager.getSelectedNodes();
nodes.forEach((node: Node) => {
node.x += evt.dx;
node.y += evt.dy;
this.nodesWidget.redrawNode(svg, node);
const links = this.graphLayout.getLinks().filter((link) => link.target.node_id === node.node_id || link.source.node_id === node.node_id);
links.forEach((link) => {
this.linksWidget.redrawLink(svg, link);
});
});
});
this.end = this.nodesWidget.draggable.end.subscribe((evt: DraggableStart<Node>) => {
let nodes = this.selectionManager.getSelectedNodes();
nodes.forEach((node: Node) => {
this.nodesEventSource.dragged.emit(new DraggedDataEvent<Node>(node));
});
});
}
public onDestroy() {
this.start.unsubscribe();
this.drag.unsubscribe();
this.end.unsubscribe();
}
}

View File

@ -0,0 +1,68 @@
import { Rectangle } from "../models/rectangle";
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
import { MapNode } from "../models/map/map-node";
import { MapLink } from "../models/map/map-link";
import { mock, instance, when } from "ts-mockito";
import { fakeAsync, tick } from "@angular/core/testing";
import { SelectionListener } from "./selection-listener";
import { SelectionManager } from "../managers/selection-manager";
import { GraphDataManager } from "../managers/graph-data-manager";
import { SelectionTool } from "../tools/selection-tool";
import { Context } from "../models/context";
describe('SelectionListener', () => {
let selectionListener: SelectionListener;
let manager: SelectionManager;
let selectionTool: SelectionTool;
beforeEach(() => {
const mockedGraphData = mock(GraphDataManager);
const node_1 = new MapNode();
node_1.id = "test1";
node_1.name = "Node 1";
node_1.x = 150;
node_1.y = 150;
const node_2 = new MapNode();
node_2.id = "test2";
node_2.name = "Node 2";
node_2.x = 300;
node_2.y = 300;
const link_1 = new MapLink();
link_1.id = "test1";
when(mockedGraphData.getNodes()).thenReturn([node_1, node_2]);
when(mockedGraphData.getLinks()).thenReturn([link_1]);
when(mockedGraphData.getDrawings()).thenReturn([]);
const graphData = instance(mockedGraphData);
const inRectangleHelper = new InRectangleHelper();
manager = new SelectionManager();
selectionTool = new SelectionTool(new Context());
selectionListener = new SelectionListener(selectionTool, graphData, inRectangleHelper, manager);
selectionListener.onInit(null);
});
afterEach(() => {
selectionListener.onDestroy();
})
it('node should be selected', fakeAsync(() => {
selectionTool.rectangleSelected.next(new Rectangle(100, 100, 100, 100));
tick();
expect(manager.getSelected().length).toEqual(1);
}));
it('node should be selected and deselected', fakeAsync(() => {
selectionTool.rectangleSelected.next(new Rectangle(100, 100, 100, 100));
tick();
selectionTool.rectangleSelected.next(new Rectangle(350, 350, 100, 100));
tick();
expect(manager.getSelected().length).toEqual(0);
}));
});

View File

@ -0,0 +1,45 @@
import { Injectable } from "@angular/core";
import { GraphDataManager } from "../managers/graph-data-manager";
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
import { SelectionTool } from "../tools/selection-tool";
import { Subscription } from "rxjs";
import { Rectangle } from "electron";
import { SelectionManager } from "../managers/selection-manager";
@Injectable()
export class SelectionListener {
private onSelection: Subscription;
constructor(
private selectionTool: SelectionTool,
private graphDataManager: GraphDataManager,
private inRectangleHelper: InRectangleHelper,
private selectionManager: SelectionManager
) {
}
public onInit(svg: any) {
this.onSelection = this.selectionTool.rectangleSelected.subscribe((rectangle: Rectangle) => {
const selectedNodes = this.graphDataManager.getNodes().filter((node) => {
return this.inRectangleHelper.inRectangle(rectangle, node.x, node.y)
});
const selectedLinks = this.graphDataManager.getLinks().filter((link) => {
return this.inRectangleHelper.inRectangle(rectangle, link.x, link.y)
});
const selectedDrawings = this.graphDataManager.getDrawings().filter((drawing) => {
return this.inRectangleHelper.inRectangle(rectangle, drawing.x, drawing.y)
});
const selected = [...selectedNodes, ...selectedLinks, ...selectedDrawings];
this.selectionManager.setSelected(selected);
});
}
public onDestroy() {
this.onSelection.unsubscribe();
}
}

View File

@ -0,0 +1,31 @@
import { Injectable } from "@angular/core";
import { Subscription } from "rxjs";
import { MapChangeDetectorRef } from "../services/map-change-detector-ref";
import { SelectionManager } from "../managers/selection-manager";
@Injectable()
export class SelectionUpdateListener {
private onSelected: Subscription;
private onUnselected: Subscription;
constructor(
private selectionManager: SelectionManager,
private mapChangeDetectorRef: MapChangeDetectorRef
) {
}
public onInit(svg: any) {
this.onSelected = this.selectionManager.selected.subscribe(() => {
this.mapChangeDetectorRef.detectChanges();
});
this.onUnselected = this.selectionManager.unselected.subscribe(() => {
this.mapChangeDetectorRef.detectChanges();
});
}
public onDestroy() {
this.onSelected.unsubscribe();
this.onUnselected.unsubscribe();
}
}

View File

@ -0,0 +1,38 @@
export class MockedGraphDataManager {
private nodes = [];
private links = [];
private drawings = [];
private symbols = [];
public setNodes(value) {
this.nodes = value;
}
public getNodes() {
return this.nodes;
}
public setLinks(value) {
this.links = value;
}
public getLinks() {
return this.links;
}
public setDrawings(value) {
this.drawings = value;
}
public getDrawings() {
return this.drawings;
}
public setSymbols(value) {
this.symbols = value;
}
public getSymbols() {
return this.symbols;
}
}

View File

@ -0,0 +1,103 @@
import { Injectable } from "@angular/core";
import { Node } from "../models/node";
import { NodeToMapNodeConverter } from "../converters/map/node-to-map-node-converter";
import { LinkToMapLinkConverter } from "../converters/map/link-to-map-link-converter";
import { DrawingToMapDrawingConverter } from "../converters/map/drawing-to-map-drawing-converter";
import { SymbolToMapSymbolConverter } from "../converters/map/symbol-to-map-symbol-converter";
import { MapNode } from "../models/map/map-node";
import { MapLink } from "../models/map/map-link";
import { Link } from "../../models/link";
import { Drawing } from "../models/drawing";
import { Symbol } from "../../models/symbol";
import { LayersManager } from "./layers-manager";
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource } from "../datasources/map-datasource";
@Injectable()
export class GraphDataManager {
constructor(
private mapNodesDataSource: MapNodesDataSource,
private mapLinksDataSource: MapLinksDataSource,
private mapDrawingsDataSource: MapDrawingsDataSource,
private mapSymbolsDataSource: MapSymbolsDataSource,
private nodeToMapNode: NodeToMapNodeConverter,
private linkToMapLink: LinkToMapLinkConverter,
private drawingToMapDrawing: DrawingToMapDrawingConverter,
private symbolToMapSymbol: SymbolToMapSymbolConverter,
private layersManager: LayersManager
) {}
public setNodes(nodes: Node[]) {
const mapNodes = nodes.map((n) => this.nodeToMapNode.convert(n));
this.mapNodesDataSource.set(mapNodes);
this.assignDataToLinks();
this.onDataUpdate();
}
public setLinks(links: Link[]) {
const mapLinks = links.map((l) => this.linkToMapLink.convert(l));
this.mapLinksDataSource.set(mapLinks);
this.assignDataToLinks();
this.onDataUpdate();
}
public setDrawings(drawings: Drawing[]) {
const mapDrawings = drawings.map((d) => this.drawingToMapDrawing.convert(d));
this.mapDrawingsDataSource.set(mapDrawings);
this.onDataUpdate();
}
public setSymbols(symbols: Symbol[]) {
const mapSymbols = symbols.map((s) => this.symbolToMapSymbol.convert(s));
this.mapSymbolsDataSource.set(mapSymbols);
}
public getNodes() {
return this.mapNodesDataSource.getItems();
}
public getLinks() {
return this.mapLinksDataSource.getItems();
}
public getDrawings() {
return this.mapDrawingsDataSource.getItems();
}
public getSymbols() {
return this.mapSymbolsDataSource.getItems();
}
private onDataUpdate() {
this.layersManager.clear();
this.layersManager.setNodes(this.getNodes());
this.layersManager.setLinks(this.getLinks());
this.layersManager.setDrawings(this.getDrawings());
}
private assignDataToLinks() {
const nodes_by_id = {};
this.getNodes().forEach((n: MapNode) => {
nodes_by_id[n.id] = n;
});
this.getLinks().forEach((link: MapLink) => {
const source_id = link.nodes[0].nodeId;
const target_id = link.nodes[1].nodeId;
if (source_id in nodes_by_id) {
link.source = nodes_by_id[source_id];
}
if (target_id in nodes_by_id) {
link.target = nodes_by_id[target_id];
}
if (link.source && link.target) {
link.x = link.source.x + (link.target.x - link.source.x) * 0.5;
link.y = link.source.y + (link.target.y - link.source.y) * 0.5;
}
});
}
}

View File

@ -1,7 +1,7 @@
import { LayersManager } from "./layers-manager"; import { LayersManager } from "./layers-manager";
import { Node } from "../models/node"; import { MapDrawing } from "../models/map/map-drawing";
import { Drawing } from "../models/drawing"; import { MapLink } from "../models/map/map-link";
import { Link } from "../../models/link"; import { MapNode } from "../models/map/map-node";
describe('LayersManager', () => { describe('LayersManager', () => {
@ -12,9 +12,9 @@ describe('LayersManager', () => {
}); });
it('nodes should be added', () => { it('nodes should be added', () => {
const node_1 = new Node(); const node_1 = new MapNode();
node_1.z = 1; node_1.z = 1;
const node_2 = new Node(); const node_2 = new MapNode();
node_2.z = 2; node_2.z = 2;
manager.setNodes([node_1, node_2]); manager.setNodes([node_1, node_2]);
@ -27,9 +27,9 @@ describe('LayersManager', () => {
}); });
it('drawings should be added', () => { it('drawings should be added', () => {
const drawing_1 = new Drawing(); const drawing_1 = new MapDrawing();
drawing_1.z = 1; drawing_1.z = 1;
const drawing_2 = new Drawing(); const drawing_2 = new MapDrawing();
drawing_2.z = 2; drawing_2.z = 2;
manager.setDrawings([drawing_1, drawing_2]); manager.setDrawings([drawing_1, drawing_2]);
@ -42,17 +42,17 @@ describe('LayersManager', () => {
}); });
it('links should be added', () => { it('links should be added', () => {
const node_1 = new Node(); const node_1 = new MapNode();
node_1.z = 1; node_1.z = 1;
const node_2 = new Node(); const node_2 = new MapNode();
node_2.z = 2; node_2.z = 2;
const link_1 = new Link(); const link_1 = new MapLink();
link_1.source = node_1; link_1.source = node_1;
link_1.target = node_1; link_1.target = node_1;
const link_2 = new Link(); const link_2 = new MapLink();
link_2.source = node_1; link_2.source = node_1;
link_2.target = node_1; link_2.target = node_1;
@ -64,9 +64,9 @@ describe('LayersManager', () => {
}); });
it('layers should be cleared', () => { it('layers should be cleared', () => {
const node_1 = new Node(); const node_1 = new MapNode();
node_1.z = 1; node_1.z = 1;
const node_2 = new Node(); const node_2 = new MapNode();
node_2.z = 2; node_2.z = 2;
manager.setNodes([node_1, node_2]); manager.setNodes([node_1, node_2]);

View File

@ -1,10 +1,10 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Layer } from "../models/layer"; import { Layer } from "../models/layer";
import { Node } from "../models/node";
import { Drawing } from "../models/drawing";
import { Link } from "../../models/link";
import { Dictionary } from "../models/types"; import { Dictionary } from "../models/types";
import { MapNode } from "../models/map/map-node";
import { MapDrawing } from "../models/map/map-drawing";
import { MapLink } from "../models/map/map-link";
@Injectable() @Injectable()
@ -23,26 +23,26 @@ export class LayersManager {
}); });
} }
public setNodes(nodes: Node[]) { public setNodes(nodes: MapNode[]) {
nodes nodes
.forEach((node: Node) => { .forEach((node: MapNode) => {
const layer = this.getLayerForKey(node.z.toString()); const layer = this.getLayerForKey(node.z.toString());
layer.nodes.push(node); layer.nodes.push(node);
}); });
} }
public setDrawings(drawings: Drawing[]) { public setDrawings(drawings: MapDrawing[]) {
drawings drawings
.forEach((drawing: Drawing) => { .forEach((drawing: MapDrawing) => {
const layer = this.getLayerForKey(drawing.z.toString()); const layer = this.getLayerForKey(drawing.z.toString());
layer.drawings.push(drawing); layer.drawings.push(drawing);
}); });
} }
public setLinks(links: Link[]) { public setLinks(links: MapLink[]) {
links links
.filter((link: Link) => link.source && link.target) .filter((link: MapLink) => link.source && link.target)
.forEach((link: Link) => { .forEach((link: MapLink) => {
const key = Math.min(link.source.z, link.target.z).toString(); const key = Math.min(link.source.z, link.target.z).toString();
const layer = this.getLayerForKey(key); const layer = this.getLayerForKey(key);
layer.links.push(link); layer.links.push(link);

View File

@ -1,95 +1,20 @@
import { Subject} from "rxjs"; import { MapNode } from "../models/map/map-node";
import { Node } from "../models/node";
import { Link } from "../../models/link";
import { Drawing } from "../models/drawing";
import { Rectangle } from "../models/rectangle";
import { SelectionManager } from "./selection-manager"; import { SelectionManager } from "./selection-manager";
import { NodesDataSource } from "../datasources/nodes-datasource";
import { LinksDataSource } from "../datasources/links-datasource";
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
import { DrawingsDataSource } from "../datasources/drawings-datasource";
describe('SelectionManager', () => { describe('SelectionManager', () => {
let manager: SelectionManager; let manager: SelectionManager;
let selectedRectangleSubject: Subject<Rectangle>;
let nodesDataSource: NodesDataSource;
beforeEach(() => { beforeEach(() => {
const linksDataSource = new LinksDataSource(); manager = new SelectionManager();
const drawingsDataSource = new DrawingsDataSource();
const inRectangleHelper = new InRectangleHelper();
selectedRectangleSubject = new Subject<Rectangle>();
nodesDataSource = new NodesDataSource();
manager = new SelectionManager(nodesDataSource, linksDataSource, drawingsDataSource, inRectangleHelper);
manager.subscribe(selectedRectangleSubject);
const node_1 = new Node();
node_1.node_id = "test1";
node_1.name = "Node 1";
node_1.x = 150;
node_1.y = 150;
nodesDataSource.add(node_1);
const node_2 = new Node();
node_2.node_id = "test2";
node_2.name = "Node 2";
node_2.x = 300;
node_2.y = 300;
nodesDataSource.add(node_2);
const link_1 = new Link();
link_1.link_id = "test1";
linksDataSource.add(link_1);
});
it('node should be selected', () => {
selectedRectangleSubject.next(new Rectangle(100, 100, 100, 100));
expect(nodesDataSource.getItems()[0].is_selected).toEqual(true);
expect(manager.getSelectedNodes().length).toEqual(1);
expect(manager.getSelectedLinks().length).toEqual(0);
});
it('node should be selected and deselected', () => {
selectedRectangleSubject.next(new Rectangle(100, 100, 100, 100));
selectedRectangleSubject.next(new Rectangle(350, 350, 100, 100));
expect(nodesDataSource.getItems()[0].is_selected).toEqual(false);
expect(manager.getSelectedNodes().length).toEqual(0);
expect(manager.getSelectedLinks().length).toEqual(0);
}); });
it('nodes should be manually selected', () => { it('nodes should be manually selected', () => {
const node = new Node(); const node = new MapNode();
node.node_id = "test1"; node.id = "test1";
manager.setSelectedNodes([node]); manager.setSelected([node]);
expect(manager.getSelectedNodes().length).toEqual(1); expect(manager.getSelected().length).toEqual(1);
}); });
it('links should be manually selected', () => {
const link = new Link();
link.link_id = "test1";
manager.setSelectedLinks([link]);
expect(manager.getSelectedLinks().length).toEqual(1);
});
it('items should be cleared', () => {
const link = new Link();
link.link_id = "test1";
const node = new Node();
node.node_id = "test1";
const drawing = new Drawing();
drawing.drawing_id = "test1";
manager.setSelectedLinks([link]);
manager.setSelectedNodes([node]);
manager.setSelectedDrawings([drawing]);
manager.clearSelection();
expect(manager.getSelectedLinks().length).toEqual(0);
expect(manager.getSelectedDrawings().length).toEqual(0);
expect(manager.getSelectedNodes().length).toEqual(0);
});
}); });

View File

@ -1,147 +1,55 @@
import { Injectable } from "@angular/core"; import { Injectable, EventEmitter } from "@angular/core";
import { Subject } from "rxjs"; import { Indexed } from "../datasources/map-datasource";
import { Subscription } from "rxjs";
import { NodesDataSource } from "../datasources/nodes-datasource";
import { LinksDataSource } from "../datasources/links-datasource";
import { Node } from "../models/node";
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
import { Rectangle } from "../models/rectangle";
import { Link} from "../../models/link";
import { DataSource } from "../datasources/datasource";
import { Drawing } from "../models/drawing";
import { InterfaceLabel } from "../models/interface-label";
import { DrawingsDataSource } from "../datasources/drawings-datasource";
export interface Selectable {
x: number;
y: number;
is_selected: boolean;
}
@Injectable() @Injectable()
export class SelectionManager { export class SelectionManager {
private selectedNodes: Node[] = []; private selection: {[id:string]: any} = {};
private selectedLinks: Link[] = [];
private selectedDrawings: Drawing[] = [];
private selectedInterfaceLabels: InterfaceLabel[] = [];
private subscription: Subscription; public selected = new EventEmitter<any[]>();
public unselected = new EventEmitter<any[]>();
constructor(private nodesDataSource: NodesDataSource, public setSelected(items: Indexed[]) {
private linksDataSource: LinksDataSource, const dictItems = this.convertToKeyDict(items);
private drawingsDataSource: DrawingsDataSource,
private inRectangleHelper: InRectangleHelper) {}
const selected = Object.keys(dictItems).filter((key) => {
return !this.isSelectedByKey(key);
}).map(key => dictItems[key]);
public subscribe(subject: Subject<Rectangle>) { const unselected = Object.keys(this.selection).filter((key) => {
this.subscription = subject.subscribe((rectangle: Rectangle) => { return !(key in dictItems);
this.onSelection(rectangle); }).map((key) => this.selection[key]);
this.selection = dictItems;
this.selected.emit(selected);
this.unselected.emit(unselected);
}
public getSelected(): Indexed[] {
return Object.keys(this.selection).map(key => this.selection[key]);
}
public isSelected(item): boolean {
const key = this.getKey(item);
return this.isSelectedByKey(key);
}
private isSelectedByKey(key): boolean {
return key in this.selection;
}
private getKey(item: Indexed): string {
const type = item.constructor.name;
return `${type}-${item.id}`;
}
private convertToKeyDict(items: Indexed[]) {
const dict = {};
items.forEach((item) => {
dict[this.getKey(item)] = item;
}); });
return this.subscription; return dict;
}
public onSelection(rectangle: Rectangle) {
this.selectedNodes = this.getSelectedItemsInRectangle<Node>(this.nodesDataSource, rectangle);
this.selectedLinks = this.getSelectedItemsInRectangle<Link>(this.linksDataSource, rectangle);
this.selectedDrawings = this.getSelectedItemsInRectangle<Drawing>(this.drawingsDataSource, rectangle);
// don't select interfaces for now
// this.selectedInterfaceLabels = this.getSelectedInterfaceLabelsInRectangle(rectangle);
}
public getSelectedNodes() {
return this.selectedNodes;
}
public getSelectedLinks() {
return this.selectedLinks;
}
public getSelectedDrawings() {
return this.selectedDrawings;
}
public setSelectedNodes(nodes: Node[]) {
this.selectedNodes = this.setSelectedItems<Node>(this.nodesDataSource, (node: Node) => {
return !!nodes.find((n: Node) => node.node_id === n.node_id);
});
}
public setSelectedLinks(links: Link[]) {
this.selectedLinks = this.setSelectedItems<Link>(this.linksDataSource, (link: Link) => {
return !!links.find((l: Link) => link.link_id === l.link_id);
});
}
public setSelectedDrawings(drawings: Drawing[]) {
this.selectedDrawings = this.setSelectedItems<Drawing>(this.drawingsDataSource, (drawing: Drawing) => {
return !!drawings.find((d: Drawing) => drawing.drawing_id === d.drawing_id);
});
}
public clearSelection() {
this.setSelectedDrawings([]);
this.setSelectedLinks([]);
this.setSelectedNodes([]);
}
private getSelectedItemsInRectangle<T extends Selectable>(dataSource: DataSource<T>, rectangle: Rectangle) {
return this.setSelectedItems<T>(dataSource, (item: T) => {
return this.inRectangleHelper.inRectangle(rectangle, item.x, item.y);
});
}
private getSelectedInterfaceLabelsInRectangle(rectangle: Rectangle) {
this.linksDataSource.getItems().forEach((link: Link) => {
if (!(link.source && link.target && link.nodes.length > 1)) {
return;
}
let updated = false;
let x = link.source.x + link.nodes[0].label.x;
let y = link.source.y + link.nodes[0].label.y;
if (this.inRectangleHelper.inRectangle(rectangle, x, y)) {
link.nodes[0].label.is_selected = true;
updated = true;
}
x = link.target.x + link.nodes[1].label.x;
y = link.target.y + link.nodes[1].label.y;
if (this.inRectangleHelper.inRectangle(rectangle, x, y)) {
link.nodes[1].label.is_selected = true;
updated = true;
}
if (updated) {
this.linksDataSource.update(link);
}
});
return [];
}
private setSelected<T extends Selectable>(item: T, isSelected: boolean, dataSource: DataSource<T>): boolean {
if (item.is_selected !== isSelected) {
item.is_selected = isSelected;
dataSource.update(item);
}
return item.is_selected;
}
private setSelectedItems<T extends Selectable>(dataSource: DataSource<T>, discriminator: (item: T) => boolean) {
const selected: T[] = [];
dataSource.getItems().forEach((item: T) => {
const isSelected = discriminator(item);
this.setSelected<T>(item, isSelected, dataSource);
if (isSelected) {
selected.push(item);
}
});
return selected;
} }
} }

View File

@ -1,7 +1,6 @@
import { Selectable } from "../managers/selection-manager";
import { DrawingElement } from "./drawings/drawing-element"; import { DrawingElement } from "./drawings/drawing-element";
export class Drawing implements Selectable { export class Drawing {
drawing_id: string; drawing_id: string;
project_id: string; project_id: string;
rotation: number; rotation: number;
@ -9,6 +8,5 @@ export class Drawing implements Selectable {
x: number; x: number;
y: number; y: number;
z: number; z: number;
is_selected = false;
element: DrawingElement; // @todo; move to context element: DrawingElement; // @todo; move to context
} }

View File

@ -1,8 +0,0 @@
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
}

View File

@ -1,6 +1,4 @@
import { Selectable } from "../managers/selection-manager"; export class InterfaceLabel {
export class InterfaceLabel implements Selectable {
constructor( constructor(
public link_id: string, public link_id: string,
public direction: string, public direction: string,
@ -9,6 +7,5 @@ export class InterfaceLabel implements Selectable {
public text: string, public text: string,
public style: string, public style: string,
public rotation = 0, public rotation = 0,
public is_selected = false
) {} ) {}
} }

View File

@ -1,10 +1,7 @@
import { Selectable } from "../managers/selection-manager"; export class Label {
export class Label implements Selectable {
rotation: number; rotation: number;
style: string; style: string;
text: string; text: string;
x: number; x: number;
y: number; y: number;
is_selected: boolean;
} }

View File

@ -1,13 +1,13 @@
import {Drawing} from "./drawing"; import { MapNode } from "./map/map-node";
import {Link} from "../../models/link"; import { MapDrawing } from "./map/map-drawing";
import {Node} from "./node"; import { MapLink } from "./map/map-link";
export class Layer { export class Layer {
constructor( constructor(
public index?: number, public index?: number,
public nodes: Node[] = [], public nodes: MapNode[] = [],
public drawings: Drawing[] = [], public drawings: MapDrawing[] = [],
public links: Link[] = [] public links: MapLink[] = []
) { ) {
} }
} }

View File

@ -0,0 +1,13 @@
import { DrawingElement } from "../drawings/drawing-element";
import { Indexed } from "../../datasources/map-datasource";
export class MapDrawing implements Indexed {
id: string;
projectId: string;
rotation: number;
svg: string;
x: number;
y: number;
z: number;
element: DrawingElement; // @todo; apply converters
}

View File

@ -0,0 +1,8 @@
export class MapLabel {
rotation: number;
style: string;
text: string;
x: number;
y: number;
isSelected: boolean;
}

View File

@ -0,0 +1,8 @@
import { MapLabel } from "./map-label";
export class MapLinkNode {
nodeId: string;
adapterNumber: number;
portNumber: number;
label: MapLabel;
}

View File

@ -0,0 +1,22 @@
import { MapLinkNode } from "./map-link-node";
import { MapNode } from "./map-node";
import { Indexed } from "../../datasources/map-datasource";
export class MapLink implements Indexed {
id: string;
captureFileName: string;
captureFilePath: string;
capturing: boolean;
linkType: string;
nodes: MapLinkNode[];
projectId: string;
distance: number; // this is not from server
length: number; // this is not from server
source: MapNode; // this is not from server
target: MapNode; // this is not from server
isSelected = false; // this is not from server
x: number; // this is not from server
y: number; // this is not from server
}

View File

@ -0,0 +1,29 @@
import { MapLabel } from "./map-label";
import { MapPort } from "./map-port";
import { Indexed } from "../../datasources/map-datasource";
export class MapNode implements Indexed {
id: string;
commandLine: string;
computeId: string;
console: number;
consoleHost: string;
consoleType: string;
firstPortName: string;
height: number;
label: MapLabel;
name: string;
nodeDirectory: string;
nodeType: string;
portNameFormat: string;
portSegmentSize: number;
ports: MapPort[];
projectId: string;
status: string;
symbol: string;
width: number;
x: number;
y: number;
z: number;
isSelected = false;
}

View File

@ -0,0 +1,7 @@
export class MapPort {
adapterNumber: number;
linkType: string;
name: string;
portNumber: number;
shortName: string;
}

View File

@ -0,0 +1,8 @@
import { Indexed } from "../../datasources/map-datasource";
export class MapSymbol implements Indexed {
id: string;
builtin: boolean;
filename: string;
raw: string;
}

View File

@ -1,9 +1,8 @@
import { Label } from "./label"; import { Label } from "./label";
import { Port } from "../../models/port"; import { Port } from "../../models/port";
import { Selectable } from "../managers/selection-manager";
export class Node implements Selectable { export class Node {
command_line: string; command_line: string;
compute_id: string; compute_id: string;
console: number; console: number;
@ -26,5 +25,4 @@ export class Node implements Selectable {
x: number; x: number;
y: number; y: number;
z: number; z: number;
is_selected = false;
} }

View File

@ -3,4 +3,4 @@ export class Point {
public x?: number, public x?: number,
public y?: number public y?: number
) {} ) {}
}; }

View File

@ -3,11 +3,11 @@ import { Injectable, EventEmitter } from "@angular/core";
@Injectable() @Injectable()
export class MapChangeDetectorRef { export class MapChangeDetectorRef {
public changesDetected = new EventEmitter<boolean>(); public changesDetected = new EventEmitter<boolean>();
public hasBeenDrawn = false; public hasBeenDrawn = false;
public detectChanges() { public detectChanges() {
this.changesDetected.emit(true); this.changesDetected.emit(true);
} }
} }

View File

@ -23,14 +23,6 @@ export class SelectionTool {
) {} ) {}
public setEnabled(enabled) { public setEnabled(enabled) {
if (this.enabled != enabled) {
if (enabled) {
this.needsActivate = true;
}
else {
this.needsDeactivate = true;
}
}
this.enabled = enabled; this.enabled = enabled;
} }
@ -80,13 +72,17 @@ export class SelectionTool {
.attr("visibility", "hidden"); .attr("visibility", "hidden");
} }
if(this.needsActivate) { const tool = canvas.select<SVGGElement>("g.selection-line-tool");
const status = tool.attr('status');
if(status !== 'activated' && this.enabled) {
this.activate(selection); this.activate(selection);
this.needsActivate = false; tool.attr('activated');
} }
if(this.needsDeactivate) { if(status !== 'deactivated' && !this.enabled) {
this.deactivate(selection); this.deactivate(selection);
this.needsDeactivate = false; tool.attr('deactivated');
} }
} }

View File

@ -2,13 +2,13 @@ import { Injectable } from "@angular/core";
import { Widget } from "./widget"; import { Widget } from "./widget";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { Drawing } from "../models/drawing";
import { DrawingShapeWidget } from "./drawings/drawing-shape-widget"; import { DrawingShapeWidget } from "./drawings/drawing-shape-widget";
import { TextDrawingWidget } from "./drawings/text-drawing"; import { TextDrawingWidget } from "./drawings/text-drawing";
import { ImageDrawingWidget } from "./drawings/image-drawing"; import { ImageDrawingWidget } from "./drawings/image-drawing";
import { RectDrawingWidget } from "./drawings/rect-drawing"; import { RectDrawingWidget } from "./drawings/rect-drawing";
import { LineDrawingWidget } from "./drawings/line-drawing"; import { LineDrawingWidget } from "./drawings/line-drawing";
import { EllipseDrawingWidget } from "./drawings/ellipse-drawing"; import { EllipseDrawingWidget } from "./drawings/ellipse-drawing";
import { MapDrawing } from "../models/map/map-drawing";
@Injectable() @Injectable()
@ -32,7 +32,7 @@ export class DrawingWidget implements Widget {
} }
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing_body = view.selectAll<SVGGElement, Drawing>("g.drawing_body") const drawing_body = view.selectAll<SVGGElement, MapDrawing>("g.drawing_body")
.data((l) => [l]); .data((l) => [l]);
const drawing_body_enter = drawing_body.enter() const drawing_body_enter = drawing_body.enter()
@ -40,7 +40,7 @@ export class DrawingWidget implements Widget {
.attr("class", "drawing_body"); .attr("class", "drawing_body");
const drawing_body_merge = drawing_body.merge(drawing_body_enter) const drawing_body_merge = drawing_body.merge(drawing_body_enter)
.attr('transform', (d: Drawing) => { .attr('transform', (d: MapDrawing) => {
return `translate(${d.x},${d.y}) rotate(${d.rotation})`; return `translate(${d.x},${d.y}) rotate(${d.rotation})`;
}); });

View File

@ -1,17 +1,17 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Widget } from "./widget"; import { Widget } from "./widget";
import { Drawing } from "../models/drawing";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { Layer } from "../models/layer"; import { Layer } from "../models/layer";
import { SvgToDrawingConverter } from "../helpers/svg-to-drawing-converter"; import { SvgToDrawingConverter } from "../helpers/svg-to-drawing-converter";
import { Draggable } from "../events/draggable"; import { Draggable } from "../events/draggable";
import { DrawingWidget } from "./drawing"; import { DrawingWidget } from "./drawing";
import { MapDrawing } from "../models/map/map-drawing";
@Injectable() @Injectable()
export class DrawingsWidget implements Widget { export class DrawingsWidget implements Widget {
public draggable = new Draggable<SVGGElement, Drawing>(); public draggable = new Draggable<SVGGElement, MapDrawing>();
public draggingEnabled = false; public draggingEnabled = false;
// public onContextMenu = new EventEmitter<NodeContextMenu>(); // public onContextMenu = new EventEmitter<NodeContextMenu>();
@ -26,15 +26,15 @@ export class DrawingsWidget implements Widget {
this.svgToDrawingConverter = new SvgToDrawingConverter(); this.svgToDrawingConverter = new SvgToDrawingConverter();
} }
public redrawDrawing(view: SVGSelection, drawing: Drawing) { public redrawDrawing(view: SVGSelection, drawing: MapDrawing) {
this.drawingWidget.draw(this.selectDrawing(view, drawing)); this.drawingWidget.draw(this.selectDrawing(view, drawing));
} }
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing = view const drawing = view
.selectAll<SVGGElement, Drawing>("g.drawing") .selectAll<SVGGElement, MapDrawing>("g.drawing")
.data((layer: Layer) => { .data((layer: Layer) => {
layer.drawings.forEach((d: Drawing) => { layer.drawings.forEach((d: MapDrawing) => {
try { try {
d.element = this.svgToDrawingConverter.convert(d.svg); d.element = this.svgToDrawingConverter.convert(d.svg);
} catch (error) { } catch (error) {
@ -42,14 +42,14 @@ export class DrawingsWidget implements Widget {
} }
}); });
return layer.drawings; return layer.drawings;
}, (l: Drawing) => { }, (l: MapDrawing) => {
return l.drawing_id; return l.id;
}); });
const drawing_enter = drawing.enter() const drawing_enter = drawing.enter()
.append<SVGGElement>('g') .append<SVGGElement>('g')
.attr('class', 'drawing') .attr('class', 'drawing')
.attr('drawing_id', (l: Drawing) => l.drawing_id) .attr('drawing_id', (l: MapDrawing) => l.id)
const merge = drawing.merge(drawing_enter); const merge = drawing.merge(drawing_enter);
@ -64,7 +64,7 @@ export class DrawingsWidget implements Widget {
} }
} }
private selectDrawing(view: SVGSelection, drawing: Drawing) { private selectDrawing(view: SVGSelection, drawing: MapDrawing) {
return view.selectAll<SVGGElement, Drawing>(`g.drawing[drawing_id="${drawing.drawing_id}"]`); return view.selectAll<SVGGElement, MapDrawing>(`g.drawing[drawing_id="${drawing.id}"]`);
} }
} }

View File

@ -1,19 +1,19 @@
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
import { Drawing } from "../../models/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"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
import { EllipseDrawingWidget } from "./ellipse-drawing";
describe('EllipseDrawingWidget', () => { describe('EllipseDrawingWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: EllipseDrawingWidget; let widget: EllipseDrawingWidget;
let drawing: Drawing; let drawing: MapDrawing;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
drawing = new Drawing(); drawing = new MapDrawing();
widget = new EllipseDrawingWidget(new QtDasharrayFixer()); widget = new EllipseDrawingWidget(new QtDasharrayFixer());
}); });
@ -34,7 +34,7 @@ describe('EllipseDrawingWidget', () => {
ellipse.ry = 40; ellipse.ry = 40;
drawing.element = ellipse; drawing.element = ellipse;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);

View File

@ -1,10 +1,10 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Drawing } from "../../models/drawing";
import { EllipseElement } from "../../models/drawings/ellipse-element"; import { EllipseElement } from "../../models/drawings/ellipse-element";
import { DrawingShapeWidget } from "./drawing-shape-widget"; import { DrawingShapeWidget } from "./drawing-shape-widget";
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable() @Injectable()
@ -17,7 +17,7 @@ export class EllipseDrawingWidget implements DrawingShapeWidget {
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing = view const drawing = view
.selectAll<SVGEllipseElement, EllipseElement>('ellipse.ellipse_element') .selectAll<SVGEllipseElement, EllipseElement>('ellipse.ellipse_element')
.data((d: Drawing) => { .data((d: MapDrawing) => {
return (d.element && d.element instanceof EllipseElement) ? [d.element] : []; return (d.element && d.element instanceof EllipseElement) ? [d.element] : [];
}); });

View File

@ -1,18 +1,18 @@
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
import { Drawing } from "../../models/drawing";
import { ImageDrawingWidget } from "./image-drawing"; import { ImageDrawingWidget } from "./image-drawing";
import { ImageElement } from "../../models/drawings/image-element"; import { ImageElement } from "../../models/drawings/image-element";
import { MapDrawing } from "../../models/map/map-drawing";
describe('ImageDrawingWidget', () => { describe('ImageDrawingWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: ImageDrawingWidget; let widget: ImageDrawingWidget;
let drawing: Drawing; let drawing: MapDrawing;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
drawing = new Drawing(); drawing = new MapDrawing();
widget = new ImageDrawingWidget(); widget = new ImageDrawingWidget();
}); });
@ -27,7 +27,7 @@ describe('ImageDrawingWidget', () => {
image.data = "data:image/svg+xml;base64,DATA"; image.data = "data:image/svg+xml;base64,DATA";
drawing.element = image; drawing.element = image;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);

View File

@ -1,9 +1,9 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Drawing } from "../../models/drawing";
import { ImageElement } from "../../models/drawings/image-element"; import { ImageElement } from "../../models/drawings/image-element";
import { DrawingShapeWidget } from "./drawing-shape-widget"; import { DrawingShapeWidget } from "./drawing-shape-widget";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable() @Injectable()
@ -11,7 +11,7 @@ export class ImageDrawingWidget implements DrawingShapeWidget {
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing = view const drawing = view
.selectAll<SVGImageElement, ImageElement>('image.image_element') .selectAll<SVGImageElement, ImageElement>('image.image_element')
.data((d: Drawing) => { .data((d: MapDrawing) => {
return (d.element && d.element instanceof ImageElement) ? [d.element] : []; return (d.element && d.element instanceof ImageElement) ? [d.element] : [];
}); });

View File

@ -1,19 +1,19 @@
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
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"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
describe('LineDrawingWidget', () => { describe('LineDrawingWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: LineDrawingWidget; let widget: LineDrawingWidget;
let drawing: Drawing; let drawing: MapDrawing;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
drawing = new Drawing(); drawing = new MapDrawing();
widget = new LineDrawingWidget(new QtDasharrayFixer()); widget = new LineDrawingWidget(new QtDasharrayFixer());
}); });
@ -32,7 +32,7 @@ describe('LineDrawingWidget', () => {
line.y2 = 40; line.y2 = 40;
drawing.element = line; drawing.element = line;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);

View File

@ -1,10 +1,10 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Drawing } from "../../models/drawing";
import { LineElement } from "../../models/drawings/line-element"; import { LineElement } from "../../models/drawings/line-element";
import { DrawingShapeWidget } from "./drawing-shape-widget"; import { DrawingShapeWidget } from "./drawing-shape-widget";
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable() @Injectable()
@ -17,7 +17,7 @@ export class LineDrawingWidget implements DrawingShapeWidget {
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing = view const drawing = view
.selectAll<SVGLineElement, LineElement>('line.line_element') .selectAll<SVGLineElement, LineElement>('line.line_element')
.data((d: Drawing) => { .data((d: MapDrawing) => {
return (d.element && d.element instanceof LineElement) ? [d.element] : []; return (d.element && d.element instanceof LineElement) ? [d.element] : [];
}); });

View File

@ -1,19 +1,19 @@
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
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"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
describe('RectDrawingWidget', () => { describe('RectDrawingWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: RectDrawingWidget; let widget: RectDrawingWidget;
let drawing: Drawing; let drawing: MapDrawing;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
drawing = new Drawing(); drawing = new MapDrawing();
widget = new RectDrawingWidget(new QtDasharrayFixer()); widget = new RectDrawingWidget(new QtDasharrayFixer());
}); });
@ -32,7 +32,7 @@ describe('RectDrawingWidget', () => {
rect.height = 200; rect.height = 200;
drawing.element = rect; drawing.element = rect;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);

View File

@ -1,10 +1,10 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Drawing } from "../../models/drawing";
import { RectElement } from "../../models/drawings/rect-element"; import { RectElement } from "../../models/drawings/rect-element";
import { DrawingShapeWidget } from "./drawing-shape-widget"; import { DrawingShapeWidget } from "./drawing-shape-widget";
import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer"; import { QtDasharrayFixer } from "../../helpers/qt-dasharray-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable() @Injectable()
@ -16,7 +16,7 @@ export class RectDrawingWidget implements DrawingShapeWidget {
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const drawing = view const drawing = view
.selectAll<SVGRectElement, RectElement>('rect.rect_element') .selectAll<SVGRectElement, RectElement>('rect.rect_element')
.data((d: Drawing) => { .data((d: MapDrawing) => {
return (d.element && d.element instanceof RectElement) ? [d.element] : []; return (d.element && d.element instanceof RectElement) ? [d.element] : [];
}); });

View File

@ -1,18 +1,18 @@
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
import { TextDrawingWidget } from "./text-drawing"; import { TextDrawingWidget } from "./text-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"; import { FontFixer } from "../../helpers/font-fixer";
import { MapDrawing } from "../../models/map/map-drawing";
describe('TextDrawingWidget', () => { describe('TextDrawingWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: TextDrawingWidget; let widget: TextDrawingWidget;
let drawing: Drawing; let drawing: MapDrawing;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
drawing = new Drawing(); drawing = new MapDrawing();
widget = new TextDrawingWidget(new FontFixer()); widget = new TextDrawingWidget(new FontFixer());
}); });
@ -32,7 +32,7 @@ describe('TextDrawingWidget', () => {
drawing.element = text; drawing.element = text;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);
@ -52,7 +52,7 @@ describe('TextDrawingWidget', () => {
text.text = 'THIS' + "\n" + 'IS TEXT'; text.text = 'THIS' + "\n" + 'IS TEXT';
drawing.element = text; drawing.element = text;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);
@ -75,7 +75,7 @@ describe('TextDrawingWidget', () => {
text.text = ' Text with whitespaces'; text.text = ' Text with whitespaces';
drawing.element = text; drawing.element = text;
const drawings = svg.canvas.selectAll<SVGGElement, Drawing>('g.drawing').data([drawing]); const drawings = svg.canvas.selectAll<SVGGElement, MapDrawing>('g.drawing').data([drawing]);
const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true); const drawings_enter = drawings.enter().append<SVGGElement>('g').classed('drawing', true);
const drawings_merge = drawings.merge(drawings_enter); const drawings_merge = drawings.merge(drawings_enter);

View File

@ -2,10 +2,10 @@ 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 { DrawingShapeWidget } from "./drawing-shape-widget"; import { DrawingShapeWidget } from "./drawing-shape-widget";
import { FontFixer } from "../../helpers/font-fixer"; import { FontFixer } from "../../helpers/font-fixer";
import { select } from "d3-selection"; import { select } from "d3-selection";
import { MapDrawing } from "../../models/map/map-drawing";
@Injectable() @Injectable()
@ -20,7 +20,7 @@ export class TextDrawingWidget implements DrawingShapeWidget {
const drawing = view const drawing = view
.selectAll<SVGTextElement, TextElement>('text.text_element') .selectAll<SVGTextElement, TextElement>('text.text_element')
.data((d: Drawing) => { .data((d: MapDrawing) => {
return (d.element && d.element instanceof TextElement) ? [d.element] : []; return (d.element && d.element instanceof TextElement) ? [d.element] : [];
}); });

View File

@ -1,10 +1,7 @@
import { Context } from "../models/context"; import { Context } from "../models/context";
import { Node } from "../models/node";
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";
import { Drawing } from "../models/drawing";
import { DrawingLineWidget } from "./drawing-line"; import { DrawingLineWidget } from "./drawing-line";
import { SelectionTool } from "../tools/selection-tool"; import { SelectionTool } from "../tools/selection-tool";
import { MovingTool } from "../tools/moving-tool"; import { MovingTool } from "../tools/moving-tool";
@ -15,35 +12,16 @@ import { Injectable } from "@angular/core";
@Injectable() @Injectable()
export class GraphLayout implements Widget { export class GraphLayout implements Widget {
private nodes: Node[] = [];
private links: Link[] = [];
private drawings: Drawing[] = [];
constructor( constructor(
private nodesWidget: NodesWidget, private nodesWidget: NodesWidget,
private drawingLineTool: DrawingLineWidget, private drawingLineTool: DrawingLineWidget,
private selectionTool: SelectionTool, private selectionTool: SelectionTool,
private movingTool: MovingTool, private movingTool: MovingTool,
private layersWidget: LayersWidget private layersWidget: LayersWidget,
private layersManager: LayersManager
) { ) {
} }
public setNodes(nodes: Node[]) {
this.nodes = nodes;
}
public setLinks(links: Link[]) {
this.links = links;
}
public getLinks() {
return this.links;
}
public setDrawings(drawings: Drawing[]) {
this.drawings = drawings;
}
public getNodesWidget() { public getNodesWidget() {
return this.nodesWidget; return this.nodesWidget;
} }
@ -84,13 +62,7 @@ export class GraphLayout implements Widget {
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`; return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
}); });
// @fix me this.layersWidget.draw(canvas, this.layersManager.getLayersList());
const layersManager = new LayersManager();
layersManager.setNodes(this.nodes);
layersManager.setDrawings(this.drawings);
layersManager.setLinks(this.links);
this.layersWidget.draw(canvas, layersManager.getLayersList());
this.drawingLineTool.draw(view, context); this.drawingLineTool.draw(view, context);
this.selectionTool.draw(view, context); this.selectionTool.draw(view, context);

View File

@ -1,61 +1,61 @@
import { Selection } from "d3-selection"; import { Selection } from "d3-selection";
import { TestSVGCanvas } from "../testing"; import { TestSVGCanvas } from "../testing";
import { Node } from "../models/node";
import { Link } from "../../models/link";
import { LinkNode } from "../../models/link-node";
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"; import { CssFixer } from "../helpers/css-fixer";
import { MapNode } from "../models/map/map-node";
import { MapLink } from "../models/map/map-link";
import { MapLinkNode } from "../models/map/map-link-node";
import { MapLabel } from "../models/map/map-label";
describe('InterfaceLabelsWidget', () => { describe('InterfaceLabelsWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: InterfaceLabelWidget; let widget: InterfaceLabelWidget;
let linksEnter: Selection<SVGGElement, Link, SVGGElement, any>; let linksEnter: Selection<SVGGElement, MapLink, SVGGElement, any>;
let links: Link[]; let links: MapLink[];
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
const node_1 = new Node(); const node_1 = new MapNode();
node_1.node_id = "1"; node_1.id = "1";
node_1.x = 100; node_1.x = 100;
node_1.y = 200; node_1.y = 200;
const node_2 = new Node(); const node_2 = new MapNode();
node_2.node_id = "2"; node_2.id = "2";
node_2.x = 300; node_2.x = 300;
node_2.y = 400; node_2.y = 400;
const link_node_1 = new LinkNode(); const link_node_1 = new MapLinkNode();
link_node_1.label = new Label(); link_node_1.label = new MapLabel();
link_node_1.label.rotation = 5; link_node_1.label.rotation = 5;
link_node_1.label.text = "Interface 1"; link_node_1.label.text = "Interface 1";
link_node_1.label.x = 10; link_node_1.label.x = 10;
link_node_1.label.y = 20; link_node_1.label.y = 20;
link_node_1.label.style = "font-size: 12px"; link_node_1.label.style = "font-size: 12px";
const link_node_2 = new LinkNode(); const link_node_2 = new MapLinkNode();
link_node_2.label = new Label(); link_node_2.label = new MapLabel();
link_node_2.label.rotation = 0; link_node_2.label.rotation = 0;
link_node_2.label.text = "Interface 2"; link_node_2.label.text = "Interface 2";
link_node_2.label.x = -30; link_node_2.label.x = -30;
link_node_2.label.y = -40; link_node_2.label.y = -40;
link_node_2.label.style = ""; link_node_2.label.style = "";
const link_1 = new Link(); const link_1 = new MapLink();
link_1.link_id = "link1"; link_1.id = "link1";
link_1.source = node_1; link_1.source = node_1;
link_1.target = node_2; link_1.target = node_2;
link_1.nodes = [link_node_1, link_node_2]; link_1.nodes = [link_node_1, link_node_2];
link_1.link_type = "ethernet"; link_1.linkType = "ethernet";
links = [link_1]; links = [link_1];
const linksSelection = svg.canvas const linksSelection = svg.canvas
.selectAll<SVGGElement, Link>('g.link') .selectAll<SVGGElement, MapLink>('g.link')
.data(links); .data(links);
linksEnter = linksSelection linksEnter = linksSelection
@ -110,14 +110,14 @@ describe('InterfaceLabelsWidget', () => {
expect(drew.nodes().length).toEqual(0); expect(drew.nodes().length).toEqual(0);
}); });
it('should draw interface label with class `selected` when selected', () => { // it('should draw interface label with class `selected` when selected', () => {
links[0].nodes[0].label.is_selected = true; // links[0].nodes[0].label.isSelected = true;
widget.draw(linksEnter); // widget.draw(linksEnter);
const drew = svg.canvas.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container'); // const drew = svg.canvas.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container');
const sourceInterface = drew.nodes()[0]; // const sourceInterface = drew.nodes()[0];
expect(sourceInterface.getAttribute('class')).toContain('selected'); // expect(sourceInterface.getAttribute('class')).toContain('selected');
}); // });
}); });

View File

@ -1,10 +1,10 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
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";
import { MapLink } from "../models/map/map-link";
@Injectable() @Injectable()
export class InterfaceLabelWidget { export class InterfaceLabelWidget {
@ -25,27 +25,25 @@ export class InterfaceLabelWidget {
const labels = selection const labels = selection
.selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container') .selectAll<SVGGElement, InterfaceLabel>('g.interface_label_container')
.data((l: Link) => { .data((l: MapLink) => {
const sourceInterface = new InterfaceLabel( const sourceInterface = new InterfaceLabel(
l.link_id, l.id,
'source', 'source',
Math.round(l.source.x + l.nodes[0].label.x), Math.round(l.source.x + l.nodes[0].label.x),
Math.round(l.source.y + l.nodes[0].label.y), Math.round(l.source.y + l.nodes[0].label.y),
l.nodes[0].label.text, l.nodes[0].label.text,
l.nodes[0].label.style, l.nodes[0].label.style,
l.nodes[0].label.rotation, l.nodes[0].label.rotation
l.nodes[0].label.is_selected
); );
const targetInterface = new InterfaceLabel( const targetInterface = new InterfaceLabel(
l.link_id, l.id,
'target', 'target',
Math.round( l.target.x + l.nodes[1].label.x), Math.round( l.target.x + l.nodes[1].label.x),
Math.round( l.target.y + l.nodes[1].label.y), Math.round( l.target.y + l.nodes[1].label.y),
l.nodes[1].label.text, l.nodes[1].label.text,
l.nodes[1].label.style, l.nodes[1].label.style,
l.nodes[1].label.rotation, l.nodes[1].label.rotation
l.nodes[1].label.is_selected
); );
if (this.enabled) { if (this.enabled) {
@ -81,7 +79,7 @@ export class InterfaceLabelWidget {
const y = l.y + bbox.height; const y = l.y + bbox.height;
return `translate(${x}, ${y}) rotate(${l.rotation}, ${x}, ${y})`; return `translate(${x}, ${y}) rotate(${l.rotation}, ${x}, ${y})`;
}) })
.classed('selected', (l: InterfaceLabel) => l.is_selected); .classed('selected', (l: InterfaceLabel) => false);
// update label // update label
merge merge
@ -95,7 +93,7 @@ export class InterfaceLabelWidget {
// update surrounding rect // update surrounding rect
merge merge
.select<SVGRectElement>('rect.interface_label_border') .select<SVGRectElement>('rect.interface_label_border')
.attr('visibility', (l: InterfaceLabel) => l.is_selected ? 'visible' : 'hidden') .attr('visibility', (l: InterfaceLabel) => false ? 'visible' : 'hidden')
.attr('stroke-dasharray', '3,3') .attr('stroke-dasharray', '3,3')
.attr('stroke-width', '0.5') .attr('stroke-width', '0.5')
.each(function (this: SVGRectElement, l: InterfaceLabel) { .each(function (this: SVGRectElement, l: InterfaceLabel) {
@ -106,7 +104,7 @@ export class InterfaceLabelWidget {
const border = InterfaceLabelWidget.SURROUNDING_TEXT_BORDER; const border = InterfaceLabelWidget.SURROUNDING_TEXT_BORDER;
current.attr('width', bbox.width + border*2); current.attr('width', bbox.width + border * 2);
current.attr('height', bbox.height + border); current.attr('height', bbox.height + border);
current.attr('x', - border); current.attr('x', - border);
current.attr('y', - bbox.height); current.attr('y', - bbox.height);

View File

@ -4,8 +4,8 @@ import { select } from "d3-selection";
import { Widget } from "./widget"; import { Widget } from "./widget";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { Link } from "../../models/link";
import { LinkStatus } from "../models/link-status"; import { LinkStatus } from "../models/link-status";
import { MapLink } from "../models/map/map-link";
@Injectable() @Injectable()
@ -13,8 +13,8 @@ export class InterfaceStatusWidget implements Widget {
constructor() {} constructor() {}
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
view.each(function (this: SVGGElement, l: Link) { view.each(function (this: SVGGElement, l: MapLink) {
const link_group = select<SVGGElement, Link>(this); const link_group = select<SVGGElement, MapLink>(this);
const link_path = link_group.select<SVGPathElement>('path'); const link_path = link_group.select<SVGPathElement>('path');
const start_point: SVGPoint = link_path.node().getPointAtLength(45); const start_point: SVGPoint = link_path.node().getPointAtLength(45);

View File

@ -2,12 +2,13 @@ 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 { SerialLinkWidget } from "./links/serial-link"; import { SerialLinkWidget } from "./links/serial-link";
import { EthernetLinkWidget } from "./links/ethernet-link"; import { EthernetLinkWidget } from "./links/ethernet-link";
import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper"; import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
import { InterfaceLabelWidget } from "./interface-label"; import { InterfaceLabelWidget } from "./interface-label";
import { InterfaceStatusWidget } from "./interface-status"; import { InterfaceStatusWidget } from "./interface-status";
import { MapLink } from "../models/map/map-link";
import { SelectionManager } from "../managers/selection-manager";
@Injectable() @Injectable()
@ -16,11 +17,12 @@ export class LinkWidget implements Widget {
constructor( constructor(
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper, private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
private interfaceLabelWidget: InterfaceLabelWidget, private interfaceLabelWidget: InterfaceLabelWidget,
private interfaceStatusWidget: InterfaceStatusWidget private interfaceStatusWidget: InterfaceStatusWidget,
private selectionManager: SelectionManager
) {} ) {}
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const link_body = view.selectAll<SVGGElement, Link>("g.link_body") const link_body = view.selectAll<SVGGElement, MapLink>("g.link_body")
.data((l) => [l]); .data((l) => [l]);
const link_body_enter = link_body.enter() const link_body_enter = link_body.enter()
@ -41,7 +43,7 @@ export class LinkWidget implements Widget {
link_body_merge link_body_merge
.select<SVGPathElement>('path') .select<SVGPathElement>('path')
.classed('selected', (l: Link) => l.is_selected); .classed('selected', (l: MapLink) => this.selectionManager.isSelected(l));
this.interfaceLabelWidget.draw(link_body_merge); this.interfaceLabelWidget.draw(link_body_merge);
this.interfaceStatusWidget.draw(link_body_merge); this.interfaceStatusWidget.draw(link_body_merge);

View File

@ -5,10 +5,10 @@ import { Selection } from "d3-selection";
import { TestSVGCanvas } from "../testing"; 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 { Link } from "../../models/link";
import { LinkWidget } from "./link"; import { LinkWidget } from "./link";
import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper"; import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
import { MapNode } from "../models/map/map-node";
import { MapLink } from "../models/map/map-link";
describe('LinksWidget', () => { describe('LinksWidget', () => {
@ -23,21 +23,21 @@ describe('LinksWidget', () => {
linkWidget = instance(mock(LinkWidget)); linkWidget = instance(mock(LinkWidget));
widget = new LinksWidget(new MultiLinkCalculatorHelper(), linkWidget); widget = new LinksWidget(new MultiLinkCalculatorHelper(), linkWidget);
const node_1 = new Node(); const node_1 = new MapNode();
node_1.node_id = "1"; node_1.id = "1";
node_1.x = 10; node_1.x = 10;
node_1.y = 10; node_1.y = 10;
const node_2 = new Node(); const node_2 = new MapNode();
node_2.node_id = "2"; node_2.id = "2";
node_2.x = 100; node_2.x = 100;
node_2.y = 100; node_2.y = 100;
const link_1 = new Link(); const link_1 = new MapLink();
link_1.link_id = "link1"; link_1.id = "link1";
link_1.source = node_1; link_1.source = node_1;
link_1.target = node_2; link_1.target = node_2;
link_1.link_type = "ethernet"; link_1.linkType = "ethernet";
layer = new Layer(); layer = new Layer();
layer.index = 1; layer.index = 1;
@ -67,7 +67,7 @@ describe('LinksWidget', () => {
it('should draw links', () => { it('should draw links', () => {
widget.draw(layersEnter); widget.draw(layersEnter);
const drew = svg.canvas.selectAll<SVGGElement, Link>('g.link'); const drew = svg.canvas.selectAll<SVGGElement, MapLink>('g.link');
const linkNode = drew.nodes()[0]; const linkNode = drew.nodes()[0];
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');

View File

@ -2,10 +2,10 @@ 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 { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper"; import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-helper";
import { Layer } from "../models/layer"; import { Layer } from "../models/layer";
import { LinkWidget } from "./link"; import { LinkWidget } from "./link";
import { MapLink } from "../models/map/map-link";
@Injectable() @Injectable()
export class LinksWidget implements Widget { export class LinksWidget implements Widget {
@ -15,32 +15,32 @@ export class LinksWidget implements Widget {
) { ) {
} }
public redrawLink(view: SVGSelection, link: Link) { public redrawLink(view: SVGSelection, link: MapLink) {
this.linkWidget.draw(this.selectLink(view, link)); this.linkWidget.draw(this.selectLink(view, link));
} }
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const link = view const link = view
.selectAll<SVGGElement, Link>("g.link") .selectAll<SVGGElement, MapLink>("g.link")
.data((layer: Layer) => { .data((layer: Layer) => {
if (layer.links) { if (layer.links) {
const layer_links = layer.links.filter((l: Link) => { const layer_links = layer.links.filter((l: MapLink) => {
return l.target && l.source; return l.target && l.source;
}); });
this.multiLinkCalculatorHelper.assignDataToLinks(layer_links); this.multiLinkCalculatorHelper.assignDataToLinks(layer_links);
return layer_links; return layer_links;
} }
return []; return [];
}, (l: Link) => { }, (l: MapLink) => {
return l.link_id; return l.id;
}); });
const link_enter = link.enter() const link_enter = link.enter()
.append<SVGGElement>('g') .append<SVGGElement>('g')
.attr('class', 'link') .attr('class', 'link')
.attr('link_id', (l: Link) => l.link_id) .attr('link_id', (l: MapLink) => l.id)
.attr('map-source', (l: Link) => l.source.node_id) .attr('map-source', (l: MapLink) => l.source.id)
.attr('map-target', (l: Link) => l.target.node_id); .attr('map-target', (l: MapLink) => l.target.id);
const merge = link.merge(link_enter); const merge = link.merge(link_enter);
@ -51,7 +51,7 @@ export class LinksWidget implements Widget {
.remove(); .remove();
} }
private selectLink(view: SVGSelection, link: Link) { private selectLink(view: SVGSelection, link: MapLink) {
return view.selectAll<SVGGElement, Link>(`g.link[link_id="${link.link_id}"]`); return view.selectAll<SVGGElement, MapLink>(`g.link[link_id="${link.id}"]`);
} }
} }

View File

@ -2,7 +2,7 @@ import { path } from "d3-path";
import { Widget } from "../widget"; import { Widget } from "../widget";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Link } from "../../../models/link"; import { MapLink } from "../../models/map/map-link";
class EthernetLinkPath { class EthernetLinkPath {
constructor( constructor(
@ -13,7 +13,7 @@ class EthernetLinkPath {
} }
export class EthernetLinkWidget implements Widget { export class EthernetLinkWidget implements Widget {
private linktoEthernetLink(link: Link) { private linktoEthernetLink(link: MapLink) {
return new EthernetLinkPath( return new EthernetLinkPath(
[link.source.x + link.source.width / 2., link.source.y + link.source.height / 2.], [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.] [link.target.x + link.target.width / 2., link.target.y + link.target.height / 2.]
@ -24,9 +24,9 @@ export class EthernetLinkWidget implements Widget {
const link = view const link = view
.selectAll<SVGPathElement, EthernetLinkPath>('path.ethernet_link') .selectAll<SVGPathElement, EthernetLinkPath>('path.ethernet_link')
.data((link) => { .data((l) => {
if(link.link_type === 'ethernet') { if (l.linkType === 'ethernet') {
return [this.linktoEthernetLink(link)]; return [this.linktoEthernetLink(l)];
} }
return []; return [];
}); });

View File

@ -2,7 +2,7 @@ import { path } from "d3-path";
import { Widget } from "../widget"; import { Widget } from "../widget";
import { SVGSelection } from "../../models/types"; import { SVGSelection } from "../../models/types";
import { Link } from "../../../models/link"; import { MapLink } from "../../models/map/map-link";
class SerialLinkPath { class SerialLinkPath {
@ -18,7 +18,7 @@ class SerialLinkPath {
export class SerialLinkWidget implements Widget { export class SerialLinkWidget implements Widget {
private linkToSerialLink(link: Link) { private linkToSerialLink(link: MapLink) {
const source = { const source = {
'x': link.source.x + link.source.width / 2, 'x': link.source.x + link.source.width / 2,
'y': link.source.y + link.source.height / 2 'y': link.source.y + link.source.height / 2
@ -60,9 +60,9 @@ export class SerialLinkWidget implements Widget {
const link = view const link = view
.selectAll<SVGPathElement, SerialLinkPath>('path.serial_link') .selectAll<SVGPathElement, SerialLinkPath>('path.serial_link')
.data((link) => { .data((l) => {
if(link.link_type === 'serial') { if (l.linkType === 'serial') {
return [this.linkToSerialLink(link)]; return [this.linkToSerialLink(l)];
} }
return []; return [];
}); });

View File

@ -1,19 +1,30 @@
import { TestSVGCanvas } from "../testing"; import { TestSVGCanvas } from "../testing";
import { Node } from "../models/node";
import { Label } from "../models/label";
import { CssFixer } from "../helpers/css-fixer"; import { CssFixer } from "../helpers/css-fixer";
import { FontFixer } from "../helpers/font-fixer"; import { FontFixer } from "../helpers/font-fixer";
import { NodeWidget } from "./node"; import { NodeWidget } from "./node";
import { MapNode } from "../models/map/map-node";
import { MapLabel } from "../models/map/map-label";
import { MockedGraphDataManager } from "../managers/graph-data-manager.spec";
import { GraphDataManager } from "../managers/graph-data-manager";
import { SelectionManager } from "../managers/selection-manager";
describe('NodesWidget', () => { describe('NodesWidget', () => {
let svg: TestSVGCanvas; let svg: TestSVGCanvas;
let widget: NodeWidget; let widget: NodeWidget;
let graphData: MockedGraphDataManager;
beforeEach(() => { beforeEach(() => {
svg = new TestSVGCanvas(); svg = new TestSVGCanvas();
widget = new NodeWidget(new CssFixer(), new FontFixer()); graphData = new MockedGraphDataManager();
// widget = new NodeWidget(
// new CssFixer(),
// new FontFixer(),
// <GraphDataManager> <unknown> graphData,
// new SelectionManager()
// );
}); });
afterEach(() => { afterEach(() => {
@ -21,9 +32,9 @@ describe('NodesWidget', () => {
}); });
describe('draggable behaviour', () => { describe('draggable behaviour', () => {
let node: Node; let node: MapNode;
const tryToDrag = () => { const tryToDrag = () => {
const drew = svg.canvas.selectAll<SVGGElement, Node>('g.node'); const drew = svg.canvas.selectAll<SVGGElement, MapNode>('g.node');
const drewNode = drew.nodes()[0]; const drewNode = drew.nodes()[0];
drewNode.dispatchEvent( drewNode.dispatchEvent(
@ -38,12 +49,12 @@ describe('NodesWidget', () => {
}; };
beforeEach(() => { beforeEach(() => {
node = new Node(); node = new MapNode();
node.x = 100; node.x = 100;
node.y = 200; node.y = 200;
node.width = 100; node.width = 100;
node.height = 100; node.height = 100;
node.label = new Label(); node.label = new MapLabel();
}); });
// it('should be draggable when enabled', () => { // it('should be draggable when enabled', () => {

View File

@ -2,13 +2,14 @@ import { Injectable, EventEmitter } from "@angular/core";
import { Widget } from "./widget"; import { Widget } from "./widget";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { Node } from "../models/node";
import { NodeContextMenu, NodeClicked, NodeDragged, NodeDragging } from "../events/nodes"; import { NodeContextMenu, NodeClicked, NodeDragged, NodeDragging } from "../events/nodes";
import { CssFixer } from "../helpers/css-fixer"; import { CssFixer } from "../helpers/css-fixer";
import { FontFixer } from "../helpers/font-fixer"; import { FontFixer } from "../helpers/font-fixer";
import { select, event } from "d3-selection"; import { select, event } from "d3-selection";
import { Symbol } from "../../models/symbol"; import { MapSymbol } from "../models/map/map-symbol";
import { D3DragEvent, drag } from "d3-drag"; import { MapNode } from "../models/map/map-node";
import { GraphDataManager } from "../managers/graph-data-manager";
import { SelectionManager } from "../managers/selection-manager";
@Injectable() @Injectable()
@ -20,21 +21,17 @@ export class NodeWidget implements Widget {
public onNodeDragged = new EventEmitter<NodeDragged>(); public onNodeDragged = new EventEmitter<NodeDragged>();
public onNodeDragging = new EventEmitter<NodeDragging>(); public onNodeDragging = new EventEmitter<NodeDragging>();
private symbols: Symbol[] = [];
constructor( constructor(
private cssFixer: CssFixer, private cssFixer: CssFixer,
private fontFixer: FontFixer, private fontFixer: FontFixer,
private graphDataManager: GraphDataManager,
private selectionManager: SelectionManager
) {} ) {}
public setSymbols(symbols: Symbol[]) {
this.symbols = symbols;
}
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const self = this; const self = this;
const node_body = view.selectAll<SVGGElement, Node>("g.node_body") const node_body = view.selectAll<SVGGElement, MapNode>("g.node_body")
.data((n) => [n]); .data((n) => [n]);
const node_body_enter = node_body.enter() const node_body_enter = node_body.enter()
@ -50,52 +47,52 @@ export class NodeWidget implements Widget {
.attr('class', 'label'); .attr('class', 'label');
const node_body_merge = node_body.merge(node_body_enter) const node_body_merge = node_body.merge(node_body_enter)
.classed('selected', (n: Node) => n.is_selected) .classed('selected', (n: MapNode) => this.selectionManager.isSelected(n))
.on("contextmenu", function (n: Node, i: number) { .on("contextmenu", function (n: MapNode, i: number) {
event.preventDefault(); event.preventDefault();
self.onContextMenu.emit(new NodeContextMenu(event, n)); self.onContextMenu.emit(new NodeContextMenu(event, n));
}) })
.on('click', (n: Node) => { .on('click', (n: MapNode) => {
this.onNodeClicked.emit(new NodeClicked(event, n)); this.onNodeClicked.emit(new NodeClicked(event, n));
}); });
// update image of node // update image of node
node_body_merge node_body_merge
.select<SVGImageElement>('image') .select<SVGImageElement>('image')
.attr('xnode:href', (n: Node) => { .attr('xnode:href', (n: MapNode) => {
const symbol = this.symbols.find((s: Symbol) => s.symbol_id === n.symbol); const symbol = this.graphDataManager.getSymbols().find((s: MapSymbol) => s.id === n.symbol);
if (symbol) { if (symbol) {
return 'data:image/svg+xml;base64,' + btoa(symbol.raw); return 'data:image/svg+xml;base64,' + btoa(symbol.raw);
} }
// @todo; we need to have default image // @todo; we need to have default image
return 'data:image/svg+xml;base64,none'; return 'data:image/svg+xml;base64,none';
}) })
.attr('width', (n: Node) => n.width) .attr('width', (n: MapNode) => n.width)
.attr('height', (n: Node) => n.height) .attr('height', (n: MapNode) => n.height)
.attr('x', (n: Node) => 0) .attr('x', (n: MapNode) => 0)
.attr('y', (n: Node) => 0) .attr('y', (n: MapNode) => 0)
.on('mouseover', function (this, n: Node) { .on('mouseover', function (this, n: MapNode) {
select(this).attr("class", "over"); select(this).attr("class", "over");
}) })
.on('mouseout', function (this, n: Node) { .on('mouseout', function (this, n: MapNode) {
select(this).attr("class", ""); select(this).attr("class", "");
}); });
node_body_merge node_body_merge
.attr('transform', (n: Node) => { .attr('transform', (n: MapNode) => {
return `translate(${n.x},${n.y})`; return `translate(${n.x},${n.y})`;
}); });
node_body_merge node_body_merge
.select<SVGTextElement>('text.label') .select<SVGTextElement>('text.label')
// .attr('y', (n: Node) => n.label.y - n.height / 2. + 10) // @todo: server computes y in auto way // .attr('y', (n: Node) => n.label.y - n.height / 2. + 10) // @todo: server computes y in auto way
.attr('style', (n: Node) => { .attr('style', (n: MapNode) => {
let styles = this.cssFixer.fix(n.label.style); let styles = this.cssFixer.fix(n.label.style);
styles = this.fontFixer.fixStyles(styles); styles = this.fontFixer.fixStyles(styles);
return styles; return styles;
}) })
.text((n: Node) => n.label.text) .text((n: MapNode) => n.label.text)
.attr('x', function (this: SVGTextElement, n: Node) { .attr('x', function (this: SVGTextElement, n: MapNode) {
if (n.label.x === null) { if (n.label.x === null) {
// center // center
const bbox = this.getBBox(); const bbox = this.getBBox();
@ -103,7 +100,7 @@ export class NodeWidget implements Widget {
} }
return n.label.x + NodeWidget.NODE_LABEL_MARGIN; return n.label.x + NodeWidget.NODE_LABEL_MARGIN;
}) })
.attr('y', function (this: SVGTextElement, n: Node) { .attr('y', function (this: SVGTextElement, n: MapNode) {
let bbox = this.getBBox(); let bbox = this.getBBox();
if (n.label.x === null) { if (n.label.x === null) {

View File

@ -1,18 +1,18 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Widget } from "./widget"; import { Widget } from "./widget";
import { Node } from "../models/node";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { Layer } from "../models/layer"; import { Layer } from "../models/layer";
import { NodeWidget } from "./node"; import { NodeWidget } from "./node";
import { Draggable } from "../events/draggable"; import { Draggable } from "../events/draggable";
import { MapNode } from "../models/map/map-node";
@Injectable() @Injectable()
export class NodesWidget implements Widget { export class NodesWidget implements Widget {
static NODE_LABEL_MARGIN = 3; static NODE_LABEL_MARGIN = 3;
public draggable = new Draggable<SVGGElement, Node>(); public draggable = new Draggable<SVGGElement, MapNode>();
public draggingEnabled = false; public draggingEnabled = false;
constructor( constructor(
@ -20,29 +20,29 @@ export class NodesWidget implements Widget {
) { ) {
} }
public redrawNode(view: SVGSelection, node: Node) { public redrawNode(view: SVGSelection, node: MapNode) {
this.nodeWidget.draw(this.selectNode(view, node)); this.nodeWidget.draw(this.selectNode(view, node));
} }
public draw(view: SVGSelection) { public draw(view: SVGSelection) {
const node = view const node = view
.selectAll<SVGGElement, Node>("g.node") .selectAll<SVGGElement, MapNode>("g.node")
.data((layer: Layer) => { .data((layer: Layer) => {
if (layer.nodes) { if (layer.nodes) {
return layer.nodes; return layer.nodes;
} }
return []; return [];
}, (n: Node) => { }, (n: MapNode) => {
return n.node_id; return n.id;
}); });
const node_enter = node.enter() const node_enter = node.enter()
.append<SVGGElement>('g') .append<SVGGElement>('g')
.attr('class', 'node') .attr('class', 'node')
.attr('node_id', (n: Node) => n.node_id) .attr('node_id', (n: MapNode) => n.id)
const merge = node.merge(node_enter); const merge = node.merge(node_enter);
this.nodeWidget.draw(merge); this.nodeWidget.draw(merge);
node node
@ -54,8 +54,8 @@ export class NodesWidget implements Widget {
} }
} }
private selectNode(view: SVGSelection, node: Node) { private selectNode(view: SVGSelection, node: MapNode) {
return view.selectAll<SVGGElement, Node>(`g.node[node_id="${node.node_id}"]`); return view.selectAll<SVGGElement, MapNode>(`g.node[node_id="${node.id}"]`);
} }
} }

View File

@ -21,6 +21,7 @@ export class MoveLayerUpActionComponent implements OnInit {
moveLayerUp() { moveLayerUp() {
this.node.z++; this.node.z++;
this.nodesDataSource.update(this.node);
this.nodeService this.nodeService
.update(this.server, this.node) .update(this.server, this.node)
.subscribe((node: Node) => {}); .subscribe((node: Node) => {});

View File

@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from "@angular/common/http/testing"; import { HttpClientTestingModule } from "@angular/common/http/testing";
import { inject } from "@angular/core/testing"; import { inject } from "@angular/core/testing";
import { mock, instance, capture, when } from "ts-mockito"; import { mock, instance, capture, when, anything } from "ts-mockito";
import { HotkeyModule, HotkeysService, Hotkey } from "angular2-hotkeys"; import { HotkeyModule, HotkeysService, Hotkey } from "angular2-hotkeys";
import { of } from "rxjs"; import { of } from "rxjs";
@ -12,13 +12,17 @@ import { NodeService } from "../../../services/node.service";
import { HttpServer } from "../../../services/http-server.service"; import { HttpServer } from "../../../services/http-server.service";
import { SelectionManager } from "../../../cartography/managers/selection-manager"; import { SelectionManager } from "../../../cartography/managers/selection-manager";
import { Server } from "../../../models/server"; import { Server } from "../../../models/server";
import { Node } from "../../../cartography/models/node";
import { Project } from "../../../models/project"; import { Project } from "../../../models/project";
import { ProjectService } from "../../../services/project.service"; import { ProjectService } from "../../../services/project.service";
import { MockedProjectService } from "../../../services/project.service.spec"; import { MockedProjectService } from "../../../services/project.service.spec";
import { SettingsService } from "../../../services/settings.service"; import { SettingsService } from "../../../services/settings.service";
import { MockedToasterService } from "../../../services/toaster.service.spec"; import { MockedToasterService } from "../../../services/toaster.service.spec";
import { mapTo } from "rxjs/internal/operators"; import { mapTo } from "rxjs/internal/operators";
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
import { MapNode } from '../../../cartography/models/map/map-node';
import { MapLabelToLabelConverter } from '../../../cartography/converters/map/map-label-to-label-converter';
import { MapPortToPortConverter } from '../../../cartography/converters/map/map-port-to-port-converter';
import { Node } from '../../../cartography/models/node';
describe('ProjectMapShortcutsComponent', () => { describe('ProjectMapShortcutsComponent', () => {
@ -27,8 +31,13 @@ describe('ProjectMapShortcutsComponent', () => {
let hotkeyServiceMock: HotkeysService; let hotkeyServiceMock: HotkeysService;
let hotkeyServiceInstanceMock: HotkeysService; let hotkeyServiceInstanceMock: HotkeysService;
let nodeServiceMock: NodeService; let nodeServiceMock: NodeService;
let node: MapNode;
beforeEach(async(() => { beforeEach(async(() => {
node = new MapNode();
const selectionManagerMock = mock(SelectionManager);
when(selectionManagerMock.getSelected()).thenReturn([node]);
nodeServiceMock = mock(NodeService); nodeServiceMock = mock(NodeService);
hotkeyServiceMock = mock(HotkeysService); hotkeyServiceMock = mock(HotkeysService);
hotkeyServiceInstanceMock = instance(hotkeyServiceMock); hotkeyServiceInstanceMock = instance(hotkeyServiceMock);
@ -43,7 +52,11 @@ describe('ProjectMapShortcutsComponent', () => {
{ provide: HotkeysService, useFactory: () => hotkeyServiceInstanceMock }, { provide: HotkeysService, useFactory: () => hotkeyServiceInstanceMock },
{ provide: ToasterService, useClass: MockedToasterService }, { provide: ToasterService, useClass: MockedToasterService },
{ provide: ProjectService, useClass: MockedProjectService }, { provide: ProjectService, useClass: MockedProjectService },
{ provide: SettingsService, useClass: SettingsService } { provide: SelectionManager, useValue: instance(selectionManagerMock)},
SettingsService,
MapNodeToNodeConverter,
MapLabelToLabelConverter,
MapPortToPortConverter
], ],
declarations: [ ProjectMapShortcutsComponent ] declarations: [ ProjectMapShortcutsComponent ]
}) })
@ -75,19 +88,14 @@ describe('ProjectMapShortcutsComponent', () => {
describe('onDeleteHandler', () => { describe('onDeleteHandler', () => {
beforeEach(() => { beforeEach(() => {
const selectionManagerMock = mock(SelectionManager);
const node = new Node();
node.node_id = "nodeid";
const server = new Server(); const server = new Server();
const project = new Project(); const project = new Project();
when(selectionManagerMock.getSelectedNodes()).thenReturn([node]); when(nodeServiceMock.delete(server, anything()))
when(nodeServiceMock.delete(server, node)) .thenReturn(of(new Node()).pipe(mapTo(null)));
.thenReturn(of(node).pipe(mapTo(null)));
component.project = project; component.project = project;
component.server = server; component.server = server;
component.selectionManager = instance(selectionManagerMock);
}); });
it('should handle delete', inject([ToasterService], (toaster: MockedToasterService) => { it('should handle delete', inject([ToasterService], (toaster: MockedToasterService) => {

View File

@ -7,6 +7,8 @@ import { Server } from '../../../models/server';
import { ToasterService } from '../../../services/toaster.service'; import { ToasterService } from '../../../services/toaster.service';
import { Project } from "../../../models/project"; import { Project } from "../../../models/project";
import { ProjectService } from "../../../services/project.service"; import { ProjectService } from "../../../services/project.service";
import { MapNode } from '../../../cartography/models/map/map-node';
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
@Component({ @Component({
@ -16,7 +18,6 @@ import { ProjectService } from "../../../services/project.service";
export class ProjectMapShortcutsComponent implements OnInit, OnDestroy { export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
@Input() project: Project; @Input() project: Project;
@Input() server: Server; @Input() server: Server;
@Input() selectionManager: SelectionManager;
private deleteHotkey: Hotkey; private deleteHotkey: Hotkey;
@ -24,7 +25,9 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
private hotkeysService: HotkeysService, private hotkeysService: HotkeysService,
private toaster: ToasterService, private toaster: ToasterService,
private nodesService: NodeService, private nodesService: NodeService,
private projectService: ProjectService private projectService: ProjectService,
private mapNodeToNode: MapNodeToNodeConverter,
private selectionManager: SelectionManager
) { } ) { }
ngOnInit() { ngOnInit() {
@ -37,14 +40,14 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy {
onDeleteHandler(event: KeyboardEvent): boolean { onDeleteHandler(event: KeyboardEvent): boolean {
if (!this.projectService.isReadOnly(this.project)) { if (!this.projectService.isReadOnly(this.project)) {
const selectedNodes = this.selectionManager.getSelectedNodes(); const selected = this.selectionManager.getSelected();
if (selectedNodes) {
selectedNodes.forEach((node) => { selected.filter((item) => item instanceof MapNode).forEach((item: MapNode) => {
this.nodesService.delete(this.server, node).subscribe(data => { const node = this.mapNodeToNode.convert(item);
this.toaster.success("Node has been deleted"); this.nodesService.delete(this.server, node).subscribe(data => {
}); this.toaster.success("Node has been deleted");
}); });
} });
} }
return false; return false;
} }

View File

@ -88,6 +88,5 @@
<app-project-map-shortcuts <app-project-map-shortcuts
*ngIf="project" *ngIf="project"
[project]="project" [project]="project"
[server]="server" [server]="server">
[selectionManager]="selectionManager">
</app-project-map-shortcuts> </app-project-map-shortcuts>

View File

@ -22,7 +22,6 @@ import { LinkService } from "../../services/link.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 { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource"; import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
import { ProgressService } from "../../common/progress/progress.service"; import { ProgressService } from "../../common/progress/progress.service";
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref'; import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
@ -31,6 +30,7 @@ import { LinkCreated } from '../../cartography/events/links';
import { NodeWidget } from '../../cartography/widgets/node'; import { NodeWidget } from '../../cartography/widgets/node';
import { DraggedDataEvent } from '../../cartography/events/event-source'; import { DraggedDataEvent } from '../../cartography/events/event-source';
import { DrawingService } from '../../services/drawing.service'; import { DrawingService } from '../../services/drawing.service';
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
@Component({ @Component({
@ -76,7 +76,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
private projectWebServiceHandler: ProjectWebServiceHandler, private projectWebServiceHandler: ProjectWebServiceHandler,
private mapChangeDetectorRef: MapChangeDetectorRef, private mapChangeDetectorRef: MapChangeDetectorRef,
private nodeWidget: NodeWidget, private nodeWidget: NodeWidget,
private selectionManager: SelectionManager, private mapNodeToNode: MapNodeToNodeConverter,
protected nodesDataSource: NodesDataSource, protected nodesDataSource: NodesDataSource,
protected linksDataSource: LinksDataSource, protected linksDataSource: LinksDataSource,
protected drawingsDataSource: DrawingsDataSource, protected drawingsDataSource: DrawingsDataSource,
@ -187,20 +187,15 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
setUpMapCallbacks(project: Project) { setUpMapCallbacks(project: Project) {
const onContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => { const onContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
const node = this.mapNodeToNode.convert(eventNode.node);
this.nodeContextMenu.open( this.nodeContextMenu.open(
eventNode.node, node,
eventNode.event.clientY, eventNode.event.clientY,
eventNode.event.clientX eventNode.event.clientX
); );
}); });
this.subscriptions.push(onContextMenu); this.subscriptions.push(onContextMenu);
this.subscriptions.push(
this.selectionManager.subscribe(
this.mapChild.graphLayout.getSelectionTool().rectangleSelected)
);
this.mapChangeDetectorRef.detectChanges(); this.mapChangeDetectorRef.detectChanges();
} }
@ -217,20 +212,26 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
} }
onNodeDragged(draggedEvent: DraggedDataEvent<Node>) { onNodeDragged(draggedEvent: DraggedDataEvent<Node>) {
this.nodesDataSource.update(draggedEvent.datum); const node = this.nodesDataSource.get(draggedEvent.datum.node_id);
node.x += draggedEvent.dx;
node.y += draggedEvent.dy;
this.nodeService this.nodeService
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y) .updatePosition(this.server, node, node.x, node.y)
.subscribe((node: Node) => { .subscribe((serverNode: Node) => {
this.nodesDataSource.update(node); this.nodesDataSource.update(serverNode);
}); });
} }
onDrawingDragged(draggedEvent: DraggedDataEvent<Drawing>) { onDrawingDragged(draggedEvent: DraggedDataEvent<Drawing>) {
this.drawingsDataSource.update(draggedEvent.datum); const drawing = this.drawingsDataSource.get(draggedEvent.datum.drawing_id);
drawing.x += draggedEvent.dx;
drawing.y += draggedEvent.dy;
this.drawingService this.drawingService
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y) .updatePosition(this.server, drawing, drawing.x, drawing.y)
.subscribe((drawing: Drawing) => { .subscribe((serverDrawing: Drawing) => {
this.drawingsDataSource.update(drawing); this.drawingsDataSource.update(serverDrawing);
}); });
} }
@ -238,8 +239,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.inReadOnlyMode = value; this.inReadOnlyMode = value;
if (value) { if (value) {
this.tools.selection = false; this.tools.selection = false;
} } else {
else {
this.tools.selection = true; this.tools.selection = true;
} }
} }

View File

@ -16,9 +16,11 @@ export class WebServiceMessage {
@Injectable() @Injectable()
export class ProjectWebServiceHandler { export class ProjectWebServiceHandler {
constructor(private nodesDataSource: NodesDataSource, constructor(
private linksDataSource: LinksDataSource, private nodesDataSource: NodesDataSource,
private drawingsDataSource: DrawingsDataSource) {} private linksDataSource: LinksDataSource,
private drawingsDataSource: DrawingsDataSource
) {}
public connect(ws: Subject<WebServiceMessage>) { public connect(ws: Subject<WebServiceMessage>) {
const subscription = ws.subscribe((message: WebServiceMessage) => { const subscription = ws.subscribe((message: WebServiceMessage) => {

View File

@ -1,9 +1,8 @@
import { Node } from "../cartography/models/node"; import { Node } from "../cartography/models/node";
import { LinkNode } from "./link-node"; import { LinkNode } from "./link-node";
import { Selectable } from "../cartography/managers/selection-manager";
export class Link implements Selectable { export class Link {
capture_file_name: string; capture_file_name: string;
capture_file_path: string; capture_file_path: string;
capturing: boolean; capturing: boolean;
@ -18,7 +17,6 @@ export class Link implements Selectable {
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; // this is not from server
x: number; // this is not from server x: number; // this is not from server
y: number; // this is not from server y: number; // this is not from server
} }

View File

@ -14,7 +14,6 @@ describe('DrawingService', () => {
let httpClient: HttpClient; let httpClient: HttpClient;
let httpTestingController: HttpTestingController; let httpTestingController: HttpTestingController;
let httpServer: HttpServer; let httpServer: HttpServer;
let service: DrawingService;
let server: Server; let server: Server;
beforeEach(() => { beforeEach(() => {
@ -32,7 +31,6 @@ describe('DrawingService', () => {
httpClient = TestBed.get(HttpClient); httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController); httpTestingController = TestBed.get(HttpTestingController);
httpServer = TestBed.get(HttpServer); httpServer = TestBed.get(HttpServer);
service = TestBed.get(DrawingService);
server = getTestServer(); server = getTestServer();
}); });

View File

@ -14,7 +14,6 @@ describe('LinkService', () => {
let httpClient: HttpClient; let httpClient: HttpClient;
let httpTestingController: HttpTestingController; let httpTestingController: HttpTestingController;
let httpServer: HttpServer; let httpServer: HttpServer;
let service: LinkService;
let server: Server; let server: Server;
beforeEach(() => { beforeEach(() => {
@ -32,7 +31,6 @@ describe('LinkService', () => {
httpClient = TestBed.get(HttpClient); httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController); httpTestingController = TestBed.get(HttpTestingController);
httpServer = TestBed.get(HttpServer); httpServer = TestBed.get(HttpServer);
service = TestBed.get(LinkService);
server = getTestServer(); server = getTestServer();
}); });

View File

@ -10,26 +10,27 @@ import {Port} from "../models/port";
export class LinkService { export class LinkService {
constructor(private httpServer: HttpServer) { } constructor(
private httpServer: HttpServer) {}
createLink( createLink(
server: Server, source_node: Node, source_port: Port, target_node: Node, target_port: Port) { server: Server, source_node: Node, source_port: Port, target_node: Node, target_port: Port) {
return this.httpServer return this.httpServer
.post( .post(
server, server,
`/projects/${source_node.project_id}/links`, `/projects/${source_node.project_id}/links`,
{"nodes": [ {"nodes": [
{ {
node_id: source_node.node_id, node_id: source_node.node_id,
port_number: source_port.port_number, port_number: source_port.port_number,
adapter_number: source_port.adapter_number adapter_number: source_port.adapter_number
}, },
{ {
node_id: target_node.node_id, node_id: target_node.node_id,
port_number: target_port.port_number, port_number: target_port.port_number,
adapter_number: target_port.adapter_number adapter_number: target_port.adapter_number
} }
]}); ]});
} }
} }

View File

@ -12,42 +12,43 @@ import { SettingsService } from "./settings.service";
@Injectable() @Injectable()
export class ProjectService { export class ProjectService {
constructor(private httpServer: HttpServer, constructor(
private settingsService: SettingsService) { } private httpServer: HttpServer,
private settingsService: SettingsService) {}
get(server: Server, project_id: string) { get(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.get<Project>(server, `/projects/${project_id}`); .get<Project>(server, `/projects/${project_id}`);
} }
open(server: Server, project_id: string) { open(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.post<Project>(server, `/projects/${project_id}/open`, {}); .post<Project>(server, `/projects/${project_id}/open`, {});
} }
close(server: Server, project_id: string) { close(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.post<Project>(server, `/projects/${project_id}/close`, {}); .post<Project>(server, `/projects/${project_id}/close`, {});
} }
list(server: Server) { list(server: Server) {
return this.httpServer return this.httpServer
.get<Project[]>(server, '/projects'); .get<Project[]>(server, '/projects');
} }
nodes(server: Server, project_id: string) { nodes(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.get<Node[]>(server, `/projects/${project_id}/nodes`); .get<Node[]>(server, `/projects/${project_id}/nodes`);
} }
links(server: Server, project_id: string) { links(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.get<Link[]>(server, `/projects/${project_id}/links`); .get<Link[]>(server, `/projects/${project_id}/links`);
} }
drawings(server: Server, project_id: string) { drawings(server: Server, project_id: string) {
return this.httpServer return this.httpServer
.get<Drawing[]>(server, `/projects/${project_id}/drawings`); .get<Drawing[]>(server, `/projects/${project_id}/drawings`);
} }
add(server: Server, project_name: string, project_id: string): Observable<any>{ add(server: Server, project_name: string, project_id: string): Observable<any>{
@ -57,7 +58,7 @@ export class ProjectService {
delete(server: Server, project_id: string): Observable<any> { delete(server: Server, project_id: string): Observable<any> {
return this.httpServer return this.httpServer
.delete(server, `/projects/${project_id}`); .delete(server, `/projects/${project_id}`);
} }
notificationsPath(server: Server, project_id: string): string { notificationsPath(server: Server, project_id: string): string {

View File

@ -14,7 +14,6 @@ describe('SymbolService', () => {
let httpClient: HttpClient; let httpClient: HttpClient;
let httpTestingController: HttpTestingController; let httpTestingController: HttpTestingController;
let httpServer: HttpServer; let httpServer: HttpServer;
let service: SymbolService;
let server: Server; let server: Server;
beforeEach(() => { beforeEach(() => {
@ -32,7 +31,6 @@ describe('SymbolService', () => {
httpClient = TestBed.get(HttpClient); httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController); httpTestingController = TestBed.get(HttpTestingController);
httpServer = TestBed.get(HttpServer); httpServer = TestBed.get(HttpServer);
service = TestBed.get(SymbolService);
server = getTestServer(); server = getTestServer();
}); });