gns3-web-ui/src/app/components/project-map/project-map.component.ts
2019-11-27 07:18:41 -08:00

851 lines
33 KiB
TypeScript

import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation, ElementRef } from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { Observable, Subject, Subscription, from } from 'rxjs';
import { webSocket } from 'rxjs/webSocket';
import { map, mergeMap } from 'rxjs/operators';
import { Project } from '../../models/project';
import { Node } from '../../cartography/models/node';
import { Link } from '../../models/link';
import { ServerService } from '../../services/server.service';
import { ProjectService } from '../../services/project.service';
import { Server } from '../../models/server';
import { Drawing } from '../../cartography/models/drawing';
import { ContextMenuComponent } from './context-menu/context-menu.component';
import { Template } from '../../models/template';
import { NodeService } from '../../services/node.service';
import { Symbol } from '../../models/symbol';
import { NodesDataSource } from '../../cartography/datasources/nodes-datasource';
import { LinksDataSource } from '../../cartography/datasources/links-datasource';
import { ProjectWebServiceHandler } from '../../handlers/project-web-service-handler';
import { DrawingsDataSource } from '../../cartography/datasources/drawings-datasource';
import { ProgressService } from '../../common/progress/progress.service';
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
import { NodeContextMenu } from '../../cartography/events/nodes';
import { NodeWidget } from '../../cartography/widgets/node';
import { DrawingsWidget } from '../../cartography/widgets/drawings';
import { DrawingService } from '../../services/drawing.service';
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
import { SettingsService, Settings } from '../../services/settings.service';
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
import { ToolsService } from '../../services/tools.service';
import { DrawingContextMenu, LinkContextMenu, LabelContextMenu, InterfaceLabelContextMenu } from '../../cartography/events/event-source';
import { MapDrawingToDrawingConverter } from '../../cartography/converters/map/map-drawing-to-drawing-converter';
import { SelectionManager } from '../../cartography/managers/selection-manager';
import { SelectionTool } from '../../cartography/tools/selection-tool';
import { MapDrawing } from '../../cartography/models/map/map-drawing';
import { MapLabel } from '../../cartography/models/map/map-label';
import { Label } from '../../cartography/models/label';
import { MapNode } from '../../cartography/models/map/map-node';
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProject.service';
import { MapLink } from '../../cartography/models/map/map-link';
import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter';
import { MovingEventSource } from '../../cartography/events/moving-event-source';
import { log } from 'util';
import { LinkWidget } from '../../cartography/widgets/link';
import { MapScaleService } from '../../services/mapScale.service';
import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer';
import { InterfaceLabelWidget } from '../../cartography/widgets/interface-label';
import { LabelWidget } from '../../cartography/widgets/label';
import { MapLinkNodeToLinkNodeConverter } from '../../cartography/converters/map/map-link-node-to-link-node-converter';
import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component';
import { ToasterService } from '../../services/toaster.service';
import { ImportProjectDialogComponent } from '../projects/import-project-dialog/import-project-dialog.component';
import { MatDialog, MatBottomSheet, mixinColor } from '@angular/material';
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
import { SaveProjectDialogComponent } from '../projects/save-project-dialog/save-project-dialog.component';
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource, Indexed } from '../../cartography/datasources/map-datasource';
import { MapSettingsService } from '../../services/mapsettings.service';
import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component';
import { EthernetLinkWidget } from '../../cartography/widgets/links/ethernet-link';
import { SerialLinkWidget } from '../../cartography/widgets/links/serial-link';
import { NavigationDialogComponent } from '../projects/navigation-dialog/navigation-dialog.component';
import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component';
import { NodeAddedEvent } from '../template/template-list-dialog/template-list-dialog.component';
import { NotificationService } from '../../services/notification.service';
import { ThemeService } from '../../services/theme.service';
@Component({
selector: 'app-project-map',
encapsulation: ViewEncapsulation.None,
templateUrl: './project-map.component.html',
styleUrls: ['./project-map.component.scss']
})
export class ProjectMapComponent implements OnInit, OnDestroy {
public nodes: Node[] = [];
public links: Link[] = [];
public drawings: Drawing[] = [];
public symbols: Symbol[] = [];
public project: Project;
public server: Server;
public projectws: WebSocket;
public ws: WebSocket;
public isProjectMapMenuVisible: boolean = false;
public isConsoleVisible: boolean = true;
public isTopologySummaryVisible: boolean = true;
public isInterfaceLabelVisible: boolean = false;
public notificationsVisibility: boolean = false;
public layersVisibility: boolean = false;
public gridVisibility: boolean = false;
tools = {
selection: true,
moving: false,
draw_link: false,
text_editing: true
};
protected settings: Settings;
private inReadOnlyMode = false;
private scrollX: number = 0;
private scrollY: number = 0;
private scrollEnabled: boolean = false;
public isLightThemeEnabled: boolean = false;
@ViewChild(ContextMenuComponent, {static: false}) contextMenu: ContextMenuComponent;
@ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent;
@ViewChild(ProjectMapMenuComponent, {static: false}) projectMapMenuComponent: ProjectMapMenuComponent;
private subscriptions: Subscription[] = [];
constructor(
private route: ActivatedRoute,
private serverService: ServerService,
private projectService: ProjectService,
private nodeService: NodeService,
public drawingService: DrawingService,
private progressService: ProgressService,
private projectWebServiceHandler: ProjectWebServiceHandler,
private mapChangeDetectorRef: MapChangeDetectorRef,
private nodeWidget: NodeWidget,
private drawingsWidget: DrawingsWidget,
private linkWidget: LinkWidget,
private labelWidget: LabelWidget,
private interfaceLabelWidget: InterfaceLabelWidget,
private mapNodeToNode: MapNodeToNodeConverter,
private mapDrawingToDrawing: MapDrawingToDrawingConverter,
private mapLabelToLabel: MapLabelToLabelConverter,
private mapLinkToLink: MapLinkToLinkConverter,
private mapLinkNodeToLinkNode: MapLinkNodeToLinkNodeConverter,
private nodesDataSource: NodesDataSource,
private linksDataSource: LinksDataSource,
private drawingsDataSource: DrawingsDataSource,
private settingsService: SettingsService,
private toolsService: ToolsService,
private selectionManager: SelectionManager,
private selectionTool: SelectionTool,
private recentlyOpenedProjectService: RecentlyOpenedProjectService,
private movingEventSource: MovingEventSource,
private mapScaleService: MapScaleService,
private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer,
private toasterService: ToasterService,
private dialog: MatDialog,
private router: Router,
private mapNodesDataSource: MapNodesDataSource,
private mapLinksDataSource: MapLinksDataSource,
private mapDrawingsDataSource: MapDrawingsDataSource,
private mapSymbolsDataSource: MapSymbolsDataSource,
private mapSettingsService: MapSettingsService,
private ethernetLinkWidget: EthernetLinkWidget,
private serialLinkWidget: SerialLinkWidget,
private bottomSheet: MatBottomSheet,
private notificationService: NotificationService,
private themeService: ThemeService
) {}
ngOnInit() {
this.themeService.getActualTheme() === 'light' ? this.isLightThemeEnabled = true : this.isLightThemeEnabled = false;
this.settings = this.settingsService.getAll();
this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible;
this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible;
this.progressService.activate();
const routeSub = this.route.paramMap.subscribe((paramMap: ParamMap) => {
const server_id = parseInt(paramMap.get('server_id'), 10);
from(this.serverService.get(server_id))
.pipe(
mergeMap((server: Server) => {
this.server = server;
return this.projectService.get(server, paramMap.get('project_id')).pipe(
map(project => {
return project;
})
);
}),
mergeMap((project: Project) => {
this.project = project;
if (this.mapSettingsService.interfaceLabels.has(project.project_id)) {
this.isInterfaceLabelVisible = this.mapSettingsService.interfaceLabels.get(project.project_id);
} else {
this.isInterfaceLabelVisible = this.project.show_interface_labels;
}
this.recentlyOpenedProjectService.setServerId(this.server.id.toString());
this.recentlyOpenedProjectService.setProjectId(this.project.project_id);
if (this.project.status === 'opened') {
return new Observable<Project>(observer => {
observer.next(this.project);
});
} else {
return this.projectService.open(this.server, this.project.project_id);
}
})
)
.subscribe(
(project: Project) => {
this.onProjectLoad(project);
},
error => {
this.progressService.setError(error);
},
() => {
this.progressService.deactivate();
}
);
});
this.subscriptions.push(routeSub);
this.subscriptions.push(
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
if (this.scrollEnabled) this.centerCanvas();
})
);
this.subscriptions.push(
this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => {
this.drawings = drawings;
this.mapChangeDetectorRef.detectChanges();
})
);
this.subscriptions.push(
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
if (!this.server) return;
nodes.forEach((node: Node) => {
node.symbol_url = `http://${this.server.host}:${this.server.port}/v2/symbols/${node.symbol}/raw`;
});
this.nodes = nodes;
this.mapChangeDetectorRef.detectChanges();
})
);
this.subscriptions.push(
this.linksDataSource.changes.subscribe((links: Link[]) => {
this.links = links;
this.mapChangeDetectorRef.detectChanges();
})
);
this.subscriptions.push(this.projectWebServiceHandler.errorNotificationEmitter.subscribe((message) => {
this.showMessage({
type: 'error',
message: message
});
}));
this.subscriptions.push(this.projectWebServiceHandler.warningNotificationEmitter.subscribe((message) => {
this.showMessage({
type: 'warning',
message: message
});
}));
this.notificationsVisibility = localStorage.getItem('notificationsVisibility') === 'true' ? true : false;
this.layersVisibility = localStorage.getItem('layersVisibility') === 'true' ? true : false;
this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? true : false;
this.addKeyboardListeners();
}
addKeyboardListeners() {
Mousetrap.bind('ctrl++', (event: Event) => {
event.preventDefault();
this.zoomIn();
});
Mousetrap.bind('ctrl+-', (event: Event) => {
event.preventDefault();
this.zoomOut();
});
Mousetrap.bind('ctrl+0', (event: Event) => {
event.preventDefault();
this.resetZoom();
});
Mousetrap.bind('ctrl+a', (event: Event) => {
event.preventDefault();
let allNodes: Indexed[] = this.mapNodesDataSource.getItems();
let allDrawings: Indexed[] = this.mapDrawingsDataSource.getItems();
let allLinks: Indexed[] = this.mapLinksDataSource.getItems();
let allSymbols: Indexed[] = this.mapSymbolsDataSource.getItems();
this.selectionManager.setSelected(allNodes.concat(allDrawings).concat(allLinks).concat(allSymbols));
});
Mousetrap.bind('ctrl+shift+a', (event: Event) => {
event.preventDefault();
this.selectionManager.setSelected([]);
});
Mousetrap.bind('ctrl+shift+s', (event: Event) => {
event.preventDefault();
this.router.navigate(['/server', this.server.id, 'preferences']);
});
}
onProjectLoad(project: Project) {
this.readonly = this.projectService.isReadOnly(project);
const subscription = this.projectService
.nodes(this.server, project.project_id)
.pipe(
mergeMap((nodes: Node[]) => {
this.nodesDataSource.set(nodes);
return this.projectService.links(this.server, project.project_id);
}),
mergeMap((links: Link[]) => {
this.linksDataSource.set(links);
return this.projectService.drawings(this.server, project.project_id);
})
)
.subscribe((drawings: Drawing[]) => {
this.drawingsDataSource.set(drawings);
this.setUpMapCallbacks();
this.setUpProjectWS(project);
this.progressService.deactivate();
});
this.subscriptions.push(subscription);
}
setUpProjectWS(project: Project) {
this.projectws = new WebSocket(this.projectService.notificationsPath(this.server, project.project_id));
this.projectws.onmessage = (event: MessageEvent) => {
this.projectWebServiceHandler.handleMessage(JSON.parse(event.data));
};
this.projectws.onerror = (event: MessageEvent) => {
this.toasterService.error('Connection to host lost.');
};
}
setUpWS() {
this.ws = new WebSocket(this.notificationService.notificationsPath(this.server));
}
setUpMapCallbacks() {
if (!this.readonly) {
this.toolsService.selectionToolActivation(true);
}
const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
});
const onEthernetLinkContextMenu = this.ethernetLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
});
const onSerialLinkContextMenu = this.serialLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
const link = this.mapLinkToLink.convert(eventLink.link);
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
});
const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
const node = this.mapNodeToNode.convert(eventNode.node);
this.contextMenu.openMenuForNode(node, eventNode.event.pageY, eventNode.event.pageX);
});
const onDrawingContextMenu = this.drawingsWidget.onContextMenu.subscribe((eventDrawing: DrawingContextMenu) => {
const drawing = this.mapDrawingToDrawing.convert(eventDrawing.drawing);
this.contextMenu.openMenuForDrawing(drawing, eventDrawing.event.pageY, eventDrawing.event.pageX);
});
const onLabelContextMenu = this.labelWidget.onContextMenu.subscribe((eventLabel: LabelContextMenu) => {
const label = this.mapLabelToLabel.convert(eventLabel.label);
const node = this.nodes.find(n => n.node_id === eventLabel.label.nodeId);
this.contextMenu.openMenuForLabel(label, node, eventLabel.event.pageY, eventLabel.event.pageX);
});
const onInterfaceLabelContextMenu = this.interfaceLabelWidget.onContextMenu.subscribe((eventInterfaceLabel: InterfaceLabelContextMenu) => {
const linkNode = this.mapLinkNodeToLinkNode.convert(eventInterfaceLabel.interfaceLabel);
const link = this.links.find(l => l.link_id === eventInterfaceLabel.interfaceLabel.linkId);
this.contextMenu.openMenuForInterfaceLabel(linkNode, link, eventInterfaceLabel.event.pageY, eventInterfaceLabel.event.pageX);
});
const onContextMenu = this.selectionTool.contextMenuOpened.subscribe((event) => {
const selectedItems = this.selectionManager.getSelected();
if (selectedItems.length < 2 || !(event instanceof MouseEvent)) return;
let drawings: Drawing[] = [];
let nodes: Node[] = [];
let labels: Label[] = [];
let links: Link[] = [];
selectedItems.forEach((elem) => {
if (elem instanceof MapDrawing) {
drawings.push(this.mapDrawingToDrawing.convert(elem));
} else if (elem instanceof MapNode) {
nodes.push(this.mapNodeToNode.convert(elem));
} else if (elem instanceof MapLabel) {
labels.push(this.mapLabelToLabel.convert(elem));
} else if (elem instanceof MapLink) {
links.push(this.mapLinkToLink.convert(elem))
}
});
this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX);
});
this.subscriptions.push(onLinkContextMenu);
this.subscriptions.push(onEthernetLinkContextMenu);
this.subscriptions.push(onSerialLinkContextMenu);
this.subscriptions.push(onNodeContextMenu);
this.subscriptions.push(onDrawingContextMenu);
this.subscriptions.push(onContextMenu);
this.subscriptions.push(onLabelContextMenu);
this.subscriptions.push(onInterfaceLabelContextMenu);
this.mapChangeDetectorRef.detectChanges();
}
onNodeCreation(nodeAddedEvent: NodeAddedEvent) {
if(!nodeAddedEvent) {
return;
}
this.nodeService.createFromTemplate(this.server, this.project, nodeAddedEvent.template, nodeAddedEvent.x, nodeAddedEvent.y, 'local').subscribe((node: Node) => {
if (nodeAddedEvent.name !== nodeAddedEvent.template.name) {
node.name = nodeAddedEvent.name;
this.nodeService.updateNode(this.server, node).subscribe(()=>{});
}
this.projectService.nodes(this.server, this.project.project_id).subscribe((nodes: Node[]) => {
nodes.filter((node) => node.label.style === null).forEach((node) => {
const fixedNode = this.nodeCreatedLabelStylesFixer.fix(node);
this.nodeService.updateLabel(this.server, node, fixedNode.label).subscribe();
});
this.nodesDataSource.set(nodes);
nodeAddedEvent.numberOfNodes--;
if (nodeAddedEvent.numberOfNodes > 0) {
nodeAddedEvent.x = nodeAddedEvent.x + 50 < this.project.scene_width/2 ? nodeAddedEvent.x + 50 : nodeAddedEvent.x;
nodeAddedEvent.y = nodeAddedEvent.y + 50 < this.project.scene_height/2 ? nodeAddedEvent.y + 50 : nodeAddedEvent.y;
this.onNodeCreation(nodeAddedEvent);
}
});
});
}
public fitInView() {
this.drawings.forEach(drawing => {
let splittedSvg = drawing.svg.split("\"");
let height: number = parseInt(splittedSvg[1], 10);
let width: number = parseInt(splittedSvg[3], 10);
drawing.element = {
width: width,
height: height
};
});
if ((this.nodes.length === 0) && (this.drawings.length === 0)) { return };
let minX : number, maxX : number, minY: number, maxY : number;
let borderedNodes: BorderedNode[] = [];
this.nodes.forEach(n => {
let borderedNode: BorderedNode = new BorderedNode();
borderedNode.node = n;
borderedNode.top = n.y;
borderedNode.left = n.x;
borderedNode.bottom = n.y + n.height;
borderedNode.right = n.x + n.width;
if ((n.y + n.label.y) < borderedNode.top) {
borderedNode.top = n.y + n.label.y;
};
if ((n.x + n.label.x) < borderedNode.left) {
borderedNode.left = n.x + n.label.x;
};
if ((n.y + n.label.y) > borderedNode.bottom) {
borderedNode.bottom = n.y + n.label.y;
};
if ((n.x + n.label.x) > borderedNode.right) {
borderedNode.right = n.x + n.label.x;
};
borderedNodes.push(borderedNode);
});
let nodeMinX = borderedNodes.sort((n,m) => n.left - m.left)[0];
let nodeMaxX = borderedNodes.sort((n,m) => n.right - m.right)[borderedNodes.length - 1];
let nodeMinY = borderedNodes.sort((n,m) => n.top - m.top)[0];
let nodeMaxY = borderedNodes.sort((n,m) => n.bottom - m.bottom)[borderedNodes.length - 1];
let borderedDrawings: BorderedDrawing[] = [];
this.drawings.forEach(n => {
let borderedDrawing: BorderedDrawing = new BorderedDrawing();
borderedDrawing.drawing = n;
borderedDrawing.top = n.y;
borderedDrawing.left = n.x;
borderedDrawing.bottom = n.y + n.element.height;
borderedDrawing.right = n.x + n.element.width;
borderedDrawings.push(borderedDrawing);
});
let drawingMinX = borderedDrawings.sort((n,m) => n.left - m.left)[0];
let drawingMaxX = borderedDrawings.sort((n,m) => n.right - m.right)[borderedDrawings.length - 1];
let drawingMinY = borderedDrawings.sort((n,m) => n.top - m.top)[0];
let drawingMaxY = borderedDrawings.sort((n,m) => n.bottom - m.bottom)[borderedDrawings.length - 1];
if (nodeMinX.left < drawingMinX.left) {
minX = nodeMinX.left;
} else {
minX = drawingMinX.left;
}
if (nodeMaxX.right > drawingMaxX.right) {
maxX = nodeMaxX.right;
} else {
maxX = drawingMaxX.right;
}
if (nodeMinY.top < drawingMinY.top) {
minY = nodeMinY.top;
} else {
minY = drawingMinY.top;
}
if (nodeMaxY.bottom > drawingMaxY.bottom) {
maxY = nodeMaxY.bottom;
} else {
maxY = drawingMaxY.bottom;
}
let margin: number = 20;
minX = minX - margin;
maxX = maxX + margin;
minY = minY - margin;
maxY = maxY + margin;
let windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
let windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
let widthOfAreaToShow = maxX - minX;
let heightOfAreaToShow = maxY - minY;
let widthToSceneWidthRatio = widthOfAreaToShow / windowWidth;
let heightToSceneHeightRatio = heightOfAreaToShow / windowHeight;
let scale = 1 / Math.max(widthToSceneWidthRatio, heightToSceneHeightRatio);
if (scale !== this.mapScaleService.currentScale) {
this.mapScaleService.setScale(scale);
this.project.scene_width = this.project.scene_width * scale;
this.project.scene_height = this.project.scene_height * scale;
if (heightToSceneHeightRatio < widthOfAreaToShow) {
this.scrollX = (minX * scale) - ((windowWidth - widthOfAreaToShow*scale)/2) + this.project.scene_width/2;
this.scrollY = (minY * scale) + this.project.scene_height/2;
} else {
this.scrollX = (minX * scale) + this.project.scene_width/2;
this.scrollY = (minY * scale) - ((windowHeight - heightOfAreaToShow*scale)/2) + this.project.scene_height/2;
}
} else {
this.scrollX = (minX * scale) + this.project.scene_width/2;
this.scrollY = (minY * scale) + this.project.scene_height/2;
}
this.scrollEnabled = true;
}
public centerCanvas() {
window.scrollTo(this.scrollX, this.scrollY);
this.scrollEnabled = false;
}
public centerView() {
if (this.project) {
let scrollX: number = (this.project.scene_width - document.documentElement.clientWidth) > 0 ? (this.project.scene_width - document.documentElement.clientWidth)/2 : 0;
let scrollY: number = (this.project.scene_height - document.documentElement.clientHeight) > 0 ? (this.project.scene_height - document.documentElement.clientHeight)/2 : 0;
window.scrollTo(scrollX, scrollY);
} else {
this.toasterService.error('Please wait until all components are loaded.');
}
}
public onDrawingSaved() {
this.projectMapMenuComponent.resetDrawToolChoice();
}
public set readonly(value) {
this.inReadOnlyMode = value;
if (value) {
this.tools.selection = false;
this.toolsService.selectionToolActivation(false);
} else {
this.tools.selection = true;
this.toolsService.selectionToolActivation(true);
}
}
public get readonly() {
return this.inReadOnlyMode;
}
public toggleMovingMode() {
this.tools.moving = !this.tools.moving;
this.movingEventSource.movingModeState.emit(this.tools.moving);
if (!this.readonly) {
this.tools.selection = !this.tools.moving;
this.toolsService.selectionToolActivation(this.tools.selection);
}
}
public toggleDrawLineMode() {
this.tools.draw_link = !this.tools.draw_link;
this.toolsService.drawLinkToolActivation(this.tools.draw_link);
}
public toggleShowInterfaceLabels(visible: boolean) {
this.isInterfaceLabelVisible = visible;
this.mapSettingsService.toggleShowInterfaceLabels(this.project.project_id, this.isInterfaceLabelVisible);
}
public toggleShowConsole(visible: boolean) {
this.isConsoleVisible = visible;
this.mapSettingsService.toggleLogConsole(this.isConsoleVisible);
}
public toggleShowTopologySummary(visible: boolean) {
this.isTopologySummaryVisible = visible;
this.mapSettingsService.toggleTopologySummary(this.isTopologySummaryVisible);
}
public toggleNotifications(visible: boolean) {
this.notificationsVisibility = visible;
if (this.notificationsVisibility) {
localStorage.setItem('notificationsVisibility', 'true');
} else {
localStorage.removeItem('notificationsVisibility');
}
}
public toggleLayers(visible: boolean) {
this.layersVisibility = visible;
this.mapSettingsService.toggleLayers(visible);
if (this.layersVisibility) {
localStorage.setItem('layersVisibility', 'true');
} else {
localStorage.removeItem('layersVisibility')
}
this.mapChild.applyMapSettingsChanges();
}
public toggleGrid(visible: boolean) {
this.gridVisibility = visible;
if (this.gridVisibility) {
localStorage.setItem('gridVisibility', 'true');
} else {
localStorage.removeItem('gridVisibility');
}
this.mapChild.gridVisibility = this.gridVisibility ? 1 : 0;
}
public toggleSnapToGrid(enabled: boolean) {
this.project.snap_to_grid = enabled;
}
private showMessage(msg) {
if (this.notificationsVisibility) {
if (msg.type === 'error') this.toasterService.error(msg.message);
if (msg.type === 'warning') this.toasterService.warning(msg.message);
}
}
public hideMenu() {
this.projectMapMenuComponent.resetDrawToolChoice()
this.isProjectMapMenuVisible = false;
}
public showMenu() {
this.isProjectMapMenuVisible = true;
}
zoomIn() {
this.mapScaleService.setScale(this.mapScaleService.getScale() + 0.1);
}
zoomOut() {
let currentScale = this.mapScaleService.getScale();
if ((currentScale - 0.1) > 0) {
this.mapScaleService.setScale(currentScale - 0.1);
}
}
resetZoom() {
this.mapScaleService.resetToDefault();
}
addNewProject() {
const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, {
width: '400px',
autoFocus: false
});
let instance = dialogRef.componentInstance;
instance.server = this.server;
}
saveProject() {
const dialogRef = this.dialog.open(SaveProjectDialogComponent, {
width: '400px',
autoFocus: false
});
let instance = dialogRef.componentInstance;
instance.server = this.server;
instance.project = this.project;
}
editProject() {
const dialogRef = this.dialog.open(EditProjectDialogComponent, {
width: '600px',
autoFocus: false
});
let instance = dialogRef.componentInstance;
instance.server = this.server;
instance.project = this.project;
}
importProject() {
let uuid: string = '';
const dialogRef = this.dialog.open(ImportProjectDialogComponent, {
width: '400px',
autoFocus: false
});
let instance = dialogRef.componentInstance;
instance.server = this.server;
const subscription = dialogRef.componentInstance.onImportProject.subscribe((projectId: string) => {
uuid = projectId;
});
dialogRef.afterClosed().subscribe(() => {
subscription.unsubscribe();
if (uuid) {
this.bottomSheet.open(NavigationDialogComponent);
let bottomSheetRef = this.bottomSheet._openedBottomSheetRef;
bottomSheetRef.instance.projectMessage = 'imported project';
const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => {
if (result) {
this.projectService.open(this.server, uuid).subscribe(() => {
this.router.navigate(['/server', this.server.id, 'project', uuid]);
});
}
});
}
});
}
exportProject() {
if (this.nodes.filter(node => node.node_type === 'virtualbox').length > 0) {
this.toasterService.error('Map with VirtualBox machines cannot be exported.')
} else if (this.nodes.filter(node =>
(node.status === 'started' && node.node_type==='vpcs') ||
(node.status === 'started' && node.node_type==='virtualbox') ||
(node.status === 'started' && node.node_type==='vmware')).length > 0) {
this.toasterService.error('Project with running nodes cannot be exported.')
} else {
location.assign(this.projectService.getExportPath(this.server, this.project));
}
}
public uploadImageFile(event) {
this.readImageFile(event.target);
}
private readImageFile(fileInput) {
let file: File = fileInput.files[0];
let fileReader: FileReader = new FileReader();
let imageToUpload = new Image();
fileReader.onloadend = () => {
let image = fileReader.result;
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
xlink:href=\"${image}\"/>\n</svg>`;
this.drawingService.add(this.server, this.project.project_id, -(imageToUpload.width/2), -(imageToUpload.height/2), svg).subscribe(() => {});
}
imageToUpload.onload = () => { fileReader.readAsDataURL(file) };
imageToUpload.src = window.URL.createObjectURL(file);
}
public closeProject() {
this.bottomSheet.open(ConfirmationBottomSheetComponent);
let bottomSheetRef = this.bottomSheet._openedBottomSheetRef;
bottomSheetRef.instance.message = 'Do you want to close the project?';
const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => {
if (result) {
this.projectService.close(this.server, this.project.project_id).subscribe(() => {
this.router.navigate(['/server', this.server.id, 'projects']);
});
}
});
}
public deleteProject() {
this.bottomSheet.open(ConfirmationBottomSheetComponent);
let bottomSheetRef = this.bottomSheet._openedBottomSheetRef;
bottomSheetRef.instance.message = 'Do you want to delete the project?';
const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => {
if (result) {
this.projectService.delete(this.server, this.project.project_id).subscribe(() => {
this.router.navigate(['/server', this.server.id, 'projects']);
});
}
});
}
public ngOnDestroy() {
this.drawingsDataSource.clear();
this.nodesDataSource.clear();
this.linksDataSource.clear();
if (this.projectws) {
if (this.projectws.OPEN) this.projectws.close();
}
if (this.ws) {
if (this.ws.OPEN) this.ws.close();
}
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
}
}
export class BorderedNode {
top: number;
left: number;
bottom: number;
right: number;
node: Node;
}
export class BorderedDrawing {
top: number;
left: number;
bottom: number;
right: number;
drawing: Drawing;
}